w3resource

Laravel (5.7) Eloquent: Getting Started

Introduction

Laravel Eloquent ORM provides a beautiful, and simple ActiveRecord implementation for working with our database. Every database table in a laravel application has a corresponding "Model". This model is used to interact with the table. Models allow us to either query the data in a table or insert new records into the table.

To work with eloquent ORM the first thing you should do is to configure a database connection in your config/database.php.

Defining Models

So how we get started with Model, we start by creating one. Models are typically in the app directory, but you can place them anywhere that can be auto-loaded according to your composer.json file. All the Eloquent models you will write in Laravel will extend the illuminate\Database\Eloquent\Model class.

The easiest way to create a model instance is by using the make:model artisan command:

php artisan make:model Flight

In the case where you will like to generate a database migration at the same time as you generate the model, you can use the -migration or -m option:

php artisan make:model Flight --migration

php artisan make:model Flight -m

Eloquent Model Conventions

Let us consider the creation of a Flight model, we will use this model to store and retrieve information from our flights database table:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
    //
}

Table Names

Note that we did not inform Eloquent which table to use for our Flight model. As a convention, the "snake case", i.e plural name of the class is used as the table name unless another name is specified explicitly. So in our case, Eloquent will assume that the Flight model stores records in the flights table. You can however specify a custom table by defining a table property on your model:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'my_flights';
}

Primary Keys

The id column will always be assumed as the primary key column by Eloquent. You can define a protected $primaryKey property to override this convention:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
    /**
     * The primary key associated with the table.
     *
     * @var string
     */
    protected $primaryKey = 'flight_id';
}

Additionally, Eloquent will assume that the primary key is an incrementing integer value, this means that by default the primary key will automatically be cast to an int. if you wish to use a non-numeric or non-incrementing primary key you must set the public $incrementing property on your model to false.

<?php
class Flight extends Model
{
    /**
     * Indicates if the IDs are auto-incrementing.
     *
     * @var bool
     */
    public $incrementing = false;
}

In the case where your primary key is not an integer, the protected $keyType property on your model should be set to string:

<?php
class Flight extends Model
{
    /**
     * The "type" of the auto-incrementing ID.
     *
     * @var string
     */
    protected $keyType = 'string';
}

Timestamps

Eloquent expects the created_at and updated_at columns to exist on your table by default. In the case where you do not wish to have these columns automatically managed by Eloquent, you should set the $timestamp property on your model to false:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
    /**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    public $timestamps = false;
}

If you want to customize the format of your timestamp, you should set the $dateFormat property on your model. This property will determine how the date attributes are stored in the database, as well as their format when the model is serialized to a JSON or an array.

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
    /**
     * The storage format of the model's date columns.
     *
     * @var string
     */
    protected $dateFormat = 'U';
}

If you have to customize the names of the columns used to store the timestamps, you can set the CREATED_AT and UPDATED_AT constants the model:

<?php

class Flight extends Model
{
    const CREATED_AT = 'creation_date';
    const UPDATED_AT = 'last_update';
}

Database Connection

The default behavior of all Eloquent models is to use the default database connection configured for your application. If you would prefer to specify a different connection for the model, you should use the $connection property:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
    /**
     * The connection name for the model.
     *
     * @var string
     */
    protected $connection = 'connection-name';
}

Default Attribute Values

In the case where you want to define the default values for dome of your model's attributes, you would want to define an $attributes property on your model:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
    /**
     * The model's default values for attributes.
     *
     * @var array
     */
    protected $attributes = [
        'delayed' => false,
    ];
}

Retrieving Models

When a model is created and associated to a database table, you are ready to start retrieving data from the database. Each Eloquent model can be thought of as a powerful query builder that allows you to fluently query the database table associated with the model. For instance:

<?php

$flights = App\Flight::all();

foreach ($flights as $flight) {
    echo $flight->name;
}

Adding Additional Constraints

The Eloquent all method returns all the results in the model's table. Since every model serves as a query builder, constraints can also be added to queries, and then use the get method to retrieve the results:

$flights = App\Flight::where('active', 1)
               ->orderBy('name', 'desc')
               ->take(10)
               ->get();

