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

Laravel中MongoDB子文档自动生成ObjectId(_id)的配置方法

Laravel中MongoDB子文档自动生成ObjectID的实现方法

不用手动给每个子文档写new \MongoDB\BSON\ObjectID(),可以通过模型层的处理自动完成,下面是几种可行的方案:

方案一:模型内部监听保存事件

在Product模型的boot方法中监听saving事件,保存前自动为skus数组里的子文档补全_id:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use MongoDB\BSON\ObjectID;

class Product extends Model
{
    protected $connection = 'mongodb';
    protected $collection = 'products';

    protected static function boot()
    {
        parent::boot();

        static::saving(function ($product) {
            if (isset($product->skus) && is_array($product->skus)) {
                $product->skus = array_map(function ($sku) {
                    // 仅当子文档没有_id时生成ObjectID
                    if (!isset($sku['_id'])) {
                        $sku['_id'] = new ObjectID();
                    }
                    return $sku;
                }, $product->skus);
            }
        });
    }
}

方案二:使用字段修改器

给skus字段定义修改器,赋值时自动处理子文档:

class Product extends Model
{
    protected $connection = 'mongodb';
    protected $collection = 'products';

    public function setSkusAttribute($value)
    {
        $processedSkus = array_map(function ($sku) {
            if (!isset($sku['_id'])) {
                $sku['_id'] = new ObjectID();
            }
            return $sku;
        }, $value);

        $this->attributes['skus'] = $processedSkus;
    }
}

使用时直接按原来的代码赋值即可,修改器会自动处理skus数组。

方案三:全局Trait复用(多模型通用)

如果多个模型都有子文档需要自动生成_id,可以封装成Trait,避免重复代码:
首先创建Trait:

namespace App\Traits;

use MongoDB\BSON\ObjectID;

trait AutoSubdocumentObjectId
{
    protected static function bootAutoSubdocumentObjectId()
    {
        static::saving(function ($model) {
            // 模型中需定义$subdocumentFields指定要处理的字段
            if (property_exists($model, 'subdocumentFields')) {
                foreach ($model->subdocumentFields as $field) {
                    if (isset($model->{$field}) && is_array($model->{$field})) {
                        $model->{$field} = array_map(function ($item) {
                            if (!isset($item['_id'])) {
                                $item['_id'] = new ObjectID();
                            }
                            return $item;
                        }, $model->{$field});
                    }
                }
            }
        });
    }
}

然后在Product模型中使用:

class Product extends Model
{
    use \App\Traits\AutoSubdocumentObjectId;

    protected $connection = 'mongodb';
    protected $collection = 'products';
    protected $subdocumentFields = ['skus']; // 指定需要处理的子文档字段
}

以上三种方案都能实现你的需求,执行原来的代码时,未手动指定_id的子文档会自动生成ObjectID。

内容的提问来源于stack exchange,提问作者kanu mahajan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:50:15