更新附加/分离雄辩关系的时间戳


Updating timestamps on attaching/detaching Eloquent relations

我正在使用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_atupdated_at(,我想在附加/分离Asset时更新Productupdated_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;
    }
}

让您的AssetProduct模型(如果需要,还有其他模型(扩展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()的任何其他调用。