Laravel Eloquent Tutorial with Practical Example


Laravel

Author: Steve Alila
Reviewer: Deepak Prasad

This Laravel Eloquent tutorial is going to be one hell of a ride! We're diving deep into everything you need to know about Laravel Eloquent. From model creation, CRUD operations, query optimization, advanced features, and relationships. You'll have your database interactions in Laravel applications on lock after this.

Laravel’s most trusted tool comes in the form of its very own ORM system Eloquent. It’s elegant and super smooth interface makes working with databases an absolute breeze. By cutting out all the fuss when it comes to complex SQL queries - developers can write minimal code while getting their database operations done stress free. This approach offers enhanced readability, maintainability and lets them do more with less worry! That’s just the tip of the iceberg though - things get real interesting once you pull back the curtain on all those fancy advanced features like relationship mapping, soft deletes, and event handling. Pulling data from databases has never been so simple or so intuitive!

And that ain’t even half of it! With Eloquent's active record implementation each model corresponds directly with a single table in your database. So you’ll never have to go digging down for specific records again!

It doesn’t end there either... This powerful ORM system not only boosts developer productivity but also fits like a glove into Laravel's ecosystem too. When combining these two forces it creates a comprehensive solution for application data management.

 

1. Steps to Build and Test Relational Data Models using Laravel Eloquent

1.1 Create a New Laravel Project

But before we begin make sure Composer is installed on your system as it is essential for installing Laravel and other PHP packages. Once you’re good head over to your terminal or command prompt and run the following command to create a new Laravel project:

sudo mkdir -p /opt/projects
sudo chmod 777 /opt/projects
cd /opt/projects
composer create-project --prefer-dist laravel/laravel LaravelEloquentTutorial

This command creates a new Laravel project named LaravelEloquentTutorial.

 

1.2 Configure the Environment

Alright now let's get down to business… You got this champ! First things first though… Make sure you’ve got your MySQL database created because we’re gonna need it for this tutorial. For reference — We created our MariaDB database already (and will use that same one here).

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_laravel_db
DB_USERNAME=user1
DB_PASSWORD=Passw0rd

 

1.3 Creating Models and Migrations

Eloquent models are the heart of Laravel's ORM. Create models for Flight and Passenger along with their migrations:

php artisan make:model Flight --migration
Laravel Eloquent Tutorial with Practical Example
php artisan make:model Passenger --migration
Laravel Eloquent Tutorial with Practical Example

 

1.4 Define Model Attributes and Relationships

In the Flight model, define attributes that can be mass-assigned using the $fillable property and specify the relationship with Passenger using the hasMany() method inside app/Models/Flight.php.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{

    use HasFactory;

    protected $table = 'my_flights';
    protected $primaryKey = 'flight_id';
    public $incrementing = false;
    protected $keyType = 'string';
    public $timestamps = false;

    protected $fillable = ['flight_id', 'airline']; // Add attributes you want to be mass-assignable

    // A Flight has many Passengers
    public function passengers()
    {
        return $this->hasMany(Passenger::class, 'flight_id');
    }
}
  • $table: By default, Eloquent assumes the table name is the plural form of the model name in snake_case. If your table name differs from this convention, specify it using the $table property.
  • $primaryKey: Eloquent assumes that each table has a primary key column named id. If this is not the case, you can set the $primaryKey property to the name of your custom key.
  • $incrementing: If your primary key is not an auto-incrementing integer, set this property to false. This is common for non-integer or UUID primary keys.
  • $keyType: This specifies the data type of the primary key. By default, it's set to 'int'. If your primary key is a string (such as a UUID), you should change this to 'string'.
  • $timestamps: By default, Eloquent expects created_at and updated_at columns to exist on your table. If your table does not have these columns, set this property to false.

In the Passenger model, define its mass-assignable attributes and its relationship to Flight using the belongsTo() method inside app/Models/Passenger.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Passenger extends Model
{
    use HasFactory;

    protected $fillable = ['flight_id', 'name'];

    // Each Passenger belongs to a Flight
    public function flight()
    {
        return $this->belongsTo(Flight::class, 'flight_id');
    }
}

 

1.5 Update Migrations with Table Structures

Edit the migration files to define the structure of flights and passengers tables. Ensure passengers table includes a foreign key that references flight_id in the flights table.

This is our database/migrations/2024_02_17_043558_create_flights_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up()
    {
        Schema::create('my_flights', function (Blueprint $table) {
            $table->string('flight_id')->primary(); // Assuming 'flight_id' is a string and primary
            $table->string('airline');
            // Add other columns as necessary
            $table->timestamps(); // This adds the `created_at` and `updated_at` columns
        });
    }

    public function down()
    {
        Schema::dropIfExists('my_flights');
    }
};

This is our database/migrations/2024_02_17_044019_create_passengers_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('passengers', function (Blueprint $table) {
            $table->id();
            $table->string('flight_id'); // Match the type with the 'flight_id' in 'my_flights'
            $table->string('name');
            $table->timestamps();

            // Foreign key constraint
            $table->foreign('flight_id')
                  ->references('flight_id')->on('my_flights')
                  ->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('passengers');
    }
};

 

1.6 Run Migrations

This command applies the migration files, creating the flights and passengers tables in your database with the specified structure.

php artisan migrate
Laravel Eloquent Tutorial with Practical Example

 

1.7 Create Seeders for Dummy Data

Seeders are used to populate your database with initial data. These commands generate seeder files for both flights and passengers

php artisan make:seeder FlightsTableSeeder
php artisan make:seeder PassengersTableSeeder
Laravel Eloquent Tutorial with Practical Example

 

1.8 Implement Seeders

Fill the seeder files with dummy data for flights and passengers. Use the DB facade or model factories to insert data into your database.

Here I have provided some dummy data for Flights Table inside database/seeders/FlightsTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Flight;

class FlightsTableSeeder extends Seeder
{
    public function run()
    {
        Flight::create([
            'flight_id' => 'FL456',
            'airline' => 'Demo Airline',
        ]);
        Flight::create([
            'flight_id' => 'FL256',
            'airline' => 'Dummy Airline',
        ]);

    }
}

Similarly I have added some passenger list inside database/seeders/FlightsTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Flight;

class FlightsTableSeeder extends Seeder
{
    public function run()
    {
        Flight::create([
            'flight_id' => 'FL456',
            'airline' => 'Demo Airline',
        ]);
        Flight::create([
            'flight_id' => 'FL256',
            'airline' => 'Dummy Airline',
        ]);

    }
}

We can also manually insert data using Laravel Tinker. Execute the following command to create a flight entry in FLight Table:

php artisan tinker
>>> use App\Models\Flight;
>>> Flight::create(['flight_id' => 'FL123', 'airline' => 'Test Airline']);
= App\Models\Flight {#4995
    flight_id: "FL123",
    airline: "Test Airline",
  }
>>> exit

Then, try retrieving it again:

>>> App\Models\Flight::with('passengers')->get();

 

1.9 Seed the Database

Run the seeders to insert the data:

php artisan db:seed
Laravel Eloquent Tutorial with Practical Example

 

1.10 Create a Controller and Define Route

The controller will contain methods to handle various requests (e.g., displaying flights and their passengers). The route defines the URL pattern that maps to these controller methods.

php artisan make:controller FlightController
Laravel Eloquent Tutorial with Practical Example

In this controller inside app/Http/Controllers/FlightController.php, using Flight::with('passengers')->get(); tells Eloquent to retrieve all flights and their associated passengers in just two queries, regardless of the number of flights. This is a significant improvement over the N+1 queries that would occur without eager loading.

<?php

namespace App\Http\Controllers;

use App\Models\Flight;
use Illuminate\Http\Request;

class FlightController extends Controller
{
    public function index()
    {
        // Eager load passengers with flights to avoid N+1 query problem
        $flights = Flight::with('passengers')->get();

        return view('flights.index', ['flights' => $flights]);
    }
}

Add a route to routes/web.php to access the index method in FlightController.

use App\Http\Controllers\FlightController;

Route::get('/flights', [FlightController::class, 'index']);

This route configuration allows users to access the list of flights and their passengers through the /flights URL, utilizing the optimized query in FlightController.

 

1.11 Create a View

Create a Blade template to display the flights and their passengers. Use Blade syntax to loop through the flights and display each flight's details and its passengers.

mkdir resources/views/flights
touch resources/views/flights/index.blade.php

Create a Blade view file to display the flights and their passengers.

<!DOCTYPE html>
<html>
<head>
    <title>Flights and Passengers</title>
</head>
<body>
    <h1>Flights and Their Passengers</h1>
    @foreach ($flights as $flight)
        <h2>{{ $flight->airline }} - Flight ID: {{ $flight->flight_id }}</h2>
        <ul>
            @foreach ($flight->passengers as $passenger)
                <li>{{ $passenger->name }} (Passenger ID: {{ $passenger->id }})</li>
            @endforeach
        </ul>
    @endforeach
</body>
</html>

In this view, we loop through each flight and its passengers to display them. You'll need to adjust the fields ($flight->airline, $passenger->name, $passenger->id) based on your actual database schema.

 

1.12 Test the Application

Start the Laravel development server with:

php artisan serve

Navigate to http://localhost:8000/flights in your web browser to see the list of flights and their passengers. This will confirm that your eager loading configuration is working correctly, optimizing your queries and efficiently loading related data.

Laravel Eloquent Tutorial with Practical Example

 

2. Advanced Laravel Eloquent Usage

2.1 Create Model Observer

Model observers are classes that allow you to hook into various model events (creating, updating, deleting, etc.) to perform actions. For example, you might want to log every time a flight is created:

php artisan make:observer FlightObserver --model=Flight
Laravel Eloquent Tutorial with Practical Example

Define Events in app/Observers/FlightObserver.php

namespace App\Observers;

use App\Models\Flight;

class FlightObserver {
    public function created(Flight $flight) {
        \Log::info("Flight created: {$flight->flight_id}");
    }
}

Register Observer in app/Providers/AppServiceProvider.php:

use App\Models\Flight;
use App\Observers\FlightObserver;

public function boot() {
    Flight::observe(FlightObserver::class);
}

 

2.2 Converting Models to Arrays/JSON

Eloquent models can be easily converted to arrays or JSON, facilitating API development inside app/Http/Controllers/FlightController.php:

namespace App\Http\Controllers;

use App\Models\Flight;
use Illuminate\Http\Request;

class FlightController extends Controller
{
    public function index()
    {
        // Eager load passengers with flights to avoid N+1 query problem
        $flights = Flight::with('passengers')->get();

        // Optionally, demonstrate converting a single flight to array and JSON
        // Fetch a single flight instance as an example
        $singleFlight = Flight::find(1);

        if ($singleFlight) {
            // Convert to array
            $flightArray = $singleFlight->toArray();

            // Convert to JSON
            $flightJson = $singleFlight->toJson();

            // For demonstration, you can use 'dd' to dump the converted values to the browser
            // Note: 'dd' will halt the execution, so comment these out to proceed with rendering the view
            // dd($flightArray, $flightJson);

            // Alternatively, pass these to the view or log them
            // \Log::info('Flight as Array:', $flightArray);
            // \Log::info('Flight as JSON:', $flightJson);
        }

        return view('flights.index', ['flights' => $flights]);
    }
}
  • This controller's index method fetches all flights along with their passengers to display them in a view, utilizing Eloquent's with method for eager loading to efficiently handle the N+1 query issue.
  • It also demonstrates fetching a single Flight instance by its ID (find(1)), then converting this instance to an array and to JSON. This is particularly useful for API development, where you might need to send model data in different formats.
  • The toArray() and toJson() methods are used to convert the Flight instance into an array and JSON format, respectively. These methods are part of Laravel's Eloquent and are incredibly handy for API responses or when needing to manipulate or inspect model data in these formats.
  • Commented-out lines with dd() are provided for demonstration purposes. dd() is a Laravel helper function that dumps the variable(s) to the browser and stops execution. This can be useful for debugging but should be commented out or removed in production code.

 

2.3 Flight Model Enhancements

