【问题标题】:How not to use the container within Laravel controller method?如何不在 Laravel 控制器方法中使用容器?
【发布时间】:2018-03-29 23:53:14
【问题描述】:

我已经开始关注 Laravel,但我对项目中的依赖关系管理有些疑问。假设我有依赖 DependencyA 使用 DependencyB 使用 DependencyC:

<?php 

namespace Some\NamespaceOf;

use Illuminate\Container\Container;

class DependencyA {

   public function someMethod(DependencyB $b, Container $container) {
      $container->call([$b, 'doSomething']);
   }

}

class DependencyB {
   public function doSomething(DependencyC $c) {
      // ... do something with $c
   } 
}

然后,在我的控制器中:

<?php

namespace App\Http\Controllers;

use App\User;
use App\Http\Controllers\Controller;
use Some\NamespaceOf\DependencyA;
use Illuminate\Container\Container;

class SomeController extends Controller
{
    // ...

    /**
     * @param DependencyA  $a
     * @return Response
     */
    public function controllerMethod(DependencyA $a, Container $container)
    {
        $container->call([$a, 'someMethod']);
        // ...
    }

    // ...
}

我不喜欢在我的组件之间反复传递控制器。

在这种情况下,最好的重构是什么?将控制器重写为:

<?php

namespace App\Http\Controllers;

use App\User;
use App\Http\Controllers\Controller;
use Some\NamespaceOf\DependencyA;
use Some\NamespaceOf\DependencyB;
use Illuminate\Container\Container;

class SomeController extends Controller
{
    // ...

    /**
     * @param DependencyA  $a
     * @return Response
     */
    public function controllerMethod(DependencyA $a, DependencyB $b)
    {
        $a->someMethod($b);
        // ...
    }

    // ...
}

这还不够,因为DependencyA 还需要容器在调用$b-&gt;doSomething() 之前查找DependencyC,然后将其传递给$c。我也必须编辑DependencyA,但是我怎样才能将DependencyC 注入它呢?

当这些依赖形成时应该怎么做? 在组件中使用容器让我觉得 IoC 原则被违反了。

感谢您的关注。

【问题讨论】:

  • 尽量不要将你的依赖作为参数发送。
  • 我认为这是唯一的方法,不是吗。但后来我觉得我失去了“语义”,但也许这只是我做的一个错误的结论,我不必关注......

标签: php laravel dependency-injection inversion-of-control


【解决方案1】:

如果我理解你的话;这可能会有所帮助:

AppServiceProvider

namespace App\Providers;

use App\Dependencies\DependencyA;
use App\Dependencies\DependencyB;
use App\Dependencies\DependencyC;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(DependencyB::class, function ($app) {
            return new DependencyB(new DependencyC());
        });

        $this->app->bind(DependencyA::class, function ($app) {
            return new DependencyA($app->make(DependencyB::class));
        });
    }
}

依赖A

namespace App\Dependencies;

class DependencyA
{
    private $dependencyB;

    public function __construct(DependencyB $dependencyB)
    {
        $this->dependencyB = $dependencyB;
    }

    public function useMe()
    {
        return $this->dependencyB->useMe();
    }
}

依赖B

namespace App\Dependencies;

class DependencyB
{
    private $dependencyC;

    public function __construct(DependencyC $dependencyC)
    {
        $this->dependencyC = $dependencyC;
    }

    public function useMe()
    {
        return $this->dependencyC->useMe();
    }
}

依赖C

namespace App\Dependencies;

class DependencyC
{
    public function useMe()
    {
        echo "Used me.";
    }
}

现在,在我们的控制器中,如果我们使用 DependencyA 作为唯一的依赖项; DependencyA 类和 DependencyB 类的依赖将分别解析。

DependantController

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Dependencies\DependencyA;

class DependantController extends Controller
{
    private $dependencyA;

    public function __construct(DependencyA $dependencyA)
    {
        $this->dependencyA = $dependencyA;
    }

    public function index()
    {
        $this->dependencyA->useMe();
    }
}

web.php

Route::get('dependant', 'DependantController@index');

现在,如果您访问例如http://127.0.0.1:8000/dependant 您将看到 Used me. 作为此实现的结果。

【讨论】:

  • 所以基本上规则不是创建带有类型提示参数的方法,而是类型提示构造函数......对吗?
  • 这与您输入提示的位置无关。如果您提供 DepA 作为索引操作的参数,Laravel 的行为将相同。但是依赖的上下文将仅限于索引。根据您的架构,这甚至可能会提高您的应用程序性能。我解决上述问题的方法称为自动注入。但是,如果需要更改对运行时的依赖,而条件绑定不是解决方案;传递手动解析的依赖项可能是一种选择。让我们将此解决方案命名为一个好习惯。你知道,没有灵丹妙药。
猜你喜欢
  • 2019-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-27
  • 2016-03-18
  • 1970-01-01
  • 2014-11-29
相关资源
最近更新 更多