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
相关产品推荐
相关产品推荐

