Laravel ORM Eloquent is brilliant!
That being said, let’s try to tackle very common questions that appear when you try to get related models loaded.
Want to skip the preface and get Straight to The Point?
Why don’t we create a popular category-post relation in not less popular MySQL db ?
So here we go!
1 2 3 4 |
1 one CATEGORY has multiple POSTS. 2 one POST is within only one CATEGORY. |
What we have here is simple ONE-TO-MANY relation that we can handle without any additional pivot tables.
Here are the tables:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
-- -- Let's keep it very simple, no constraints, only basic fields CREATE TABLE categories ( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL ); CREATE TABLE posts ( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, category_id INT(10) UNSIGNED ); |
Of course we don’t want to run SQL queries ourselves, so for the artisans, a migration files
app/database/migrations/TIMESTAMP_create_categories_table.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; class CreateCategoriesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('categories', function(Blueprint $table) { $table->increments('id'); $table->string('name', 255); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('categories'); } } |
app/database/migrations/TIMESTAMP_create_posts_table.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; class CreatePostsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('posts', function(Blueprint $table) { $table->increments('id'); $table->string('title', 255); $table->integer('category_id')->unsigned(); // remember that increments() makes the field unsigned! }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('posts'); } } |
Next let’s setup our relations:
app/models/Category.php
1 2 3 4 5 6 7 8 9 10 11 |
<?php class Category extends \Eloquent { public function posts() { return $this->hasMany('Post'); } } |
app/models/Post.php
1 2 3 4 5 6 7 8 9 10 11 |
<?php class Post extends \Eloquent { public function category() { return $this->belongsTo('Category'); } } |
Seed the database …
Now, say you want to get all the categories and all the related posts without unwanted redundant queries – all we have to do is eager load relation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// Category::with('posts')->get(); // it will get collection of categories with collections of posts related to them, like this: [4] > Category::with('posts')->get()->toArray(); array( 0 => array( 'id' => '1', 'name' => 'noname', 'posts' => array( 0 => array( 'id' => '1', 'title' => 'first', 'category_id' => '1' ), 1 => array( 'id' => '3', 'title' => 'some long title', 'category_id' => '1' ) ) ), 1 => array( 'id' => '2', 'name' => 'dev', 'posts' => array( ) ), 2 => array( 'id' => '3', 'name' => 'blog', 'posts' => array( 0 => array( 'id' => '2', 'title' => 'second post', 'category_id' => '3' ) ) ) ) |
Now, something harder, what I’m asked about pretty often. Let’s get all the categories and their related posts, but limited in any way:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// // get categories with posts that have title like '%st%'; Category::with(['posts' => function ($query) { $query->where('title', 'LIKE', '%st%'); }])->get(); // it will get for us: [5] > Category::with(['posts' => function ($query) { [5] *> $query->where('title', 'LIKE', '%st%'); [5] *> }])->get()->toArray(); array( 0 => array( 'id' => '1', 'name' => 'noname', 'posts' => array( 0 => array( 'id' => '1', 'title' => 'first', 'category_id' => '1' ) ) ), 1 => array( 'id' => '2', 'name' => 'dev', 'posts' => array( ) ), 2 => array( 'id' => '3', 'name' => 'blog', 'posts' => array( 0 => array( 'id' => '2', 'title' => 'second post', 'category_id' => '3' ) ) ) ) |
And last but not least! Now you may want only those categories, that have posts again with title like ‘%st’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
Category::with('posts')->whereHas('posts', function ($query) { $query->where('title','LIKE','%st'); })->get(); // Again tinkering with artisan we get: [6] > Category::with('posts')->whereHas('posts', function ($query) { [6] *> $query->where('title','LIKE','%st'); [6] *> })->get()->toArray(); array( 0 => array( 'id' => '1', 'name' => 'noname', 'posts' => array( 0 => array( 'id' => '1', 'title' => 'first', 'category_id' => '1' ), 1 => array( 'id' => '3', 'title' => 'some long title', 'category_id' => '1' ) ) ), 1 => array( 'id' => '3', 'name' => 'blog', 'posts' => array( 0 => array( 'id' => '2', 'title' => 'second post', 'category_id' => '3' ) ) ) ) |
As simple as that!
And for all those collections we needed only 2 queries to the database. Impressive isn’t it?
Be sure to check other features and leave a comment with any questions.
Pingback: Querying relations in Laravel 4: nested relation | SOFTonSOFA()