Writing your first Laravel app - Part Four

Writing your first Laravel app - Part Four

Rai Omido

Howdy! It's Rai here. Welcome to our fourth lesson.

# Introduction

This lesson picks from where lesson three left off. We are going to learn a few things about Laravel's Eloquent ORM (Object Relational Mapping). ORM enables us to write both simple and complex SQL queries using Object-Oriented Programming.

Instead of doing something like this;

SELECT * FROM users WHERE active = '1';

In Laravel, you may do something like;

User::where('active', '1')->get();

This makes our coding much easier and faster. We spend less time trying to store and retrieve our data, and more time using it to make good software.

Enough said, let us get to work.

# More on Models

Models are the core of Laravel's ORM. For each database table that you create, you may create a corresponding Model that will be used to interact with that table.

Like we saw earlier, the easiest way to create a Model is by using the make:model artisan command.

php artisan make:model Project

You may also create a model and the corresponding table's migration in one line like so;

php artisan make:model Project --migration

//Or better;

php artisan make:model Project -m

This will create a Project.php file in app/Models and a **_create_projects_table.php file inside database/migrations.

By default, Laravel uses the pluralized snake-case form of the Model class name as the table name. You can however define a table name that you prefer. We shall see more about this later.

# Common Model Conventions

Models in Laravel provide a number of options to help us modify Laravel's default behavior. This is helpful as it allows us to perform tasks like defining tables, setting default values for table columns or even modifying our data at runtime.

~Table Names

You may manually define your table name by declaring a table property inside your Model like below.

<?php

namespace App\Models;

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

class Project extends Model
{
use HasFactory;

/**
* By default, Laravel assumes 'projects' as the table name.
* You change that here
* @var string
*/
protected $table = 'my_projects';
}
~The primary key

As is the case with the table name, you may use the protected $primaryKey property to define a custom primary key for your table. By default, Laravel assumes the primary key is id.

...
protected $primaryKey = 'project_id';
...
~Default Attribute Values

If you'd like to define some default values for attributes for your data, you may use the $attributes property.

For instance, if your table contains the fields, id, name, active. You may want to set a default value for, say, the active field, so that incase data is submitted, and the value for that field is missing, your default value will be used instead.

To set a default value for the active field, you would do something like,

...

protected $attributes = [
  'active' => false,
];

...

# Retrieving Data

Each Model allows us to query its corresponding database table with much ease.

For instance, if you wanted to retrieve all records from the projects table, you'd do something like;

Project::all()

It's just that simple.

You may also add additional constraints to the query using the different methods provided by Laravel.

$projects = Project::where('active', true)
->orderBy('name', 'desc')
->take(10)
->get()

Laravel Models are actually query builders. So all the methods implemented in Laravel's query builder interface are also available for Models. You can read more about query builders in the Laravel documentation.

Don't worry if you don't know all these methods off the top of your head. You'll barely ever need to. They are always available in the Laravel docs. Besides, you can be sure that you'll be able to memorize most of the common ones over time.

# Eloquent Collections

An eloquent collection is an object that contains multiple Model instances.

Let's say you have 10 projects in your database. When you use the all() method to retrieve all the project records, you will have ten instances of the Project class. All these Model instances are returned as an instance of a Illuminate\Database\Eloquent\Collection object. This allows us to work with multiple records at once.

The Collection object provides us with many methods to aid data manipulation. A full list can be found in the docs.

# Inserting and Updating Data

To insert a new record in the database, you have two options.

The first option is to instantiate a Model, set the attribute values, and use the save() method to insert into the database.

$post = new Post;
$post->name = 'How to add paypal to your Website';
$post->body = 'This is a short body for this awesome blog post';
$post->active = true;
$post->save();

The record may also be updated pretty much the same way, but you need to have a model instance for an existing record.

$post = Post::find(1);
$post->active = false;
$post->save()
~Mass Assignment

Mass assignment can be used to create and update records in one line. However, Laravel does not allow mass assignment by default. You are required to define a $fillable or $guarded property to allow or guard against mass assignment.

In your Model;

...

# Guard the active field against being filled through mass assignment

protected $guarded = [
  'active'
];

# Allow the active field to be filled through mass assignment

protected $fillable= [
  'active'
];

..

To use this in our Models, for instance, we would do;