By utilizing features such as attribute casting, date mutators, accessors, and mutators, alongside defining eloquent relationships, developers can significantly reduce boilerplate code, ensure data integrity, and simplify complex data operations. We will be implementing these enhancements inside app/Models/Flight.php

 

Attribute Casting

Attribute casting is an automatic conversion of attributes to a common data type that occurs when you access them on your model. This feature is great for preserving the integrity of data and simplifying how it’s handled in your application.

protected $casts = [
    'is_active' => 'boolean', // Automatically casts the is_active attribute to boolean
    'flight_date' => 'datetime', // Converts flight_date to Carbon instance for date manipulation
];

 

Date Mutators

Date mutators are responsible for changing date fields into instances of Carbon, which is a PHP library for handling dates. This change allows you to easily perform operations on dates. Laravel treats created_at and updated_at as date instances by default, but you can make additional date fields automatically mutated as well by specifying them.

protected $dates = ['departure_time']; // Ensures departure_time is treated as a Carbon instance

 

Accessors and Mutators

Accessors and mutators allow you to format Eloquent attribute values when you retrieve or set them on model instances. This is useful for applying automatic data formatting rules.

Accessor: Customize how a model attribute is retrieved.

public function getAirlineAttribute($value) {
    return strtoupper($value); // Always return airline names in uppercase
}

Mutator: Customize how a model attribute is set.

public function setFlightIdAttribute($value) {
    $this->attributes['flight_id'] = strtoupper($value); // Store flight IDs in uppercase
}

 

Utilizing Eloquent Relationships

Eloquent relationships define how model entities relate to each other, simplifying the management of database relations.

One-to-Many Relationship: A flight can have many passengers. This relationship is defined in the Flight model to easily retrieve the passengers associated with a flight.

public function passengers() {
    return $this->hasMany(Passenger::class, 'flight_id');
}

This setup allows for intuitive interaction with related models, such as retrieving all passengers for a flight by accessing $flight->passengers.

Here is my app/Models/Flight.php file with all these changes for reference:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{

    use HasFactory;
    // use SoftDeletes;

    protected $table = 'my_flights';
    protected $primaryKey = 'flight_id';
    public $incrementing = false;
    protected $keyType = 'string';
    public $timestamps = true;

    protected $fillable = ['flight_id', 'airline']; // Add attributes you want to be mass-assignable

    protected $casts = [
        'is_active' => 'boolean',
        'flight_date' => 'datetime:Y-m-d',
    ];
    protected $dates = ['departure_time'];

    // Accessor
    public function getFlightIdAttribute($value) {
        return strtoupper($value);
    }

    // Mutator
    public function setFlightIdAttribute($value) {
        $this->attributes['flight_id'] = strtolower($value);
    }

    // A Flight has many Passengers
    public function passengers()
    {
	return $this->hasMany(Passenger::class, 'flight_id');
    }
}

 

2.4 Eloquent Collections

Eloquent Collections provide a fluent, convenient wrapper for working with arrays of models. You can create a new method in your FlightController to specifically showcase these Eloquent Collection methods.

use App\Models\Flight;
use Illuminate\Http\Request;

public function collectionsDemo(Request $request)
{
    // Initialize variables
    $containsTestAirline = false;
    $specificFlight = null;
    $flights = collect([]);

    // Get flight_id from request query parameters
    $requestedFlightId = $request->query('flight_id');

    if ($requestedFlightId) {
        // Fetch a specific flight by flight_id, including passengers
        $specificFlight = Flight::with('passengers')->where('flight_id', $requestedFlightId)->first();

        // Check if the specific flight exists and if it contains a specific airline
        if ($specificFlight) {
            $containsTestAirline = $specificFlight->airline == 'Test Airline';
            $flights = collect([$specificFlight]); // Wrap the specific flight in a collection for consistency
        }
    } else {
        // Fetch all flights with passengers to avoid N+1 query problem
        $flights = Flight::with('passengers')->get();

        // Check if any flight in the collection contains a specific airline
        $containsTestAirline = $flights->contains('airline', 'Test Airline');
    }

    // Prepare the flights data, ensuring consistency in the output format
    $flightsData = $flights->map(function ($flight) {
        return [
            'flight_id' => $flight->flight_id,
            'airline' => $flight->airline,
            'passengers' => $flight->passengers->toArray(), // Convert related passengers to an array
            'created_at' => $flight->created_at,
            'updated_at' => $flight->updated_at,
        ];
    });

    return response()->json([
        'specificFlight' => $specificFlight ? $specificFlight->toArray() : null,
        'containsTestAirline' => $containsTestAirline,
        'flights' => $flightsData
    ]);
}

