在laravel外观上使用依赖注入

2020/11/30 17:22 · php ·  · 0评论

我已经阅读了许多资料,暗示为方便起见,laravel Facade最终存在了,应该注入这些类以允许松散耦合。甚至Taylor Otwell都有帖子解释如何执行此操作。看来我并不是唯一一个对此感到奇怪的人

use Redirect;

class Example class
{
    public function example()
    {
         return Redirect::route("route.name");
    }
}

会成为

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

很好,除了我开始发现某些构造函数和方法开始采用四个以上的参数。

由于Laravel IoC似乎只注入到类构造函数和某些方法(控制器)中,所以即使我具有相当精简的函数和类,我也发现这些类的构造函数已被所需的类打包,然后注入到所需的方法。

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

例如,我使用服务来控制业务/视图逻辑,而不是使用处理它们的控制器-它们只是路由视图。因此,控制器将首先获取其对应的service,然后parameter在其url中获取。一个服务功能还需要检查表单中的值,所以我需要Requestand 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]);
    }
}

这是一个例子。实际上,我的许多构造函数已经注入了多个类(模型,服务,参数,外观)。我已经开始将构造函数注入(如果适用)“卸载”到方法注入,并且让调用这些方法的类使用其构造函数来注入依赖项。

有人告诉我,根据经验,方法或类构造函数的四个以上参数是不好的作法/代码异味。但是,如果您选择注入laravel外墙的路径,我看不到如何真正避免这种情况。

我把这个主意弄错了吗?我的类/函数不够精简吗?我是否错过了laravels容器的要点,还是真的需要考虑创建自己的IoC容器?一些其他的答案似乎在laravel容器能消除我的问题暗示?

就是说,在这个问题上似乎还没有达成共识。

这是构造函数注入的好处之一-当您做很多事情时,它会变得很明显,因为构造函数参数太大。

首先要做的是拆分责任过多的控制器。

假设您有一个页面控制器:

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
    }
}

这是拆分为两个控制器ClientController的主要候选人AboutController

一旦做到这一点,如果您仍然有太多的依赖关系,就该寻找我称为间接依赖关系的时间了(因为我无法为它们命名正确!)-依赖关系不能被依赖类直接使用,而是传递给另一个依赖项。

这样的例子是addClientAction-它需要一个请求和一个验证器,才能将它们传递给clientRepostory

我们可以通过创建专门用于根据请求创建客户端的新类来重构,从而减少我们的依赖关系,并简化控制器和存储库:

//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
     }
}

对于多少个依赖关系没有严格的规定。好消息是,如果您使用松耦合构建应用程序,则重构相对简单。

我宁愿看到一个具有6或7个依赖项的构造函数,而不是无参数的构造函数和在整个方法中隐藏的一堆静态调用

外墙的一个问题是,在进行自动化的单元测试时,必须编写额外的代码来支持外墙。

至于解决方案:

1.手动解决依赖关系

解决依赖性的一种方法,如果您不希望这样做。构造函数或方法注入,是直接调用app():

