Best way (s) to use Dependency injection in Laravel

7

What is the best way to use Injection of dependence in laravel? The one I was using was this:

public function __construct(Cliente $clientes, Telefone $telefones){
    $this->clientes = $clientes;
    $this->telefones = $telefones;
}

From what I've read, laravel solves this automatically because of Reflection. However searching on the internet alternatives I came across this other way (linked in route):

App::bind('ClienteController', function($app) {
    $controller = new ClienteController(
        new Cliente,
        new Telefone
    );
    return $controller;
});

The issues are: What is the best? Which one is correct? Which will give me less headache with future changes? Are there any more organized?

    
asked by anonymous 30.01.2014 / 13:41

3 answers

9

Laravel came to make us write simple code, objective and uncomplicated. In the books quoted by Daniel, of which the second I am the translator, you will see the first form:

public function __construct(Cliente $clientes, Telefone $telefones){
    $this->clientes = $clientes;
    $this->telefones = $telefones;
}

That's the one that Taylor preaches in books and screencasts. The most you may want to do in these cases is to turn those classes into repositories, creating interfaces for them:

public function __construct(ClienteInterface $clientes, TelefoneInterface $telefones){
    $this->clientes = $clientes;
    $this->telefones = $telefones;
}

And create a Service Provider to inform Laravel which implementation of these interfaces you will want to use:

<?php namespace Angelo\Repositorios;

use Illuminate\Support\ServiceProvider;

class BackendServiceProvider extends ServiceProvider {

    public function register()
    {
        $this->app->bind(
            'Angelo\Repositorios\ClienteInterface',
            'Angelo\Repositorios\Cliente'
        );

        $this->app->bind(
            'Angelo\Repositorios\TelefoneInterface',
            'Angelo\Repositorios\Telefone'
        );            
    }

}

This also ensures that you can easily override the implementation of these classes if you ever need to store the tables in a different data format or manager. Without having to change driver codes.

An example interface:

<?php namespace Angelo\Repositorios;

interface ClienteInterface {

    public function all();

}

An example implementation of the interface

<?php namespace Angelo\Repositorios;

use Angelo\Modelos\Eloquent\Cliente as ClienteEloquent;

class Cliente implements ClienteInterface {

    private $model;

    public function __construct(ClienteEloquent $model)
    {
        $this->model = $model
    }

    public function all() 
    {
        return $this->model->all();
    }

}

And the data model:

<?php namespace Angelo\Modelos\Eloquent;

use Eloquent;

class Cliente implements Eloquent {

    private $table = 'clientes';

}

But the ideal is to use an interface to the data model too:

use Angelo\Modelos\ClienteInterface;

...

public function __construct(ClienteInterface $model)
{
    $this->model = $model
}

And bind your implementation to Eloquent so you can easily change the implementation if necessary:

$this->app->bind(
      'Angelo\Modelos\ClienteInterface',
      'Angelo\Modelos\Eloquent\Cliente'
);
    
30.01.2014 / 14:57
2

I recommend VERY reading this book (including the free sample of it is just the dependency injection chapter):

Laravel: From Apprentice to Craftsman

In this book you should learn the most correct way to do this, since it was written by Laravel creator .

And if you doubt how to implement this and the other forms / techniques that the book addresses, I recommend you read this other book, it is based on the previous book and shows the "real world" application

Implementing Laravel

After reading these 2 books, I drastically changed the way I created my applications in Laravel.

    
30.01.2014 / 14:43
2

I faced the same problem in recent months, the book > Laravel: From Apprentice to Craftsman helped a lot.

Following the model he described, I did something a little different. I put my application in a folder inside

  

app \

     

app \ Meuapp

I put my repositories in

  

app \ Myapp \ Repository

     

app \ Myapp \ Repository \ ClientsRepository.php

And I work with a provider

  

app / Meuapp / AppProvider.php

<?php namespace Meuapp\Providers;
use Illuminate\Support\ServiceProvider;

class AppProvider extends ServiceProvider {

public function register(){
    $this->app->singleton('RepoClientes', '\Meuapp\Repository\ClientesRepository');
 }
}

?>

So I can easily instantiate my repository.

public function __construct(){
  $this->clientes = App::make('RepoClientes');
}

In my case I have many repositories and I do not usually use interfaces (my fault) on several occasions I needed to instantiate more than 3 of them, I also have the need for my IDE to auto detect the methods that each repository has available

/**
 * @var \Meuapp\Repository\ClientesRepository
 */
public $clientes;

I can see how confused it was.

So I took a magic php method to solve this problem __ get ()

As all my controllers extend BaseController I added the following method to it:

public function __get($var)
{
    switch ($var):
        case 'clientes':
            $this->clientes = App::make('RepoClientes');
            return $this->clientes;
            break;
            endswitch;
    }

And about the auto complete of my IDE I add the following line:

/**
 * Class BaseController
 *
 * @property \Meuapp\Repository\ClientesRepository $clientes
 */
 class BaseController extends Controller{
  public function __get($var){}
}

This may not be the best option, but it has been very good for me.

One advantage I see is being able to load exclusively what my method will need with ease and flexibility.

Remembering that for this to work you need to edit 2 files:

  

composer.json

"autoload": {
    "psr-0": {
        "Meuapp": "app/",
    }
},
  

app / config / app.php

'providers' => array(
...
'Meuapp\Providers\AppProvider',
 )

I hope I have helped

    
03.02.2014 / 16:56