Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 4: load class dynamically from string in database

I wish i knew how to search for this question / phrase it more appropriately. This hampered my search for prior questions; bear with me if this is a duplicate.

See the update / edits at the bottom of this post

Background / what i'm trying to do:

I have a URL that looks a lot like this:

http://myapp.com/calculate/$fileID/$calculateID

$fileID and $calculateID are keys that I use to keep track of a data set and something I call a 'calculation'. Essentially, that URL says perform $calculateID on the data in $fileID.

I go to my database (mongo) and ask for a php class name or sring or file path or what have you that matches $calculateID. For example's sake let's say the table looks like this:

 +-----+-------------------+
 | _id |     phpFile       |
 +-----+-------------------+
 |   1 | basicCalcs.php    |
 |   2 | advancedCalcs.php |
 |   3 | summaryCalcs.php  |
 +-----+-------------------+

Note: is is safe to assume that each file in the phpFile column has a common interface / set of public methods.

Example:

http://myapp.com/calculate/23/2

would go to the database, get the data from set 23 and then load up the functions in advancedCalcs.php. Once advancedCalcs.php has been loaded, a function within will receive the data. From there, a set of calculations and transformations are performed on the data.

My question

My question is what is a 'laravel 4 friendly' way to dynamically load up advancedCalcs.php and feed the data into a set of methods? Is there a way to lazy load this type of thing. Currently, i am only aware of the very unsophisticated require_once() method. I would really like to avoid this as i am convinced that laravel 4 has functionality to dynamically load an underlying class and hook it up to a common interface.

EDIT 1

Thanks to Antonio Carlos Ribeiro, i was able to make some progress.

After running dump-autoload command, my vendor/composer/autoload_classmap.php file has a few new entries that look like this:

'AnalyzeController' => $baseDir . '/app/controllers/AnalyzeController.php',
'AppName\\Calc\\CalcInterface' => $baseDir . '/app/calculators/CalcInterface.php',
'AppName\\Calc\\basicCalcs' => $baseDir . '/app/calculators/basicCalcs.php',

With code like the sample below, i can create an instance of the basicCalcs class:

$className = "AppName\\Calc\\basicCalcs";

$instance = new $className; 

var_dump($instance);

Where the basicCalcs.php file looks like this:

//PATH: /app/calculators/basicCalcs.php
<?php namespace Reporter\Calc;


class basicCalcs {


    public function sayHi(){
        echo("hello world! i am basicCalcs");
    }

};

?>

Updated question: How can i create an alias similar to the AnalyzeController entry in autoload_classmap.php rather than having to refer to basicCalcs.php with a full namespace?

like image 874
user1521764 Avatar asked Jun 23 '13 23:06

user1521764


2 Answers

Add your library folder to your composer.json autoload:

"autoload": {
    "classmap": [
        "app/commands",
        "app/controllers",
        "app/models",
        "app/extended",
        "app/calculators",  <------- This is where you put all your calculators
        "app/database/migrations",
        "app/database/seeds",
        "app/tests/TestCase.php"
    ]
},

Update your autoloaded classes:

composer dump-autoload

If you want to be sure it did worked, check if your classes are autoloaded opening the file

vendor/composer/autoload_classmap.php

To instantiate them dinamically you better have this as a table class names:

+-----+-------------------+-------------------+
| _id |     phpFile       |  namespace        |
+-----+-------------------+-------------------+
|   1 | basicCalcs        | Reporter\Calc\    |
|   2 | advancedCalcs     | Reporter\Calc\    |
|   3 | summaryCalcs      | Reporter\Calc\    |
+-----+-------------------+-------------------+

Then you just have to use it

class CalculateController extends Controller {

    public function calculate($fileID, $calculateID)
    {
        $file = phpFile::find($fileID);

        $className = $file->namespace . $file->phpFile;

        $calculator = new $className; //// <--- this thing will be autoloaded

        return $calculator->calculate( $calculateID );
    }

}

I'm assuming your calculators are all:

class basicCalcs {

    public function calculate($calculateID)
    {
        return performCalculation( $calculateID ); /// something like this
    }

}

And your router is somethink like

Route::get('/calculate/{fileID}/{calculateID}', array('as'=>'calculate', 'uses'=>'CalculateController@calculate'));
like image 166
Antonio Carlos Ribeiro Avatar answered Nov 15 '22 14:11

Antonio Carlos Ribeiro


In Laravel4 the file composer.json is responsible for this, following is an example

{
    "require": {
        "laravel/framework": "4.0.*"
    },
    "autoload": {
        "classmap": [
            "app/commands",
            "app/controllers",
            "app/models",
            "app/database/migrations",
            "app/database/seeds",
            "app/tests/TestCase.php"
        ]
    },
    "scripts": {
        "post-update-cmd": "php artisan optimize"
    },
    "config": {
        "preferred-install": "dist"
    },
    "minimum-stability": "dev"
}

Notice the autoload section where classmap is used to to tell laravel4 from which folders it should load classes and also which file should it load. For example, "app/controllers", will be used to load all classes from the app/controllers folder and "app/tests/TestCase.php" will make Laravel4 to autoload the class TestCase.php from app/tests/ folder. So, add your library folder in to the classmap section. Once you have added your folder path in autoload -> classmap section then you have to run

composer dumpautoload // or
composer dump-autoload

from the command line.

like image 33
The Alpha Avatar answered Nov 15 '22 14:11

The Alpha