为什么我应该在 Laravel 中同时使用特征和服务?

Why should I use both traits and services in Laravel?

我有一个简单的问题,我很难回答,我希望你们能帮助我。所以我正在 laravel 上开发应用程序。我想让控制器变得清晰,所以我在模型 类 本身内部有复杂的查询(我不想使用存储库)。如果我想要一些将用于许多模型的功能,我将此功能放在特征中并在需要时使用它。所以这是我的问题.. 我需要服务 类 吗?特征还不够吗?

假设您在特征中有一个方法需要一个 Cat 模型和一个 Dog 模型。每次使用该特征时,都需要传入一个 Cat 实例和一个 Dog 实例才能完成它的工作。

最终这会变得很累。如果你最终添加了一个特征,而这个特征现在需要一个 Bird 模型,会发生什么?现在你有很多代码需要修改,只是为了在每次你想使用它时开始将 Bird 传递给特征中的方法。

这就是服务 class 变得非常方便的地方。在服务 class' 构造函数中,您可以像这样注入 UserPermissionRole 模型...

public function __construct(Cat $cat, Dog $dog, Bird $bird)
{
    $this->cat = $cat;
    $this->dog = $dog;
    $this->bird = $bird;
}

然后您可以向您的应用程序添加一个服务提供商,它通过传入适当的模型来指示Laravel如何创建此服务class。从那里,您可以将此服务自动注入您的控制器,因为您认为合适,目前您只是为此使用了一个特征。

这意味着如果你需要添加一个依赖项,你只需要更新一个,可能是两个文件。服务 class 本身向构造函数和可能的服务提供者添加附加项。所有使用它的控制器都不需要更新以将这些额外的项目传递给构造函数,因为这是由 Laravel(更合适的 IoC 容器)处理的。

这对测试也极为有利。将依赖项注入 class 时,仅设置测试环境来模拟这些依赖项比尝试动态构建模拟以传递到特征方法更容易。测试特征也更加困难,因为某些东西需要 use 该特征才能测试特征的方法。

// we can use a service provider to tell laravel how to create this service (https://laravel.com/docs/5.7/providers)
class PetService
{
    protected $cat;
    protected $dog;
    protected $bird;

    public function __construct(Cat $cat, Dog $dog, Bird $bird)
    {
        $this->cat = $cat;
        $this->dog = $dog;
        $this->bird = $bird;
    }

    public function doStuff()
    {
        //  does things with $this->cat, $this->dog, $this->bird
    }
}


class PetController
{
    // use PetTrait;

    protected $petService;

    // laravel will automatically create the PetService for us and pass it into the constructor when building this controller (https://laravel.com/docs/5.7/container)
    public function __construct(PetService $petService) 
    {
        $this->petService = $petService;
    }

    public function someControllerMethod()
    {
        // $this->doStuff(new Cat, new Dog, new Bird); // In order to modify this method, we need to update each time it's used.
        $this->petService->doStuff(); // We don't need to worry about passing in arguments since it's handled by the service container when constructing the pet service and can make changes without updating each controller that uses it.
    }
}

更详细地说,如果您决定 Bird 现在需要 ChickenTurkeyDodo 的实例才能运行,会发生什么正确。现在您需要再次检查使用 Bird 的任何特征并更新每个特征以传入其每个依赖项的实例。

这就是为什么使用服务容器和提供程序来规划和构建服务的依赖项 classes 是有利的。它大大提高了应用程序的可维护性。在一个非常大和复杂的应用程序中,这将是迄今为止最有益的。

但是,如果您的应用程序相当简单,则它可能没有那么有用,甚至可能会增加不必要的复杂性,而且实际上,如果特征对您来说更有意义,那么此时使用它们可能会更好。让您的应用在不需要时灵活地使用服务并不好。