Laravel多态多对多关联问题:表不存在报错排查
问题描述
业务场景
学生可拥有普通文档(一对多关联),同时学生的护照、签证等也可关联文档,且单个文档可关联多个对象。
Student模型代码
class Student extends Model { public function documents() { return $this->hasMany('App\StudentDocument'); } public function visas() { return $this->hasMany('App\StudentVisa'); } public function passports() { return $this->hasMany('App\StudentPassport'); } }
StudentPassport模型代码
class StudentPassport extends Model { public function student_documents() { return $this->morphToMany(StudentDocument::class, 'student_documentable'); } }
护照存储逻辑代码
public function store(StudentPassportRequest $request, $student_id) { $student = Student::findOrFail($student_id); $passport = $student->passports()->create($request->all()); if ($request->file('student_document_file')->isValid()) { $uploaded_file = $request->file('student_document_file'); $filename = time().'-'.$uploaded_file->getClientOriginalName(); Storage::disk('local')->putFileAs( 'student_document_files/'. \Auth::user()->userable_id .'/'. $student_id .'/', $uploaded_file, $filename ); $student_document = new StudentDocument; $student_document->filename = $filename; $student_document->student_document_type_id = StudentDocumentType::where('student_document_type','Passport')->first()->id; $student_document->original_filename = $uploaded_file->getClientOriginalName(); $student_document->mime = $uploaded_file->getMimeType(); $student_document->student_id=$student_id; $passport->student_documents()->save($student_document); } return redirect('/baadmin/students/'. $student_id .'#kt_tabs-passports')->with('flash_message', ['success','Created Successfully','Student Passport "'. $request->input('passport_number') .'" created successfully!']); }
报错信息
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'student_documentables' doesn't exist INSERT INTO `student_documentables` ( `student_document_id`, `student_documentable_id`, `student_documentable_type` ) VALUES (5, 503, App \ StudentPassport)
student_documents表结构
CREATE TABLE `student_documents` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `student_id` bigint(20) unsigned NOT NULL, `student_document_type_id` bigint(20) unsigned NOT NULL, `filename` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, `mime` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, `original_filename` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, `deleted_at` timestamp NULL DEFAULT NULL, `primary_date` datetime NOT NULL DEFAULT current_timestamp(), `secondary_date` datetime DEFAULT NULL, `student_documentable_id` bigint(20) DEFAULT NULL, `student_documentable_type` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`), KEY `student_id_index` (`student_id`), KEY `student_document_type_id_index` (`student_document_type_id`), KEY `student_documentable_id_index` (`student_documentable_id`) ) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
核心问题
参照Laravel官方文档配置多态多对多关联,将示例中的tag改为student_documents后,Laravel尝试写入student_documentables表,但实际关联字段已存在于student_documents表中,请问配置哪里出错了?
解决方案
你用错了关联类型:当前使用的morphToMany是多态多对多关联,这种关联要求单独的中间表(也就是Laravel自动查找的student_documentables),但你的表结构是把多态关联字段(student_documentable_id、student_documentable_type)直接存在了student_documents表中,这属于多态一对多关联的结构,需要调整关联定义:
- 修改StudentPassport模型的关联
将morphToMany替换为morphMany,因为一个护照可以对应多个文档:
class StudentPassport extends Model { public function student_documents() { return $this->morphMany(StudentDocument::class, 'student_documentable'); } }
- 给StudentDocument模型添加反向多态关联
添加morphTo方法,让文档可以关联回对应的护照/签证等模型:
class StudentDocument extends Model { // 反向多态关联,指向拥有该文档的模型(护照、签证等) public function student_documentable() { return $this->morphTo(); } // 保留原有的和学生的一对多关联 public function student() { return $this->belongsTo(Student::class); } }
- 存储逻辑无需修改
原来的$passport->student_documents()->save($student_document);代码可以正常使用,morphMany的save方法会自动把护照的ID和类型填充到student_documents表的student_documentable_id和student_documentable_type字段中,不会再去寻找中间表。
逻辑说明
你的业务场景里,单个文档属于一个特定对象(要么是学生的普通文档,要么是某个护照/签证的文档),同时一个护照可以拥有多个文档,这完全符合多态一对多的适用场景;而多态多对多是用于「一个文档可以同时属于多个护照/签证,同时一个护照也可以关联多个文档」的场景,需要额外的中间表维护关联关系,显然和你的表结构不匹配。
内容的提问来源于stack exchange,提问作者SupaMonkey
相关产品推荐
相关产品推荐

