【问题标题】:Soft deleting / detaching and restoring / attaching relationships with composite keys使用复合键软删除/分离和恢复/附加关系
【发布时间】:2015-09-26 17:31:51
【问题描述】:

我有 2 个模型,它们由具有复合键的关系连接 - 它们是产品和类别。我需要对所有表使用软删除,以便在需要时恢复模型和关系。

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

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 字段不明确(因为它将类别表连接到 product_categories)。

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

【问题讨论】:

    标签: php laravel eloquent


    【解决方案1】:

    基本上,将只有一个 deleted_at 字段,而不是使用 $product->categories() 在两个(ProductCategory)模型中使用两个自定义(通用)方法,例如,您可以创建一个像这个:

    // 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);
    

    你可以这样使用:

    $product = Product->find(1);
    
    $product->softDeleteProductCategory(1, 2); // Set timestamp to deleted_at
    
    $product->restoreProductCategory(1, 2); // Set null to deleted_at
    

    希望这对你有用。

    【讨论】:

    • 感谢您的建议。我刚刚开始使用 Laravel,之前没有遇到过特性。您的方法实际上与我提出的方法相似 - 即处理此问题的自定义方法。理想情况下,我只想让默认方法支持软删除——尽管老实说,我不确定这有多可行。我将发布我自己创建的同步版本作为答案。
    【解决方案2】:

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

    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();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2010-10-11
      • 2012-04-23
      • 1970-01-01
      • 2018-11-18
      • 1970-01-01
      • 2021-10-10
      • 2016-08-08
      • 2020-07-29
      • 1970-01-01
      相关资源
      最近更新 更多