In the provided collectionsDemo method, several Laravel Eloquent methods are utilized to interact with the database and manipulate the collection of models. Here's a summary of the Eloquent methods used:

  1. with('passengers'): This method is used for eager loading the related passengers models alongside the Flight models to prevent the N+1 query problem. It ensures that all related passengers are loaded in a single query when fetching flights.
  2. where('flight_id', $requestedFlightId): Applied to the Flight model, this method filters the flights to find the one with the specified flight_id. It's used to retrieve a specific flight based on the request parameter.
  3. first(): After applying the where clause, first() fetches the first result of the query, returning a single model instance or null if no matching model is found.
  4. get(): This method retrieves all records that match the query constraints. Without any constraints, it fetches all instances of the model from the database.
  5. contains('airline', 'Test Airline'): Used on a collection, this method checks if any of the models in the collection have an airline attribute equal to "Test Airline".
  6. whereIn('flight_id', $flightIds): This collection method filters the models based on whether their flight_id is in the specified array of IDs.
  7. map(): A collection method that applies a callback to each item in the collection and returns a new collection with the items returned by the callback. In this context, it's used to format the flights and their passengers' data.
  8. toArray(): Converts the model or collection into a plain PHP array. This is particularly useful when preparing data for JSON responses.

 

2.5 Register a Route for the Demonstration

To access this demonstration through your web application, you need to define a route that points to this new method. Open your routes/web.php file and add a new route:

Route::get('/flights/collections-demo', [FlightController::class, 'collectionsDemo']);

This route enables you to navigate to /flights/collections-demo in your browser to see the results of the Eloquent Collections demonstrations.

 

2.6 Testing the Implementation

I have created and added some more flight details into the table for the demonstration using tinker as showed earlier. Start the application web server if not running already:

php artisan serve

Access this endpoint from a web browser or a tool like Postman with a query parameter.

To test fetching all flights and checking for the presence of "Test Airline", simply access /flights/collections-demo without any query parameters.

Laravel Eloquent Tutorial with Practical Example

To filter by a specific flight_id, append the query parameter like /flights/collections-demo?flight_id=FL123. This will return details for the flight "FL123", if it exists, along with its passengers.

Laravel Eloquent Tutorial with Practical Example

The collectionsDemo method showcases Laravel Eloquent's prowess in efficiently fetching and manipulating database records. It demonstrates eager loading, dynamic filtering based on request parameters, and collection manipulation techniques such as contains, whereIn, and map. This method elegantly handles conditional data retrieval and formatting, exemplifying Eloquent's capability to streamline complex database interactions and data presentation in Laravel applications.

With the collectionsDemo method, you can see how Laravel Eloquent can fetch and manipulate database records with ease. Eager loading? Check. Dynamic filtering? Check. The ability to use collection manipulation techniques like contains, whereIn, and map? Absolutely. This method has a way of handling conditional data retrieval and formatting that just makes sense. And it does that while showcasing Eloquent’s power in simplifying complex database interactions and data presentation in Laravel applications.

 

This article takes a deep dive into Laravel's Eloquent ORM, and showcases how crucial it is to Laravel applications for powerful database interaction. From model generation and migration handling, to query optimization and CRUD operations, it covers all the fundamentals such as model conventions; advanced features like attribute casting and relationships; and the use of Eloquent Collections. The guide really emphasizes on how Eloquent simplifies complex data management tasks by being super efficient.

 

Steve Alila

Steve Alila

He specializes in web design, WordPress development, and data analysis, with proficiency in Python, JavaScript, and data extraction tools. Additionally, he excels in web API development, AI integration, and data presentation using Matplotlib and Plotly. You can connect with him on his LinkedIn profile.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!