Learning Management Platform for Written Tutorial Series.

Laravel 6 Models

Laravel 6 Models

In the previous tutorial Laravel Migrations, we created the database schema for the tutorial project Larashop. In this tutorial, we are going to look at how we can use Eloquent ORM to eloquently add and retrieve Topics data from the database.

Topics to be covered

In this lesson, we will cover the following topics

  • Creating a Model using Artisan
  • Inserting Records
  • Reading Records
  • Updating Records
  • Deleting Records
  • Defining one-to-many Relationships
  • Defining many-to-many Relationships
  • Retrieving Relationship Data

Creating a Model using Artisan

In this section, we will use artisan to create a model. As a convention, models in Laravel use singular names. For example, the model for the table categories is Category for brands it will be Brand.

Run the following command

php artisan make:model Category

Laravel will create the model in the /app directory

Open our model Category.php in the /app directory

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    //
}

HERE,

  • namespace App; defines the application namespace for the model which is app
  • use Illuminate\Database\Eloquent\Model; imports the Model class from the framework
  • class Category extends Model{} defines a class Category that extends the Model class

By extending the Model class, our model inherits a number of methods from the parent class. Laravel guesses the name of the table that belongs to our model by getting the plural version of the model. For example, if our model is named Category, then the table name will be categories which is the plural of our model name. That is the beauty of following Laravel conventions when working with models and migrations.

But Laravel doesn't force you to use the conventions. If you named your table something else, then you can specified the table name like so

protected $table = 'name';

Inserting Records

Let's now create our first record using model Category from the shell utility tinker

php artisan tinker

The above command starts up a shell program that allows you to interact with Laravel from the terminal

Run the following commands from

$c = new App\Category()
$c->name = 'Kids'
$c->url = 'kids'
$c->description = 'Items that are meant for kids'
$c->save()

HERE,

  • $c = new App\Category() creates an instance of the Category model
  • $c->name = 'Kids' assigns the value Kids to the name property of our model
  • $c->url = 'kids' assigns the value kids to the url property of our model
  • $c->description = 'Items that are meant for kids' assigns the value Items that are meant for kids to the description property of our model.
  • $c->save() calls the save method on the instance variable $c which persists the data to the database.

After you execute the above command, you should be able to see the record in the database.

Reading Records

You can also read the records from the database by calling the all method on the instance variable like so

$c->all()

HERE,

  • $c->all() calls the all method on our instance variable. The method all returns all the rows from the database table. It is equivalent to SELECT * FROM categories. The all method returned a collection object which we can loop through.

Executing the above code produces the following results

=> Illuminate\Database\Eloquent\Collection {#3035
     all: [
       App\Category {#3036
         id: 1,
         name: "Kids",
         url: "kids",
         description: "Items that are meant for kids",
         created_at_ip: null,
         updated_at_ip: null,
         created_at: "2019-09-08 20:55:11",
         updated_at: "2019-09-08 20:55:11",
       },
     ],
   }
>>>

We can also retrieve a single record by calling the find method like so

$c->find(1)

HERE,

  • $c->find(1) calls the find method on our instance variable $c which returns a single record. find uses the primary key id which filters for a record with the id value of 1. Its SQL statement equivalent is SELECT * FROM categories WHERE id = 1 LIMIT 1. The results of find are always limited to record and the result returned is an objected.

Executing the above code produces the following results

=> App\Category {#3017
     id: 1,
     name: "Kids",
     url: "kids",
     description: "Items that are meant for kids",
     created_at_ip: null,
     updated_at_ip: null,
     created_at: "2019-09-08 20:55:11",
     updated_at: "2019-09-08 20:55:11",
   }
>>>

Let's now try to retrieve a record with an id that does not exist.

$c->find(3)

HERE,

  • $c->find(3) we attempt to find a record with an id of 3 which does not exist.

Executing the above code returns the following result

null

From the above results, we can conclude that when we call the find method, if the record exists then we can an object which is the instance of the Model and the database fields are available as attributes of the class. If the record does not exist, we get a null value.

Let's now look at how to retrieve a record using the where clause like so

$c->where('id','=',1)->get()

HERE,

  • $c->where('id','=',1)->get() calls the where method on our instance variable. We pass in the first argument id which is the database field where we want to apply the filter. = is the comparison operator for our condition. 1 is the filter value for our condition. We then call the get method which reprieves the record. The SQL statement equivalent of this query is SELECT * FROM categories WHERE id = 1.

