Laravel: многие-ко-многим с общим столом

У меня есть модель Locations которой есть много Employees – аналогично Employees местным местам

Это хорошо и хорошо работает, но потом я посмотрел на добавление PhoneNumbers . Либо Location либо Employee могут иметь номер телефона (служебные номера и личные номера)

Логически:

Locations hasMany PhoneNumbers (несколько офисных линий) и Employees hasMany PhoneNumbers (home / cell?)

Однако, когда вы создаете отношения hasMany, подобные этому в Laravel, он добавляет поле в таблицу PhoneNumbers . Итак, теперь у нас есть два поля: location_id и employee_id

Я могу заставить это работать, если я сделаю location_id и employee_id нулевыми, например:

 +----+--------------+-------------+-------------+ | id | number | location_id | employee_id | +----+--------------+-------------+-------------+ | 1 | 800-555-0123 | 1 | null | | 2 | 800-555-0124 | 1 | null | | 3 | 800-555-0125 | 1 | null | | 4 | 859-555-0199 | null | 1 | ... 

Однако это не очень хорошо масштабируется, если я добавляю новые объекты, которые могут иметь номера телефонов (клиенты? Кандидаты на работу? Поставщики?)

Как я могу создать несколько отдельных отношений «многие ко многим» с одной и той же вторичной таблицей?

Примечание. В этом примере я мог бы просто создать поле phone_number на каждой отдельной таблице ( locations.phone_number , employees.phone_number и т. Д.), Однако я хочу избежать этого по двум причинам:

  1. Целостность данных (если все телефонные номера находятся в одной общей таблице, легко проверить, что дублированные номера телефонов не введены)
  2. Привязка к более сложным моделям (замените PhoneNumber на Image и теперь у вас есть гораздо больше данных для решения)

Вы ищете полиморфные отношения Ларавеля. Вместо создания нового поля для каждой связанной таблицы вы имеете два поля: связанный идентификатор и родственный тип.

В вашей модели Location и Employee добавьте следующее соотношение:

 public function phones() { return $this->morphMany('PhoneNumber', 'phonable'); } 

В вашей модели PhoneNumber добавьте следующее соотношение:

 public function phonable() { return $this->morphTo(); } 

В таблице phone_numbers добавьте два новых поля: phonable_type и phonable_id. При миграции эти поля добавляются с помощью метода $table->morphs('phonable'); morphs() : $table->morphs('phonable');

Когда все будет установлено, ваши данные будут выглядеть так:

 +----+--------------+-------------+---------------+ | id | number | phonable_id | phonable_type | +----+--------------+-------------+---------------+ | 1 | 800-555-0123 | 1 | Location | | 2 | 800-555-0124 | 1 | Location | | 3 | 800-555-0125 | 1 | Location | | 4 | 859-555-0199 | 1 | Employee | ... 

С помощью этой установки вы можете сделать любую модель, которую хотите phonable только добавив к ней отношение morphOne() или morphMany() .

Кроме того, атрибуты отношений будут генерировать правильную модель, связанную с типом. Учитывая приведенные выше данные:

 var_dump(PhoneNumber::find(1)->phonable); // will dump Location object var_dump(PhoneNumber::find(4)->phonable); // will dump Employee object 

Документацию по полиморфным отношениям можно найти здесь (4.2) или здесь (5.0) .