How to use Laravel IOC container outside of Laravel for method injection

Narration: I can't get method injection using Laravel container installed using composer ( https://packagist.org/packages/illuminate/container ). Injection only works when used in an object constructor. For example:

class SomeClass {
    function __construct(InjectedClassWorksHere $obj) {}
    function someFunction(InjectedClassFailsHere $obj) {}
}

      

Long story: I've been looking to re-factor a large Laravel project, but due to business pressure, I can't invest in the time I would like. In an effort to avoid throwing out the "baby with bath water", I use custom Laravel components to improve the elegance of the code I develop on the old branch. One of my favorite new techniques I've chosen when evaluating Laravel is the concept of dependency injection. I was glad to know later that I could use this EXTERNAL Laravel project. Now it works for me and everything is fine, except that the dev version of the container found on the net does not support the injection method.

Anyone else could get the container to work and do method injection outside of the Laravel project?

My approach so far ...

composer.json

"illuminate/support": "5.0.*@dev",
"illuminate/container": "5.0.*@dev",

      

Application bootstrap code:

use Illuminate\Container\Container;

$container = new Container();
$container->bind('app', self::$container); //not sure if this is necessary

$dispatcher = $container->make('MyCustomDispatcher');
$dispatcher->call('some URL params to find controller');

      

With the above, I can inject into the constructors of my controllers, but not the methods of my methods. What am I missing?

Full source ... (C: \ workspace \ LMS> php cmd \ test_container.php)

<?php

// This sets up my include path and calls the composer autoloader
require_once "bare_init.php";

use Illuminate\Container\Container;
use Illuminate\Support\ClassLoader;
use Illuminate\Support\Facades\Facade;

// Get a reference to the root of the includes directory
$basePath = dirname(dirname(__FILE__));

ClassLoader::register();
ClassLoader::addDirectories([
    $basePath
]);

$container = new Container();
$container->bind('app', $container);
$container->bind('path.base', $basePath);

class One {
    public $two;
    public $say = 'hi';
    function __construct(Two $two) {
        $this->two = $two;
    }
}

Class Two {
    public $some = 'thing';
    public function doStuff(One $one) {
        return $one->say;
    }
}

/* @var $one One */
$one = $container->make(One);
var_dump($one);
print $one->two->doStuff();

      

When I run the above I get ...

C:\workspace\LMS>php cmd\test_container.php
object(One)#9 (2) {
  ["two"]=>
  object(Two)#11 (1) {
    ["some"]=>
    string(5) "thing"
  }
  ["say"]=>
  string(2) "hi"
}

PHP Catchable fatal error:  Argument 1 passed to Two::doStuff() must be an instance of One, none 
given, called in C:\workspace\LMS\cmd\test_container.php on line 41
and defined in C:\workspace\LMS\cmd\test_container.php on line 33

Catchable fatal error: Argument 1 passed to Two::doStuff() must be an instance of One, none  
given, called in C:\workspace\LMS\cmd\test_container.php on line 41 and
defined in C:\workspace\LMS\cmd\test_container.php on line 33

      

Or, a simpler example illustrating injection working in a constructor, but not a method ...

class One {
    function __construct(Two $two) {}
    public function doStuff(Three $three) {}
}

class Two {}
class Three {}

$one = $container->make(One); // totally fine. Injection works
$one->doStuff(); // Throws Exception. (sad trombone)

      

+3


source to share


1 answer


Just pass an instance One

to your call Two

:

$one = $container->make('One');
var_dump($one);
print $one->two->doStuff($one);

      

returns ...

object(One)#8 (2) {
  ["two"]=>
  object(Two)#10 (1) {
    ["some"]=>
    string(5) "thing"
  }
  ["say"]=>
  string(2) "hi"
}
hi

      


Update: Corrected answer after further research

As mentioned below, in Laravel 5.0, method injection is only available on routes and controllers. This way you can bring them into your project and also get a little more Laravel-y in the process. Here's how:

Bcomposer.json

must be added to illuminate/routing

and illuminate/events

:

{
    "require-dev": {
        "illuminate/contracts": "5.0.*@dev",
        "illuminate/support": "5.0.*@dev",
        "illuminate/container": "5.0.*@dev",
        "illuminate/routing": "5.0.*@dev",
        "illuminate/events": "5.0.*@dev"
    },
    "autoload": {
        "psr-4": {
            "App\\": "app/"
        }
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}

      

Inrouting.php

configure Laravel Routing and Control Services:

/**
 * routing.php
 *
 * Sets up Laravel routing and controllers
 *
 * adapted from http://www.gufran.me/post/laravel-components
 * and http://www.gufran.me/post/laravel-illuminate-router-package-in-your-application
 */
$basePath = str_finish(dirname(__FILE__), '/app/');

$controllersDirectory = $basePath . 'Controllers';

// Register directories into the autoloader
Illuminate\Support\ClassLoader::register();
Illuminate\Support\ClassLoader::addDirectories($controllersDirectory);

// Instantiate the container
$app = new Illuminate\Container\Container();
$app['env'] = 'production';

$app->bind('app', $app); // optional
$app->bind('path.base', $basePath); // optional

// Register service providers
with (new Illuminate\Events\EventServiceProvider($app))->register();
with (new Illuminate\Routing\RoutingServiceProvider($app))->register();

require $basePath . 'routes.php';

$request = Illuminate\Http\Request::createFromGlobals();
$response = $app['router']->dispatch($request);
$response->send();

      



In,Controllers/One.php

create the class as a controller, so we can use the L5 method:

/**
 * Controllers/One.php
 */
Class One extends Illuminate\Routing\Controller {
    public $some = 'thingOne';
    public $two;
    public $three;

    function __construct(Two $two) {
        $this->two = $two;
        echo('<pre>');
        var_dump ($two);
        echo ($two->doStuffWithTwo().'<br><br>');
    } 

    public function doStuff(Three $three) {
        var_dump ($three);
        return ($three->doStuffWithThree());
    }
}

      

Inroutes.php

define our test route:

$app['router']->get('/', 'One@dostuff');

      

Finally, inindex.php

load everything and define our classes to test dependency injection:

/**
 * index.php
 */

// turn on error reporting
ini_set('display_errors',1);  
error_reporting(E_ALL);

require 'vendor/autoload.php';
require 'routing.php';

// the classes we wish to inject
Class Two {
    public $some = 'thing Two';
    public function doStuffWithTwo() {
        return ('Doing stuff with Two');
    }
}
Class Three {
    public $some = 'thing Three';
    public function doStuffWithThree() {
        return ('Doing stuff with Three');
    }
}

      

Click index.php and you get this:

object(Two)#40 (1) {
  ["some"]=>
  string(9) "thing Two"
}
Doing stuff with Two

object(Three)#41 (1) {
  ["some"]=>
  string(11) "thing Three"
}
Doing stuff with Three

      

Some notes ...

  • You don't need to explicitly bind the classes. Laravel will take care of this.
  • You now have the added bonus of Laravel routing and controllers.
  • This works because now we don't need to call $one->doStuff();

    , with an empty parameter that throws an exception (since it doStuff

    expects an instance). Instead, the router calls doStuff

    and resolves the IoC container to us.
  • Subscribe to http://www.gufran.me/post/laravel-illuminate-router-package-in-your-application where you go through all of this and this appears to be the inspiration for the Matt Stauffer project mentioned higher. Both of them are very cool and noteworthy.
+5


source







All Articles