Executing the above code produces the following results

=> Illuminate\Database\Eloquent\Collection {#3015
     all: [
       App\Category {#3025
         id: 1,
         name: "Kids",
         url: "kids",
         description: "Items that are meant for kids",
         created_at_ip: null,
         updated_at_ip: null,
         created_at: "2019-09-08 20:55:11",
         updated_at: "2019-09-08 20:55:11",
       },
     ],
   }

Notice that the result returned is a collection and not an object or our model. A collection is simply an array that contains objects of all database results returned.

Laravel allows us to rewrite the above query using a shorthand syntax like so

$c->whereId(1)->get()

HERE,

  • $c->whereId(1)->get() we are using camel casing to construct our where clause. When Laravel encounters a Capital letter after the where, it treats the word as a database field and constructs the necessary query. The default comparison operator for this shorthand syntax is = and the value is the one that is passed in as an argument which in our case if 1.

Laravel difference between find and where clause

The following are some of the differences between find and where methods

  • find always uses the primary key by default to filter for records while where allows you to use any valid database field
  • find always returns a single row while where calls the get method which is not limited to returning a single result only
  • find returns an object instance of the model while where which uses the get method returns a collection
  • find returns null if no row has been returned while where which uses the get method always returns a collection which can be empty when no results have been returned from the database.

We can limit the results of the where clause to a single row only by calling the first method on the instance variable like so

$c->whereId(1)->first()

HERE,

  • $c->whereId(1)->first() calls the first method which behaves similar the find method. Laravel will search for the records that match the filter criteria but only returns the first record that matches the search criteria.

Executing the above code produces the following result

=> App\Category {#3025
     id: 1,
     name: "Kids",
     url: "kids",
     description: "Items that are meant for kids",
     created_at_ip: null,
     updated_at_ip: null,
     created_at: "2019-09-08 20:55:11",
     updated_at: "2019-09-08 20:55:11",
   }
>>> $c->whereId(8)->first()

The short hand syntax works well if you follow Laravel database conventions

More Laravel select query examples

The following example demonstrates how you can perform various operations when working with query builders

$c->where('id','<',7)->get()

Returns all records that have the id value that is less than 7

$c->where('id','<',3)->get()

Returns all rows with the id that is greater than 3

$c->where('id','<>',1)->get()

Returns all rows where the id value is not equal to 1

$c->where('id','<',7)->take(3)->get()

Returns all rows where the id value is less than 7 but the rows returned are limited to 3 only as specified by the argument 3 that is passed to the method take

$c->where('id','<',7)->orderBy('id')->get()

Returns all records with the id value that is less than 7 and the results are ordered in ascending order as specified by the orderBy method. The orderBy methods orders the collection by the field name which is passed in as an argument to the function.

We can also sort results in descending order like so

$c->where('id','<',7)->orderBy('id','desc')->get()

Updating Records

Eloquent objects have the ability to persist changes made to the record to the database. This means we can retrieve a record. Makes changes to it using the instance variable then write back the results to the database.

This example demonstrates this principle. We will first retrieve the record, update it then write it back to the database

$category = App\Category::find(1)

HERE,

  • $category = App\Category::find(1) creates an instance variable $category that is assigned the record that has been retrieved from the database using the find method. Our record from the database has the if of 1.

Executing the above code produces the following result

=> App\Category {#3024
     id: 1,
     name: "Kids",
     url: "kids",
     description: "Clothes for kids",
     created_at_ip: null,
     updated_at_ip: null,
     created_at: "2019-09-08 20:55:11",
     updated_at: "2019-09-09 06:16:15",
   }
>>>

We will now update the description field like so

$category->description = 'Designer clothes for children'

HERE,

  • $category->description = 'Designer clothes for children' assigns the value Designer clothes for children to the field description

We will now call the save method on our instance variable like so

$category->save()

Executing the above code produces the following result

true

That means that the changes that we made to the instance variable properties have been persisted to the database.

We can now just tyope the instance variable name in tinker to see what values our properties now hold like so

$category

If you press the enter key, then you should be able to see the following results printed in the console

=> App\Category {#3024
     id: 1,
     name: "Kids",
     url: "kids",
     description: "Designer clothes for children",
     created_at_ip: null,
     updated_at_ip: null,
     created_at: "2019-09-08 20:55:11",
     updated_at: "2019-09-09 07:32:52",
   }