/* @var $email_services App\Contracts\EmailServicesContract
$email_services = app('App\Contracts\EmailServicesContract');

2.重构

有时,当我发现自己将太多的服务或依赖项传递给一个类时,也许我违反了《单一责任原则》。在那些情况下,可能需要通过将服务或依赖项分解为较小的类来进行重新设计。我将使用另一种服务来包装一组相关的类,以将其用作外观。本质上,它将是服务/逻辑类的层次结构。

示例:我有一项服务,可以生成推荐产品并通过电子邮件将其发送给用户。我称该服务为服务WeeklyRecommendationServices,它接受其他2个服务作为依赖项-一个Recommendation服务,服务是用于生成建议的黑匣子(并且它具有自己的依赖项-可能是产品的回购,一个或两个帮助程序),以及一个EmailService其中可能将Mailchimp作为依赖项)。一些较低级别的依赖项(例如重定向,验证器等)将位于那些子服务中,而不是充当入口点的服务中。

3.使用Laravel全局函数

一些外立面上都提供在Laravel 5.函数调用例如,你可以使用redirect()->back()替代Redirect::back(),以及view('some_blade)替代View::make('some_blade')我相信这dispatch和其他一些常用的外墙都是一样的。

(编辑添加)4.使用特征
当我今天在排队的作业中工作时,我还观察到注入依赖项的另一种方法是使用特征。
例如,
LaravelDispathcesJobs特性具有以下几行:

   protected function dispatch($job)
    {
        return app('Illuminate\Contracts\Bus\Dispatcher')->dispatch($job);
    }

任何使用特征的类都可以访问受保护的方法,并可以访问依赖项。它比构造函数或方法签名中具有许多依赖关系更整洁,比全局变量更清晰(关于所涉及的依赖关系),并且比手动DI容器调用更易于自定义。缺点是,每次调用该函数时,都必须从DI容器中检索依赖关系,

构成Laravel中的路由机制一部分的类方法(中间件,控制器等)也具有用于注入依赖项的类型提示-不需要全部将其注入构造函数中。即使我对任何四个参数的经验法则都不熟悉,这也可能有助于使构造函数保持苗条。PSR-2可以将方法定义扩展到多行,大概是因为要求四个以上的参数并不罕见。

在您的示例中,您可以妥协地RequestandValidator服务注入构造函数中,因为它们经常被多个方法使用。

至于建立共识,Laravel必须更自以为是,以使应用程序足够相似以使用一种“一刀切”的方法。不过,一个更简单的电话是,我认为外墙在将来的版本中会像渡渡鸟一样。

与其说是一个非常有效的观点,不如说是一个答案,而是与同事交谈后的一些思考。

  1. 如果在版本之间更改laravel的内部结构(显然过去已经发生过),则注入已解析的Facade类路径将破坏升级中的所有内容-同时主要使用默认的Facades和Helper方法(如果不完全的话)可以避免此问题。

  2. 尽管解耦代码通常是一件好事,但注入这些已解析的Facade类路径的开销却使类变得杂乱无章-对于接管项目的开发人员而言,花更多的时间来尝试遵循这些代码,而这些代码可能会更好地用于修复错误或测试。新开发人员必须记住哪些注入类是开发人员,哪些是laravel。不了解laravel的开发人员必须花时间查找API。最终,引入错误或缺少关键功能的可能性增加了。

  3. 由于外观已经可以测试,因此开发速度变慢,并且可测试性并未真正提高。快速开发是首先使用laravel的强项。时间总是一个约束。

  4. 其他大多数项目都使用laravel外墙。大多数具有laravel使用经验的人都使用外墙。创建一个不遵循先前项目现有趋势的项目通常会减慢速度。未来经验不足(或懒惰!)的开发人员可能会忽略外观注入,并且该项目最终可能会采用混合格式。(甚至代码审查员都是人)

好吧,您的想法和疑虑并得到纠正,我也有。Facades有一些好处(我通常不使用它们),但是如果您只使用它,我建议您仅在控制器中使用它们,因为控制器至少对我来说只是入口和出口。

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

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

只需记住将您的代码分解成小块,每个小块都由自己来处理。当您正确地破坏代码时,在大多数情况下,您将没有太多的构造函数参数,并且代码将易于测试和模拟。

最后一点,如果您要构建一个小型应用程序或什至是一个大型应用程序中的页面,例如“联系页面”和“联系页面提交”,则可以肯定地使用立面在控制器中完成所有操作,这仅取决于项目的复杂性。

我喜欢laravel的优美结构。现在,从我的方法中,我不会将所有外观都注入控制器方法中,为什么?仅在控制器错误的实践中注入重定向外观,而在其他实践中可能需要这样做。通常,对于所有使用某些东西或仅将其最佳实践通过方法注入它们的人来说,应该将所有最常用的东西声明为所有东西,因为当您在顶部声明时,这会影响您的内存优化以及速度。码。希望这会有所帮助

本文地址:http://php.askforanswer.com/zailaravelwaiguanshangshiyongyilaizhuru.html
文章标签: ,   ,   ,  
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!