突变体-访问属性差异


Mutators - accessing attributes difference

在我的模型中,我用这种方式定义了两个突变体:

public function setUrlAttribute($value) {
    $this->url = $value;
    $this->domain = parse_url($value, PHP_URL_HOST);
    $this->fillSlug();
}
public function setTitleAttribute($value) {
    $this->title = $value;
    $this->fillSlug();
}
private function fillSlug() {
    $this->slug = $this->createSlug($this->title) . '-' . $this->domain;
}

然而,我注意到了一些奇怪的事情——当我以这种方式定义访问者时,当我设置url和title时,实际上它们的新值并没有保存。但是,基于相同属性的(域和段塞)会正确保存。

例如:

$model = Model::find(1); // url = 'http://oldurl.com', title = 'old title'
$model->url = 'http://testurl.com';
$model->title = 'test title';
$model->save();
$model = Model::find(1); 

现在我的属性是:

url: oldurl.com
title: old title
domain: testurl.com
slug: test-title-testurl.com

这很奇怪,因为例如slug是基于$this->title的,所以值应该是相同的,但事实并非如此。

使其工作的解决方案是使用:

public function setUrlAttribute($value) {
    $this->attributes['url'] = $value;
    $this->domain = parse_url($value, PHP_URL_HOST);
    $this->fillSlug();
}
public function setTitleAttribute($value) {
    $this->attributes['title'] = $value;
    $this->fillSlug();
}
private function fillSlug() {
    $this->slug = $this->createSlug($this->title) . '-' . $this->domain;
}

因此不直接访问属性,而是使用$this->attributes['key_name']

现在的问题是,为什么在使用mutator时,字段的值必须使用$this->attributes才能按预期工作,而其他字段即使对更改的值也可以使用正常的属性访问?

tldr;简单地说,在赋值器中使用$this->title会在对象上创建public property,稍后使用$model->title访问该对象(它再也不会碰到神奇的__call),并且它永远不会接触属性数组,所以实际上您并没有更改字段

@AlleyShairu是对的,但没有解释发生了什么,所以它来了:

// having this mutator
public function setTitleAttribute($value)
{
  $this->title = $value;
}
// and doing so:
$model->title; // 'old title'
$model->title = 'some string';
// then this is true:
$model->title; // 'some string'
// but
$model->getAttribute('title'); // 'old title'

也就是说,您的属性数组从未与该变元器接触,因此当您保存模型时,title保持不变。

这就是为什么应该在赋值函数中使用$this->attributes['attributeName'],而不是直接调用$this->attributeName

需要注意的是:如果不是这样的话,您最终会对赋值函数进行无限递归调用。

据我所知,如果您已经定义了赋值函数,Laravel不会处理该特定属性的setAttribute($key,$value)。所以你需要使用访问它

$this->attributes['key_name'] in your mutators.

当您执行$this->domain=parse_url($value,PHP_url_HOST)时;Laravel实际上在做

$this->attributes[$key] = $value;

这就是setAttribute在laravel 中的样子

public function setAttribute($key, $value)
{
    // First we will check for the presence of a mutator for the set operation
    // which simply lets the developers tweak the attribute as it is set on
    // the model, such as "json_encoding" an listing of data for storage.
    if ($this->hasSetMutator($key))
    {
        $method = 'set'.studly_case($key).'Attribute';
        return $this->{$method}($value);
    }
    // If an attribute is listed as a "date", we'll convert it from a DateTime
    // instance into a form proper for storage on the database tables using
    // the connection grammar's date format. We will auto set the values.
    elseif (in_array($key, $this->getDates()) && $value)
    {
        $value = $this->fromDateTime($value);
    }
    $this->attributes[$key] = $value;
}

正如您在setAttribute方法中看到的那样,如果存在一个赋值函数,它只调用该函数,而$this->attributes[$key] = $value;行永远不会执行。