Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I do constructor-injected dependencies in Laravel 4?

Tags:

php

laravel

My team likes the idea of constructor-injected dependencies because it makes deps very clear when looking at a class. With the use of the facades, I'm aware they can be mocked and swapped, but one would have to examine every line of a class to figure out what it depends on! I discovered that I could find the true class behind the facade with, for instance, Form::getFacadeRoot().

The controller code that I've ended up with is:

use Illuminate\Html\FormBuilder as Form;
use Illuminate\Validation\Factory as Validator;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage as Session;
use Illuminate\Http\Request as Input;
use Illuminate\Routing\Redirector as Redirect;
use Illuminate\View\Environment as View;

class HomeController extends BaseController {

    protected $form;
    protected $validator;
    protected $session;
    protected $input;
    protected $redirect;
    protected $view;

    protected $layout = 'layouts.master';

    protected $validationRules = array(
        'name'  => array('required', 'min:3'),
        'email' => array('required', 'regex:/^.+@.+\..{2,4}$/')
    );

    public function __construct(Form $form, Validator $validator, Session $session,
        Input $input, Redirector $redirect, View $view
    ) {
        $this->form      = $form;
        $this->validator = $validator;
        $this->session   = $session;
        $this->input     = $input;
        $this->redirect  = $redirect;
        $this->view      = $view;
    }

        ...
}

When my test does $this->client->request('Get', '/');, it errors out:

Illuminate\Container\BindingResolutionException: Unresolvable dependency resolving [Parameter #2 [ <required> $csrfToken ]].

Am I on even close to the right track here? I'm sort of making this up as I go along because I don't see much discussion on this issue. Feel free to comment on my reason for even trying; I could be sold on facades, yet.

Thanks !

like image 756
Adam Avatar asked Dec 04 '25 07:12

Adam


2 Answers

You need to map the class dependencies to a class using Laravel's IoC container. This can be done using the App facade. So in your example above with the constructor

public function __construct(Form $form, Validator $validator, Session $session, Input $input, Redirector $redirect, View $view)

You would create a binding that would look something along the lines of:

App::bind('Form', function(){
    return new Illuminate\Html\FormBuilder()
});

Taylor Otwell recommends using Interfaces as contracts for the class dependencies. So Ideally your finished code would look something like that below (I've slimed it down a bit for the example).

For your controller:

use Namespace\For\FormInterface;

class HomeController extends BaseController {

    public function __construct(FormInterface $form)
    {
        $this->form = $form;
    }

    public function myHomePage()
    {
        $this->form->myFormFunction()
    }
}

For the interface:

namespace Namespace\For;

interface FormInterface(){

    public function myFormFunction()
}

The class to be injected:

use Namespace\For\FormInterface;

class MyFormClass implements FormInterface{

    public function myFormFunction()
    {
        // Do some stuff here
    }
}

And then finally you create the binding that brings it all together:

App::bind('Namespace\For\FormInterface', function()
{
    return new MyFormClass();
});

What's happening here is every time Laravel sees an instance of FormInterface type hinted in a controller if creates a new myFormFunction() and passes it in as the param. By using interfaces it gives your class dependencies a contract to follow to ensure that they can be easily swapped without causing errors. So say your team later develops a new and improved form class you would simply update your binding like so:

App::bind('Namespace\For\FormInterface', function()
{
    return new  MyNewFormClass();
});

I would highly recommend looking into Service Providers as they provide an excellent way to manage packages and IoC bindings. A good article on Service Providers can be found here:

http://fideloper.com/laravel-4-where-to-put-bindings

And you can read more about Laravel's IoC container here:

http://laravel.com/docs/ioc

Furthermore if you can get your hands on a copy of the book From Apprentice to Artisan. Advanced Application Architecture With Laravel 4, by Taylor Otwell I would highly recommend a read. Its easy to follow and really goes into detail about managing dependency injection.

Hope that helps.

like image 166
ArranJacques Avatar answered Dec 06 '25 19:12

ArranJacques


I think you're going to sacrifice quite a bit on readability, if you choose this route.

Ultimately, dependency injection is just a pattern to allow for testability. The facades are easily testable without injection, so I don't see much value in doing this...

like image 41
JeffreyWay Avatar answered Dec 06 '25 19:12

JeffreyWay