Refreshing Models

We can use the fresh and refresh methods to refresh our models, the fresh method will re-retrieve the model from our database. This will not affect the existing model instance:

$flight = App\Flight::where('number', 'FR 900')->first();
$freshFlight = $flight->fresh();

The refresh method on the other hand will re-hydrate the existing model using fresh data from the database. Additionally, all of the loaded relationships of the returned data will be refreshed as well:

$flight = App\Flight::where('number', 'FR 900')->first();
$flight->number = 'FR 456';
$flight->refresh();
$flight->number; // "FR 900"

Collections

Eloquent methods like all and get which retrieves multiple results, an instance of the Illuminate\Database\Eloquent\Collection will be returned. The collection class will provide a variety of helpful method for working with your Eloquent results:

$flights = $flights->reject(function ($flight) {
    return $flight->cancelled;
});

You can also loop over the collection like an array:

```foreach ($flights as $flight) {
    echo $flight->name;
}

Chunking Results

In cases when you need to process thousands of Eloquent records, you should use the chunk command. This chunk method will retrieve a "chunk" of Eloquent models, and feed them to a given Closure for processing. Using the chunk method will help you conserve memory when working with large result sets:

Flight::chunk(200, function ($flights) {
    foreach ($flights as $flight) {
        //
    }
});

The first argument passed to the method is the number of records that you wish to receive per "chunk". The Closure passed as the second argument will be called for each chunk that is retrieved from the database. A database query will be executed to retrieve each chunk of records passed to the Closure.

Using Cursors

The cursor method will allow you to iterate through your database records using a cursor, this will only execute a single query. When processing large a amount of data, the cursor method can be used to greatly reduce your memory usage:

foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
    //
}

Retrieving Single Models / Aggregates

Aside retrieving all of the records for a given table, you can also retrieve single records using find or first. Rather than returning a collection of models, these methods will return a single model instance:

// Retrieving a model by its primary key...
$flight = App\Flight::find(1);

```// Retrieving the first model matching the query constraints...
$flight = App\Flight::where('active', 1)->first();

You can also call the find method with an array of primary keys, which will return a collection of the matching records:

$flights = App\Flight::find([1, 2, 3]);

Not Found Exceptions

Sometimes you may want to throw an exception if a model is not found. This is useful in routes or controllers. The findOrFail and firstOrFail methods will retrieve the first result of the query; however, when no result is found, an Illuminate\Database\Eloquent\ModelNotFoundException will be thrown:

``$model = App\Flight::findOrFail(1);
$model = App\Flight::where('legs', '>', 100)->firstOrFail();

If you don't catch the exception, a 404 HTTP response will be automatically sent back to the user. It is not necessary to write explicit checks to return 404 responses when using these methods:

Route::get('/api/flights/{id}', function ($id) {
    return App\Flight::findOrFail($id);
});

Retrieving Aggregates

You can also use the count, sum, max, and other aggregate methods that provided by the query builder. These methods will return the appropriate scalar value instead of a full model instance:

$count = App\Flight::where('active', 1)->count();
$max = App\Flight::where('active', 1)->max('price');

Inserting &Updating Models

Inserts

When you want to create a new record in the database, you have to create a model instance, and set attributes on the model and then you call the save method:

<?php

namespace App\Http\Controllers;

