在特征中使用构造函数的替代方法
Alternative for using constructor in traits
我有一个特质,全权负责Hashing 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;
}
我的想法是让它可以重用,我不想将所有这些 Hasher 逻辑写入我的模型。
构造函数中 parent::__construct();
的问题导致 MyModel
的构造函数不触发并且 Model
的构造函数尝试从 Hasher
获取数据(据我了解) .
我知道我可以创建一个新的 class 来扩展模型并使用 MyModel extends BaseModel
,但我不太喜欢扩展模型 class。 (希望这不是唯一的方法)
对于解决此问题,您还有哪些其他想法?
为什么不在 trait 中声明一个函数,然后像这样在模型的构造函数中调用它:
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();
}
}
你应该使用 Eloquent bootstrappers/initializers.
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 将自动在构造函数中调用它。如果你实现一个名为 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]
);
}
}
}
你可以注意到我之前解释的逻辑,引导程序被调用并且初始化器被注册(但还没有被调用)。
然后构造函数调用this
/**
* Initialize any initializable traits on the model.
*
* @return void
*/
protected function initializeTraits()
{
foreach (static::$traitInitializers[static::class] as $method) {
$this->{$method}();
}
}
这样,之前注册的每个初始化程序都会被调用。
我有一个特质,全权负责Hashing 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;
}
我的想法是让它可以重用,我不想将所有这些 Hasher 逻辑写入我的模型。
构造函数中 parent::__construct();
的问题导致 MyModel
的构造函数不触发并且 Model
的构造函数尝试从 Hasher
获取数据(据我了解) .
我知道我可以创建一个新的 class 来扩展模型并使用 MyModel extends BaseModel
,但我不太喜欢扩展模型 class。 (希望这不是唯一的方法)
对于解决此问题,您还有哪些其他想法?
为什么不在 trait 中声明一个函数,然后像这样在模型的构造函数中调用它:
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();
}
}
你应该使用 Eloquent bootstrappers/initializers.
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 将自动在构造函数中调用它。如果你实现一个名为 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]
);
}
}
}
你可以注意到我之前解释的逻辑,引导程序被调用并且初始化器被注册(但还没有被调用)。 然后构造函数调用this
/**
* Initialize any initializable traits on the model.
*
* @return void
*/
protected function initializeTraits()
{
foreach (static::$traitInitializers[static::class] as $method) {
$this->{$method}();
}
}
这样,之前注册的每个初始化程序都会被调用。