使用复合键软删除/分离和恢复/附加关系

时间:2015-07-08 16:51:13

标签: laravel laravel-5 laravel-orm

我有两个模型由一个具有复合键的关系连接 - 这些是产品和类别。我需要在所有表上使用软删除,以便在需要时可以恢复模型和关系。

在我的产品型号中,我有:

function categories()
{
    return $this->belongsToMany('App\Category', 'product_categories')->whereNull('product_categories.deleted_at')->withTimestamps();
}

在我的分类模型中,我有:

function products()
{
    return $this->belongsToMany('App\Product', 'product_categories')->whereNull('product_categories.deleted_at')->withTimestamps();
}

我在其他地方读过关于链接whereNull方法的内容,因为像$category->products->contains($product->id)这样的查询会返回软删除的关系。

我的问题是处理删除和恢复这些软删除关系的最佳方法是什么?例如,对于恢复,我尝试过:

$product->categories()->restore($category_id);

上面产生了一个SQL错误,说出deleted_at字段含糊不清(因为它将categories表加入了product_categories)。

更新 - 根本问题似乎是BelongsToMany类不支持软删除 - 因此附加,分离和同步都会执行硬删除。覆盖这个课程的最佳方法是什么?

2 个答案:

答案 0 :(得分:5)

基本上,只有一个-- ARRANGE CREATE TABLE A( [Id] [int] IDENTITY(1,1) NOT NULL, CONSTRAINT PK_A PRIMARY KEY (Id) ) CREATE TABLE B( [Id] INT IDENTITY(1,1) NOT NULL, [A_Id] INT NOT NULL, CONSTRAINT PK_B PRIMARY KEY (Id), CONSTRAINT FK_B_A FOREIGN KEY (A_Id) REFERENCES A(Id) ) INSERT A DEFAULT VALUES DECLARE @A_Id INT SELECT @A_Id = SCOPE_IDENTITY() INSERT INTO B VALUES (@A_Id) INSERT INTO B VALUES (@A_Id) INSERT INTO B VALUES (@A_Id) INSERT INTO B VALUES (@A_Id) INSERT INTO B VALUES (@A_Id) -- ACT ALTER TABLE A ADD New_Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() ALTER TABLE B ADD New_A_Id UNIQUEIDENTIFIER NULL UPDATE B SET B.New_A_Id = (SELECT A.New_Id FROM A WHERE A.Id = B.A_Id) ALTER TABLE B DROP FK_B_A ALTER TABLE B DROP COLUMN A_Id EXEC sp_RENAME 'B.New_A_Id', 'A_Id', 'COLUMN' ALTER TABLE A DROP PK_A ALTER TABLE A DROP COLUMN Id ALTER TABLE A ADD CONSTRAINT PK_A PRIMARY KEY (New_Id) EXEC sp_RENAME 'A.New_Id', 'Id', 'COLUMN' ALTER TABLE B ADD CONSTRAINT FK_B_A FOREIGN KEY (A_Id) REFERENCES A(Id) -- ASSERT SELECT * FROM A SELECt * FROM B 字段,而不是deleted_at使用两个($product->categories()Product)模型中的两个自定义(常用)方法例如,您可以创建如下特征:

Category

然后使用// SoftDeletePC.php trait SoftDeletePC { // SoftDelete public function softDeleteProductCategory($productId, $categoryId) { \DB::table('product_categories') ->where('product_id', $productId) ->where('category_id', $categoryId) ->update(['deleted_at' => \DB::raw('NOW()')]); } // Restore public function restoreProductCategory($productId, $categoryId) { \DB::table('product_categories') ->where('product_id', $productId) ->where('category_id', $categoryId) ->update(['deleted_at' => null]); } } 在两个模型中使用此特征,并从两个模型中调用方法,例如:

use TraitProductCategory

所以,而不是使用它:

// App/Product.php
class product extends Model {
    use SoftDeletePC;
}

// App/Category.php
class Category extends Model {
    use SoftDeletePC;
}

您可以使用以下内容:

Product->find(1)->categories()->restore(2);

希望这可能适合你。

答案 1 :(得分:1)

我最终在我的产品模型中创建了一些自定义方法来完成我的需求 - 不是我理想的解决方案,但它仍然可以工作。我的自定义同步如下所示:

class Product extends Model
{
    // update relationship to categories
    function categories_sync($category_ids)
    {
        // categories
        $existing_category_ids = $this->categories()->lists('category_id')->all();
        $trashed_category_ids = $this->categories('onlyTrashed')->lists('category_id')->all();

        if(is_array($category_ids)) {

            foreach($category_ids as $category_id) {
                if(in_array($category_id, $trashed_category_ids)) {
                    $this->categories()->updateExistingPivot($category_id, ['deleted_at' => null]);
                }
                elseif(!in_array($category_id, $existing_category_ids)) {
                    $this->categories()->attach($category_id);
                }
            }

            foreach($existing_category_ids as $category_id) {
                if(!in_array($category_id, $category_ids)) {
                    $this->categories()->updateExistingPivot($category_id, ['deleted_at' => date('YmdHis')]);
                }
            }
        }
        else {
            foreach($existing_category_ids as $category_id) {
                $this->categories()->updateExistingPivot($category_id, ['deleted_at' => date('YmdHis')]);
            }
        }
    }
}

以上依赖于扩展的categories()方法:

// relationship to categories
function categories($trashed=false)
{
    if($trashed=='withTrashed') {
        return $this->belongsToMany('App\Category', 'product_categories')->withTimestamps();
    }
    elseif($trashed=='onlyTrashed') {
        return $this->belongsToMany('App\Category', 'product_categories')->whereNotNull('product_categories.deleted_at')->withTimestamps();
    }
    else {
        return $this->belongsToMany('App\Category', 'product_categories')->whereNull('product_categories.deleted_at')->withTimestamps();
    }
}