4.2 Database and Eloquent

Introduction to Elquent

Eloquent is an ActiveRecord ORM. It’s a database abstraction layer that provides a single interface to interact with multiple database types.

Creating and Defining Eloquent Models

First, we can create a model like this:

1
php artisan make:model User

This is what we’ll get in app/User.php

1
2
3
4
5
6
<?php
use Illuminate\Database\Eloquent\Model;
class User extends Model
{

}

If you want to automatically create a migration when you create the model, pass -m or –migration flag

1
php artisan make:model User -m

Table name

The default behavior for the table names is that Laravel ‘snake cases’ and pluralizes your class name, so SecondaryUser will access a table named secondary_users.

If you want to customize the name, set the $table property on the model:

1
protected $table = 'users_secondary';

Primary key

Laravel assumes, by default, each table will have an autoincrementing integer primary key, and it will be named id

If you want to customize the name of primary key, change the $primaryKey property

1
protected $primaryKey = 'contact_id';

And if you want to set it to be nonincrementing, use

1
public $incrementing = false;

Timestamps

Eloquent expects every table to have created_at and updated_at timestamp columns. If your table wont’ have them, disable the $timestamps functionality

1
public $timestamps = false;

Retrieving Data with Eloquent

Most of the time we pull data from database with Eloquent, we’ll use static calls on Eloquent model

Everything we can do on DB facade we can do on our Eloquent objects.

Get all

1
Users::all(); // return all users in database

Get one

we can use first() to return only the first record from a query, or find() to pull just the record with the provided ID. For either, we can append orFail to the method name

1
2
User::where('is_verified',true)->first();
User::find($userId);

Any single return will return an instance of the Eloquent class.

Get Many

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

It will return collections.

Chunking responses with chunk()

If you’ve need to deal with a large amount of records at a time. Laravel makes it possible to break your requests into maller pieces( chunks) and process them in batches.

1
2
3
4
5
6
User::chunk(100, function($users){
foreach($users as $user)
{
// do something
}
});

Aggregates

1
2
3
User::where('vip',true)->count();
User::sum('votes');
User::avg('skill_level');

Inserts and Updates with Eloquent

Inserts

1
2
3
4
5
6
7
8
9
10
11
12
$user = new User;
$user->name = 'hello';
$user->email = 'world@gmail.com';
$user->save();

// or

$user = new User([
'name' => 'hello',
'email' => 'world'
]);
$user->save();

Until we save(), this instance has never been saved to the database.

This means it doesn’t have an id. But when you use Model::create()

1
2
3
4
$user = User::create([
'name' => 'hello',
'email' => 'world@gmail.com'
]);

You don’t need to call save() , as it’s part of the Model::create() function.

Updates

We can call the update() method to updte one or more record

1
2
3
4
5
6
7
8
9
$user = User::find(1);
$user->email = 'hello@gmail.com';
$user->save();

//or

User::where('created_at','<',Carbon::now->subYear())->update([
'longeviy' => 'ancient'
]);

When we use the Model::update() we don’t need to call save()

Mass assignment

When you define a model’s fillable or guarded properties, we can create or update a record like this:

1
2
3
4
5
6
7
8
9
10
11
public function update(User $user, Request $request)
{
$contact->update($request->all());
}

// or

public function store(Request $request)
{
User::create($request->only(['name','email']));
}

firstOrCreate() and firstOrNew()

The firstOr*() function is used to tell Laravel that ‘give me an instance with these properties or if ti doesn’t exist, create it’.

Those two methods take an array of keys and values as their first parameter

1
$user = User::firstOrCreate(['name'=>'hello','email'=>'world']);

They’ll both look for and retrieve the first record matching those parameters, and if there are no matching records, they’ll create an instance with thsoe properties;

The different is firstOrCreate() will persist that instance to the database and then return it, while firstOrNew() will return it without saving it.

Deleting with Eloquent

Normal deletes

1
2
3
4
5
6
7
$contact = Contact::find(1);
$contact->delete();

//or

Contact::destory(1);
Contact::destory([2,3,4]);

Soft deletes

Soft deletes mark databse row as deleted without actually deleting them from the database. In Laravel, every query will be scoped to ignore the soft deleted data, unless you explicitly ask to bring them back.

Eloquent’s soft delete functionality requires a deleted_at column to be added to the table.

Enabling soft deletes

There are three things to do to enable soft deletes

  1. Adding the deleted_at column in a migration using softDeletes() method.

    1
    2
    3
    Schema::create('Users', function(Blueprint $table){
    $table->softDeletes()
    });
  2. Importing the SoftDeletes trait in the model.

  3. Adding teh deleted_at column to your $dates property.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?php
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\SoftDeletes;

    class Contact extends Model
    {
    use SoftDeletes; // use the trait
    protected $dates = ['deleted_at']; // mark this column as a date
    }

Once you make these changes, every delete() and destory() call will now set the delete_at column on your row to be the current data and time instead deleting that row.

Querying with soft deletes

First, you can add soft-deleted items to a query

1
$users = User::withTrashed()->get();

Next, you can use trashed() method to see if a particular instance has been soft deleted

1
2
3
4
if($user->trashed())
{

}

Finally, you can get only soft-deleted items

1
$user = User::onlyTrashed()->get();

Restoring soft-deleted items

If you want to resotre a soft-deleted item, you can run resotre() method on an instance or a query

1
2
$user->restore();
User::onlyTrashed()->restore();

Force deleting soft-deleted entities

1
User::onlyTrashed()->forceDelete();

Scopes

