Introduction
Laravel includes Eloquent, an object-relational mapper (ORM) that makes it enjoyable to interact with your database. When using Eloquent, each database table has a corresponding "Model" that is used to interact with that table. In addition to retrieving records from the database table, Eloquent models allow you to insert, update, and delete records from the table as well.
Tip!! Before getting started, be sure to configure a database connection in your application's
config/database.php
configuration file. For more information on configuring your database, check out the database configuration documentation.
Generating Model Classes
To get started, let's create an Eloquent model. Models typically live
in the app\Models
directory and extend the
Illuminate\Database\Eloquent\Model
class. You may use the
make:model
Artisan command to
generate a new model:
php artisan make:model Flight
If you would like to generate a database
migration when you generate the model, you may use the
--migration
or -m
option:
php artisan make:model Flight --migration
You may generate various other types of classes when generating a model, such as factories, seeders, policies, controllers, and form requests. In addition, these options may be combined to create multiple classes at once:
# Generate a model and a FlightFactory class...
php artisan make:model Flight --factory
php artisan make:model Flight -f
# Generate a model and a FlightSeeder class...
php artisan make:model Flight --seed
php artisan make:model Flight -s
# Generate a model and a FlightController class...
php artisan make:model Flight --controller
php artisan make:model Flight -c
# Generate a model, FlightController resource class, and form request classes...
php artisan make:model Flight --controller --resource --requests
php artisan make:model Flight -crR
# Generate a model and a FlightPolicy class...
php artisan make:model Flight --policy
# Generate a model and a migration, factory, seeder, and controller...
php artisan make:model Flight -mfsc
# Shortcut to generate a model, migration, factory, seeder, policy, controller, and form requests...
php artisan make:model Flight --all
# Generate a pivot model...
php artisan make:model Member --pivot
Eloquent Model Conventions
Models generated by the make:model
command will be
placed in the app/Models
directory. Let's examine a basic
model class and discuss some of Eloquent's key conventions:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
//
}
Table Names
After glancing at the example above, you may have noticed that we did
not tell Eloquent which database table corresponds to our
Flight
model. By convention, the "snake case", plural name
of the class will be used as the table name unless another name is
explicitly specified. So, in this case, Eloquent will assume the
Flight
model stores records in the flights
table, while an AirTrafficController
model would store
records in an air_traffic_controllers
table.
If your model's corresponding database table does not fit this
convention, you may manually specify the model's table name by defining
a table
property on the model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'my_flights';
}
Primary Keys
Eloquent will also assume that each model's corresponding database
table has a primary key column named id
. If necessary, you
may define a protected $primaryKey
property on your model
to specify a different column that serves as your model's primary
key:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'flight_id';
}
In addition, Eloquent assumes that the primary key is an incrementing
integer value, which means that Eloquent will automatically cast the
primary key to an integer. If you wish to use a non-incrementing or a
non-numeric primary key you must define a public
$incrementing
property on your model that is set to
false
:
<?php
class Flight extends Model
{
/**
* Indicates if the model's ID is auto-incrementing.
*
* @var bool
*/
public $incrementing = false;
}
If your model's primary key is not an integer, you should define a
protected $keyType
property on your model. This property
should have a value of string
:
<?php
class Flight extends Model
{
/**
* The data type of the auto-incrementing ID.
*
* @var string
*/
protected $keyType = 'string';
}
"Composite" Primary Keys
Eloquent requires each model to have at least one uniquely identifying "ID" that can serve as its primary key. "Composite" primary keys are not supported by Eloquent models. However, you are free to add additional multi-column, unique indexes to your database tables in addition to the table's uniquely identifying primary key.
Timestamps
By default, Eloquent expects created_at
and
updated_at
columns to exist on your model's corresponding
database table. Eloquent will automatically set these column's values
when models are created or updated. If you do not want these columns to
be automatically managed by Eloquent, you should define a
$timestamps
property on your model with a value of
false
:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
}
If you need to customize the format of your model's timestamps, set
the $dateFormat
property on your model. This property
determines how date attributes are stored in the database as well as
their format when the model is serialized to an array or JSON:
<?php
namespace App\Models;
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 need to customize the names of the columns used to store the
timestamps, you may define CREATED_AT
and
UPDATED_AT
constants on your model:
<?php
class Flight extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
}
Database Connections
By default, all Eloquent models will use the default database
connection that is configured for your application. If you would like to
specify a different connection that should be used when interacting with
a particular model, you should define a $connection
property on the model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The database connection that should be used by the model.
*
* @var string
*/
protected $connection = 'sqlite';
}
Default Attribute Values
By default, a newly instantiated model instance will not contain any
attribute values. If you would like to define the default values for
some of your model's attributes, you may define an
$attributes
property on your model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The model's default values for attributes.
*
* @var array
*/
protected $attributes = [
'delayed' => false,
];
}
Retrieving Models
Once you have created a model and its associated database
table, you are ready to start retrieving data from your database.
You can think of each Eloquent model as a powerful query builder allowing you to fluently query the
database table associated with the model. The model's all
method will retrieve all of the records from the model's associated
database table:
use App\Models\Flight;
foreach (Flight::all() as $flight) {
echo $flight->name;
}
Building Queries
The Eloquent all
method will return all of the results
in the model's table. However, since each Eloquent model serves as a query builder, you may add additional
constraints to queries and then invoke the get
method to
retrieve the results:
$flights = Flight::where('active', 1)
->orderBy('name')
->take(10)
->get();
Tip!! Since Eloquent models are query builders, you should review all of the methods provided by Laravel's query builder. You may use any of these methods when writing your Eloquent queries.
Refreshing Models
If you already have an instance of an Eloquent model that was
retrieved from the database, you can "refresh" the model using the
fresh
and refresh
methods. The
fresh
method will re-retrieve the model from the database.
The existing model instance will not be affected:
$flight = Flight::where('number', 'FR 900')->first();
$freshFlight = $flight->fresh();
The refresh
method will re-hydrate the existing model
using fresh data from the database. In addition, all of its loaded
relationships will be refreshed as well:
$flight = Flight::where('number', 'FR 900')->first();
$flight->number = 'FR 456';
$flight->refresh();
$flight->number; // "FR 900"
Collections
As we have seen, Eloquent methods like all
and
get
retrieve multiple records from the database. However,
these methods don't return a plain PHP array. Instead, an instance of
Illuminate\Database\Eloquent\Collection
is returned.
The Eloquent Collection
class extends Laravel's base
Illuminate\Support\Collection
class, which provides a variety of helpful methods
for interacting with data collections. For example, the
reject
method may be used to remove models from a
collection based on the results of an invoked closure:
$flights = Flight::where('destination', 'Paris')->get();
$flights = $flights->reject(function ($flight) {
return $flight->cancelled;
});
In addition to the methods provided by Laravel's base collection class, the Eloquent collection class provides a few extra methods that are specifically intended for interacting with collections of Eloquent models.
Since all of Laravel's collections implement PHP's iterable interfaces, you may loop over collections as if they were an array:
foreach ($flights as $flight) {
echo $flight->name;
}
Chunking Results
Your application may run out of memory if you attempt to load tens of
thousands of Eloquent records via the all
or
get
methods. Instead of using these methods, the
chunk
method may be used to process large numbers of models
more efficiently.
The chunk
method will retrieve a subset of Eloquent
models, passing them to a closure for processing. Since only the current
chunk of Eloquent models is retrieved at a time, the chunk
method will provide significantly reduced memory usage when working with
a large number of models:
use App\Models\Flight;
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});
The first argument passed to the chunk
method is the
number of records you wish to receive per "chunk". The closure passed as
the second argument will be invoked 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.
If you are filtering the results of the chunk
method
based on a column that you will also be updating while iterating over
the results, you should use the chunkById
method. Using the
chunk
method in these scenarios could lead to unexpected
and inconsistent results. Internally, the chunkById
method
will always retrieve models with an id
column greater than
the last model in the previous chunk:
Flight::where('departed', true)
->chunkById(200, function ($flights) {
$flights->each->update(['departed' => false]);
}, $column = 'id');
Streaming Results Lazily
The lazy
method works similarly to the chunk
method in the sense
that, behind the scenes, it executes the query in chunks. However,
instead of passing each chunk directly into a callback as is, the
lazy
method returns a flattened LazyCollection
of Eloquent models, which lets you interact with the results as a single
stream:
use App\Models\Flight;
foreach (Flight::lazy() as $flight) {
//
}
If you are filtering the results of the lazy
method
based on a column that you will also be updating while iterating over
the results, you should use the lazyById
method.
Internally, the lazyById
method will always retrieve models
with an id
column greater than the last model in the
previous chunk:
Flight::where('departed', true)
->lazyById(200, $column = 'id')
->each->update(['departed' => false]);
You may filter the results based on the descending order of the
id
using the lazyByIdDesc
method.
Cursors
Similar to the lazy
method, the cursor
method may be used to significantly reduce your application's memory
consumption when iterating through tens of thousands of Eloquent model
records.
The cursor
method will only execute a single database
query; however, the individual Eloquent models will not be hydrated
until they are actually iterated over. Therefore, only one Eloquent
model is kept in memory at any given time while iterating over the
cursor.
Note: Since the
cursor
method only ever holds a single Eloquent model in memory at a time, it cannot eager load relationships. If you need to eager load relationships, consider using thelazy
method instead.
Internally, the cursor
method uses PHP generators
to implement this functionality:
use App\Models\Flight;
foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) {
//
}
The cursor
returns an
Illuminate\Support\LazyCollection
instance. Lazy collections allow you
to use many of the collection methods available on typical Laravel
collections while only loading a single model into memory at a time:
use App\Models\User;
$users = User::cursor()->filter(function ($user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
Although the cursor
method uses far less memory than a
regular query (by only holding a single Eloquent model in memory at a
time), it will still eventually run out of memory. This is due
to PHP's PDO driver internally caching all raw query results in its
buffer. If you're dealing with a very large number of Eloquent
records, consider using the
lazy
method instead.
Advanced Subqueries
Subquery Selects
Eloquent also offers advanced subquery support, which allows you to
pull information from related tables in a single query. For example,
let's imagine that we have a table of flight destinations
and a table of flights
to destinations. The
flights
table contains an arrived_at
column
which indicates when the flight arrived at the destination.
Using the subquery functionality available to the query builder's
select
and addSelect
methods, we can select
all of the destinations
and the name of the flight that
most recently arrived at that destination using a single query:
use App\Models\Destination;
use App\Models\Flight;
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
])->get();
Subquery Ordering
In addition, the query builder's orderBy
function
supports subqueries. Continuing to use our flight example, we may use
this functionality to sort all destinations based on when the last
flight arrived at that destination. Again, this may be done while
executing a single database query:
return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
)->get();
Retrieving Single Models / Aggregates
In addition to retrieving all of the records matching a given query,
you may also retrieve single records using the find
,
first
, or firstWhere
methods. Instead of
returning a collection of models, these methods return a single model
instance:
use App\Models\Flight;
// Retrieve a model by its primary key...
$flight = Flight::find(1);
// Retrieve the first model matching the query constraints...
$flight = Flight::where('active', 1)->first();
// Alternative to retrieving the first model matching the query constraints...
$flight = Flight::firstWhere('active', 1);
Sometimes you may wish to retrieve the first result of a query or
perform some other action if no results are found. The
firstOr
method will return the first result matching the
query or, if no results are found, execute the given closure. The value
returned by the closure will be considered the result of the
firstOr
method:
$model = Flight::where('legs', '>', 3)->firstOr(function () {
// ...
});
Not Found Exceptions
Sometimes you may wish to throw an exception if a model is not found.
This is particularly useful in routes or controllers. The
findOrFail
and firstOrFail
methods will
retrieve the first result of the query; however, if no result is found,
an Illuminate\Database\Eloquent\ModelNotFoundException
will
be thrown:
$flight = Flight::findOrFail(1);
$flight = Flight::where('legs', '>', 3)->firstOrFail();
If the ModelNotFoundException
is not caught, a 404 HTTP
response is automatically sent back to the client:
use App\Models\Flight;
Route::get('/api/flights/{id}', function ($id) {
return Flight::findOrFail($id);
});
Retrieving Or Creating Models
The firstOrCreate
method will attempt to locate a
database record using the given column / value pairs. If the model can
not be found in the database, a record will be inserted with the
attributes resulting from merging the first array argument with the
optional second array argument:
The firstOrNew
method, like firstOrCreate
,
will attempt to locate a record in the database matching the given
attributes. However, if a model is not found, a new model instance will
be returned. Note that the model returned by firstOrNew
has
not yet been persisted to the database. You will need to manually call
the save
method to persist it:
use App\Models\Flight;
// Retrieve flight by name or create it if it doesn't exist...
$flight = Flight::firstOrCreate([
'name' => 'London to Paris'
]);
// Retrieve flight by name or create it with the name, delayed, and arrival_time attributes...
$flight = Flight::firstOrCreate(
['name' => 'London to Paris'],
['delayed' => 1, 'arrival_time' => '11:30']
);
// Retrieve flight by name or instantiate a new Flight instance...
$flight = Flight::firstOrNew([
'name' => 'London to Paris'
]);
// Retrieve flight by name or instantiate with the name, delayed, and arrival_time attributes...
$flight = Flight::firstOrNew(
['name' => 'Tokyo to Sydney'],
['delayed' => 1, 'arrival_time' => '11:30']
);
Retrieving Aggregates
When interacting with Eloquent models, you may also use the
count
, sum
, max
, and other aggregate methods provided by the
Laravel query builder. As you might expect,
these methods return a scalar value instead of an Eloquent model
instance:
$count = Flight::where('active', 1)->count();
$max = Flight::where('active', 1)->max('price');
Inserting & Updating Models
Inserts
Of course, when using Eloquent, we don't only need to retrieve models
from the database. We also need to insert new records. Thankfully,
Eloquent makes it simple. To insert a new record into the database, you
should instantiate a new model instance and set attributes on the model.
Then, call the save
method on the model instance:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Flight;
use Illuminate\Http\Request;
class FlightController extends Controller
{
/**
* Store a new flight in the database.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
// Validate the request...
$flight = new Flight;
$flight->name = $request->name;
$flight->save();
}
}
In this example, we assign the name
field from the
incoming HTTP request to the name
attribute of the
App\Models\Flight
model instance. When we call the
save
method, a record will be inserted into the database.
The model's created_at
and updated_at
timestamps will automatically be set when the save
method
is called, so there is no need to set them manually.
Alternatively, you may use the create
method to "save" a
new model using a single PHP statement. The inserted model instance will
be returned to you by the create
method:
use App\Models\Flight;
$flight = Flight::create([
'name' => 'London to Paris',
]);
However, before using the create
method, you will need
to specify either a fillable
or guarded
property on your model class. These properties are required because all
Eloquent models are protected against mass assignment vulnerabilities by
default. To learn more about mass assignment, please consult the mass assignment documentation.
Updates
The save
method may also be used to update models that
already exist in the database. To update a model, you should retrieve it
and set any attributes you wish to update. Then, you should call the
model's save
method. Again, the updated_at
timestamp will automatically be updated, so there is no need to manually
set its value:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->name = 'Paris to London';
$flight->save();
Mass Updates
Updates can also be performed against models that match a given
query. In this example, all flights that are active
and
have a destination
of San Diego
will be marked
as delayed:
Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);
The update
method expects an array of column and value
pairs representing the columns that should be updated. The
update
method returns the number of affected rows.
Note: When issuing a mass update via Eloquent, the
saving
,saved
,updating
, andupdated
model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update.
Examining Attribute Changes
Eloquent provides the isDirty
, isClean
, and
wasChanged
methods to examine the internal state of your
model and determine how its attributes have changed from when the model
was originally retrieved.
The isDirty
method determines if any of the model's
attributes have been changed since the model was retrieved. You may pass
a specific attribute name to the isDirty
method to
determine if a particular attribute is dirty. The isClean
will determine if an attribute has remained unchanged since the model
was retrieved. This method also accepts an optional attribute
argument:
use App\Models\User;
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false
$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true
$user->save();
$user->isDirty(); // false
$user->isClean(); // true
The wasChanged
method determines if any attributes were
changed when the model was last saved within the current request cycle.
If needed, you may pass an attribute name to see if a particular
attribute was changed:
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
$user->title = 'Painter';
$user->save();
$user->wasChanged(); // true
$user->wasChanged('title'); // true
$user->wasChanged('first_name'); // false
The getOriginal
method returns an array containing the
original attributes of the model regardless of any changes to the model
since it was retrieved. If needed, you may pass a specific attribute
name to get the original value of a particular attribute:
$user = User::find(1);
$user->name; // John
$user->email; // john@example.com
$user->name = "Jack";
$user->name; // Jack
$user->getOriginal('name'); // John
$user->getOriginal(); // Array of original attributes...
Mass Assignment
You may use the create
method to "save" a new model
using a single PHP statement. The inserted model instance will be
returned to you by the method:
use App\Models\Flight;
$flight = Flight::create([
'name' => 'London to Paris',
]);
However, before using the create
method, you will need
to specify either a fillable
or guarded
property on your model class. These properties are required because all
Eloquent models are protected against mass assignment vulnerabilities by
default.
A mass assignment vulnerability occurs when a user passes an
unexpected HTTP request field and that field changes a column in your
database that you did not expect. For example, a malicious user might
send an is_admin
parameter through an HTTP request, which
is then passed to your model's create
method, allowing the
user to escalate themselves to an administrator.
So, to get started, you should define which model attributes you want
to make mass assignable. You may do this using the
$fillable
property on the model. For example, let's make
the name
attribute of our Flight
model mass
assignable:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name'];
}
Once you have specified which attributes are mass assignable, you may
use the create
method to insert a new record in the
database. The create
method returns the newly created model
instance:
$flight = Flight::create(['name' => 'London to Paris']);
If you already have a model instance, you may use the
fill
method to populate it with an array of attributes:
$flight->fill(['name' => 'Amsterdam to Frankfurt']);
Mass Assignment & JSON Columns
When assigning JSON columns, each column's mass assignable key must
be specified in your model's $fillable
array. For security,
Laravel does not support updating nested JSON attributes when using the
guarded
property:
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'options->enabled',
];
Allowing Mass Assignment
If you would like to make all of your attributes mass assignable, you
may define your model's $guarded
property as an empty
array. If you choose to unguard your model, you should take special care
to always hand-craft the arrays passed to Eloquent's fill
,
create
, and update
methods:
/**
* The attributes that aren't mass assignable.
*
* @var array
*/
protected $guarded = [];
Upserts
Occasionally, you may need to update an existing model or create a
new model if no matching model exists. Like the
firstOrCreate
method, the updateOrCreate
method persists the model, so there's no need to manually call the
save
method.
In the example below, if a flight exists with a
departure
location of Oakland
and a
destination
location of San Diego
, its
price
and discounted
columns will be updated.
If no such flight exists, a new flight will be created which has the
attributes resulting from merging the first argument array with the
second argument array:
$flight = Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
If you would like to perform multiple "upserts" in a single query,
then you should use the upsert
method instead. The method's
first argument consists of the values to insert or update, while the
second argument lists the column(s) that uniquely identify records
within the associated table. The method's third and final argument is an
array of the columns that should be updated if a matching record already
exists in the database. The upsert
method will
automatically set the created_at
and
updated_at
timestamps if timestamps are enabled on the
model:
Flight::upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);
Deleting Models
To delete a model, you may call the delete
method on the
model instance:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->delete();
You may call the truncate
method to delete all of the
model's associated database records. The truncate
operation
will also reset any auto-incrementing IDs on the model's associated
table:
Flight::truncate();
Deleting An Existing Model By Its Primary Key
In the example above, we are retrieving the model from the database
before calling the delete
method. However, if you know the
primary key of the model, you may delete the model without explicitly
retrieving it by calling the destroy
method. In addition to
accepting the single primary key, the destroy
method will
accept multiple primary keys, an array of primary keys, or a collection of primary keys:
Flight::destroy(1);
Flight::destroy(1, 2, 3);
Flight::destroy([1, 2, 3]);
Flight::destroy(collect([1, 2, 3]));
Note: The
destroy
method loads each model individually and calls thedelete
method so that thedeleting
anddeleted
events are properly dispatched for each model.
Deleting Models Using Queries
Of course, you may build an Eloquent query to delete all models matching your query's criteria. In this example, we will delete all flights that are marked as inactive. Like mass updates, mass deletes will not dispatch model events for the models that are deleted:
$deleted = Flight::where('active', 0)->delete();
Note: When executing a mass delete statement via Eloquent, the
deleting
anddeleted
model events will not be dispatched for the deleted models. This is because the models are never actually retrieved when executing the delete statement.
Soft Deleting
In addition to actually removing records from your database, Eloquent
can also "soft delete" models. When models are soft deleted, they are
not actually removed from your database. Instead, a
deleted_at
attribute is set on the model indicating the
date and time at which the model was "deleted". To enable soft deletes
for a model, add the
Illuminate\Database\Eloquent\SoftDeletes
trait to the
model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Flight extends Model
{
use SoftDeletes;
}
Tip!! The
SoftDeletes
trait will automatically cast thedeleted_at
attribute to aDateTime
/Carbon
instance for you.
You should also add the deleted_at
column to your
database table. The Laravel schema builder
contains a helper method to create this column:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::table('flights', function (Blueprint $table) {
$table->softDeletes();
});
Schema::table('flights', function (Blueprint $table) {
$table->dropSoftDeletes();
});
Now, when you call the delete
method on the model, the
deleted_at
column will be set to the current date and time.
However, the model's database record will be left in the table. When
querying a model that uses soft deletes, the soft deleted models will
automatically be excluded from all query results.
To determine if a given model instance has been soft deleted, you may
use the trashed
method:
if ($flight->trashed()) {
//
}
Restoring Soft Deleted Models
Sometimes you may wish to "un-delete" a soft deleted model. To
restore a soft deleted model, you may call the restore
method on a model instance. The restore
method will set the
model's deleted_at
column to null
:
$flight->restore();
You may also use the restore
method in a query to
restore multiple models. Again, like other "mass" operations, this will
not dispatch any model events for the models that are restored:
Flight::withTrashed()
->where('airline_id', 1)
->restore();
The restore
method may also be used when building relationship queries:
$flight->history()->restore();
Permanently Deleting Models
Sometimes you may need to truly remove a model from your database.
You may use the forceDelete
method to permanently remove a
soft deleted model from the database table:
$flight->forceDelete();
You may also use the forceDelete
method when building
Eloquent relationship queries:
$flight->history()->forceDelete();
Querying Soft Deleted Models
Including Soft Deleted Models
As noted above, soft deleted models will automatically be excluded
from query results. However, you may force soft deleted models to be
included in a query's results by calling the withTrashed
method on the query:
use App\Models\Flight;
$flights = Flight::withTrashed()
->where('account_id', 1)
->get();
The withTrashed
method may also be called when building
a relationship query:
$flight->history()->withTrashed()->get();
Retrieving Only Soft Deleted Models
The onlyTrashed
method will retrieve
only soft deleted models:
$flights = Flight::onlyTrashed()
->where('airline_id', 1)
->get();
Pruning Models
Sometimes you may want to periodically delete models that are no
longer needed. To accomplish this, you may add the
Illuminate\Database\Eloquent\Prunable
or
Illuminate\Database\Eloquent\MassPrunable
trait to the
models you would like to periodically prune. After adding one of the
traits to the model, implement a prunable
method which
returns an Eloquent query builder that resolves the models that are no
longer needed:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;
class Flight extends Model
{
use Prunable;
/**
* Get the prunable model query.
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function prunable()
{
return static::where('created_at', '<=', now()->subMonth());
}
}
When marking models as Prunable
, you may also define a
pruning
method on the model. This method will be called
before the model is deleted. This method can be useful for deleting any
additional resources associated with the model, such as stored files,
before the model is permanently removed from the database:
/**
* Prepare the model for pruning.
*
* @return void
*/
protected function pruning()
{
//
}
After configuring your prunable model, you should schedule the
model:prune
Artisan command in your application's
App\Console\Kernel
class. You are free to choose the
appropriate interval at which this command should be run:
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('model:prune')->daily();
}
Behind the scenes, the model:prune
command will
automatically detect "Prunable" models within your application's
app/Models
directory. If your models are in a different
location, you may use the --model
option to specify the
model class names:
$schedule->command('model:prune', [
'--model' => [Address::class, Flight::class],
])->daily();
If you wish to exclude certain models from being pruned while pruning
all other detected models, you may use the --except
option:
$schedule->command('model:prune', [
'--except' => [Address::class, Flight::class],
])->daily();
You may test your prunable
query by executing the
model:prune
command with the --pretend
option.
When pretending, the model:prune
command will simply report
how many records would be pruned if the command were to actually
run:
php artisan model:prune --pretend
Note: Soft deleting models will be permanently deleted (
forceDelete
) if they match the prunable query.
Mass Pruning
When models are marked with the
Illuminate\Database\Eloquent\MassPrunable
trait, models are
deleted from the database using mass-deletion queries. Therefore, the
pruning
method will not be invoked, nor will the
deleting
and deleted
model events be
dispatched. This is because the models are never actually retrieved
before deletion, thus making the pruning process much more
efficient:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\MassPrunable;
class Flight extends Model
{
use MassPrunable;
/**
* Get the prunable model query.
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function prunable()
{
return static::where('created_at', '<=', now()->subMonth());
}
}
Replicating Models
You may create an unsaved copy of an existing model instance using
the replicate
method. This method is particularly useful
when you have model instances that share many of the same
attributes:
use App\Models\Address;
$shipping = Address::create([
'type' => 'shipping',
'line_1' => '123 Example Street',
'city' => 'Victorville',
'state' => 'CA',
'postcode' => '90001',
]);
$billing = $shipping->replicate()->fill([
'type' => 'billing'
]);
$billing->save();
To exclude one or more attributes from being replicated to the new
model, you may pass an array to the replicate
method:
$flight = Flight::create([
'destination' => 'LAX',
'origin' => 'LHR',
'last_flown' => '2020-03-04 11:00:00',
'last_pilot_id' => 747,
]);
$flight = $flight->replicate([
'last_flown',
'last_pilot_id'
]);
Query Scopes
Global Scopes
Global scopes allow you to add constraints to all queries for a given model. Laravel's own soft delete functionality utilizes global scopes to only retrieve "non-deleted" models from the database. Writing your 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 simple. First, define a class that
implements the Illuminate\Database\Eloquent\Scope
interface. Laravel does not have a conventional location where you
should place scope classes, so you are free to place this class in any
directory that you wish.
The Scope
interface requires you to implement one
method: apply
. The apply
method may add
where
constraints or other types of clauses to the query as
needed:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class AncientScope 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('created_at', '<', now()->subYears(2000));
}
}
Tip!! If your global scope is adding columns to the select clause of the query, you should use the
addSelect
method instead ofselect
. This will prevent the unintentional replacement of the query's existing select clause.
Applying Global Scopes
To assign a global scope to a model, you should override the model's
booted
method and invoke the model's
addGlobalScope
method. The addGlobalScope
method accepts an instance of your scope as its only argument:
<?php
namespace App\Models;
use App\Scopes\AncientScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
static::addGlobalScope(new AncientScope);
}
}
After adding the scope in the example above to the
App\Models\User
model, a call to the
User::all()
method will execute the following SQL
query:
select * from `users` where `created_at` < 0021-02-18 00:00:00
Anonymous Global Scopes
Eloquent also allows you to define global scopes using closures,
which is particularly useful for simple scopes that do not warrant a
separate class of their own. When defining a global scope using a
closure, you should provide a scope name of your own choosing as the
first argument to the addGlobalScope
method:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
static::addGlobalScope('ancient', function (Builder $builder) {
$builder->where('created_at', '<', now()->subYears(2000));
});
}
}
Removing Global Scopes
If you would like to remove a global scope for a given query, you may
use the withoutGlobalScope
method. This method accepts the
class name of the global scope as its only argument:
User::withoutGlobalScope(AncientScope::class)->get();
Or, if you defined the global scope using a closure, you should pass the string name that you assigned to the global scope:
User::withoutGlobalScope('ancient')->get();
If you would like to remove several or even all of the query's global
scopes, you may 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 you to define common sets of query constraints
that you may easily re-use throughout your application. For example, you
may need to frequently retrieve all users that are considered "popular".
To define a scope, prefix an Eloquent model method with
scope
.
Scopes should always return the same query builder instance or
void
:
<?php
namespace App\Models;
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 void
*/
public function scopeActive($query)
{
$query->where('active', 1);
}
}
Utilizing A Local Scope
Once the scope has been defined, you may call the scope methods when
querying the model. However, you should not include the
scope
prefix when calling the method. You can even chain
calls to various scopes:
use App\Models\User;
$users = User::popular()->active()->orderBy('created_at')->get();
Combining multiple Eloquent model scopes via an or
query
operator may require the use of closures to achieve the correct logical grouping:
$users = User::popular()->orWhere(function (Builder $query) {
$query->active();
})->get();
However, since this can be cumbersome, Laravel provides a "higher
order" orWhere
method that allows you to fluently chain
scopes together without the use of closures:
$users = App\Models\User::popular()->orWhere->active()->get();
Dynamic Scopes
Sometimes you may wish to define a scope that accepts parameters. To
get started, just add your additional parameters to your scope method's
signature. Scope parameters should be defined after the
$query
parameter:
<?php
namespace App\Models;
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);
}
}
Once the expected arguments have been added to your scope method's signature, you may pass the arguments when calling the scope:
$users = User::ofType('admin')->get();
Comparing Models
Sometimes you may need to determine if two models are the "same" or
not. The is
and isNot
methods may be used to
quickly verify two models have the same primary key, table, and database
connection or not:
if ($post->is($anotherPost)) {
//
}
if ($post->isNot($anotherPost)) {
//
}
The is
and isNot
methods are also available
when using the belongsTo
, hasOne
,
morphTo
, and morphOne
relationships. This method is
particularly helpful when you would like to compare a related model
without issuing a query to retrieve that model:
if ($post->author()->is($user)) {
//
}
Events
Tip!! Want to broadcast your Eloquent events directly to your client-side application? Check out Laravel's model event broadcasting.
Eloquent models dispatch several events, allowing you to hook into
the following moments in a model's lifecycle: retrieved
,
creating
, created
, updating
,
updated
, saving
, saved
,
deleting
, deleted
, restoring
,
restored
, and replicating
.
The retrieved
event will dispatch when an existing model
is retrieved from the database. When a new model is saved for the first
time, the creating
and created
events will
dispatch. The updating
/ updated
events will
dispatch when an existing model is modified and the save
method is called. The saving
/ saved
events
will dispatch when a model is created or updated - even if the model's
attributes have not been changed. Event names ending with
-ing
are dispatched before any changes to the model are
persisted, while events ending with -ed
are dispatched
after the changes to the model are persisted.
To start listening to model events, define a
$dispatchesEvents
property on your Eloquent model. This
property maps various points of the Eloquent model's lifecycle to your
own event classes. Each model event class
should expect to receive an instance of the affected model via its
constructor:
<?php
namespace App\Models;
use App\Events\UserDeleted;
use App\Events\UserSaved;
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 defining and mapping your Eloquent events, you may use event listeners to handle the events.
Note: When issuing a mass update or delete query via Eloquent, the
saved
,updated
,deleting
, anddeleted
model events will not be dispatched for the affected models. This is because the models are never actually retrieved when performing mass updates or deletes.
Using Closures
Instead of using custom event classes, you may register closures that
execute when various model events are dispatched. Typically, you should
register these closures in the booted
method of your
model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
static::created(function ($user) {
//
});
}
}
If needed, you may utilize queueable anonymous event listeners when registering model events. This will instruct Laravel to execute the model event listener in the background using your application's queue:
use function Illuminate\Events\queueable;
static::created(queueable(function ($user) {
//
}));
Observers
Defining Observers
If you are listening for many events on a given model, you may use
observers to group all of your listeners into a single class. Observer
classes have method names which reflect the Eloquent events you wish to
listen for. Each of these methods receives the affected 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 your
App/Observers
directory. If this directory does not exist,
Artisan will create it for you. Your fresh observer will look like the
following:
<?php
namespace App\Observers;
use App\Models\User;
class UserObserver
{
/**
* Handle the User "created" event.
*
* @param \App\Models\User $user
* @return void
*/
public function created(User $user)
{
//
}
/**
* Handle the User "updated" event.
*
* @param \App\Models\User $user
* @return void
*/
public function updated(User $user)
{
//
}
/**
* Handle the User "deleted" event.
*
* @param \App\Models\User $user
* @return void
*/
public function deleted(User $user)
{
//
}
/**
* Handle the User "forceDeleted" event.
*
* @param \App\Models\User $user
* @return void
*/
public function forceDeleted(User $user)
{
//
}
}
To register an observer, you need to call the observe
method on the model you wish to observe. You may register observers in
the boot
method of your application's
App\Providers\EventServiceProvider
service provider:
use App\Models\User;
use App\Observers\UserObserver;
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
User::observe(UserObserver::class);
}
Tip!! There are additional events an observer can listen to, such as
saving
andretrieved
. These events are described within the events documentation.
Observers & Database Transactions
When models are being created within a database transaction, you may
want to instruct an observer to only execute its event handlers after
the database transaction is committed. You may accomplish this by
defining an $afterCommit
property on the observer. If a
database transaction is not in progress, the event handlers will execute
immediately:
<?php
namespace App\Observers;
use App\Models\User;
class UserObserver
{
/**
* Handle events after all transactions are committed.
*
* @var bool
*/
public $afterCommit = true;
/**
* Handle the User "created" event.
*
* @param \App\Models\User $user
* @return void
*/
public function created(User $user)
{
//
}
}
Muting Events
You may occasionally need to temporarily "mute" all events fired by a
model. You may achieve this using the withoutEvents
method.
The withoutEvents
method accepts a closure as its only
argument. Any code executed within this closure will not dispatch model
events, and any value returned by the closure will be returned by the
withoutEvents
method:
use App\Models\User;
$user = User::withoutEvents(function () use () {
User::findOrFail(1)->delete();
return User::find(2);
});
Saving A Single Model Without Events
Sometimes you may wish to "save" a given model without dispatching
any events. You may accomplish this using the saveQuietly
method:
$user = User::findOrFail(1);
$user->name = 'Victoria Faith';
$user->saveQuietly();