Bonjour,
Ce n'est pas une demande d'aide, juste une petite question au niveau de la logique de Laravel 5 :

Dans les méthodes des contrôleurs, pourquoi injecter l'interface plutôt que la classe elle-même ?

Merci à vous et bonne journée :)

3 réponses


yanis-git
Réponse acceptée

Cela t'ouvre la possibilité de changer ta Classe en aillant la garantie que si elle respecte le contrat imposé par la classe d'interface, le reste de ton code fonctionnera parfaiement.

Exemple : Tu injectes "LoggerInterface" Au quel tu as 3 signatures :
public function connect();
public function read();
public function write($value);

Tu pourras avoir indifférament une classe : FileLogger qui se "connecte" à un fichier, sait écrire dedans et lire dedans.
Tout comme tu pourras avoir une classe : DatabaseLogger qui se connecte à une DB, sais y ecrire et lire des logs.

Dans ton "controller" ou n'importe quelle classe qui reçois un objet de type LoggerInterface, tu as la garantie de savoir faire ces 3 actions. Tu entendras le souvent l'appélation : "développement par contrat".

Wizix
Auteur

Okep donc le concept est vraiment cool, merci pour ta réponse !

Une autre question du coup me vient en tête, comment fait Laravel pour savoir quelle classe implémentant mon interface il doit instancier ?

Il faut que je bind mes fonctions d'après ce que j'ai compris, donc dans mon AppServiceProvider.php je rajoute ces lignes :

public function register()
{
    $this->app->bind(
        'App\Repositories\LoggerInterface', 
        'App\Repositories\DatabaseLogger '
    );

    $this->app->bind(
        'App\Repositories\LoggerInterface', 
        'App\Repositories\FileLogger'
    );

    $this->app->bind(
        'App\Repositories\LoggerInterface', 
        'App\Repositories\DropboxLogger'
    );
}

Mais après ? Comment dire a Laravel je veux logger dans la base de donnée ou dans un fichier ?

le IOC pour Injection Object Container est la méchanique de Laravel pour compléter le concept énoncé dans mon précédent poste. Maintenant que tu as compris le concept de développement par contrat, il faut savoir qu'en pratique pour que cela fonctionne tu dois respecter une certaine logique :

<?php

class MyDependance implements MyDependanceInterface
{
    // [...]
}

class BadClass
{
    protected $myDependance;

    public function __construct()
    {
        $this->myDependance = new MyDependance();
    }
}

class GoodClass
{
    protected $myDependance;

    public function __construct(MyDependanceInterface $myDependance)
    {
        $this->myDependance = $myDependance;
    }
}

La classe GoodClass et BadClass font la même chose, à savoir "peupler" un attribut "myDependance" avec un object de type "MyDependanceInterface" (petite tips : MyDependance à en réalité 2 types à savoir MyDependanceInterface et MyDependance)

La différence majeur est que BadClass embarque une dépendance en dure, c'est à dire que dans le code de ta classe, tu instancies un objet directement. Tu n'as donc pas un code générique. Si demain tu créé une nouvelle classe avec le type MyDependanceInterface, tu devras aussi changer la class BadClass pour pouvoir l'utiliser.

Il faut donc passer les dépendances comme attribut dans ton constructeur (ou un setter). On appel ça l'injection de dépendance. à noter que j'ai pris soin de bien mettre MyDependanceInterface comme type de mon paramètre pour le rendre générique.

Laravel te propose donc 3 méthodes pour faire ça simplement :

  • bind
  • make
  • singleton

Pour le moment nous allons regarder uniquement bind et make, tu regarderas singleton sur la documentation.

make permet de créé un objet. Bind permet d'attacher une factory (comprendre un methode qui sera automatiquement appelé pour "construire" ou "instancier" ton objet) à un type.

Ainsi dans mon exemple tu peux faire :

/**
 A chaque fois que tu veux créé un objet de type MyDependanceInterface (donc par extension MyDependance), la méthode ci dessous sera appelé.
**/
$this->app->bind(MyDependanceInterface::class,function($app){
    // Si tu veux retourner un nouveau type ici, tu as juste à changer cette ligne et tout fonctionnera :) 
    return new MyDependance();
});
/**
  * Comme au dessus mais pour GoodClass, et comme tu vois, tu passes directement l'object en paramètre. 
**/
$this->app->bind(GoodClass::class,function($app){
    return new GoodClass($app->make(MyDependanceInterface::class));
});