Local and global scopes in Eloquent allow you to define prebuilt ‘scopes’ that you can use either every time a model is queried(‘global’) or every time you query it with a particular method chain(‘local’)

Local scopes

Local scopes are the simplest to understand

1
$vips = User::where('vip',true)->where('trial',false)->get();

To avoid writing above code again and again to fetch the ‘active vip’, we can define a scope like this in our Model

1
2
3
4
5
6
7
8
9
10
class User
{
public function scopeActiveVips($query)
{
return $query->where('vip',true)->where('trial',false);
}
}

// Now we can use it like this
User::activeVips()->get();

You can also define scope with parameters

1
2
3
4
5
6
7
8
9
10
class User
{
public function scopeStatus($query, $status)
{
return $query->where('status',$status);
}
}

// Now we can use it like this
User::status('frined')->get();

To define a local scope, we add a method to the Eloquent class that begins with “scope” and then contains the title-cased version of the scope name. This method is passed a query builder and needs to return a query builder, but of course you can modify the query before returning—that’s the whole point.

Global scopes

The global scope will be applied on every query made from a given model.

There are two way to define a global scope:

  1. Using an closure

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class User
    {
    protected static function boot(){
    parent::boot();
    static::addGlobalScope('active',function(Illuminate\Database\Eloquent\Builder $builder){
    $builder->where('active',true);
    })
    }
    }
  2. Using an entire class

    Create a class that implements Illuminate\Database\Eloquent\Scope, that mean it will have an apply() method that takes an instance of a query builder and an instance of the model.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?php
    namespace App\Scopes;
    use Illuminate\Database\Eloquent\Scope;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\Builder;

    class ActiveScope extends Scope{
    public function apply(Builder $builder, Model $model){
    return $builder->where('active',true);
    }
    }

    To apply this scope to a model, once again override the parent’s boot() method and call addGlobalScope() on the call using static

    1
    2
    3
    4
    5
    6
    7
    class User
    {
    public static function boot(){
    parent::boot();
    static::addGlobalScope(new ActiveScope);
    }
    }

Removing global scopes

1
2
3
4
5
6
7
8
9
10
11
// remove global scope added by a closure
User::withoutGlobalScope('active')->get();

// remove a global scope added by a class
User::withoutGlobalScope(ActiveScope::class)->get();

// remove two global scopes added by classes
User::withoutGlobalScope([ActiveScople::class,VipScope::class])->get();

// remove all global scope for the model
User::withoutGlobalScopes()->get();

Customizing Field Interactions with Accessors, Mutators, and Attribute Casting

Accessor

Accessors allow you to define custom attributes on your Eloquent models for when you are reading data from the model instance.

You can define an accessor by writing a method on your moel with the following structure: get{PascalCasedPropertyName}Attribute. So, if you property name is first_name, the accessor method would be named getFirstNameAttribute

1
2
3
4
5
6
7
8
9
10
class User extends Model
{
public function getNameAttribute($value)
{
return $value ?:'(No name provided)';
}
}

//we can use it like this
$name = $user->name;

But we can also use accessors to define attributes that never existed in the database

1
2
3
4
5
6
7
8
9
10
class User extends Model
{
public function getFullNameAttribute()
{
return $this->first_name. ' ' . $this->last_name;
}
}

//we can use it like this:
$fullname = $user->full_name

Mutators

Mutators work the same way as accessor, except they’re for determining how to process setting the data instead of getting it

You define a mutator by writing a method on your model with the following structure: set{PascalCasedPropertyName} attribute.

1
2
3
4
5
6
7
8
9
10
class User extends Model
{
public function setNameAttribute($value)
{
$this->attributes['amount'] = $value > 0 ? $value : 0;
}
}

//we can use it like this
$user->amount = 15;

But we can also use mutators to define attributes that never existed in the database

1
2
3
4
5
6
7
8
9
10
class Order extends Model
{
public function setWorkgroupNameAttribute($workgroupName)
{
$this->attributes['email'] = "{$workgroupName}@ourcompany.com";
}
}

// we can use it like this
$user->workgroup_name = 'hello';

Attribute casting

It allows you to define that any of your columns should always be treated, both on read and on write, as if you are of a particular data type

1
2
3
4
5
6
7
class Contact
{
protected $casts = [
'vip' => 'boolean',
'children_names' => 'array'
];
}

Date mutators

You can choose for particular columns to be mutated as timestamp columns by adding them to the dates array

1
2
3
4
5
6
class Contact
{
protected $dates = [
'met_at'
];
}

Eloquent Collection

When you make any query call in Eloquent that has the potential to return multiple row, instead of an array they’ll come packaged in an Eloquent collection, which is a specialized type of collection.

check the collection document

Eloquent Serializtion

Returning models difrectly from route methods

If you return the result of an Eloquent call in a controller, it will be automatically cast to a string, and therefore returned as JSON.

1
2
3
Route::get('api/contacts',function(){
return Contact::all();
});

Hiding attributes from JSON

You can either blacklist attributes, hiding the ones you list:

1
2
3
4
class Contact extends Model
{
public $hidden = ['password','remember_token'];
}

or whitelist attributes, showing only the ones you list:

1
2
3
4
class Contact extends Model
{
public $visible = ['name','email','status'];
}

This also works for relationships

1
2
3
4
5
6
7
class User extends Model 
{
public $hidden = ['contacts'];
public function contacts() {
return $this->hasMany(Contact::class);
}
}

Eloquent Relationships

In a relational database model, it’s expected that you will have tables that are related to each other—hence the name. Eloquent provides simple and powerful tools to make the process of relating your database tables easier than ever before.

One to One

Let’s start simple: a Contact has one PhoneNumber