Exercise - Master-Detail Relationship

In this exercise, we will create a Laravel app that utilizes a master-detail relationship. The exercise requirements are:

  • Create a master table called articles.
  • Create a detail table, called article_details.
  • Create models for both tables and represent relationships between tables.
  • Use models to retrieve all records.
  • Create a view and display the data.

Creating a Laravel project

Create a new Laravel project in the terminal:

laravel new laravel-master-detail

We can go with the default options and choose the MySQL database.

Navigate to the /laravel-master-detail folder and start a local server. Execute both commands:

cd laravel-master-detail
composer run dev

Alternatively, start a simple local server by using the following command:

php artisan serve

Create a master table (articles)

Open a new terminal window. Create a migration file that in turn creates the master articles table:

php artisan make:migration create_articles_table

Open the newly created migration file whose name ends with the create_articles_table.php wording. Add the title column to our articles table by modifying the existing Schema::create() function:

Schema::create('articles', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->timestamps();
});

Create a detail table (article_details)

Now, we will create a detail table that will hold the lengthy text for each record in the master articles table. In a terminal, create a migration that in turn creates the article_details table:

php artisan make:migration create_article_details_table

Open the newly created migration file that ends with the create_article_details_table.php wording. Modify the Schema::create() function and add two additional columns.

The first additional column will be the article_id foreign key for the articles table, and it will be constrained on the database level.

The second additional column will be the article_text column which will hold the lengthy text for each record inside the articles table. The complete Schema::create() function now looks like:

Schema::create('article_details', function (Blueprint $table) {
    $table->id();
    $table->foreignId('article_id')->constrained();
    $table->text('article_text');
    $table->timestamps();
});

Running the migrations

To create these two tables, we run the migrations in a terminal:

php artisan migrate
Running the migrations that create the master and the detail tables.

Inserting sample records

To quickly insert a few sample records in both the articles and the article_details tables, we start the tinker tool in a terminal:

php artisan tinker

And paste and execute the following code:

DB::table('articles')->insert([
	['title' => 'First Article', 'created_at' => now(), 'updated_at' => now()],
	['title' => 'Second Article', 'created_at' => now(), 'updated_at' => now()],
	['title' => 'Third Article', 'created_at' => now(), 'updated_at' => now()],
]);

DB::table('article_details')->insert([
	['article_id' => 1, 'article_text' => 'This is the detail text for the first article.', 'created_at' => now(), 'updated_at' => now()],
	['article_id' => 2, 'article_text' => 'This is the detail text for the second article.', 'created_at' => now(), 'updated_at' => now()],
	['article_id' => 3, 'article_text' => 'This is the detail text for the third article.', 'created_at' => now(), 'updated_at' => now()],
]);

exit;

Creating Models

Now, we will create two models, one for each table, and specify relationships between the two tables.

Create an Article model

To create an Article model that represents the articles table, in the terminal, we write:

php artisan make:model Article

This command creates the app/Models/Article.php file that has an Article model class. This class is automatically associated with the articles table.

Create an ArticleDetail model

To create an Article_Detail model that represents the article_details table, in the terminal, we run the following command:

php artisan make:model ArticleDetail

Creating relationships

The articles and article_details tables have a one-to-one relationship.

Create a relationship inside the Article model

Each record inside an articles table has one related record inside the article_details table. To create this "Has One" relationship, we open the app/Models/Article.php file and paste the following code:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;

class Article extends Model
{
    // define a has-one relationship:
    public function detail() : HasOne
    {
        return $this->hasOne(ArticleDetail::class);
    }
}

This code defines a HasOne relationship from the perspective of the master articles table towards the article_details table which acts as a detail table. It is used to fetch the records from the detail table. The relationship is defined inside the public method we named detail(). The framework automatically assumes there will be a foreign key in the article_details table with the name of articles_id, which indeed is the case.

Create a relationship inside the ArticleDetail model

Each record inside an article_details table belongs to one related record inside the articles table. To create this "Belongs To" relationship, we open the app/Models/ArticleDetail.php file and paste the following code:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class ArticleDetail extends Model
{
    // define the belongs-to relationship
    public function article() : BelongsTo
    {
        return $this->belongsTo(Article::class);
    }
}

This code defines a belongs-to, one-to-one relationship from the perspective of the detail, article_details table towards the master, articles table. The relationship is defined inside a public member function we named article().

Fetch records using a relationship

Now, we can use the Article model with a relationship to fetch records from the articles table together with the related records from the article_details table. Open the web.php file and paste the following code:

<?php

use App\Models\Article;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    $myArticles = Article::with('detail')->get();
    return view('articles', ['myArticles' => $myArticles]);
});

This code defines an index route and uses the Article class with a relationship to fetch all the records from the articles table together with the related records from the article_details table. Then, it passes the collection of fetched records, called, $myArticles to our, yet to be created, articles view.

Create a view

Finally, we create a view, a blade template file that contains the HTML code for our page. On the command line, we write:

php artisan make:view articles

This command creates a resources/views/articles.blade.php template file. Open the file and paste the following code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Master - Detail Exercise</title>

</head>
<body>
<h1>Laravel master - detail exercise</h1>    
    @foreach($myArticles as $myArticle)
        <h2>{{ $myArticle->id }}</h2>
        <h3>{{ $myArticle->title }}</h3>
        <p>{{ $myArticle->detail->article_text }}</p>
    @endforeach
    
</body>
</html>

This code will be compiled into a final HTML output. The code uses the @foreach loop to iterate over the entire collection of fetched records. Individual articles table columns are displayed using the {{ }} blade echo statements. Column values from the article_details table are fetched through the detail relationship.

Display records in a browser

Finally, if we access the http://127.0.0.1:8000 local address, we see the following output in our browser window:

Web browser showing the master-detail relationship and records.