更新附加/分离 Eloquent 关系的时间戳
Posted
技术标签:
【中文标题】更新附加/分离 Eloquent 关系的时间戳【英文标题】:Updating timestamps on attaching/detaching Eloquent relations 【发布时间】:2015-01-03 02:08:35 【问题描述】:我正在使用 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 试图在关系上调用->touchOwners()
而没有检查它是否是null
:
php 致命错误:在第 1583 行的 /projectdir/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php 中调用未定义的方法 Illuminate\Database\Eloquent\Collection::touchOwners()
我用来添加/删除Asset
s 的代码是这样的:
Product::find( $validId )->assets()->attach( $anotherValidId );
Product::find( $validId )->assets()->detach( $anotherValidId );
我哪里错了?
【问题讨论】:
我想你必须在assets()
方法上添加withTimestamps()
。 withTimestamps()
应该可以在这里找到:github.com/laravel/framework/issues/109
@arjan 我希望更新的时间戳在Product
上,而不是在数据透视表上
你用的是哪个版本的 laravel 4?
【参考方案1】:
您可以使用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
字段来附加数据。当然,你需要重复分离。
【讨论】:
HasMany 上的好地方 - 这是一个错字 :-) 如果可能的话,我想避免创建辅助方法,因为我可能会对其他一些模型做类似的事情,尽管如果没有其他方法,那么这是一个有效的解决方法 @Joe 我不知道是否可以通过其他方式完成,似乎无法使用事件侦听器跟踪附加/分离操作,但也许我错了,或者其他人会找到更好的解决方案 @Joe 我已经用可能的解决方案更新了我的答案,以便在许多模型中使用它【参考方案2】:从code source开始,创建相关模型的新实例时需要将$touch
设置为false:
Asset::create(array(),array(),false);
或使用:
$asset = new Asset;
// ...
$asset->setTouchedRelations([]);
$asset->save();
【讨论】:
看来::create()
方法在BelongsToMany
关系上,而不是在Model
上,因此您不能只将额外参数传递给Asset::create()
。第二个选项可能有效,但如果可能的话,我宁愿继续使用::create()
抱歉,我没有正确理解您的问题,显然@Marcin Nabiałek 的回答是一个很好的解决方法【参考方案3】:
解决方案:
创建一个扩展 Eloquent 的 BaseModel
,对 create
方法进行简单调整:
BaseModel.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
属性:
Asset.php(和其他模型):
class Asset extends BaseModel
protected $touches = [ 'products' ];
...
在播种机中,将create
的第二个参数设置为将'touch'
指定为false
的数组:
Asset::create([...],['touch' => false])
解释:
Eloquent 的 save()
method 接受一个(可选的)选项数组,您可以在其中指定两个标志:'timestamps'
和 'touch'
。如果 touch
设置为 false
,那么 Eloquent 将不会触及相关模型,无论您在模型上指定了任何 $touches
属性。这是 Eloquent 的 save()
方法的所有内置行为。
问题是 Eloquent 的 create()
method 不接受任何传递给 save()
的选项。通过扩展 Eloquent(带有 BaseModel
)以接受 $options
数组作为第二个属性,并将其传递给 save()
,您现在可以在所有扩展模型上调用 create()
时使用这两个选项BaseModel
。
请注意,$options
数组是可选的,因此这样做不会破坏代码中可能对create()
的任何其他调用。
【讨论】:
这个问题在 Laravel/Framework 版本4.2.6
上不存在,但在 4.2.11
上确实存在。在我看来,Laravel 应该能够自动检测到这一点,对吧?也就是说,这不就是个bug吗?以上是关于更新附加/分离 Eloquent 关系的时间戳的主要内容,如果未能解决你的问题,请参考以下文章
Laravel Eloquent 在结果中附加一个非关系数据
如何更新 Laravel Eloquent 管理的时间戳(created_at 和 updated_at)的时区?