【问题标题】:DI object methodDI对象方法
【发布时间】:2015-02-18 19:12:46
【问题描述】:

如何在一些对象方法而不是构造函数中注入依赖关系?

以下示例适用于 __constructor 注入

如何在 indexAction 中注入 DateTime 对象?

app.php

 $app['index.controller'] = $app->share(function() use ($app) {
       return new Controllers\IndexController(new \DateTime());
     });

IndexController.php

namespace Moo\Controllers;

class IndexController
{
  private $date;
  public function __construct(\DateTime $date)
  {
    $this->date = $date;
  }
  public function indexAction()
  {
   return $this->date->format('y-m-d');
  }
}

【问题讨论】:

  • 在构造函数中注入它有什么问题?
  • 如果我有 5 个方法,并且每个方法都需要不同的对象来处理怎么办。只在依赖于构造函数的方法中注入不是不合理吗?
  • 那么将所有对象放在一个数组中怎么样?或者您可以为特定对象调用的服务?那么php的类对象被设计成有一个构造函数来在运行“actions”和“dependencyInjection”之前设置属性,因为你所有的actions都是这个类的一部分,你也可以考虑为不同的对象设置不同的控制器,我猜一个带有“通用对象获取器”的服务是最好的解决方案
  • 您正在寻找方法注入。它位于 Laravel 5 mattstauffer.co/blog/laravel-5.0-method-injection。在那里你可以找到灵感,因为它是基于 Symfony 组件构建的。

标签: php symfony dependency-injection silex


【解决方案1】:

如果您的类根据调用的方法具有不同的依赖关系,那么这些方法可能应该在单独的类中定义。

对于控制器,我认为规则很简单。您的操作方法所需的依赖项应通过构造函数传递。任何带有请求的东西都应该作为方法参数。

我不确定您要注入哪种依赖项。 如果它们只是服务,那么您应该将控制器拆分为多个类。大量的构造函数参数是代码异味。很高兴您对此感到担忧,但您试图以错误的方式解决它。

如果依赖项来自请求,则应将其注入控制器方法(操作)中。 控制器方法应该接受请求并返回响应。

所有路由占位符都会自动注册为请求属性。因此,如果您的日期来自请求:

$app->get('/my/path/{date}', 'index.controller:indexAction');

它将作为请求属性提供:

public function indexAction(Request $request)
{
    $request->attributes->get('date');
}

任何请求属性都可以直接注入到控制器中:

public function indexAction($date)
{
}

这也意味着,如果您手动设置请求属性,则可以将其注入您的控制器。按名称匹配:

// somewhere in your code (event perhaps)
$request->attributes->set('myDate', new \DateTime());

// in your controller
public function indexAction(\DateTime $myDate)
{
}

最后,您可以使用 route variable converters 将请求附带的简单类型转换为更复杂的类型。

$callback = function ($post, Request $request) {
    return new Post($request->attributes->get('slug'));
};

$app->get('/blog/{id}/{slug}', 'your.controller:indexAction')
    ->convert('post', $callback);

阅读文档了解更多信息。

【讨论】:

    【解决方案2】:

    仅作记录,因为您可能不想这样做,确实可以通过使用 扩展方法:

    <?php
    
    $app['some_service'] = $app->share(function() use ($app) {
      return SomeClass($app['some_dependency']);
    });
    
    $app['some_service'] = $app->extend('some_service', function($instance, $app) {
       $instance->setSomeDependency($app['another_dependency']);
    
       return $instance;
    });
    

    话虽如此,您应该考虑 @JakubZalas 解释的内容,因为您不想在 extend 方法中调用控制器操作(您希望被调度程序调用)。

    【讨论】:

    • 每次都会注入deps,不是吗?
    • AFAIK 它不会因为服务是共享的,所以每次你想从容器中检索它时它都不会创建一个新实例,因此它不会每次都调用 extend 方法,仅在服务的创建,但我可能完全错了 :-) 我不在我的开发计算机上,所以我现在无法测试它,值得记住并回家尝试
    • 我的意思是每次初始化服务时它都会注入所有的依赖。它被初始化一次,但所有服务都被注入,即使是那些不一定会被使用的服务。这不是我们用容器能解决的问题,职责需要分离。
    • 绝对正确,即使可能不需要它们,它也会注入依赖项,我完全同意这一点“这不是我们可以用容器解决的问题,需要分离职责”。我只是想表明方法注入是可能的,但正如你指出的那样可能不是要走的路线
    猜你喜欢
    • 2012-09-04
    • 2015-01-10
    • 2021-08-29
    • 1970-01-01
    • 1970-01-01
    • 2010-11-08
    • 1970-01-01
    • 2021-10-03
    • 1970-01-01
    相关资源
    最近更新 更多