How To Create a One-To-Many Relationship in Laravel Eloquent

The demo Landing Laravel application that you set up as a prerequisite for this series contains a single database table to store links. In this tutorial you’ll modify this initial database structure to include a second table, which you will use to organize links into lists.

For the links and lists example we’re going to use in this series, each link is part of only one list, but each list can have multiple links. This kind of relationship is also known as a one-to-many relationship.

A one-to-many relationship happens when one item, which we’ll call type A, can be linked to several items of type B, but the opposite doesn’t hold true: an item of type B can only be linked to one item of type A. Transposing this scenario to the current demo application models, A is the list type, and B is the link type.

To get started, you’ll need to create a model and a database table to represent a List of links. Then, you’ll update the existing Link model and table to include the relationship between both models. Because the term List is reserved for PHP internals, you won’t be able to name your new model with that term. You can call this new model LinkList instead.

First, make sure you’re in the application directory:

  • cd ~/landing-laravel

Create a new model using artisan:

  • docker-compose exec app php artisan make:model LinkList

This will generate a new model class in the app/Model directory:

app/Model/LinkList.php

If you look at your app/Console/Commands directory, you’ll notice that there’s already a class file named LinkList.php. This is not to be confused with the Eloquent model you just created. This class contains a CLI command that lists all the links in the database via artisan.

To avoid confusion in the future, now is a good moment to rename that class and its command signature to a different name. In this case use the class name LinkShow since that name also describes what the class does. To rename the app/Console/Commands/LinkList.php file to another valid name, run this command in your terminal:

  • mv app/Console/Commands/LinkList.php app/Console/Commands/LinkShow.php

Then, open the file app/Console/Commands/LinkShow.php in your code editor to change the class name from LinkList to LinkShow, and the command signature from link:list to link:show, like the highlighted lines in the following code listing. This is how the file should look like once you’re finished:

app/Console/Commands/LinkShow.php
<?php

namespace AppConsoleCommands;

use AppModelsLink;
use IlluminateConsoleCommand;

class LinkShow extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'link:show';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'List links saved in the database';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $headers = [ 'id', 'url', 'description' ];
        $links = Link::all(['id', 'url', 'description'])->toArray();
        $this->table($headers, $links);

        return 0;
    }
}

Save and close the file when you’re done. To check that everything worked as expected, run your newly renamed link:show artisan command:

  • docker-compose exec app php artisan link:show

You’ll receive output like this:

Output
+----+-------------------------------------------------+----------------------------------+ | id | url | description | +----+-------------------------------------------------+----------------------------------+ | 1 | https://digitalocean.com/community | DigitalOcean Community | | 2 | https://digitalocean.com/community/tags/laravel | Laravel Tutorias at DigitalOcean | | 3 | https://digitalocean.com/community/tags/php | PHP Tutorials at DigitalOcean | +----+-------------------------------------------------+----------------------------------+

The new app/Model/LinkList.php class that you generated with the previous artisan make:model command contains generic code for a new Eloquent class. Unlike other ORMs such as Doctrine, Eloquent doesn’t alter database structures, handling only data itself. Eloquent models are usually lean, with class properties automatically inferred from the model’s table structure.

This approach to handling data-only with Eloquent means that you don’t need to set up any properties for the LinkList class because they will be inferred from the database table structure for that model.

Structural database operations are typically handled in Laravel via database migrations. Migrations allow developers to programmatically define structural changes to the database, such as creating, modifying, and deleting tables.

You’ll now create a new migration to set up the lists table in the database.

The artisan command line tool included by default with Laravel contains several helper methods to bootstrap new components such as controllers, models, migrations, among others. To create a new migration using artisan, run:

  • docker-compose exec app php artisan make:migration create_link_lists_table
Output
Created Migration: 2021_07_07_152554_create_link_lists_table

This command will generate a new file under the database/migrations directory in your Laravel application, using an auto-generated name based on the current date and time, and the migration name. That file contains generic code that you’ll modify to set up the lists table.

Using your code editor, open the generated migration file. The file currently looks like this:

database/migrations/2021_07_07_152554_create_link_lists_table.php
<?php

