【问题标题】:Laravel5 dependency injection on ModelLaravel5 对模型的依赖注入
【发布时间】:2016-02-11 17:41:36
【问题描述】:

我有一个名为 Surface 的 Eloquent 模型,它依赖于 ZipCodeRepository 对象:

class Surface extends Model{
    public function __construct(ZipCodeRepositoryInterface $zipCode){...}

还有一个具有许多表面的地址对象。

class Address extends Model{
    public surfaces() { return $this->hasMany('App/Surface'); }
}

我的问题是当我打电话给$address->surfaces 时出现以下错误:

Argument 1 passed to App\Surface::__construct() must be an instance of App\Repositories\ZipCodeRepositoryInterface, none given

我认为 IoC 会自动注入它。

【问题讨论】:

标签: laravel-5 ioc-container


【解决方案1】:

感谢@svmm 引用the question mentioned in the comments。我发现您不能在模型上使用依赖注入,因为您必须更改不适用于 Eloquent 框架的构造函数上的签名。

作为中间步骤,我在重构代码时所做的是在构造函数中使用App::make 来创建对象,例如:

class Surface extends Model{
    public function __construct()
    {
        $this->zipCode = App::make('App\Repositories\ZipCodeRepositoryInterface');
    }

这样 IoC 仍将获取已实现的存储库。在我可以将函数拉入存储库以删除依赖项之前,我只会这样做。

【讨论】:

  • 谢谢!将依赖项传递给 Model 构造函数时,执行 Eloquent find(id) 时出现“参数太少”错误。这种方法效果更好
  • 谢谢,经过1小时的寻找解决方案,这就是解决问题的方法。
【解决方案2】:

在 Laravel 5.7 中,您可以使用全局 resolve(...) 方法。我不认为全局 App 是在更新版本的 Laravel 中定义的。

$myService = resolve(ServiceName::class);

Resolving in Laravel docs

【讨论】:

    【解决方案3】:

    但是,通过构造函数或方法注入将服务注入模型可能不是一个好习惯,请考虑以不需要这样做的方式设计系统,而是将模型注入服务.

    让我们看一个例子(只是一个虚拟的例子,以便进入重点!)。

    • 假设我们有 Basket 和 Order 模型,我们想将订单添加到购物篮中
    • 我们有一个折扣服务,可以根据订单计算折扣
    • 每次用户将订单添加到购物篮时,我们都需要计算新的折扣并将其设置在购物篮中

    一种方法是:

    class OrderController
    {
        function store(User $user, Order $order)
        {
            $basket = $user->getBasket();
            $basket->addOrder($order);
        }
    }
    
    class Basket
    {
        private $discountService;
    
        public function __construct(DiscountService $discountService)
        {
            $this->discountService = $discountService;
        }
    
        function addOrder(Order $order)
        {
            $this->orders[] = $order;
            $discount = $this->discountService->calculateFor($this->orders);
            $this->discount = $discount;
        }
    }
    
    class DiscountService
    {
        function calculateFor(array $orders) {
            // code for calculating discount;
            return $discount;
        }
    }
    

    在这种方法中,我们将折扣服务注入到篮子模型中

    另一种更好的方法是这样的:

    class OrderController
    {
        private $discountService;
    
        public function __construct(DiscountService $discountService)
        {
            $this->discountService = $discountService;
        }
    
        function store(User $user, Order $order)
        {
            $basket = $user->getBasket();
            $basket->addOrder($order);
            $this->discountService->setDiscount($basket);
        }
    }
    
    class Basket
    {
        function addOrder(Order $order)
        {
            $this->orders[] = $order;
        }
    
        function getOrders()
        {
            return $this->orders;
        }
    
        function setDiscount(int $discount)
        {
            $this->discount = $discount;
        }
    }
    
    class DiscountService
    {
        function setDiscount(Basket $basket) {
            $discount = $this->calculateFor($basket->getOrders());
            $basket->setDiscount($discount);
        }
    
        private function calculateFor(array $orders)
        {
            // code for calculating discount
            return $discount;
        }
    }
    
    • 在第一个方法中,篮子正在决定是否有折扣,但这不是篮子的关注点
    • 在第一种方法中,购物篮取决于折扣服务,但在现实世界中,您不需要折扣服务来拥有购物篮

    【讨论】:

    • 这个答案还向我展示了模拟的需要如何揭示代码气味。
    猜你喜欢
    • 1970-01-01
    • 2011-09-22
    • 2018-01-16
    • 1970-01-01
    • 2021-04-17
    • 2012-12-17
    • 2014-07-26
    • 2020-11-20
    • 1970-01-01
    相关资源
    最近更新 更多