Hire the author: Obua E
Introduction
Eloquent is Laravel’s powerful Object Relation Model (ORM) that provides a beautiful, simple active record implementation for working with your database.
Each database table has a corresponding Model which is used to interact with that table.
Throughout this article, i will be demonstrating Laravel eloquent relationship use cases and tricks.
Glossary
- ORM: Refers to advanced implementation of the PHP Active Record Pattern, which makes it very easy to interact with the application database.
- Eloquent ORM is a very powerful yet very expressive ORM, which allows us to work with the database objects and relationships using much eloquent and expressive syntax.
- Laravel Telescope: This is a debugging tool managed by Laravel which we shall be used for testing the performance of the eloquent relationship queries.
- Composer: This is a package manager used to manage Laravel dependencies.
Step by Step procedure
In this lesson, we shall be setting up a Laravel application from scratch to demonstrate eloquent ORM relationship usage use cases with relevant examples.
Prerequisites
1. Laravel ^7
2. Laravel Telescope
3. Composer
5. Any database of choice but supported by Laravel, in this lesson we shall be using Maria DB
6. PHP 7.2 and above installed
7. NodeJs 12 and above
SETUP DEVELOPMENT ENVIRONMENT
Step 1: Install Laravel 7 using composer
composer create-project --prefer-dist laravel/laravel:^7.0 laravel-eloquent |
Step 2: Install article dependencies using composer.
Run these composer commands in this order
composer require laravel/ui:^2.4 | |
composer require laravel/telescope |
Step 3: Configure dependencies for use with Laravel.
Run these artisan commands in this order.
//Run this command in the root directory of the project installed using composer | |
//This command adds ui scaffoldings for the application | |
php artisan ui bootstrap | |
//This commands sets up the ui to use bootstrap for authentication | |
php artisan ui bootstrap --auth | |
//This commands install node dependencies and compiles frontend assets for laravel default auth functionalities | |
npm install && npm run dev | |
//This command installs telescope module into laravel to ready it for its functions | |
php artisan telescope:install | |
//This command publishes frontend/ui assets to the public folder for ui rendering. | |
php artisan telescope:publish |
Step 4: Setup telescope and schema in the AppServiceProvider with default fallback values
- Add Schema Facade and then call Schema::defaultStringLength(191) to the service providers register method.
- Register Laravel telescope service provider only when Laravel is in local environment
<?php | |
namespace App\Providers; | |
use Illuminate\Support\ServiceProvider; | |
//Add the schema facade | |
use Illuminate\Support\Facades\Schema; | |
class AppServiceProvider extends ServiceProvider | |
{ | |
/** | |
* Register any application services. | |
* | |
* @return void | |
*/ | |
public function register() | |
{ | |
//Provide migration string() function default value. | |
Schema::defaultStringLength(191); | |
//Register laravel telescope service provider only when the application is in local environment | |
if ($this->app->isLocal()) { | |
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class); | |
$this->app->register(TelescopeServiceProvider::class); | |
} | |
} | |
/** | |
* Bootstrap any application services. | |
* | |
* @return void | |
*/ | |
public function boot() | |
{ | |
// | |
} | |
} |
ELOQUENT MODEL AND MIGRATION SETUP
Step 1: Run the following artisan commands to create models and migrations.
We shall be using only three tables for this article i.e. users, posts, and profiles to demonstrate one-to-one and one-to-many relationships.
The users table comes inbuilt with a fresh Laravel application
//These commands generates models and their respective migration files | |
php artisan make:model Post -m | |
php artisan make:model Profile -m |
Step 2: Setup the migrations and models to ready them for eloquent relationship usage.
To have relationships between database tables, first, you still need to take care of database fields and foreign keys. Usually, in database migration statement it looks something like this:
<?php | |
Schema::create('posts', function (Blueprint $table) { | |
$table->id(); | |
$table->unsignedBigInteger('user_id'); | |
$table->foreign('user_id')->references('id')->on('users'); | |
//It can be done like this as well in a more simplified way | |
//$table->foreignId('user_id')->constrained(); | |
$table->timestamps(); | |
}); | |
?> |
In this example we’re defining posts.user_id field with foreign key to users.id field.
In pure MySQL, it would look like this:
ALTER TABLE posts
ADD FOREIGN KEY (user_id) REFERENCES users(id);
All migration structure
Posts migration schema
<?php | |
use Illuminate\Database\Migrations\Migration; | |
use Illuminate\Database\Schema\Blueprint; | |
use Illuminate\Support\Facades\Schema; | |
class CreatePostsTable extends Migration | |
{ | |
/** | |
* Run the migrations. | |
* | |
* @return void | |
*/ | |
public function up() | |
{ | |
Schema::create('posts', function (Blueprint $table) { | |
$table->id(); | |
$table->foreignId('user_id')->constrained(); | |
$table->string('title'); | |
$table->text('description'); | |
$table->enum('status',['ACTIVE','IN ACTIVE'])->default('ACTIVE'); | |
$table->timestamps(); | |
}); | |
} | |
/** | |
* Reverse the migrations. | |
* | |
* @return void | |
*/ | |
public function down() | |
{ | |
Schema::dropIfExists('posts'); | |
} | |
} |
Profiles migration schema
<?php | |
use Illuminate\Database\Migrations\Migration; | |
use Illuminate\Database\Schema\Blueprint; | |
use Illuminate\Support\Facades\Schema; | |
class CreateProfilesTable extends Migration | |
{ | |
/** | |
* Run the migrations. | |
* | |
* @return void | |
*/ | |
public function up() | |
{ | |
Schema::create('profiles', function (Blueprint $table) { | |
$table->id(); | |
$table->foreignId('user_id')->constrained(); | |
$table->string('profession'); | |
$table->timestamps(); | |
}); | |
} | |
/** | |
* Reverse the migrations. | |
* | |
* @return void | |
*/ | |
public function down() | |
{ | |
Schema::dropIfExists('profiles'); | |
} | |
} |
Let’s setup model relationship
# One-to-one ORM Relationships
Let’s look at the first type of relationships.
We will take a look at the most often example when a User has a Profile but they are in separate database tables. So then they are related with one-to-one relationship – one user has only one profile:
It would work with models User and Profile. Let’s see how it’s defined using hasOne function. User model would look like this:
<?php | |
/** | |
* User | |
* | |
* @author Obua Emmanuel <eobua6882@gmail.com> | |
* @copyright 2021 Obua Emmanuel | |
* @link http://emmanuelobua.me | |
* @version 1.0.0 | |
* | |
*/ | |
namespace App; | |
use Illuminate\Contracts\Auth\MustVerifyEmail; | |
use Illuminate\Foundation\Auth\User as Authenticatable; | |
use Illuminate\Notifications\Notifiable; | |
class User extends Authenticatable | |
{ | |
public function profile() | |
{ | |
return $this->hasOne(Profile::class); | |
} | |
} |
This is read as a user has one profile; start with Class name then followed by relationship name then the related model name
Laravel automatically “knows” that there is a relationship between the main table’s id field and the related table’s user_id. it is formed by the main table’s name (users table in this case), putting it to a singular and adding _id.
But if in your case fields are different, you can add them as parameters.
For example, if you had a field users.user_id and then profiles.profile_user_id, the relationship would look like this:
return $this->hasOne(Profile::class, ‘profile_user_id’, ‘user_id’);
So second parameter is a related table’s field name, and third parameter is a main table’s field.
With our one-to-one relationship defined, we can use it like this.
Here’s our Controller:
<?php | |
/** | |
* ProfileController | |
* | |
* @author Obua Emmanuel <eobua6882@gmail.com> | |
* @copyright 2021 Obua Emmanuel | |
* @link http://www.emmanuelobua.me | |
* @version 1.0.0 | |
* | |
*/ | |
namespace App\Http\Controllers; | |
use Illuminate\Http\Request; | |
use App\User; | |
use Auth; | |
class ProfileController extends Controller | |
{ | |
public function index() | |
{ | |
$user = User::find(Auth::id()); | |
return view('profile', compact('user')); | |
} | |
} |
Then in our profile.blade.php view file we can use something like this:
@extends('layouts.app') | |
@section('content') | |
<div class="container"> | |
<div class="row justify-content-center"> | |
<div class="col-md-8"> | |
<div class="card"> | |
<div class="card-header">{{ __('Dashboard') }}</div> | |
<div class="card-body"> | |
{{-- Echo the profession of the user --}} | |
Profession: {{ $user->profile->profession }} | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
@endsection |
# one-to-many ORM relationship
Let’s look at the second type of relationships.
We will take a look at the most often example when a User has Posts but they are in separate database tables. So then they are related with one-to-many relationship – one user has many posts:
It would work with models User and Post. Let’s see how it’s defined using hasMany function. The User model would look like this:
<?php | |
/** | |
* User | |
* | |
* @author Obua Emmanuel <eobua6882@gmail.com> | |
* @copyright 2021 Obua Emmanuel | |
* @link http://emmanuelobua.me | |
* @version 1.0.0 | |
* | |
*/ | |
namespace App; | |
use Illuminate\Contracts\Auth\MustVerifyEmail; | |
use Illuminate\Foundation\Auth\User as Authenticatable; | |
use Illuminate\Notifications\Notifiable; | |
class User extends Authenticatable | |
{ | |
/** | |
* A user has many posts | |
* @return void | |
*/ | |
public function posts() | |
{ | |
return $this->hasMany(Post::class); | |
} | |
} |
ELOQUENT BASIC USAGE
Create and delete
<?php | |
namespace App\Http\Controllers\Auth; | |
use App\Http\Controllers\Controller; | |
use App\Providers\RouteServiceProvider; | |
use App\User; | |
use App\Profile; | |
use Illuminate\Foundation\Auth\RegistersUsers; | |
use Illuminate\Support\Facades\Hash; | |
use Illuminate\Support\Facades\Validator; | |
class RegisterController extends Controller | |
{ | |
/* | |
|-------------------------------------------------------------------------- | |
| Register Controller | |
|-------------------------------------------------------------------------- | |
| | |
| This controller handles the registration of new users as well as their | |
| validation and creation. By default this controller uses a trait to | |
| provide this functionality without requiring any additional code. | |
| | |
*/ | |
use RegistersUsers; | |
/** | |
* Where to redirect users after registration. | |
* | |
* @var string | |
*/ | |
protected $redirectTo = RouteServiceProvider::HOME; | |
/** | |
* Create a new controller instance. | |
* | |
* @return void | |
*/ | |
public function __construct() | |
{ | |
$this->middleware('guest'); | |
} | |
/** | |
* Get a validator for an incoming registration request. | |
* | |
* @param array $data | |
* @return \Illuminate\Contracts\Validation\Validator | |
*/ | |
protected function validator(array $data) | |
{ | |
return Validator::make($data, [ | |
'name' => ['required', 'string', 'max:255'], | |
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], | |
'password' => ['required', 'string', 'min:8', 'confirmed'], | |
]); | |
} | |
/** | |
* Create a new user instance after a valid registration. | |
* | |
* @param array $data | |
* @return \App\User | |
*/ | |
protected function create(array $data) | |
{ | |
$user = User::create([ | |
'name' => $data['name'], | |
'email' => $data['email'], | |
'password' => Hash::make($data['password']), | |
]); | |
//Normal way of creating record | |
Profile::create([ | |
'user_id' => $user->id, | |
'profession' => $data['profession'] | |
]); | |
/*Or call the relationship as a function to return the profile model instance | |
* and call the normal create method, in this case the user_id field is added automatically behind the scene, | |
* all you need to do is add other fields apart from user_id to the array. | |
*/ | |
/* | |
$user->profile()->create([ | |
'profession' => $data['profession'] | |
]); | |
*/ | |
return $user; | |
} | |
} |
You can delete a relationship query by calling the relationship as a function and call the delete method.
$user = User::find(1);
$user->profile()->delete();
ELOQUENT ADVANCED USAGE
When it comes to using Eloquent Relationships, apart from code readability, key considerations must be put in place when choosing to use eloquent relationships among which includes CPU resource usages, etc.
My personal recommendation for using eloquent relationships is to:
- Eager load model relationships and also only use eloquent relationships to create, update and delete only as it has less impact on memory than querying records from the database.
- Use Eloquent relationship to query records only when you are not dealing with big data as it causes timeouts before querying all the big data.
- Cache request, query, and response for a given period of time so that when querying for the same data, the cached response is returned to the user.
All these are demonstrated with documentation on the project on the GitHub repo linked below in the conclusion section
Learning Tools
To best learn about eloquent relationship, I recommend reading documentation and relevant blog post
Learning Strategy
I had to first learn about the following key areas before using Laravel eloquent relationship i.e. Database development lifecycle which includes drawing Entity-Relationship Diagram which is one of the key stages in the cycle which creates relationships between related tables.
This made it easier to use Laravel eloquent relationship with the help of its inbuilt reusable functions.
By searching databases design principles as a search string, i got the basics of database designs.
Am now able to setup all the laravel eloquent relationship inbuilt and use them efficiently.
Reflective Analysis
As a result of using eloquent relationships, the benefits are best seen when:
Firstly creating new records, secondly updating existing records, and lastly deleting models/ records as it makes it easy to connect with related models through standard existing eloquent relationships.
Because of the eloquent relationship, Development is made easier and faster by focusing on an object-oriented approach rather than writing plain SQL queries.
Also, by defining the relationship in the model classes, we can avoid the lengthier join queries in the controllers
Conclusion
Laravel eloquent has a lot more than illustrated in this article, you can follow from their official documentation https://laravel.com/docs/7.x/eloquent at any time for the latest update for any version you prefer. Here is the link to the Github repository to get started.
Really nice documented. I have checked the overall setup and process.