You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 17:31:15