我正在使用Laravel 4,并且有2个模型:
class Asset extends 'Eloquent {
public function products() {
return $this->belongsToMany('Product');
}
}
class Product extends 'Eloquent {
public function assets() {
return $this->belongsToMany('Asset');
}
}
Product
上面有标准时间戳(created_at
,updated_at
(,我想在附加/分离Asset
时更新Product
的updated_at
字段。
我在Asset
模型上尝试了这个:
class Asset extends 'Eloquent {
public function products() {
return $this->belongsToMany('Product')->withTimestamps();
}
}
。但这根本没有做任何事情(显然(。编辑:显然这是为了更新数据透视表上的时间戳,而不是在关系自己的表上更新它们(即更新assets_products.updated_at
,而不是products.updated_at
(。
然后我在Asset
模型上尝试了这个:
class Asset extends 'Eloquent {
protected $touches = [ 'products' ];
public function products() {
return $this->belongsToMany('Product');
}
}
。这有效,但随后打破了我的种子,它调用了Asset::create([ ... ]);
,因为显然 Laravel 试图在不检查它是否null
的情况下调用关系->touchOwners()
:
PHP 致命错误:调用未定义的方法 Illuminate''Database''Eloquent''Collection::touchOwners(( 在/projectdir/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php 第 1583 行
我用来添加/删除Asset
的代码是这样的:
Product::find( $validId )->assets()->attach( $anotherValidId );
Product::find( $validId )->assets()->detach( $anotherValidId );
我哪里出错了?
touch
方法手动执行此操作:
$product = Product::find($validId);
$product->assets()->attach($anotherValidId);
$product->touch();
但是,如果您不想每次都手动执行此操作,则可以通过以下方式简化Product
模型中的此创建方法:
public function attachAsset($id)
{
$this->assets()->attach($id);
$this->touch();
}
现在你可以这样使用它:
Product::find($validId)->attachAsset($anotherValidId);
当然,您可以对分离操作执行相同的操作。
我注意到你有一个关系belongsToMany
另一个hasMany
- 两者都应该相当belongsToMany
,因为它是多对多的关系
编辑
如果您想在许多模型中使用它,则可以使用以下方法创建 trait 或创建另一个扩展 Eloquent 的基类:
public function attach($id, $relationship = null)
{
$relationship = $relationship ?: $this->relationship;
$this->{$relationship}()->attach($id);
$this->touch();
}
现在,如果你需要这个功能,你只需要从另一个基类扩展(或使用trait(,现在你可以向Product
类添加一个额外的属性:
private $relationship = 'assets';
现在您可以使用:
Product::find($validId)->attach($anotherValidId);
或
Product::find($validId)->attach($anotherValidId, 'assets');
如果您需要使用更新字段附加数据updated_at
。当然,您需要重复分离。
在创建相关模型的新实例时,需要从代码源中将$touch
设置为 false:
Asset::create(array(),array(),false);
或使用:
$asset = new Asset;
// ...
$asset->setTouchedRelations([]);
$asset->save();
解决方案:
创建一个扩展 Eloquent 的BaseModel
,对 create
方法进行简单的调整:
基本型号.php:
class BaseModel extends Eloquent {
/**
* Save a new model and return the instance, passing along the
* $options array to specify the behavior of 'timestamps' and 'touch'
*
* @param array $attributes
* @param array $options
* @return static
*/
public static function create(array $attributes, array $options = array())
{
$model = new static($attributes);
$model->save($options);
return $model;
}
}
让您的Asset
和Product
模型(如果需要,还有其他模型(扩展BaseModel
而不是Eloquent
,并设置 $touches
属性:
资产.php(和其他模型(:
class Asset extends BaseModel {
protected $touches = [ 'products' ];
...
在播种机中,将 create
的第二个参数设置为一个数组,该数组将'touch'
指定为 false
:
Asset::create([...],['touch' => false])
解释:
Eloquent 的 save()
方法接受一个(可选(选项数组,您可以在其中指定两个标志:'timestamps'
和 'touch'
。如果touch
设置为 false
,则无论您在模型上指定了任何$touches
属性,Eloquent 都不会触及相关模型。这都是 Eloquent save()
方法的内置行为。
问题是 Eloquent 的 create()
方法不接受任何传递给save()
的选项。通过扩展 Eloquent (带 BaseModel
( 以接受 $options
数组作为第二个属性,并将其传递给 save()
,您现在可以在所有扩展BaseModel
的模型上调用 create()
时使用这两个选项。
请注意,$options
数组是可选的,因此这样做不会中断对代码中可能具有create()
的任何其他调用。