在我的模型中,我用这种方式定义了两个突变体:
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;
行永远不会执行。