Mutators - 访问属性差异

时间:2014-10-05 11:02:36

标签: php laravel laravel-4 eloquent

我在我的模型中以这种方式定义了2个mutators:

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时,以这种方式定义了访问器,实际上他们的新值没有被保存。但是,基于相同属性的(域和slug)会正确保存。

例如:

$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的字段值为mutator时必须使用$this->attributes按预期工作,而其他字段可以使用普通属性访问,即使是更改的值?

2 个答案:

答案 0 :(得分:2)

<强> tldr;简单地说 - 在mutator中使用$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'

也就是说,您的属性数组永远不会被该mutator触及,因此当您保存模型时,title保持不变。

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

作为一个注释:如果不是这样的话,你最终会对mutator进行无限递归调用。

答案 1 :(得分:1)

据我所知,如果你定义了mutator,Laravel不会处理该特定属性的setAttribute($key,$value)。所以你需要使用

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

你什么时候做$ this-&gt; 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方法中看到的,如果存在mutator函数,它只调用该函数并且行$this->attributes[$key] = $value;永远不会执行。