Polymorphic relations

One great feature of Eloquent is the possibility to have an entity whose relationship is polymorphic. The two parts of the word, poly and morphic, are from the Greek language. Since poly means many and morphic means shape, we can now easily imagine a relationship having multiple forms.

Amenitiable relationships

An amenity in our example software is something that is associated with a room, such as a Jacuzzi. Certain amenities, such as covered parking or an airport shuttle service, could also relate to an accommodation itself. We could create two pivot tables for this, one called amenity_room and another called accommodation_amenity. Another great way to do this is to combine the two into one table and use a field to distinguish between the two types or relationship.

To do this, we will need a field to distinguish between amenity and room and amenity and room, something we could call a relationship type. Laravel's Eloquent skillfully handles this automatically.

Eloquent uses the suffix -able to make this happen. In our example, we would create a table that has the following fields:

  • id
  • name
  • description
  • amenitiable_id
  • amenitiable_type

The first three fields are familiar, but two new fields added. One of them will contain the ID of either the accommodation or the room.

The Amenity table structure

For example, given a room with ID 5, amenitiable_id will be 5 while amenitiable_type will be Room. Given an accommodation with ID 5, amenitiable_id will be 5 while amenitiable_type will be Accommodation:

id

name

description

amenitiable_id

amenitiable_type

1

Wireless internet

Internet conn.

5

Room

2

Covered parking

Parking in garage

5

Accommodation

3

Sea view

Ocean view from room

5

Room

The Amenity model

In terms of code, the Amenity model will now contain an "amenitiable" function:

<?php
namespace MyCompanyAccommodation;

use IlluminateDatabaseEloquentModel;

class Amenity extends Model
{
    public function rooms(){
        return $this->belongsToMany('MyCompanyAccommodationRoom'),
    }
    public function amenitiable()
    {
        return $this->morphTo();
    }

The Accommodation model

The Accommodation model will change the amenities method to use morphMany instead of hasMany:

<?php namespace MyCompany;

use IlluminateDatabaseEloquentModel;

class Accommodation extends Model {
    public function rooms(){
        return $this->hasMany('MyCompanyAccommodationRoom'),
    }

    public function amenities()
    {
        return $this- >morphMany('MyCompanyAccommodationAmenity', 'amenitiable'),
    }
}

The Room model

The Room model will contain the same morphMany method:

<?php
namespace MyCompanyAccommodation;

use IlluminateDatabaseEloquentModel;

class Room extends Model
{
    protected $casts = ['room_number'=>'integer'];
    public function accommodation(){
        return $this->belongsTo('MyCompanyAccommodation'),
    }
    public function amenities() {
        return $this- >morphMany('MyCompanyAccommodationAmenity', 'amenitiable'),
    }

}

Now, when the amenities are requested for a room or an accommodation, Eloquent will automatically distinguish between them:

$accommodation->amenities();
$room->amenities();

Each of these functions returns the correct type of amenity for room and for accommodation.

Many-to-many polymorphic relations

It is possible, though, that some amenities could be shared between a room and an accommodation. In this case, a many-to-many polymorphic relation is used. The pivot table now adds several fields:

amenity_id

amenitiable_id

amenitiable_type

1

5

Room

1

5

Accommodation

2

5

Room

2

5

Accommodation

As illustrated, both the room with ID 5 and the accommodation with ID 5 have amenities with IDs 1 and 2.

Has relationships

If we would like to select all of the accommodations that are associated to a franchise, the has() method is used, where the relation is passed as the parameter:

MyCompanyAccommodation::has('franchise')->get();

We will get the following JSON array:

[{"id":9,"name":"LovelyHotel","description":"Lovely Hotel Greater Pittsburgh","location_id":1,"created_at":null,"updated_at": "2015-03-13 22:00:23","deleted_at":null,"franchise_id":1}, {"id":12,"name": "Grand Hotel","description":"Grand Hotel Greater Cleveland","location_id":2,"created_at": "2015-02-0820:09:35","updated_at": "2015-02-0820:09:35","deleted_at":null,"franchise_id":1}]

Notice that the franchise_id value is 1, which means the accommodations have a franchise associated with them. Optionally, a where may be added to the has creating a whereHas function. The code is as follows:

MyCompanyAccommodation::whereHas('franchise',
                  function($query){
      $query->where('description','like','%Pittsburgh%'), 
      })->get();

Notice that whereHas takes a closure as its second parameter.

This would return only the accommodations where the description contains Pittsburgh, so the returned array would contain only results like this:

[{"id":9,"name":"LovelyHotel","description":"Lovely Hotel Greater Pittsburgh","location_id":1,"created_at":null,"updated_at": "2015-03-13 22:00:23","deleted_at":null,"franchise_id":1}]

Eager loading

Another great mechanism that Eloquent provides is eager loading. If we want return all of the franchises together with all of their accommodations, we simply need to add an accommodations function to our Franchise model as follows:

    public function accommodations()
    {
        return $this->hasMany('MyCompanyAccommodation'),
    }

Then, by adding a with clause to the statement, the accommodations are returned for each franchise:

MyCompanyFranchise::with('accommodations')->get();

We can also list the rooms associated with each accommodation as follows:

MyCompanyFranchise::with('accommodations','rooms')->get();

If we want to return the rooms nested inside of the accommodation array, then the following syntax should be used:

MyCompanyFranchise::with('accommodations','accommodations.rooms') ->get();

We will get the following output:

[{"id":1,"accommodations":
[
{"id":9,
"name":"Lovely Hotel",
"description":"Lovely Hotel Greater Pittsburgh",
"location_id":1,
"created_at":null,
"updated_at":"2015-03-13 22:00:23",
"deleted_at":null,
"franchise_id":1,
"rooms":[{"id":1,"room_number":0,"created_at":null,"updated_at": null,"deleted_at":null,"accommodation_id":9},
]},
{"id":12,"name":"GrandHotel","description":"Grand Hotel Greater Cleveland","location_id":2,"created_at":"2015-02-08…

In this example, rooms is contained within accommodation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset