Альтернатива использованию конструктора в трейтах - PullRequest
1 голос
/ 08 мая 2020

У меня есть черта, отвечающая за хеширование ID.

trait Hasher
{
    protected $hasher;

    public function __construct()
    {
        $salt = $this->hashSalt ?? null;
        $length = $this->hashLength ?? null;
        $chars = $this->hashChars ?? null;

        $this->hasher = new Hashids($salt, $length, $chars);

        parent::__construct(); // I hoped this would trigger MyModel's constructor 
                               // but it doesn't work as expected.
     }
}

Я пробовал использовать его в своей модели.

class MyModel extends Model
{
    use Hasher;

    private $hashSalt = 'Test';
    private $hashChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    private $hashLength = 6;
}

Идея заключалась в том, чтобы сделать его многоразовым, но я не стал ' Я хочу записать все эти хашеры c в мою модель.

Проблема с parent::__construct(); в конструкторе приводит к тому, что конструктор MyModel не запускается, а конструктор Model пытается получить данные из Hasher (насколько я понимаю).

Я знаю, что могу создать новый класс, расширяющий Model, и использовать MyModel extends BaseModel, но я не большой поклонник расширения класса Model. (Надеюсь, это не единственный способ)

Какие еще у вас есть идеи для решения этой проблемы?

Ответы [ 2 ]

2 голосов
/ 08 мая 2020

Вы должны использовать загрузчики / инициализаторы Eloquent.

trait Hasher
{
    protected $hasher;

    protected function initializeHasher()
    {
        $salt = $this->hashSalt ?? null;
        $length = $this->hashLength ?? null;
        $chars = $this->hashChars ?? null;

        $this->hasher = new Hashids($salt, $length, $chars);
     }
}

Реализуя метод в вашей характеристике с именем initialize{traitName}, Laravel автоматически вызовет его в конструкторе. То же самое происходит, если вы реализуете метод stati c с именем boot{traitName}, он будет вызываться при первом использовании вашей модели. Вот полное объяснение

Illuminate\Database\Eloquent\Model::__construct

/**
     * Create a new Eloquent model instance.
     *
     * @param  array  $attributes
     * @return void
     */
    public function __construct(array $attributes = [])
    {
        $this->bootIfNotBooted();

        $this->initializeTraits();

        $this->syncOriginal();

        $this->fill($attributes);
    }

Здесь важны две вещи: вызов bootIfNotBooted косвенно запускает этот метод

/**
     * Boot all of the bootable traits on the model.
     *
     * @return void
     */
    protected static function bootTraits()
    {
        $class = static::class;

        $booted = [];

        static::$traitInitializers[$class] = [];

        foreach (class_uses_recursive($class) as $trait) {
            $method = 'boot'.class_basename($trait);

            if (method_exists($class, $method) && ! in_array($method, $booted)) {
                forward_static_call([$class, $method]);

                $booted[] = $method;
            }

            if (method_exists($class, $method = 'initialize'.class_basename($trait))) {
                static::$traitInitializers[$class][] = $method;

                static::$traitInitializers[$class] = array_unique(
                    static::$traitInitializers[$class]
                );
            }
        }
    }

Вы можете заметить здесь logi c, как я объяснил ранее, вызываются загрузчики и регистрируются инициализаторы (но еще не вызываются). Затем конструктор вызывает это

/**
     * Initialize any initializable traits on the model.
     *
     * @return void
     */
    protected function initializeTraits()
    {
        foreach (static::$traitInitializers[static::class] as $method) {
            $this->{$method}();
        }
    }

И таким образом вызывается каждый ранее зарегистрированный инициализатор.

1 голос
/ 08 мая 2020

Почему бы вам не объявить функцию в трейте и не вызвать ее в конструкторе модели следующим образом:

trait Hasher
{
    protected $hasher;

    public function hash()
    {
        $salt = $this->hashSalt ?? null;
        $length = $this->hashLength ?? null;
        $chars = $this->hashChars ?? null;

        $this->hasher = new Hashids($salt, $length, $chars);
     }
}

А затем в конструкторе вашей модели:

class MyModel extends Model
{
    use Hasher;

    private $hashSalt = 'Test';
    private $hashChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    private $hashLength = 6;

    public function __construct()
    {
        $this->hash();
    }
}
...