Невозможно создать несколько отношений "многие ко многим" в сводной таблице


Я пытаюсь создать приложение в Laravel 5, которое отслеживает медицинские осмотры пациентов, чтобы я мог создавать историю пациентов, я использую Красноречивый в качестве рекомендации по абстракции модели.

Я создал три таблицы и сводную таблицу для этой цели с помощью миграции laravel, они показаны на диаграмме ниже: enter image description here

Пользователи могут быть либо врачами, либо администраторами, они вводят результаты оценки пациента для конкретного экзамена, который они проводят, например, у меня есть сводная таблица, которая включает три внешних ключа для связи трех сущностей:

  • идентификатор экзамена
  • идентификатор пациента_id
  • идентификатор пользователя

В Красноречивом я сделал эти Многие ко многим моделям, следуя документации Laravel Многие ко многим для поддержки предлагаемой бизнес-логики:

<?php
    // Patient.php
    class Patient extends Model
    {
        public function exams() {

            return $this->belongsToMany('App\Exam', 'exam_patient');
        }
    }

    // Exam.php
    class Exam extends Model
    {
        public function patients()
        {

            return $this->belongsToMany('App\Patient', 'exam_patient');
        }

        public function users()
        {

            return $this->belongstoMany('App\User', 'exam_patient');
        }
    }

    // User.php
    class User extends Model
    {
        public function exams() {

            return $this->belongsToMany('App\Exam', 'exam_patient');
        }
    }

В tinker я пытаюсь смоделировать общий вариант использования, прежде чем вносить серьезные изменения: Врач проводит осмотр пациента:

 >>> $user= App\User::first()
=> App\User {#671
     id: "1",
     name: "Victor",
     email: "",
     created_at: "2015-10-10 18:33:54",
     updated_at: "2015-10-10 18:33:54",
   }

>>> $patient= App\Patient::first()
=> App\Patient {#669
     id: "1",
     username: "",
     name: "Tony",
     lastname: "",
     birthday: "0000-00-00",
     created_at: "2015-10-10 18:32:56",
     updated_at: "2015-10-10 18:32:56",
   }

>>> $exam= App\Exam::first()
=> App\Exam {#680
     id: "1",
     name: "das28",
     title: "Das28",
     created_at: "2015-10-10 18:31:31",
     updated_at: "2015-10-10 18:31:31",
   }

>>> $user->save()
=> true
>>> $patient->save()
=> true
>>> $exam->save()
=> true

Проблема в том, когда я пытаюсь связать (или прикрепить) на по крайней мере, в двух моделях я получаю следующую ошибку:

   >>> $patient->exams()->attach(1) 
Illuminate\Database\QueryException with message
    'SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a
    child row: a foreign key constraint fails (`cliniapp`.`exam_patient`, CONSTRAINT
    `exam_patient_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
    ON DELETE CASCADE) (SQL: insert into `exam_patient` (`exam_id`, `patient_id`)
    values (1, 1))'

Как я могу связать три модели, если я даже не могу прикрепить две из них? Есть ли в tinker какой-либо способ массово прикрепить их, чтобы я мог связать их в своей сводной таблице (exam_patient), и MySQL не показывает эту ошибку ИЛИ если я ошибаюсь в своем подходе? Любая помощь будет очень признательна.

Author: tblancog, 2015-10-12

1 answers

Когда в ваших отношениях Много-много есть дополнительные поля, вы должны "зарегистрировать" их при объявлении отношения, как показано в разделе Получение столбцов промежуточной таблицы приведенной выше ссылки.

Поэтому вы всегда должны объявлять свои "дополнительные" сводные таблицы, которые различаются в зависимости от того, где вы определяете взаимосвязь:

class Patient extends Model
{
    public function exams()
    {
        return $this->belongsToMany('App\Exam', 'exam_patient')->withPivot('user_id');
    }
}

class Exam extends Model
{
    public function patients()
    {
        return $this->belongsToMany('App\Patient', 'exam_patient')->withPivot('user_id');
    }

    public function users()
    {
        return $this->belongstoMany('App\User', 'exam_patient')->withPivot('patient_id');
    }
}

class User extends Model
{
    public function exams()
    {
        return $this->belongsToMany('App\Exam', 'exam_patient')->withPivot('patient_id');
    }
}

В разделе Присоединение/отсоединение вы можете увидеть, что вы можете установить эти дополнительные поля вот так:

$patient->exams()->attach($exam->id, ['user_id' => $user->id]);

Ваша проблема заключалась в том, что ограничение внешнего ключа user_id в вашей таблице exam_patient не срабатывало, когда вы создавали отношения пациент/экзамен без передачи данных для столбца user_id, поэтому вышеприведенное будет работать.

Если вы по какой-либо причине не хотите всегда передавать идентификатор пользователя таким образом, вы также можете сделать этот столбец идентификатора пользователя недействительным в базе данных.

Конечно, можно утверждать, что использование двух сводных таблиц (exam_patient и exam_user), или использование отношений "многие ко многим" полиморфных здесь было бы лучшим подходом.


Как мы можем видеть из документов Laravel по полиморфным отношениям, для их реализации здесь мы бы удалили таблицу exam_patient для экзаменуемой таблицы, в которой было бы 3 столбца: exam_id, examable_id и examable_type.

Модели будут выглядеть следующим образом:

class Patient extends Model
{
    public function exams()
    {
        return $this->morphToMany('App\Exam', 'examable');
    }
}

class Exam extends Model
{
    public function patients()
    {
        return $this->morphedByMany('App\Patient', 'examable');
    }

    public function users()
    {
        return $this->morphedByMany('App\User', 'examable');
    }
}

class User extends Model
{
    public function exams()
    {
        return $this->morphToMany('App\Exam', 'examable');
    }
}

Чтобы полностью обновить проверяемую таблицу, вы должны сделать что-то вроде:

$exam->patients()->save($patient);
$exam->users()->save($user);

Итак, это два запросы вместо одного, вы теряете связи с внешним ключом, а "проверяемые" - неудобное название.

Аргументом в пользу этого было бы то, что отношения немного более декларативны, и проще провести один экзамен для одного пациента, принадлежащего нескольким пользователям/врачам.

 4
Author: Johnny Magrippis, 2015-10-12 08:01:26