In app/Models/Category.php;

class Category extends Model
{
use HasFactory;

protected $fillable = [
'title',
'active',
];
}

And in app/Models/Post.php;

class Post extends Model
{
use HasFactory;

protected $fillable = [
'title',
'body',
'user_id',
'category_id',
'active',
];
}

After allowing our fields to be mass assignable, we can now proceed to do the actual mass assignment using the create() method.

# Retreive a user
$user= User::find(1);

# Create a category
$category = Category::create([
  'title' => 'Computer Programming',
  'active' => true
]);

# Create a Post
$post = Post::create([
'title' => 'How to integrate Mpesa into your Website',
'body' => 'Your very awesome sentenses and paragraphs go here!',
'user_id' => $user->id,
'category_id' => $category->id,
  'active' => true
]);

Awesome!

# Deleting Data

To delete a record, you can use the delete() method.

$post = Post::find(1);
$post->delete();

You can also use the destroy() method to delete a record without retrieving it first.

Post::destroy(1);

# Delete multiple records
Post::destroy([1, 3, 6]);

You may also delete a record by query.

Post::where('active', false)->delete();
~Soft Deleting Records

With soft deleting, records are not actually removed from the database. Instead, a deleted_at attribute is used to determine if a record has been deleted or not. If it has a non-null value, the record has been soft deleted.

To enable soft deleting for a Model, the Illuminate\Database\Eloquent\SoftDeletes trait is used like so.

...
use Illuminate\Database\Eloquent\SoftDeletes;

class Post extends Model
{
use SoftDeletes;
}
...

You also ensure you add a deleted_at field in your database table. You may do this using any other method, but you can add it in your migration file like so;

...
public function up()
{
Schema::create('posts', function (Blueprint $table) {
...
$table->softDeletes();
...
});
}
...

Now, when you run your migrations, a deleted_at column will be created in your table.

The soft deleting functionality will now take precedence whenever you call the delete() method.  Instead of the record being literally removed, the deleted_at column will be filled with the current date and time. Now, each time you fetch data, all the records with a value in the deleted_at column will be left out.

~Querying Soft Deleted Records

Whenever you try to query records using the normal eloquent methods, all the soft deleted records will not be retrieved.

If you'd wish to retrieve soft deleted records, you may use the withTrashed() or onlyTrashed() method.

# Retrieve all, including soft deleted records
Post::withTrashed()->where('active', true)->get();

# Reterive only soft deleted records
Post::onlyTrashed()->where('active', true)->get();
~Restoring Soft Deleted Records

In case you would like to restore soft deleted records, you may use the restore() method.

$post->restore();

# Restore multiple records
Post::onlyTrashed()->where('active', true)->restore();
~Permanently deleting Soft Deleted Records

You may use the forceDelete() method to permanently delete soft-deleted records.

$post->forceDelete();

# Accessors and Mutators

Accessors allow us to modify database values after retrieving them from the database. For example, you may use accessors to combine a first name and a last name, to form a full name.

Mutators, on the other hand, allow us to modify attribute values before inserting them into the database. For instance, you may use a mutaror to hash a password before inserting it into the database.

~Defining an Accessor

To set an accessor, you may define a getWhateverYouWantAttribute() method in your model. Where WhateverYouWant is the name of the field you would like to be made available.

For instance, let's say you need to concatenate the first_name and last_name columns to create a full_name, you would do;

public function getFullNameAttribute() {
      return "{$this->first_name} {$this->last_name}";
}

Then you can access your new value like you would any other attributes from a model instance.

$user->full_name;
~Defining a Mutator

To set a mutator you need to define a setWhateverYouWantAttribute() as we did with the accessor. But this has to be the name of a column in your database.

public function setPasswordAttribute($input) {
      $this->attributes['password'] = bcrypt($input);
}

Whenever you perform an insert or update operation, the value for the password will be hashed before being inserted into the database.

You can also add extra logic to the method if you'd love to. For example, say you wanted to make sure that $input is not empty, before trying to hash it.

public function setPasswordAttribute($input) {
    if($input) {
       $this->attributes['password'] = bcrypt($input);
      }
}

# Conclusion

Hopefully, you now have a good idea of how Laravel's Eloquent ORM works. Next up, we are going to dig a little deeper into Routes and Controllers. See you in the fifth lesson.