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

Laravel HasManyThrough关联创建投票时触发内存耗尽错误

Laravel投票系统种子器执行内存耗尽问题

错误信息

执行种子器创建投票数据时,即使只创建单条数据也触发内存耗尽错误:

PHP Fatal error:  Allowed memory size of 2147483648 bytes exhausted (tried to allocate 20480 bytes) in /Users/gabriel/Desktop/proyectos/laraapp.com/vendor/monolog/monolog/src/Monolog/Logger.php on line 350
PHP Fatal error:  Allowed memory size of 2147483648 bytes exhausted (tried to allocate 20480 bytes) in /Users/gabriel/Desktop/proyectos/laraapp.com/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php on line 203

已尝试将PHP的memory_limit调整为1024M和2048M,问题仍存在。

模型关联代码

Poll模型

public function options()
{
    return $this->hasMany('App\Models\Option');
}

public function votes()
{
    return $this->hasManyThrough('App\Models\Vote', 'App\Models\Option');
}

Option模型

public function poll()
{
    return $this->belongsTo('App\Models\Poll');
}

public function votes()
{
    return $this->hasMany('App\Models\Vote');
}

Vote模型

public function user()
{
    return $this->belongsTo('App\Models\User');
}

public function option()
{
    return $this->belongsTo('App\Models\Option');
}

数据库迁移代码

Polls表迁移

Schema::create('polls', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->string('question');
    $table->integer('isActive');
    $table->timestamps();
});

Options表迁移

Schema::create('options', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->unsignedBigInteger('poll_id')->index()->nullable();
    $table->foreign('poll_id')->references('id')->on('polls')->onDelete('cascade')->onUpdate('cascade')->nullable();
    $table->string('text');
    $table->timestamps();
});

Votes表迁移

Schema::create('votes', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->unsignedBigInteger('user_id')->index()->nullable();
    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade')->nullable();
    $table->unsignedBigInteger('option_id')->index()->nullable();
    $table->foreign('option_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade')->nullable();
    $table->unique(['user_id', 'option_id']);
    $table->timestamps();
});

种子器与JSON数据

Polls.json内容

[
    { "isActive" : 1, "question" : "¿Qué es lo más importante para ti en una primera cita?",  "optionOne" : "Una buena conversación", "optionTwo" : "Atracción física", "optionThree" : "Sentido del humor","optionFour" : "Intereses comunes"  }
]

PollsSeeder代码

$json = File::get(base_path() . '/json/Polls.json');
$polls = json_decode($json, true);

Log::info('Decoded JSON:', $polls);

foreach ($polls as $key => $data) 
{
    $poll = Poll::create([
        'question' => $data['question'],
        'isActive' => $data['isActive'],
    ]);

    if($data['optionOne'] != '')
    {
        Option::create([
            'poll_id' => $poll->id,
            'text' => $data['optionOne'],
        ]);
    }

    if($data['optionTwo'] != '')
    {
        Option::create([
            'poll_id' => $poll->id,
            'text' => $data['optionOne'],
        ]);
    }

    if($data['optionThree'] != '')
    {
        Option::create([
            'poll_id' => $poll->id,
            'text' => $data['optionOne'],
        ]);
    }

    if($data['optionFour'] != '')
    {
        Option::create([
            'poll_id' => $poll->id,
            'text' => $data['optionOne'],
        ]);
    }
}

问题分析与修复方案

1. 数据库外键配置错误(核心问题)

在Votes表迁移中,option_id的外键错误指向了users.id,正确应该指向options.id。这个错误会导致数据库约束异常,Laravel在处理异常时可能触发无限日志循环,最终耗尽内存。

修复后的Votes迁移代码:

Schema::create('votes', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->unsignedBigInteger('user_id')->index()->nullable();
    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade')->nullable();
    $table->unsignedBigInteger('option_id')->index()->nullable();
    // 修正外键关联表为options
    $table->foreign('option_id')->references('id')->on('options')->onDelete('cascade')->onUpdate('cascade')->nullable();
    $table->unique(['user_id', 'option_id']);
    $table->timestamps();
});

2. 种子器数据赋值错误

创建Option时,optionTwo、optionThree、optionFour的text字段都错误使用了data['optionOne'],导致所有选项内容重复,需修正为对应字段:

if($data['optionTwo'] != '')
{
    Option::create([
        'poll_id' => $poll->id,
        'text' => $data['optionTwo'], // 改为optionTwo
    ]);
}

if($data['optionThree'] != '')
{
    Option::create([
        'poll_id' => $poll->id,
        'text' => $data['optionThree'], // 改为optionThree
    ]);
}

if($data['optionFour'] != '')
{
    Option::create([
        'poll_id' => $poll->id,
        'text' => $data['optionFour'], // 改为optionFour
    ]);
}

3. 内存优化建议

  • 执行迁移刷新:运行php artisan migrate:fresh --seed确保数据库结构完全更新。
  • 批量创建选项:改用$poll->options()->createMany()减少DB查询次数,降低内存占用:
    $options = [];
    foreach(['optionOne', 'optionTwo', 'optionThree', 'optionFour'] as $key) {
        if(!empty($data[$key])) {
            $options[] = ['text' => $data[$key]];
        }
    }
    $poll->options()->createMany($options);
    
  • 临时关闭模型事件:如果模型有观察者或事件监听,可在种子器开头添加Model::unsetEventDispatcher();避免不必要的内存消耗。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 21:57:04