Eloquent relations

Relationships such as one-to-one, one-to-many (or many-to-one), and many-to-many are familiar to database programmers. Laravel's Eloquent has brought these concepts into an object-oriented environment. Additionally, Eloquent has even more powerful tools such as polymorphic relations, where entities can be related to more than one other entity. In the following examples, we will see the relationship between accommodations, rooms, and amenities.

Eloquent relations

One-to-one

The first relation is one-to-one. In our example software, we can use the example of a room in our accommodation. A room may only (at least easily) belong to one accommodation, so the room belongs to the accommodation. Inside the Room Eloquent model, the following code tells Eloquent that the room belongs to the accommodation function:

class Room extends Eloquent {
     public function accommodation()
     {
         return $this->belongsTo('MyCompanyAccommodation'),
     }
}

Sometimes, the database tables do not follow the active record pattern, especially if the programmer inherits a legacy database. If the database used a table called bedroom instead of rooms, then the class would add an attribute to indicate the table name:

class Room extends Eloquent {
    protected $table = 'bedroom';
}

When the following route code is executed, then the accommodation object will be returned as a JSON object:

Route::get('test-relation',function(){
    $room = MyCompanyAccommodationRoom::find(1);
    return $room->accommodation;
});

The response will be as follows:

{"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}

Tip

One common mistake is to use the following command:

return $room->accommodation();

In this case, the programmer expects to return the model. This will return the actual belongsTo relation and in the context of the RESTful API, there will be an error thrown:

Object of class IlluminateDatabaseEloquentRelationsBelongsTo could not be converted to string

This is because Laravel can convert the JSON object to a string, but not a relationship.

The SQL run is as follows:

select * from rooms where rooms.id = '1' limit 1
select * from accommodations where accommodations.id = '9' limit 1

Eloquent tends to favor multiple simpler queries as opposed to doing larger joins.

First the room is found. Then, limit 1 is added because find is only used to find a single entity or row. Once the accommodation_id is found, the next query will find the accommodation with that corresponding ID and return the object. If the active record pattern was followed, the SQL that Eloquent produces is extremely readable.

One-to-many

The second relation is one-to-many. In our example software, we can use the example of an accommodation having many rooms. Since rooms may belong to one accommodation, then the the accommodation has many rooms. Inside the Accommodation Eloquent model, the following code tells Eloquent that the accommodation has many rooms.

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

In a similar route, the following code is run. This time, a collection of rooms objects will be returned as JSON-formatted objects inside an array:

Route::get('test-relation',function(){
    $accommodation = MyCompanyAccommodation::find(9);
    return $accommodation->rooms;
});

The response will be the following array:

[{"id":1,"room_number":0,"created_at":null,"updated_at":null, "deleted_at":null,"accommodation_id":9},{"id":3,"room_number": 12,"created_at":"2015-03-14 08:52:25","updated_at":"2015-03-14  08:52:25","deleted_at":null,"accommodation_id":9},{"id":6, "room_number":12,"created_at":"2015-03-14  09:03:36","updated_at":"2015-03-14  09:03:36","deleted_at":null,"accommodation_id":9},{"id": 14,"room_number":12,"created_at":"2015-03-14  09:26:36","updated_at":"2015-03- 1409:26:36","deleted_at":null,"accommodation_id":9}]

The SQL run is as follows:

select * from accommodations where accommodations.id = ? limit 1
select * from rooms where rooms.accommodation_id = '9' and  rooms.accommodation_id is not null

As before, the accommodation is found. The second query will find the rooms that belong to that accommodation. A check is added to confirm that the accommodation_id is not null.

Many-to-many

In our example software application, the relationship between amenity and room is many-to-many. Each room can have many amenities, such as Internet access and a Jacuzzi, and each amenity is shared among many rooms: every room in an accommodation could and should have internet access! The following code, which uses a belongsToMany relationship, enables an amenity to belong to many rooms:

class Amenity {
  public function rooms(){
        return $this- >belongsToMany('MyCompanyAccommodationRoom'),
    }
}

The test route, which tells us how each room has a certain amenity, is written as follows:

Route::get('test-relation',function(){
    $amenity = MyCompanyAccommodationAmenity::find(3);
    return $amenity->rooms;
});

A list of rooms is returned:

[{"id":1,"room_number":0,"created_at":2015-03-14 08:10:45,"updated_at":null,"deleted_at":null, "accommodation_id":9},{"id":5,"room_number":12, "created_at":"2015-03-14 09:00:38","updated_at":"2015-03-14", 09:00:38","deleted_at":null,"accommodation_id":12},
...]

The SQL executed is as follows:

select * from amenities where amenities.id = ? limit 1
select rooms.*, amenity_room.amenity_id as pivot_amenity_id, amenity_room.room_id as pivot_room_id from rooms inner join amenity_room on rooms.id = amenity_room.room_id where amenity_room.amenity_id = 3

We recall the belongToMany relationship that returns the rooms that have a particular amenity:

class Amenity {
   public function rooms(){
        return $this- >belongsToMany('MyCompanyAccommodationRoom'),
    }
}

Eloquent skillfully gives us the corresponding belongsToMany relationship to determine which amenities a particular room has. The syntax is exactly the same:

class Room {
     public function amenities(){
         return $this- >belongsToMany('MyCompanyAccommodationAmenity'),
     }
 }

The test route is virtually the same, just substituting amenities for rooms:

Route::get('test-relation',function(){
    $room = MyCompanyAccommodationRoom::find(1);
    return $room->amenities;
});

The result is a list of amenities for the room with ID 1:

[{"id":1,"name":"Wifi","description":"Wireless Internet Access","created_at":"2015-03-1409:00:38","updated_at":"2015-03-14 09:00:38","deleted_at":null},{"id":2,"name": "Jacuzzi","description":"Hot tub","created_at":"2015-03-14 09:00:38","updated_at":null,"deleted_at":null},{"id":3,"name": "Safe","description":"Safe deposit box for protecting valuables","created_at":"2015-03-1409:00:38","updated_at": "2015-03-1409:00:38","deleted_at":null}]

The query used is as follows:

select * from rooms where rooms.id = 1 limit 1
select amenities.*, amenity_room.room_id as pivot_room_id, amenity_room.amenity_id as pivot_amenity_id from amenities inner join amenity_room on amenities.id = amenity_room.amenity_id where amenity_room.room_id = '1'

The query, substituting room_id for amenity_id and rooms for amenities, is clearly parallel.

Has-many-through

One great feature of Eloquent is "has-many-through". What if the requirements of the software change and we are asked to group some of the accommodations together into franchises? If an application user would like to search for a room, any of the rooms in any of the accommodations that belong to that franchise could be found. A franchises table will be added, and a nullable column to the accommodations table called franchise_id will be added. This will optionally allow for an accommodation to belong to a franchise. Rooms already belong to accommodations through the accommodation_id column.

A room belongs to an accommodation through its accommodation_id key, while an accommodation belongs to a franchise through its franchise_id key.

Eloquent allows us to retrieve the rooms associated to a franchise by using hasManyThrough:

<?php namespace MyCompany;

use IlluminateDatabaseEloquentModel;

class Franchise extends Model {

    public function rooms()
    {
        return $this- >hasManyThrough('MyCompanyAccommodationRoom', 'MyCompanyAccommodation'),
    }
}

The hasManyThrough relationship takes the target or the "has" as its first parameter (in this case, the room) and its "through" as the second parameter (in this case, the accommodation).

The logic stated as a phrase is: This franchise has many rooms through its accommodations.

Using the previous test route, the code is written as follows:

Route::get('test-relation',function(){
    $franchise = MyCompanyFranchise::find(1);
    return $franchise->rooms;
});

The rooms are returned as an array as would be expected:

[{"id":1,"room_number":0,"created_at":null,"updated_at":null,"deleted_at":null,"accommodation_id":9,"franchise_id":1}, {"id":3,"room_number":12,"created_at":"2015-03-14 08:52:25","updated_at":"2015-03-14 08:52:25","deleted_at":null,"accommodation_id":9, "franchise_id":1},{"id":6,"room_number":12,"created_at":"2015-03-14 09:03:36","updated_at":"2015-03-14 09:03:36","deleted_at":null,"accommodation_id":9, "franchise_id":1},
]

The queries executed are as follows:

select * from franchises where franchises.id = ? limit 1
select rooms.*, accommodations.franchise_id from rooms inner join accommodations on accommodations.id = rooms.accommodation_id where accommodations.franchise_id = 1
..................Content has been hidden....................

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