【问题标题】:Refactor Laravel controller重构 Laravel 控制器
【发布时间】:2022-01-16 19:50:42
【问题描述】:

我正在尝试重构我的代码。我的问题是:如何重构一个同时将数据保存在多个表中的方法?

我认为编写这段代码是必不可少的。另一方面,它对于控制器的方法来说太长了。我可以将其中一些移动到模型中吗?

我有一个product,它与Pricespecialsmaintenance等有一些关系。

public function store(AddProductRequest $request)
{
    $shopperId = 1;

    $product = new Product([
        'title' => $request->title,
        'slug' => to_slug_fa($request->title, Product::class),
        'detail' => $request->detail,
        'color_id' => $request->colorId,
        'category_id' => $request->categoryId,
        'img' => $request->img,
        'description' => $request->description,
    ]);

    $category = Category::find($request->categoryId);

    $category->Product()->save($product);

    $code = create_code($request->categoryId, $product['id'], $request->colorId);

    $product->update(['code' => $code]);

    $price = new Price([
        'price' => $request->price,
        'product_id' => $product->id,
    ]);

    $product->price()->save($price);

    $stocks = [];

    foreach ($request->sizeIds as $value) {
        if ($request->quantity[$value] != null) {
            $stocks[] = new Stock([
                'stock' => $request->quantity[$value],
                'product_id' => $product->id,
                'size_id' => $value,
                'shopper_id' => $shopperId,
            ]);
        }
    }

    $product->stock()->saveMany($stocks);

    $productAttributes = [];

    foreach ($request->attributeIds as $value) {
        if (isset($request->productAttribute[$value])) {
            $productAttributes[] = new ProductAttribute([
                'product_id' => $product->id,
                'attribute_id' => $value,
                'value' => $request->productAttribute[$value],
            ]);
        }
    }

    $product->ProductAttribute()->saveMany($productAttributes);

    $productDetail = [];

    foreach ($request->productDetails as $value) {
        if (isset($value)) {
            $productDetail[] = new ProductDetail([
                'description' => $value,
                'parent_id' => $product->id,
            ]);
        }
    }

    $product->ProductDetail()->saveMany($productDetail);

    $product_Maintenance = [];

    foreach ($request->productMaintenance as $value) {
        if (isset($value)) {
            $product_Maintenance[] = new ProductMaintenance([
                'description' => $value,
                'parent_id' => $product->id,
            ]);
        }
    }

    $product->ProductMaintenance()->saveMany($product_Maintenance);

    return back();
}

