I have Locations
model which hasMany Employees
-- similarly Employees
belongsTo Locations
This is nice and works well, but then I looked at adding PhoneNumbers
. Either a Location
or an Employee
could have a phone number (office numbers versus personal numbers)
Logically:
Locations
hasMany PhoneNumbers
(multiple office lines)
and
Employees
hasMany PhoneNumbers
(home / cell ?)
However when you create a hasMany relationship like this in Laravel it adds a field to the PhoneNumbers
table. So we now have two fields: location_id
and employee_id
I can get this to work if I make location_id
and employee_id
nullable, like so:
+----+--------------+-------------+-------------+
| 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 |
...
However this doesn't scale very well if I add new entities that can possess phone numbers (customers? job applicants? suppliers?)
How can I create multiple separate many-to-many relationships with the same secondary table?
Note: In this example I could just create a phone_number
field on each individual tables (locations.phone_number
, employees.phone_number
, etc) however I wish to avoid this for two reasons:
- Data integrity (if all phone numbers are in one common table it's easy to verify duplicate phone numbers are not entered)
- Binding to more complex models (replace
PhoneNumber
withImage
and now you have a lot more data to deal with)
You're looking for Laravel's polymorphic relationship. Instead of creating a new field for each related table, you have two fields: related id and related type.
On both your Location and Employee model, add the following relationship:
On your PhoneNumber model, add the following relationship:
On your phone_numbers table, add two new fields: phonable_type and phonable_id. In a migration, these fields are added with the
morphs()
method:$table->morphs('phonable');
Once everything is setup, your data would look like this:
With this setup, you can make any model you want
phonable
just by adding amorphOne()
ormorphMany()
relationship to it.Additionally, the relationship attributes will generate the correct model related to the type. Given the data above:
The documentation on polymorphic relationships can be found here (4.2) or here (5.0).