2014-10-05 66 views
1

我在我的模型中定义的2个存取器是这样的:存取器 - 访问属性差异

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和标题的新值没有保存。但是(域和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 

这是很奇怪的,因为塞的例子是$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']

现在的问题 - 为什么突变使用现场的存取器值时,必须使用$this->attributes使用按预期工作,而其他字段即使对于更改的值也可以使用正常的属性访问?

回答

2

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

作为说明:如果不是这种情况,您最终将无限递归调用mutator。

+0

所以在mutator中我应该使用'$ this-> attributes' - 现在已经很清楚了。但是增肌的其他领域呢?我可以使用'$ this-> property'访问其他字段,或者在右侧使用'$ this-> property'作为突变属性吗?我的意思是我的第二个解决方案 - 现在是完全安全还是'fillSlug',为了以防万一,我还应该使用'$ this-> attributes ['title']'而不是'$ this-> title'? – 2014-10-06 08:30:53

+0

只要没有设置,您就可以使用' - > property',但是获取(当然访问器会被调用,如果有的话)。如果在'fillSlug'中引用'slug'属性,那么你可以像这样使用它,但是记住它会触发mutator,如果你定义一个'slug' attr有一天。 – 2014-10-06 09:08:35

1

从我知道如果你已经定义了增变器Laravel不处理该特定属性的setAttribute($key,$value)。所以,你需要使用

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

当过你正在做这 - $>域= parse_url($值,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;从未执行。

+0

好的,但是为什么在第一种情况下金属块正确创建(它使用'$这个 - > title')并且此属性使用set'$这 - >标题= $值;'不使用'$这 - >属性'。在这种情况下,属性和普通属性变成2个不同的集合? – 2014-10-05 12:18:01

+0

我看到你的观点,'$ this-> title'也从属性数组中获取它的值。这确实是一种奇怪的行为,或者我们错过了什么?期待解决方案。 – 2014-10-05 13:45:04

+1

这种由__set调用的雄辩方法允许您通过动态属性访问属性。该数组中的所有属性都可以通过动态属性自动访问。它由该方法的最后一行处理。 $ model-> attribute ='';调用调用setAttribute的__set。和$ model->属性;调用调用getAttribute的__get。这都是基于魔术方法__get和__set。 – lagbox 2014-10-05 14:24:04