Part of the Series:
A Practical Introduction to Laravel Eloquent ORM
Eloquent is an object relational mapper (ORM) that is included by default within the Laravel framework. In this project-based series, you’ll learn how to make database queries and how to work with relationships in Laravel Eloquent. To practice the examples explained throughout the series, you’ll improve a demo application with new models and relationships.
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.
Creating a LinkList
Model
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:
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
Renaming the Existing LinkList CLI Command
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 |
+----+-------------------------------------------------+----------------------------------+
Creating a Migration for the LinkList Model
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 Listdescription
: a string representing the description of a Listslug
: 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.
Updating the Links Migration
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.