Best(s) way(s) to use Dependency injection in Laravel

Asked

Viewed 4,468 times

8

What(s) is the (s) best(s) way(s) to use Dependency injection in the Expandable? The one I was using was this:

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

From what I read, Laravel automatically resolves this due to Reflection. But searching the internet alternatives I came across this other way (linked on the route):

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

The questions are as follows: Which is the best? Which is the right one? Which one will give me the least headache with future changes? Have some more organized?

3 answers

10

Laravel came to make us write simple, objective and uncomplicated code. In the books cited 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;
}

Which is the one that Taylor preaches in books and screencasts. The most you might want to do in these cases is turn these 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 replace the implementation of these classes if you ever need to store the tables in a different format or data manager. Without having to change controller codes.

An example of an interface:

<?php namespace Angelo\Repositorios;

interface ClienteInterface {

    public function all();

}

An example of interface implementation

<?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 for the data model as well:

use Angelo\Modelos\ClienteInterface;

...

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

And bind his implementation to Eloquent so that you can easily change the implementation if needed:

$this->app->bind(
      'Angelo\Modelos\ClienteInterface',
      'Angelo\Modelos\Eloquent\Cliente'
);
  • You could add the code from 1 of the 2 repositories please? ClienteInterface and Cliente for example (just a chunk of code, showing what to insert into the interface, and what to insert into the implementing class)

  • @Patrickmaciel, I added earlier, I hope you clarify.

2

I recommend VERY MUCH the reading of this book (including, the free sample of it is precisely the chapter of addiction injection):

Laravel: From Apprentice to Craftsman

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

And if you have any doubt about how to implement this and the other ways / techniques that the book covers, I recommend you read this other book, it is based on the previous book and shows the application "in the real world"

Implementando Laravel

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

  • 3

    Could you quote and comment on some relevant excerpts from the books? The ideal is for the person to find the solution they are looking for right here, and the simple indication of an external resource does not solve this.

  • All right, thanks for the suggestion, you’re right. ^^.

2

I’ve faced the same problem in the last few months, the book Laravel: From Apprentice to Craftsman helped a lot.

Following the model described by him I made some slightly different. I put my application in a folder inside

app\

app Meuapp

I put my repositories in

app Meuapp Repository

app Meuapp Repository Clientesrepository.php

And I work with a preacher

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 do not usually use interfaces (my fault) on several occasions I needed to instantiate more than 3 of them, I also have the need of my IDE auto detect the methods that each repository has available

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

I could see how confused he was.

So I took advantage of a magical php method to solve this problem the __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 on 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 proved very good to me.

One advantage I see is being able to carry 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’ve helped

Browser other questions tagged

You are not signed in. Login or sign up in order to post.