use App\Flight;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class FlightController extends Controller
{
    /**
     * Create a new flight instance.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // Validate the request...

        $flight = new Flight;

        $flight->name = $request->name;

        $flight->save();
    }
}

In the above example, we have assigned the name parameter from the incoming HTTP request to the name attribute of the App\Flight model instance. When the save method is called, a new record will be inserted into the database. The created_at and updated_at timestamps will automatically get set when the save method is called, hence we don't need to set them manually.

Updates

The save method can also be used to update models that already exist in the database. When you want to update a model, the first thing to do is to retrieve it, then set any of the attributes you wish to update, and finally you can call the save method. There is no need to set the updated_at manually as it will be updated automatically:

$flight = App\Flight::find(1);
$flight->name = 'New Flight Name';
$flight->save();>

Mass Updates

You can also perform updates against any number of models that match a given query. In the example below, all the flights that are active and that also have a destination of san Diego wil be marked as delayed:

App\Flight::where('active', 1)
          ->where('destination', 'San Diego')
          ->update(['delayed' => 1]);

The update method will expect an array of column and value pairs which represents the column that should be updated.

Mass Assignment

You can also use the create method to save a new model in a single line. The inserted model instance is returned to you from the method. But before doing this you must first specify either a fillable or guarded attribute on the model, because all Eloquent models protect against mass assignment by default.

A mass-assignment vulnerability will occur when a user passes an unexpected HTTP parameter through a request, and that parameter makes changes a column in your database you did not expect. For instance, a very malicious user might send an is_admin parameter through an HTTP request, which will then be passed into your model's create method, allowing the user to escalate their privilege to that of an administrator.

So, to start you should define which model attributes you want to make mass assignable. You can do this using the $fillable property on the model. For instance, let us make the name attribute of our Flight model mass assignable:

>
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name'];
}

when we make the attributes mass assignable, we can then use the create method to insert a new record in the database. The create method will return the saved model instance:

$flight = App\Flight::create(['name' => 'Flight 10']);
If you already have a model instance, you can use the fill method to populate it with an array of attributes:
```$flight->fill(['name' => 'Flight 22']);

Guarding Attributes

While the $fillable property serves as a "white list" of attributes that should be mass assignable, you can also choose to use $guarded. The $guarded property should contain an array of attributes that you don't want to be mass assignable. All other attributes not in the array will be mass assignable. So, $guarded will function like a "black list". Importantly, you can use either $fillable or $guarded - not both. In the example shown below, all of the attributes except for price will be mass assignable:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The attributes that aren't mass assignable.
     *
     * @var array
     */
    protected $guarded = ['price'];
}

In scenarios when you would like to make all attributes mass assignable, you can define the $guarded property as an empty array:

/**
 * The attributes that are not mass assignable.
 *
 * @var array
 */
protected $guarded = [];

Other Creation Methods

firstOrCreate/ firstOrNew

There are two other methods which you can use to create models by mass assigning attributes: firstOrCreate and firstOrNew. The firstOrCreate method attempts to locate a database record using the given column / value pairs. If the model cannot be found in the database, a record is inserted with the attributes from the first parameter, along with the ones in the optional second parameter.

Just like firstOrCreate, the firstOrNew method, attempts to locate a record in the database matching the given attributes. However, if a model is not seen, a new model instance is returned. Note that the model that is returned by firstOrNew has not yet been persisted to the database. You need to call save manually to persist it:

// this line will retrieve flight by name, or create it in the case it doesn't exist...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);
// this will retrieve flight by name, or create it with the name, delayed, and arrival_time attributes...
$flight = App\Flight::firstOrCreate(
    ['name' => 'Flight 10'],
    ['delayed' => 1, 'arrival_time' => '11:30']
);
// Retrieve by name, or instantiate...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);
// Retrieve by name, or instantiate with the name, delayed, and arrival_time attributes...
$flight = App\Flight::firstOrNew(
    ['name' => 'Flight 10'],
    ['delayed' => 1, 'arrival_time' => '11:30']
);

updateOrCreate

'

There are situations where you want to update an existing model or or you want to create a new model if none exists. Laravel provides us with an updateOrCreate method to do this in one step. Just like the firstOrCreate method, updateOrCreate will persist the model, so there is no need to call save():

// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99, 'discounted' => 1]
);

Deleting Models

When you want to delete a model, you can call the delete method on a model instance:

$flight = App\Flight::find(1);
$flight->delete();

Deleting an Existing Model by Key

In the example shown above, we retrieve the model from the database before calling the delete method. However, if we know the primary key of the model, we may delete the model without retrieving it by calling the destroy method. In addition to a single primary key as its argument, the destroy method accepts multiple primary keys, a collection of primary keys or an array of primary keys:

App\Flight::destroy(1);
App\Flight::destroy(1, 2, 3);
App\Flight::destroy([1, 2, 3]);
App\Flight::destroy(collect([1, 2, 3]));