use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;

class CreateLinkListsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('link_lists', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

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

The migration runs the up() method when executed with the artisan migrate command. This is where your table definition goes, and by default it creates an id primary key field and two timestamp fields (created_at and updated_at), defined with the timestamps() Schema method. Those fields are automatically filled by Eloquent when a model is created and updated, respectively. The down() method is called when the migration is rolled back with the artisan rollback command, and typically executes code to drop the table or revert structural changes.

You’ll change the up method to include the following fields:

  • title: a string representing the title of this List
  • description: a string representing the description of a List
  • slug: a unique, short string based on the title, typically used to create user-friendly URLs

In a one-to-many relationship, the many side (which in this scenario corresponds to the links table) is the one to hold the column reference (or foreign key) to the other element (corresponding to the lists table). That means you’ll have to modify the links table later on, in order to include a reference field that will link that table to the lists table.

The lists table, on the other hand, doesn’t need any special field to reference its links.

Replace the current content in your migration file with the following code:

database/migrations/2021_07_07_152554_create_link_lists_table.php
<?php

use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;

class CreateLinkListsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('link_lists', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->string('title', 60);
            $table->string('slug', 60)->unique();
            $table->text('description')->nullable();
        });
    }

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

Save the file when you’re done.

Next, open the existing links migration file in your code editor. In the demo project, you’ll find the migration at the following path:

2020_11_18_165241_create_links_table.php

First, include a use directive pointing to the fully qualified class name for the LinkList class, at the beginning of the file and right after the last use line:

…
use IlluminateSupportFacadesSchema;
use AppModelsLinkList;
...

Next, include the following line in the table definition, within the up method and right after the line that sets up the description field:

$table->text('description');
$table->foreignIdFor(LinkList::class);

The foreignIdFor() method creates a foreign key column to the referenced Eloquent model. It uses default nomenclature to set up a field that is linked to the primary key field of the referenced table.

This is how the full migration class should look like when you’re done:

database/migrations/2020_11_18_165241_create_links_table.php
<?php

use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;
use AppModelsLinkList;

class CreateLinksTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('links', function (Blueprint $table) {
            $table->id();
            $table->string('url', 200);
            $table->text('description');
            $table->foreignIdFor(LinkList::class);
            $table->timestamps();
        });
    }

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

Save the file when you’re finished editing it. Next, wipe the database and then run the migration command again to recreate your database structure with the updated migrations files:

  • docker-compose exec app php artisan db:wipe
  • docker-compose exec app php artisan migrate

Configuring Eloquent Model Relationships

The database tables are now set up, but you still need to configure the Eloquent models to define the relationship between them.

On the List model, which is the one side of the relationship, you’ll set up a new method named links. This method will work as a proxy to access the links that are related to each list, using the hasMany method from the parent IlluminateDatabaseEloquentModel class.

In your code editor, open the file app/Model/LinkList.php. Replace the current generic code with the following content:

app/Model/LinkList.php
<?php

namespace AppModels;

use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateDatabaseEloquentModel;

class LinkList extends Model
{
    use HasFactory;

    public function links()
    {
        return $this->hasMany(Link::class);
    }
}

Save the file when you’re done.

Next, you’ll edit the many side of the relationship to include a reference back to the List model, so that links can access their respective list. This is done with the belongsTo method from the parent Model class. This method is used to define the inverse side of the one-to-many relationship.

Open the Link model in your code editor:

app/Model/Link.php

Replace the current content in your Link.php file with the following code:

app/Model/Link.php
<?php

namespace AppModels;

use IlluminateDatabaseEloquentModel;

class Link extends Model
{
    public function link_list()
    {
        return $this->belongsTo(LinkList::class);
    }
}

Save the file when you’re done.

With both models updated, your database is now configured completely, but it is currently empty. In the next section of this series, you’ll learn how to insert new records in the database using Eloquent models.

This tutorial is part of an ongoing weekly series about Laravel Eloquent. You can subscribe to the Laravel tag if you want to be notified when new tutorials are published.

Originally posted on DigitalOcean Community Tutorials
Author: Erika Heidi

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *