简体   繁体   中英

Laravel pass arguments in trait constructor

I have a TimezoneTrait which is used in the User model. I also have an UserRepositoryInterface which is loaded through a service provider and works good across all classes so the binding should be fine:

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

public function provides()
{
    return [
        UserRepositoryInterface::class,
    ];
}

Now the problem I have is that I have to use that repository in my trait, so I naturally did this:

private $userRepository;

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

But the dump shows that the repository is null . Can't traits be injected with dependencies?

Defining __constructor within trait is actually wrong. Or just a bad design. Constructors should be specific to a class to which they belong , not traits. Another issue then, you're importing trait in Model class, that means you should specifically follow its rule about how a trait in a model is loaded .

At the boot ing stage of a model, it searches imported traits recursively within class and automagically call a method that is using boot{TraitNameHere} naming convention, statically. That proves traits in model does not involved in Laravel's dependency injection cycle.

To make it happen, you can use Laravel global helper to load stored instance inside container, like the facade App::make(DefinedKeyHere) . Then store the assigned instance into a static property to make it retained until the runtime ends and also because the recalling method is static .

trait TimezoneTrait
{
    protected static $userRepository;

    protected static function bootTimezoneTrait()
    {
        static::$userRepository = \App::make(UserRepositoryInterface::class);
    }
}

If you're currently trying to avoid using global helper, listening to the model booting event is also helpful. Example inside EventServiceProvider,

Event::listen('eloquent.booting:*', function (Model $model) {
    $model->setUserRepository($this->app[UserRepositoryInterface::class]);
});

Then the trait would be,

trait TimezoneTrait
{
    protected static $userRepository;

    public function static setUserRepository(UserRepositoryInterface $userRepository)
    {
        static::$userRepository = $userRepository;
    }
}

Take a note that I defined setUserRepository as static, but you can also make it non-static, too.

And to expand a little bit about model event, model has several events to fire whenever it's doing its related action.

Example events from Laravel 5.5,

public function getObservableEvents()
{
    return array_merge(
        [
            'creating', 'created', 'updating', 'updated',
            'deleting', 'deleted', 'saving', 'saved',
            'restoring', 'restored',
        ],
        $this->observables
    );
}

And other two default events that are fired when its instantiated (also unserialized) which are booting and booted . And the method which is use to fire the event, notice the event name.

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM