从另一个角度来分析 Laravel 框架的依赖注入功能

翻译 Summer ⋅ 于 5个月前 ⋅ 最后回复由 tiandaye 3天前 ⋅ 1498 阅读 ⋅ 原文地址
这是一篇社区协同翻译的文章,已完成翻译,更多信息请点击 协同翻译介绍

file

这个 StackOverflow回答 完美的解释了依赖注入这个概念

从根本上说,依赖注入不是让对象创建一个依赖关系,也不是让工厂对象去创建对象,而是将所需的依赖变成一个外部对象,使之成为一个"某些人的问题”

你为"某些人的问题”注入了类的依赖关系。在Laravel中,这个"某人”是 服务容器 。在Laravel中,服务容器负责通过构造函数注入类的依赖关系。

岳池 翻译于 5个月前

任何时候,你在一个控制器类中请求一个依赖,这个服务容器负责:

  1. 自动地在构造函数中检测依赖关系
  2. 如果需要构建这个依赖关系
  3. 通过构造函数创建对象形成依赖关系

来看一个非常简单的例子。

<?php
namespace App\Http\Controllers;
use App\User;
use App\Repositories\UserRepository;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
    protected $userRepository;
    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }
    public function show($id)
    {
        $user = $this->userRepository->find($id);
        return view('user.profile', ['user' => $user]);
    }
}

假如,你有一个 UserController 类需要 UserRepository 作为一个构造函数依赖。

  1. 服务容器使用 PHP反射类 来检测,事实 UserRepository 需要被优先解析。
  2. 然后,它构造 UserRepository 实例。
  3. 然后,它构造 UserController 类实例。

依赖关系是如何被解析和注入的,我被很多 Laravel 开发人员不知道这个简单而强大的技术感到迷惑。 这是一个非常强大的技术,它可以被用来解决复杂对象的依赖关系。

rayle 翻译于 5个月前

如果由于某种原因,您不希望Laravel自动构建一个对象,您还可以通过传递一个可用于创建依赖关系的回调来告诉Laravel Service Container如何构造该对象。

<?php
$container->bind('My\Service', function($container) {
  return new My\Service($container->make('My\AnotherService'));
});

您需要创建一个服务提供商来注册上述服务。

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class MyServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(\My\Service::class, function ($app) {
            return new \My\Service($app->make('My\AnotherService'));
        });
    }
}

My\Service 需要被解析的时候,负责返回一个对象的回调函数就会被调用。

<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class MyController extends Controller
{
    protected $myService;
    public function __construct(\My\Service $myService)
    {
        $this->myService = $myService;
    }
    // .. 方法
}
RyanFeng 翻译于 5个月前

真实的例子

假设你的应用需要Facebook的PHP SDK来访问Facebook的API,你的控制器就是这样的:

<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
use Facebook\Facebook;
class FacebookApiAccessController extends Controller
{
    protected $facebook;
    public function __construct(Facebook\Facebook $facebook)
    {
        $this->facebook = $facebook;
    }
    //.. action methods here
}

现在,您需要告诉Service Container如何构建 Facebook\Facebook的实例.

<?php
$container->singleton('Facebook\Facebook', function() {
  return new \Facebook\Facebook([
    'app_id' => config('services.facebook.app_id'),
    'app_secret' => config('services.facebook.app_secret'),
    'default_graph_version' => 'v2.10',
  ]);
});

注意,我已经调用了方法singleton而不是bind。 唯一的区别是用singleton注册的服务被缓存,随后的解析服务调用返回缓存的服务。

RyanFeng 翻译于 5个月前

结论

依赖注入是一种强大的技术,你可以在 Laravel 中用来简化对象的创建. 默认情况下, Laravel 的服务容器会自动的用反射去检测和解决依赖关系. 但是, 你可以指定回调来解析服务.

sane 翻译于 5个月前

查看其他 1 个版本

原文地址:https://medium.com/devcupboard/a-differe...

译文地址:https://laravel-china.org/topics/7496/an...


本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

本帖已被设为精华帖!
回复数量: 4
  • tiandaye
    5个月前

    还是看不太明白 bindsingleton 的区别。

  • xcaptain
    5个月前

    学习了,有个问题请教一下,如果一个controller依赖很多服务(比如说6个)怎么办,是在构造函数中一次性全部注册进来,还是分action单独注册

  • 王举 2
    5个月前

    @tiandaye

    A very simple proof is to test out the bevahior. Since the Laravel Application simply extends Illuminate\Container\Container, we'll use just the container (in my case I even only added the container as dependency to my composer.json) to test.

    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";

    The result is as expected:

    Bind: test vs. test2
    
    Singleton: test2 vs. test2

    Might be a dirty proof, but indeed it is one.

    All the magic lies in the Container::make method. If the binding is registered as shared (which means as singleton), the class instance is returned, otherwise a new instance every time.

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

    BTW, Container::singleton is the same as Container::bind with the third parameter set to true.
    摘自:https://stackoverflow.com/questions/25229064/laravel-difference-appbind-and-appsingleton :rabbit:

  • tiandaye
    5个月前

    @王举 3q

暂无评论~~
您需要登陆以后才能留下评论!

Composer 中国全量镜像

Top 100 扩展包

Lumen 中文文档

Laravel 速查表

Laravel 中文文档

Laravel 项目开发规范

Laravel 开发环境部署

GraphQL PHP 中文文档

社区文档撰写指南

TDD 构建 Laravel 论坛笔记

PHP PSR 标准规范

PHP 设计模式全集

Dingo API 中文文档