Deleting Models By Query.

You could also run a delete statement on a set of models. In the example below, we delete all flights that are marked as inactive. Just like mass updates, mass deletes will not fire any model events for models that are deleted:

$deletedRows = App\Flight::where('active', 0)->delete();

Soft Deleting

As opposed to actually removing records from your database, Eloquent can also perform "soft delete" on models. When we soft delete models, the models are not actually removed from your database. Instead, we set a deleted_at attribute on the model and this column is inserted into the database. If a model has a non-null deleted_at value, the model has been soft deleted. To enable soft deletes on a model, you should use the Illuminate\Database\Eloquent\SoftDeletes trait on the model:

<?php

namespace App;

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

class Flight extends Model
{
    use SoftDeletes;
}
.

Additionally, you need to add the deleted_at column to your database table. Laravel's schema builder contains a helper method to create the deleted_at column:

Schema::table('flights', function (Blueprint $table) {
    $table->softDeletes();
});

Now, when the delete method is called on the model, the deleted_at column will be set to the current time and date. And, when we query a model that uses soft deletes, the soft deleted models will be automatically excluded from all query results.

To know if a given model instance has been soft deleted, you should use the trashed method:

```if ($flight->trashed()) {
    //
}

Querying Soft Deleted Models

Including Soft Deleted Models

Just as it was noted above, all soft deleted models will automatically be excluded from query results. However, you can force soft deleted models to appear in a result set using the withTrashed method on the query:

$flights = App\Flight::withTrashed()
                ->where('account_id', 1)
                ->get();
`

The withTrashed method can also be used on a relationship query:

$flight->history()->withTrashed()->get();

Retrieving Only Soft Deleted Models

The onlyTrashed method retrieves only soft deleted models:

$flights = App\Flight::onlyTrashed()
                ->where('airline_id', 1)
                ->get();

Restoring Soft Deleted Models

There are times when you may wish to "un-delete" a soft deleted model. To restore a soft deleted model back into an active state, you should use the restore method on a model instance:

$flight->restore();

You can also use the restore method in a query to quickly restore multiple models. And like other "mass" operations, this won't fire any model events for the models that are restored:

App\Flight::withTrashed()
        ->where('airline_id', 1)
        ->restore();

Like the withTrashed method, the restore method may also be used on relationships:

$flight->history()->restore();

Permanently Deleting Models

There are times when you need to truly remove a model from your database. For you to permanently remove a soft deleted model from the database, use the forceDelete method:

// Force deleting a single model instance...
$flight->forceDelete();

// Force deleting all related models...
$flight->history()->forceDelete();

Query Scopes

Global Scopes

Global scopes will allow us to add constraints to all queries for a given model. Laravel's own soft delete functionality utilizes global scopes to only pull the "non-deleted" models from the database. Writing our own global scopes can provide a convenient, easy way to make sure every query for a given model receives certain constraints.

Writing Global Scopes

Writing a global scope is quite simple. Fisrt define a class that implements the Illuminate\Database\Eloquent\Scope interface. This interface requires us to implement one method: apply. The apply method adds where constraints to the query as needed:

<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class AgeScope implements Scope
{
    /**
     * Apply the scope to a given Eloquent query builder.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        $builder->where('age', '>', 200);
    }
}

Applying Global Scopes

When we need to assign a global scope to a model, we should override a given model's boot method and use the addGlobalScope method:

<?php

namespace App;

use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope(new AgeScope);
    }
}

After we add the scope, a query to User::all() will produce the following SQL statement:

select * from `users` where `age` > 200

Anonymous Global Scopes

Eloquent will also allow you to define global scopes using Closures, this is particularly useful for simple scopes that do not warrant a separate class:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class User extends Model
{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('age', function (Builder $builder) {
            $builder->where('age', '>', 200);
        });
    }
}

Removing Global Scopes

If you would love to remove a global scope for a given query, you can use the withoutGlobalScope method. This method accepts the class name of the global scope as its only argument:

User::withoutGlobalScope(AgeScope::class)->get();

Or, if you defined the global scope using a Closure:

User::withoutGlobalScope('age')->get();

If you would love to remove several or even all the global scopes, you can use the withoutGlobalScopes method:

// Remove all of the global scopes...
User::withoutGlobalScopes()->get();

// Remove some of the global scopes...
User::withoutGlobalScopes([
    FirstScope::class, SecondScope::class
])->get();

Local Scopes

Local scopes allow us to define common sets of constraints that we may easily re-use throughout your application. For instance, we may need to frequently retrieve all users that are considered "popular". Defining a scope is simple, just prefix an Eloquent model method with scope.

All Scopes should always return a query builder instance:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Scope a query to only include popular users.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }

    /**
     * Scope a query to only include active users.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('active', 1);
    }
}

Utilizing A Local Scope

Once a scope has been defined, you can call the scope methods when querying the model. However, you should not include the scope prefix when calling that method. You can chain calls to various scopes, for instance:

$users = App\User::popular()->active()->orderBy('created_at')->get();

Combining multiple Eloquent model scopes using an or query operator may require the use of Closure callbacks:

$users = App\User::popular()->orWhere(function (Builder $query) {
    $query->active();
})->get();

But, because Laravel knows this can be cumbersome, Laravel provides a "higher order" orWhere method that will allow you to fluently chain these scopes together without the use of Closures:

$users = App\User::popular()->orWhere->active()->get();

Dynamic Scopes

There are times when you may wish to define a scope that accepts parameters. To get started with this, you just need to add your additional parameters to your scope. Scope parameters should be defined after $query parameter:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    /**
     * Scope a query to only include users of a given type.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @param  mixed  $type
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOfType($query, $type)
    {
        return $query->where('type', $type);
    }
}

Now, you can pass the parameters when calling the scope:

$users = App\User::ofType('admin')->get();

Comparing Models

Sometimes you might have to determine if two models are the "same". The is method may be used to quickly verify two models have same primary key, table, and database connection:

if ($post->is($anotherPost)) {
    //
}

Events

Eloquent models can fire several events, allowing us to hook into the following points in a model's lifecycle: retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored. Events allows us to easily execute code each time a specific model class is saved or updated in the database. Each event will receive the instance of the model through its constructor.

The retrieved event fires when an existing model is retrieved from the database. Whenever a new model is saved for the first time, the creating and created events will both fire. If a model already exists in the database and then the save method is called, the updating / updated events will fire. However, in both scenario, the saving / saved events will fire.

To get started, define a $dispatchesEvents property on our Eloquent model that maps various points of the Eloquent model's lifecycle to our own event classes:

<?php

namespace App;

use App\Events\UserSaved;
use App\Events\UserDeleted;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The event map for the model.
     *
     * @var array
     */
    protected $dispatchesEvents = [
        'saved' => UserSaved::class,
        'deleted' => UserDeleted::class,
    ];
}

After define and map our Eloquent events, we may use event listeners to handle the events.

Observers

Defining Observers

In cases where we are listening for many events on a given model, we may use observers to group all of our listeners into a single class. Observers classes have method names which reflect the Eloquent events we wish to listen for. Each of these methods will receive the model as their only argument. The make:observer Artisan command is the easiest way to create a new observer class:

php artisan make:observer UserObserver --model=User

This command will place the new observer in our App/Observers directory. If this directory does not exist, Artisan will create it for us. Our fresh observer will look like the following:

<?php

namespace App\Observers;

use App\User;

class UserObserver
{
    /**
     * Handle the User "created" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * Handle the User "updated" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function updated(User $user)
    {
        //
    }

    /**
     * Handle the User "deleted" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function deleted(User $user)
    {
        //
    }
}

If we want to register an observer, we will use the observe method on the model we wish to observe. We can register observers in the boot method of one of our service providers. In this instance, we will register the observer in the AppServiceProvider:

<?php

namespace App\Providers;

use App\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        User::observe(UserObserver::class);
    }
}

Previous: Laravel Tutorial (5.7) Seeding
Next: Laravel (5.7) Eloquent Relationships



Follow us on Facebook and Twitter for latest update.