【问题讨论】:

    标签: php laravel laravel-8


    【解决方案1】:

    根据您的可填充属性,有些行是多余的(无用)。

    如果category_idProduct 中的可填充属性,那么这些行不会做任何事情,因为 category_id 已经被设置了

        $category = Category::find($request->categoryId);
    
        $category->Product()->save($product);
    

    我认为这可以移至created/saved 事件。

        $code = create_code($request->categoryId, $product['id'], $request->colorId);
    
        $product->update(['code' => $code]);
    

    如果product_idPrice 中的可填充属性,则不会执行任何操作,因为您已经在这之前几行设置了product_id

        $product->price()->save($price); 
    

    同样的处理

        $product->stock()->saveMany($stocks);
        $product->ProductAttribute()->saveMany($productAttributes);
        $product->ProductMaintenance()->saveMany($product_Maintenance);
    

    只要你有类似的东西

    $results = []
    foreach ($something as $item) {
        $result[] = ...;
    }
    

    它可以被map 操作替换。是否增加复杂性取决于您自己的决定。

    如果我必须重写它,我会使用集合方法来处理所有循环部分,在 Product 中内联 code,并尽可能使用 create。这意味着所有模型都必须设置其 $fillable$guarded 属性。

    class Product extends Model
    {
        protected static function booted()
        {
            static::created(function ($product) {
                // you might need to inline the create_code function here
                $product->code = create_code($product->category_id, $product->id, $product->color_id); 
                $product->save();
            });
        }
    }
    
    
    public function store(AddProductRequest $request)
    {
        $shopperId = 1;
    
        $product = Product::create([
            'title' => $request->title,
            'slug' => to_slug_fa($request->title, Product::class),
            'detail' => $request->detail,
            'color_id' => $request->colorId,
            'category_id' => $request->categoryId,
            'img' => $request->img,
            'description' => $request->description,
        ]);
        
        $product->price()->create(['price' => $request->price]);
    
        collect($request->sizeIds)
            ->filter(fn($value) => $request->quantity[$value] != null);
            ->map(fn($value) => [
                'stock' => $request->quantity[$value],
                'size_id' => $value,
                'shopper_id' => $shopperId,
            ])
            ->each(fn($value) => $product->stock()->create($value));
    
        collect($request->attributeIds)
            ->filter(fn($value) => isset($request->productAttribute[$value]))
            ->map(fn($value) => [
                'attribute_id' => $value,
                'value' => $request->productAttribute[$value],
            ])
            ->each(fn($value) => $product->ProductAttribute()->create($value));
    
        collect($request->productDetails)
            ->filter(fn($value) => isset($value)) // or just ->filter()
            ->map(fn($value) => [
                'description' => $value,
            ])
            ->each(fn($value) => $product->ProductDetail()->create($value));
    
        collect($request->productMaintenance)
            ->filter(fn($value) => isset($value)) // or just ->filter()
            ->map(fn($value) => [
                'description' => $value,
            ])
            ->each(fn($value) => $product->ProductMaintenance()->create($value));
    
        return back();
    }
    

    顺便说一句。我不认为这是一种臃肿的方法。你清楚地看到正在发生的事情。您正在保存产品及其关系。您可以根据需要移动逻辑,甚至可以将其拆分为不同的方法。

    public function store(AddProductRequest $request)
    {
        $shopperId = 1;
    
        $product = Product::create([
            'title' => $request->title,
            'slug' => to_slug_fa($request->title, Product::class),
            'detail' => $request->detail,
            'color_id' => $request->colorId,
            'category_id' => $request->categoryId,
            'img' => $request->img,
            'description' => $request->description,
        ]);
        
        $this->storePrice($product, $request);
        $this->storeStocks($product, $request, $shopperId);
        $this->storeProductAttributes($product, $request);
        $this->storeProductDetails($product, $request);
        $this->storeProductMaintenances($product, $request);
    
        return back();
    }
    
    private function storePrice(Product $product, $request)
    {
        $product->price()->create(['price' => $request->price]);
    }
    
    private function storeStocks(Product $product, $request, $shopperId);
    {
        collect($request->sizeIds)
            ->filter(fn($value) => $request->quantity[$value] != null);
            ->map(fn($value) => [
                'stock' => $request->quantity[$value],
                'size_id' => $value,
                'shopper_id' => $shopperId,
            ])
            ->each(fn($value) => $product->stock()->create($value));
    }
    
    private function storeProductAttributes(Product $product, $request);
    {
        collect($request->attributeIds)
            ->filter(fn($value) => isset($request->productAttribute[$value]))
            ->map(fn($value) => [
                'attribute_id' => $value,
                'value' => $request->productAttribute[$value],
            ])
            ->each(fn($value) => $product->ProductAttribute()->create($value));
    }
    
    private function storeProductDetails(Product $product, $request);
    {
        collect($request->productDetails)
            ->filter(fn($value) => isset($value)) // or just ->filter()
            ->map(fn($value) => [
                'description' => $value,
            ])
            ->each(fn($value) => $product->ProductDetail()->create($value));
    }
    
    private function storeProductMaintenances(Product $product, $request)
    {
        collect($request->productMaintenance)
            ->filter(fn($value) => isset($value)) // or just ->filter()
            ->map(fn($value) => [
                'description' => $value,
            ])
            ->each(fn($value) => $product->ProductMaintenance()->create($value));
    }
    

    或者创建一个像App\Action\CreateProduct 类一样可以做所有事情的特殊类

    public function store(AddProductRequest $request)
    {
        new CreateProduct($request->validated());
    
        return back();
    }
    

    但这不会使逻辑本身变得更简单。

    【讨论】:

    • 我只想说这是一个绝妙而彻底的答案。哇。我唯一的评论是你可能会用 ->filter() 替换 ->filter(fn($value) => isset($value))
    • 说到底还是有点主观。顺便感谢您的提示!我不知道过滤器可以在没有闭包的情况下使用。我会将其添加为评论
    • 这个答案会帮助很多人。干得好。
    • 非常感谢您的全面指导。这绝对是非常有用的。
    【解决方案2】:

    是的,你可以,而且实际上最好这样做,你可以始终遵循这条规则:让你的控制器让你的模型变胖,让你的视图变笨..

    【讨论】:

      【解决方案3】:

      这是一个主观问题。但是你的问题仍然是合法的。控制器的任务只是接受请求,检查请求是否有效并进一步委托。从现在开始,这取决于您的架构和品味。在您的情况下,我认为可以将存储模型及其关系的逻辑放入服务中。在这里,您可以检查您的代码是否可以使用可以多次使用的代码部分来制作功能。开始就足够了。以后你可能会自己意识到什么是重要的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-04-13
        • 2019-04-30
        • 2013-12-09
        • 2013-11-20
        • 2019-12-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多