Laravel 9中无需array_merge按关联分组ProductOption
Laravel 9 产品选项按
region_code分组优化方案 我正在构建一个Laravel 9项目,已创建Product与ProductOption模型。需要将产品选项按region_code字段(该字段并非始终有值)分组展示。我知道执行$product->options->groupBy('region_code')可得到预期结果,但希望分组后的选项作为Product模型的一部分返回,目前用array_merge实现——虽然可行,但要转数组再合并,感觉繁琐。我曾尝试在with闭包中使用groupBy('region_code'),但未得到有效结果。
当前代码及问题
当前实现代码:
$selectedProduct = $request->input('product'); $selectedType = $request->input('type'); $product = Product::where('slug', $selectedProduct) ->where('is_enabled', true) ->with(['options' => function ($query) use ($selectedType) { $query->where('is_enabled', true) ->whereNotNull('region_code') ->where('slug', like, "%$selectedType%"); }])->first(); if (! $product) { return response()->json([ 'message' => "No product found." ], 404); } $product = array_merge($product->toArray(), [ 'options' => $product->options->groupBy('region_code')->toArray() ]); return response()->json([ 'product' => $product ], 200);
我尝试过直接覆盖options属性,但分组效果完全丢失:
$options = $product->options->groupBy('region_code'); $product->options = $options; return response()->json([ 'product' => $product ], 200);
优化方案
1. 新增模型访问器(推荐)
在Product模型中定义访问器,直接返回分组后的选项:
// app/Models/Product.php public function getGroupedOptionsAttribute() { return $this->options->groupBy('region_code'); }
若需要在JSON输出中自动包含该字段,在模型的$appends数组中添加:
protected $appends = ['grouped_options'];
之后直接返回$product即可,JSON响应会自动带上grouped_options字段。如果想直接替换原options字段,可先重命名原关联:
// app/Models/Product.php // 重命名原关联避免冲突 public function rawOptions() { return $this->hasMany(ProductOption::class); } // 定义访问器替换原options字段 public function getOptionsAttribute() { return $this->rawOptions->groupBy('region_code'); }
查询时将with(['options' => ...])改为with(['rawOptions' => ...]),之后$product->options就是分组后的结果。
2. 直接修改序列化数据
在返回JSON前,直接处理模型的数组形式,比array_merge更直观:
$productArray = $product->toArray(); $productArray['options'] = $product->options->groupBy('region_code')->toArray(); return response()->json([ 'product' => $productArray ], 200);
3. 数据库层面分组(适合大数据量场景)
若想在查询阶段完成分组,可自定义关联,但需处理数据序列化,灵活性不如集合分组:
// app/Models/Product.php public function groupedOptions() { return $this->hasMany(ProductOption::class) ->selectRaw('*, GROUP_CONCAT(id) as option_ids') ->groupBy('region_code'); }
模型表结构
Product表结构
Schema::create('products', function (Blueprint $table) { $table->uuid('id')->primary(); $table->string('title'); $table->string('slug')->unique(); $table->string('description')->nullable(); $table->json('extra')->nullable(); $table->boolean('is_enabled')->default(1)->index(); $table->timestamps(); $table->softDeletes(); });
ProductOption表结构
Schema::create('product_options', function (Blueprint $table) { $table->uuid('id')->primary(); $table->foreignUuid('product_id'); $table->string('title'); $table->string('slug')->unique(); $table->string('description')->nullable(); $table->string('region_code')->nullable()->index(); $table->json('extra')->nullable(); $table->string('stripe_id')->unique(); $table->integer('quantity')->default(1); $table->boolean('is_enabled')->default(1)->index(); $table->timestamps(); $table->softDeletes(); });
内容的提问来源于stack exchange,提问作者Ryan H
相关产品推荐
相关产品推荐

