Php 在laravel facades上使用依赖项注入

Php 在laravel facades上使用依赖项注入,php,laravel,dependency-injection,laravel-facade,Php,Laravel,Dependency Injection,Laravel Facade,我读过很多资料,暗示laravel facade的最终存在是为了方便,这些类应该允许松散耦合。甚至解释如何做到这一点。看来我不是唯一一个这样做的人 将成为 use Illuminate\Routing\Redirector as Redirect; class Example class { protected $redirect; public function __constructor(Redirect $redirect) { $this-&g

我读过很多资料,暗示laravel facade的最终存在是为了方便,这些类应该允许松散耦合。甚至解释如何做到这一点。看来我不是唯一一个这样做的人

将成为

use Illuminate\Routing\Redirector as Redirect;

class Example class
{
    protected $redirect;

    public function __constructor(Redirect $redirect)
    {
        $this->redirect = $redirect
    }

    public function example()
    {
         return $this->redirect->route("route.name");
    }
}
这很好,只是我开始发现一些构造函数和方法开始使用4+参数

由于Laravel IoC似乎只注入类构造函数和某些方法(控制器),即使我有相当精简的函数和类,我发现类的构造函数中挤满了所需的类,然后注入所需的方法

现在我发现,如果我继续使用这种方法,我将需要自己的IoC容器,如果我使用像laravel这样的框架,这感觉就像是重新发明轮子

例如,我使用服务来控制业务/视图逻辑,而不是处理它们的控制器——它们只是路由视图。因此,控制器将首先获取其相应的
服务
,然后获取其url中的
参数
。一个服务函数还需要检查表单中的值,因此我需要
Request
Validator
。就像那样,我有四个参数

// MyServiceInterface is binded using the laravel container
use Interfaces\MyServiceInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Factory as Validator;

...

public function exampleController(MyServiceInterface $my_service, Request $request, Validator $validator, $user_id) 
{ 
    // Call some method in the service to do complex validation
    $validation = $my_service->doValidation($request, $validator);

    // Also return the view information
    $viewinfo = $my_service->getViewInfo($user_id);

    if ($validation === 'ok') {
        return view("some_view", ['view_info'=>$viewinfo]);
    } else {
        return view("another_view", ['view_info'=>$viewinfo]);
    }
}
这是一个单一的例子。实际上,我的许多构造函数已经注入了多个类(模型、服务、参数、外观)。我已经开始将构造函数注入(如果适用)卸载到方法注入,并让调用这些方法的类使用它们的构造函数来注入依赖项

有人告诉我,根据经验,一个方法或类构造函数的四个以上参数是不好的做法/代码味道。然而,我看不出如果你选择注射拉维立面的道路,你怎么能真正避免这种情况

我把这个想法弄错了吗?我的类/功能是否不够精简?我是否遗漏了laravels容器的要点,或者我真的需要考虑创建自己的IoC容器?其他人似乎暗示laravel容器能够消除我的问题


这就是说,在这个问题上似乎没有一个明确的共识…

类方法构成了Laravel(中间件、控制器等)路由机制的一部分-它们不都需要注入构造函数中。这可能有助于保持构造函数的苗条,即使我不熟悉任何四参数限制的经验法则;大概是因为需要四个以上的参数并不少见

// MyServiceInterface is binded using the laravel container
use Interfaces\MyServiceInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Factory as Validator;

...

public function exampleController(MyServiceInterface $my_service, Request $request, Validator $validator, $user_id) 
{ 
    // Call some method in the service to do complex validation
    $validation = $my_service->doValidation($request, $validator);

    // Also return the view information
    $viewinfo = $my_service->getViewInfo($user_id);

    if ($validation === 'ok') {
        return view("some_view", ['view_info'=>$viewinfo]);
    } else {
        return view("another_view", ['view_info'=>$viewinfo]);
    }
}
在您的示例中,您可以在构造函数中插入
请求
验证程序
服务作为折衷方案,因为它们通常由多个方法使用


至于建立共识,Laravel必须更加固执己见,以使应用程序足够相似,从而使用“一刀切”的方法。不过,一个更简单的说法是,我认为在未来的版本中,facades会像渡渡鸟一样出现。

与其说是一个答案,不如说是一个值得思考的问题,在与我的同事交谈后,他们提出了一些非常有效的观点

  • 如果laravel的内部结构在不同版本之间发生了更改(这显然在过去发生过),注入解析的facade类路径将破坏升级过程中的所有内容,而使用默认facades和helper方法(如果不是完全的话)可以避免此问题

  • 尽管解耦代码通常是一件好事,但注入这些解析的facade类路径的开销会使类变得杂乱无章——对于接管项目的开发人员来说,更多的时间被花在尝试遵循代码上,而这些时间可以更好地用于修复bug或测试。新开发人员必须记住哪些注入的类是开发人员,哪些是larvels。不熟悉laravel的开发人员必须花时间查找API。最终,引入bug或缺少关键功能的可能性会增加

  • 由于Facade已经是可测试的,所以开发速度变慢了,可测试性也没有得到真正的提高。快速开发首先是使用laravel的一个优点。时间总是一种限制

  • 大多数其他项目使用laravel立面。大多数有使用laravel经验的人都使用正面。创建一个不遵循以前项目的现有趋势的项目通常会减慢速度。未来缺乏经验(或懒惰!)的开发人员可能会忽略facade注入,项目可能会以混合格式结束。(即使是代码审阅者也是人)


  • 好吧,你的想法和顾虑是正确的,我也有。 Facade有一些好处(我通常不使用它们),但是如果你使用just,我建议只在控制器中使用它们,因为控制器至少对我来说只是入口和出口

    对于您给出的示例,我将展示我通常如何处理它:

    // MyServiceInterface is binded using the laravel container
    use Interfaces\MyServiceInterface;
    use Illuminate\Http\Request;
    use Illuminate\Validation\Factory as Validator;
    
    ...
    class ExampleController {
    
        protected $request;
    
        public function __constructor(Request $request) {
            // Do this if all/most your methods need the Request
            $this->request = $request;
        }
    
        public function exampleController(MyServiceInterface $my_service, Validator $validator, $user_id) 
        { 
            // I do my validation inside the service I use,
            // the controller for me is just a funnel for sending the data
            // and returning response
    
            //now I call the service, that handle the "business"
            //he makes validation and fails if data is not valid
            //or continues to return the result
    
            try {
                $viewinfo = $my_service->getViewInfo($user_id);
                return view("some_view", ['view_info'=>$viewinfo]);
            } catch (ValidationException $ex) {
                return view("another_view", ['view_info'=>$viewinfo]);
            }
        }
    }
    
    
    
    class MyService implements MyServiceInterface {
    
        protected $validator;
    
        public function __constructor(Validator $validator) {
            $this->validator = $validator;
        }
    
        public function getViewInfo($user_id, $data) 
        { 
    
            $this->validator->validate($data, $rules);
            if  ($this->validator->fails()) {
                //this is not the exact syntax, but the idea is to throw an exception
                //with the errors inside
                throw new ValidationException($this->validator);
            }
    
            echo "doing stuff here with $data";
            return "magic";
        }
    }
    
    只需记住,将代码分解为各个小部分,每个部分都有自己的责任。 当您正确地中断代码时,在大多数情况下,您将不会有那么多构造函数参数,代码将很容易进行测试和模拟


    最后一点,如果您正在构建一个小型应用程序,甚至是一个大型应用程序中的一个页面,例如“联系人页面”和“联系人页面提交”,那么您肯定可以使用facades在控制器中完成所有操作,这完全取决于项目的复杂程度。

    我喜欢laravel,因为它的建筑很漂亮。现在,从我的方法来看,我不会将所有的立面都注入控制器方法,只是为什么?仅在控制器中注入重定向外观是错误的做法,因为它可能需要在其他控制器中注入重定向外观。主要是那些最常用的东西应该被声明,而对于那些使用了一些或者只有使用了它们的人来说,它的最佳实践是通过方法注入它们,就像你在顶部声明的时候一样
    /* @var $email_services App\Contracts\EmailServicesContract
    $email_services = app('App\Contracts\EmailServicesContract');
    
       protected function dispatch($job)
        {
            return app('Illuminate\Contracts\Bus\Dispatcher')->dispatch($job);
        }
    
    Class PageController
    {
    
        public function __construct(
            Request $request,
            ClientRepositoryInterface $clientrepo,
            StaffRepositortInterface $staffRepo
            )
        {
    
         $this->clientRepository = $clientRepo;
         //etc etc
    
        }
    
        public function aboutAction()
        {
            $teamMembers = $this->staffRepository->getAll();
            //render view
        }
    
        public function allClientsAction()
        {
            $clients = $this->clientRepository->getAll();
            //render view
        }
    
        public function addClientAction(Request $request, Validator $validator)
        {
            $this->clientRepository->createFromArray($request->all() $validator);
            //do stuff
        }
    }
    
    //think of a better name!
    Class ClientCreator 
    {
        public function __construct(Request $request, validator $validator){}
    
        public function getClient(){}
        public function isValid(){}
        public function getErrors(){}
    }
    
    public function addClientAction(ClientCreator $creator)
    { 
         if($creator->isValid()){
             $this->clientRepository->add($creator->getClient());
         }else{
             //handle errors
         }
    }