Laravel: Différence App::bind et App::singleton

je suis un peu confus sur toutes les belles choses que laravel a à offrir en termes de conteneur et de façades de L'IOC. Comme je ne suis pas un programmeur expérimenté, il devient accablant d'apprendre.

je me demandais, quelle est la différence entre ces deux exemples:

  1. Une façade 'Foo' et inscrit dans le conteneur à l'aide de App::bind()

  2. une façade à "Foo" et enregistré dans le conteneur via App::singleton()

Dans mon meilleur compréhension Foo::method() sera réécrit comme $app->make['foo']->method() donc dans le premier exemple plusieurs instances du Foo la classe sera créée et dans le second exemple, puisqu'elle est liée par un App::singleton(), la même instance de Foo sera retourné chaque fois qu'une méthode sur cet objet est appelée.

je suis désolé si la réponse à cette question est trop évidente, mais je ne trouve aucune confirmation à ce sujet et nulle part ceci n'est clairement expliquer.

42
demandé sur Luuk Van Dongen 2014-08-10 17:28:58

3 réponses

C'est exactement comme ça.

une preuve très simple est de tester le bevahior. Depuis L'Application Laravel étend simplement Illuminate\Container\Container, nous n'utiliserons que le container (dans mon cas j'ai même ajouté le container comme dépendance à mon compositeur.json) pour tester.

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

class FirstClass
{
    public $value;
}

class SecondClass
{
    public $value;
}

// Test bind()
$container = new Illuminate\Container\Container();

$container->bind('FirstClass');

$instance = $container->make('FirstClass');
$instance->value = 'test';

$instance2 = $container->make('FirstClass');
$instance2->value = 'test2';

echo "Bind: $instance->value vs. $instance2->value\n";

// Test singleton()
$container->singleton('SecondClass');

$instance = $container->make('SecondClass');
$instance->value = 'test';

$instance2 = $container->make('SecondClass');
$instance2->value = 'test2'; // <--- also changes $instance->value

echo "Singleton: $instance->value vs. $instance2->value\n";

Le résultat est comme prévu:

Bind: test vs. test2

Singleton: test2 vs. test2

C'est peut-être une preuve sale, mais en fait c'en est une.

Toute la magie réside dans le Container::make méthode. Si la liaison est enregistrée comme partagée (ce qui signifie Comme singleton), l'instance de classe est retournée, sinon une nouvelle instance à chaque fois.

Source: https://github.com/laravel/framework/blob/4.2/src/Illuminate/Container/Container.php#L442

BTW,Container::singleton est le même que Container::bind avec le troisième paramètre défini à true.

46
répondu niclasleonbock 2015-05-08 16:27:59

les façades fonctionnent comme un singleton, même si la reliure sous-jacente n'est pas un singleton.

disons que vous avez:

$app->bind('foo', 'FooConcrete'); // not a singleton

et:

class Foo extends \Illuminate\Support\Facades\Facade {
    protected static function getFacadeAccessor() { return 'foo'; }
}

Ensuite, cela permettra de créer 2 instances de FooConcrete, comme d'habitude:

app('foo');
app('foo');

mais cela ne créera qu'une seule instance de FooConcrete et le réutiliser:

Foo::someMethod();
Foo::someMethod();

C'est parce que resolveFacadeInstance() stocke les instances résolues.



Il y a un exception cependant. La plupart du temps, la définition de getFacadeAccessor() retourne un chaîne, comme montré ci-dessus, mais il peut aussi retourner un objet. Exemple de l' Schema Façade:

protected static function getFacadeAccessor() {
    return static::$app['db']->connection()->getSchemaBuilder();
}

dans un tel cas,resolveFacadeInstance() ne stocke pas l'instance.

si getFacadeAccessor() retourne une nouvelle instance, chaque appel à la Façade crée une nouvelle instance.

14
répondu Gras Double 2016-08-15 14:53:34

mais quelque part J'ai lu que Laravel traite les classes appelées via façades toujours comme des Singleton?

J'ai donc rencontré ce problème:

j'ai un démo classe normalement lié par

$this->app->bind('demo', function() { return new Demo(); }

un montage une façade

protected static function getFacadeAccessor() { return 'demo'; }

La classe elle-même ressemble à ceci

class Demo 
    {

        private $value1;        
        private $value2;        

        public function setVal1($value)
        {
            $this->value1 = $value;
        }

        public function setVal2($value)
        {
            $this->value2 = $value;
        }

        public function getVals()
        {
            return 'Val 1: ' . $this->value1 . ' Val 2: ' . $this->value2;
        }   

    }

Vous m'avez dit que si je voulais utiliser une façade sur cette classe, instancier un objet de la classe, puis d'appeler la méthode sur cette objet.

Crosse je l'ai testé et trouvé cela très étrange (au moins pour moi) comportement:

Si je fais

Demo::setVal1('13654');
et
Demo::setVal2('random string')

Je ne devrais pas pouvoir utiliser Demo::getVals() pour récupérer les valeurs que je viens de créer, devrais-je? Depuis chaque fois qu'une méthode de façade est utilisée, un nouvel objet sera instancié et comment un objet peut-il récupérer les propriétés d'un autre objet? Il devrait y avoir trois instances différentes mais je suis quand même capable de récupérer les propriétés de ces d'autres instances...

2
répondu Luuk Van Dongen 2017-11-13 07:48:12