>>>

Notice how the value of the description property has changed compared to the original value that we initially had.

Deleting Records

Laravel has two ways of deleting records from rthe database. We can complete remove rthe record from rthe database or you can mark it as deleted. Marking the record is deleted is known as soft deleting in laravel.

We will make some updates to the Brand model so that we can be able to soft delete records. as for the categories model, we will look at how to completely delete the record from the database.

Laravel Soft Delete

For us to be able to soft delete records from the database, we will need to use the soft delete trait like so.

Run the following command to create a new model Brand

php artisan make:model Brand

Open the model /app/Brand.php

Update the code to the following

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Brand extends Model
{
    use SoftDeletes;
}

HERE,

  • namespace App; defines the model namespace
  • use Illuminate\Database\Eloquent\Model; imports the class Model class that our child classes will inherit from.
  • use Illuminate\Database\Eloquent\SoftDeletes; imports the SoftDeletes trait from the framework.
  • class Brand extends Model defines a class Brand that extends the Model class
  • use SoftDeletes; applies the SoftDeletes trait to our class Brand

In the previous tutorial, we created the brands table without support for soft deleting records. Let's now modify our table so that it can support soft deleting

Run the following command to create a migration file that modifies our table so that it can support soft deletes.

php artisan make:migration add_soft_deletes_to_brands --table=brands

Open the newly created migration file and modify it as follows

<?php

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

class AddSoftDeletesToBrands extends Migration
{
    public function up()
    {
        Schema::table('brands', function (Blueprint $table) {
            $table->softDeletes();
        });
    }

    public function down()
    {
        Schema::table('brands', function (Blueprint $table) {
            //
        });
    }
}

HERE,

  • $table->softDeletes(); adds a deleted_at column to our table that is a timestamp field.

Run the following command to execute the migration file

php artisan migrate

Executing the above command creates a deleted_at field in the database.

Let's now fire up tinker and create a record that we will soft delete like so

php artisan tinker

Run the following command to create a brand record

$b = new App\Brand
$b->name = 'Gucci'
$b->url = 'gucci'
$b->description = 'I think the spelling should be guchi not gucci'
$b->save()

HERE,

  • $b = new App\Brand creates an instance of our model Brand then we assign some values to the model properties.
  • $b->save() the save method persist the data to the database.

We should now have a single record in our brands table.

Let us now soft delete the newly created record.

Run the following command to delete the record

$b->delete()

You can now check record in the database

The record will still be their but the deleted_at column will have a timestamp

Let's now try to access the deleted record using the find like so

$brand = App\Brand::find(1)

Executing the above command returns null. This is because we deleted our record. As long as it is marked as soft deleted, eloquent will; not include it in queries.

Let's now look at how to permanently delete records from the database.

We will delete a record with the id of 1 in the categories table

$category = App\Category::find(1)
$category->delete()

Executing the above code permanently deletes the record with the id value of 1.

Soft deleting allows us to partially delete a record and has the capability of restoring the record back while permanently deleting a record removes it eternally from the database.

Defining one-to-many Relationships

In the previous lesson, we created a relationship between the brands and products tables. In this section, we will define the relationship between the two tables.

Let's start by creating the model for the Product using artisan

php artisan make:model Product

The relationship between brands and products is defined using id in brands as the primary key and brand_id in products table as the foreign key.

Open the model Product.php and update the code as follows

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    public function brand() {
		return $this->belongsTo(Brand::class, 'brand_id');
	}
}

HERE,

  • public function brand() {...} defines a function brand that defines a relationship between products and brands using the foreign key brand_id
  • return $this->belongsTo(Brand::class, 'brand_id'); defines the one-to-many relationship to the Brand model as specified by Brand::class and brand_id arguments.

Let us now create the corresponding relationship in the Brand model

Modify the code for the Brand model as follows

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Brand extends Model
{
    use SoftDeletes;

    public function products()
    {
        return $this->hasMany(Product::class,'brand_id','id');
    }
}

HERE,

  • public function products() defines a method products that defines the relationship between the Product model and Brand class. hasMany means a single brand can have more than one product.

Let's now add some data to our tables then test out the relationships

Retrieving Relationship Data

Let's add two brands to our table like so

Fire up tinker if it is not already running like so

php artisan tinker

Run the following commands to create two records in the brands table

$b1 = new App\Brand()
$b1->name = 'Vesace'
$b1->url = 'vesace'
$b1->description = 'Medusa made cute'
$b1->save()

