Php 特征构造函数中的Laravel传递参数

Php 特征构造函数中的Laravel传递参数,php,laravel,dependency-injection,traits,Php,Laravel,Dependency Injection,Traits,我有一个TimezoneTrait,在User模型中使用。我还有一个UserRepositoryInterface,它是通过服务提供商加载的,在所有类中都能正常工作,因此绑定应该很好: public function register() { $this->app->bind(UserRepositoryInterface::class, UserRepository::class); } public function provides() { return [

我有一个
TimezoneTrait
,在
User
模型中使用。我还有一个
UserRepositoryInterface
,它是通过服务提供商加载的,在所有类中都能正常工作,因此绑定应该很好:

public function register()
{
    $this->app->bind(UserRepositoryInterface::class, UserRepository::class);
}

public function provides()
{
    return [
        UserRepositoryInterface::class,
    ];
}
现在我面临的问题是,我必须在我的trait中使用该存储库,所以我自然会这样做:

private $userRepository;

public function __construct(UserRepository $userRepository)
{
    $this->userRepository = $userRepository;
}

但是转储显示存储库为
null
。trait不能被注入依赖项吗?

在trait中定义构造函数实际上是错误的。或者只是一个糟糕的设计,不是性格。另一个问题是,您正在模型类中导入trait,这意味着您应该特别遵循它关于如何导入的规则

在模型的
boot
ing阶段,它在类中递归搜索导入的特征,并自动调用静态使用
boot{TraitNameHere}
命名约定的方法。这证明了模型中的特征不涉及拉雷维尔的依赖注入周期

要实现这一点,您可以使用Laravel global helper在容器中加载存储的实例,如facade
App::make(DefinedKeyHere)
。然后将分配的实例存储到一个静态属性中,使其保留到运行时结束,这也是因为调用方法是
静态

trait TimezoneTrait
{
    protected static $userRepository;

    protected static function bootTimezoneTrait()
    {
        static::$userRepository = \App::make(UserRepositoryInterface::class);
    }
}
如果您当前试图避免使用全局帮助器,那么收听模型引导事件也很有帮助。EventServiceProvider中的示例

Event::listen('eloquent.booting:*', function (Model $model) {
    $model->setUserRepository($this->app[UserRepositoryInterface::class]);
});
那么这个特点就是,

trait TimezoneTrait
{
    protected static $userRepository;

    public function static setUserRepository(UserRepositoryInterface $userRepository)
    {
        static::$userRepository = $userRepository;
    }
}
请注意,我将
setUserRepository
定义为静态,但也可以将其设置为非静态

为了稍微扩展一下模型事件,模型在执行相关操作时有几个事件要触发

来自Laravel 5.5的示例事件

public function getObservableEvents()
{
    return array_merge(
        [
            'creating', 'created', 'updating', 'updated',
            'deleting', 'deleted', 'saving', 'saved',
            'restoring', 'restored',
        ],
        $this->observables
    );
}
以及在实例化(也未序列化)时触发的其他两个默认事件,它们是
启动
启动
。以及用于触发事件的方法,请注意事件名称

protected function fireModelEvent($event, $halt = true)
{
    // ...

    return ! empty($result) ? $result : static::$dispatcher->{$method}(
        "eloquent.{$event}: ".static::class, $this
    );
}

声明
受保护的$userRepository可以工作吗?不,就我所知,它不能与它们所使用的类一起注入。然而,没有什么能阻止你在任何你正在注入另一个类的类中使用trait。引导和构造之间的区别是什么?构造函数仅在实例化类时调用,但在注册所有服务提供程序后最初调用boot。这是否意味着将独立于类是否实例化而调用boot?这是不同的
boot
,您所引用的是应用程序
boot
绑定,但这只是发生在模型上的
boot
事情。一旦一个模型被实例化,这个
boot
就像上面所说的那样,递归地调用整个traits方法,应用静态事件,以及其他任何事情。一旦
启动
,它将存储保留的值,告诉
启动
阶段已经完成。所以,每当您再次重新实例化它时——比如通过
新模型
模型::创建
,等等——就避免了那些消耗大量内存的阶段。