一个Eloquent Laravel模型上的__construct。

43

我在我的模型的__construct方法中运行了一个自定义的setter。

这是我想要设置的属性。

    protected $directory;

我的构造函数

    public function __construct()
    {
        $this->directory = $this->setDirectory();
    }

设置器:

    public function setDirectory()
    {
        if(!is_null($this->student_id)){
            return $this->student_id;
        }else{
            return 'applicant_' . $this->applicant_id;
        }
    }

我的问题是,在我的 setter 内部,从数据库中获取的模型的属性 $this->student_id 返回了 null。 当我在 setter 中使用 dd($this) 时,我注意到我的 #attributes: [] 是一个空数组。因此,模型的属性直到触发 __construct() 方法之后才会被设置。如何在我的构造方法中设置 $directory 属性?

2个回答

120

你需要将你的构造函数改为:

public function __construct(array $attributes = array())
{
    parent::__construct($attributes);

    $this->directory = $this->setDirectory();
}

第一行(parent::__construct())将在您的代码运行之前运行 Eloquent Model自己的构造方法,这将为您设置所有属性。此外,构造函数方法签名的更改是为了继续支持 Laravel 期望的用法:$model = new Post(['id' => 5, 'title' => 'My Post']);

通常的规则是,在扩展类时,要始终记住检查您是否覆盖了现有方法,以便它不再运行(尤其是与魔术方法 __construct__get 等相关的方法)。 您可以检查原始文件的源代码,以查看它是否包含您正在定义的方法。


1
这并不能帮助你,你将在$attributes中获得空数组。该模型稍后加载数据,而不是在构造函数阶段。请参见下面的答案(by Jed Lynch)。 - Mike
1
有点晚回复你的评论 @Mike,但是 FWIW 这个答案是在 2015 年写的(如果你能相信的话!),我相信当时是正确的,即使在 2020+,现在可能有更好的方法。我已经不再使用 Laravel 了,所以无法对此发表意见。我想知道是否有一种方法可以在 SO 中将答案标记为“当时正确但现在已过时”... - alexrussell

9

我永远不会在Eloquent中使用构造函数。Eloquent有实现您想要的功能的方法。我会使用一个带有事件监听器的boot方法。它可能看起来像这样。

protected static function boot()
{
    parent::boot();

    static::retrieved(function($model){
         $model->directory = $model->student_id ?? 'applicant_' . $model->applicant_id;
    });
}   

这里展示了您可以使用的所有模型事件:retrievedcreatingcreatedupdatingupdatedsavingsaveddeletingdeletedtrashedforceDeletedrestoringrestoredreplicating。请点击这里查看详情。

有关事件的一些更多信息 - Nick
据我所知,在加载关系之前,会先调用检索函数。 - Christopher Raymond

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接