$b2 = new App\Brand()
$b2->name = 'NWO'
$b2->url = 'nwo'
$b2->description = 'I have no idea what it means'
$b2->save()

HERE,

  • The above code creates two variables $b1 and $b2 that we assign values to and call the save method to write the values to the database.

Let's now create 10 products. Laravel has a concept of Model Factories that use a library called faker to create dummy records in the database. Let us take advantage of that feature and create a factory that will create 10 dummy records in our products table.

Run the following command in the terminal

php artisan make:factory ProductFactory --model=Product

HERE,

  • php artisan make:factory ProductFactory --model=Product creates a factory called ProductFactory for the model Product as specified by the option --model=Product.

Our product factory will be created in the directory /database/factories with the file name ProductFactory

Open the product factory and update the code to the following

<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Product;
use Faker\Generator as Faker;

$factory->define(Product::class, function (Faker $faker) {
    return [
        'category_id' => 2,
        'brand_id' => $faker->numberBetween($min = 2, $max = 3),
        'name' => $faker->word,
        'url' => $faker->word,
        'description' => $faker->sentence($nbWords = 6, $variableNbWords = true),
        'created_at_ip' => $faker->ipv4,
        'updated_at_ip' => $faker->ipv4,
    ];
});

HERE,

  • use App\Product; imports the Product model
  • use Faker\Generator as Faker; imports faker library
  • $factory->define(Product::class, function (Faker $faker) {...} defines a product factory that return an array of values that correspond to the attributes of our model
  • 'category_id' => 2, sets the category for all our products to 2
  • 'brand_id' => $faker->numberBetween($min = 2, $max = 3), sets the brand_id value to any number between 2 and 3. We are limiting the brand_id values to only 2 and 3 because our database enforces the relationship between brands and products and we only have 2 and 3 as valid brand ids
  • 'name' => $faker->word, assigns a random word as the product name
  • 'url' => $faker->word, assigns a random word as the product url
  • 'description' => $faker->sentence($nbWords = 6, $variableNbWords = true), assigns a random sentence as the product description
  • 'created_at_ip' => $faker->ipv4, assigns a random IP address address to the created_at_ip field
  • 'updated_at_ip' => $faker->ipv4, assigns a random IP address to the updated_at_ip field

Now that we have created our product factory, we can seed database records using tinker like so

$products = factory(App\Product::class, 10)->create();

HERE,

  • $products = factory(App\Product::class, 10)->create(); creates 10 random products in our table

Now that we have some dummy data, it is time to test our relationships.

Run the following tinker command

$product = App\Product::find(5)

The above command will produce the following results

=> App\Product {#3024
     id: 5,
     category_id: 2,
     brand_id: 3,
     name: "perferendis",
     url: "eos",
     description: "Ut reiciendis omnis voluptatibus id amet.",
     created_at_ip: "46.16.197.208",
     updated_at_ip: "106.89.95.40",
     created_at: "2019-09-09 09:31:35",
     updated_at: "2019-09-09 09:31:35",
   }
>>> 

Let us now pull the brand information from our product

$product->brand

HERE,

  • $product->brand calls a property brand on the instance variable product which is of Product type. This essentially calls the method brand that we defined in Product and returns a single row that contains the brand information.

Executing the above code produces the following results

=> App\Brand {#3041
     id: 3,
     name: "NWO",
     url: "nwo",
     description: "I have no idea what it means",
     created_at_ip: null,
     updated_at_ip: null,
     created_at: "2019-09-09 09:00:54",
     updated_at: "2019-09-09 09:00:54",
     deleted_at: null,
   }
>>>

We can call the attributes of brand on the $product instance variable like so

$product->brand->name

HERE,

  • $product->brand->name calls the name attribute of brand which is a property on the variable $product

Executing the above code produces the following result

"NWO"

Summary

Eloquent ORM is a powerful easy to use database model. We can automatically generate boiler-plate code for models using artisan from the command line and if we follow Laravel conventions then we can start using our model right away. Eloquent instances have methods for assign values to table attributes and persisting the data to the database. Soft delete partially simply marks a record as deleted but it still exists in the database while permanent delete removes the record from the database. Relationships between models are defined using methods.

What next?

In the next tutorial, we will introduce you to variables and learn what role they play in python applications. If you enjoyed this lesson then show us your appreciation by creating a free accounts.


...