Laravel Eloquent

Eloquent: Getting Started
Eloquent: Relationships
Eloquent: Collections

Active record pattern
Active Record Basics
Active Record – Object-relational mapping in Rails
ORM, Ruby and ActiveRecord.
Ask HN: Raw SQL vs. ORM?
What is an ORM, how does it work, and how should I use one? [closed]

Generating Model Classes

Generate a new model

1
php artisan make:model Flight

Generate a model and a migration, factory, seeder, and controller

1
php artisan make:model Flight -mfsc

Eloquent Model Conventions

1
2
protected $fillable = ['name', 'email', 'password'];	设置了可以更新的字段。
protected $hidden = ['password', 'remember_token']; 设置了哪些字段会被隐藏。

Table Names

protected $table = 'users';

Primary Keys

protected $primaryKey = 'user_id';

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

public $incrementing = false;

If your model’s primary key is not an integer, you should define a protected $keyType property on your model.

protected $keyType = 'string';

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.

Timestamps

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

public $timestamps = false;

Database Connections

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

protected $connection = 'sqlite';

Default 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

1
2
3
protected $attributes = [
'delayed' => false,
];

Retrieving Models

1
2
3
4
5
6
7
8
9
10
11
12
13
foreach (Flight::all() as $flight) {
echo $flight->name;
}

// Building Queries
$flights = Flight::where('active', 1)
->orderBy('name')
->take(10)
->get();

// Refreshing Models
$flight = Flight::where('number', 'FR 900')->first();
$freshFlight = $flight->fresh();

Chunking Results

1
2
3
4
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:

1
2
3
4
5
6
7
foreach (Flight::lazy() as $flight) {
//
}

Flight::where('departed', true)
->lazyById(200, $column = 'id')
->each->update(['departed' => false]);

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.

1
2
3
foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) {
//
}

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 the lazy method instead.

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

1
2
3
4
5
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
])->get();

Retrieving Single Models / Aggregates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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.
$model = Flight::where('legs', '>', 3)->firstOr(function () {
// ...
});

// Sometimes you may wish to throw an exception if a model is not found. This is particularly useful in routes or controllers.
$flight = Flight::findOrFail(1);
$flight = Flight::where('legs', '>', 3)->firstOrFail();

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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

these methods return a scalar value instead of an Eloquent model instance

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

Inserting & Updating Models

Inserts

1
2
3
4
5
6
7
8
$flight = new Flight;
$flight->name = $request->name;
$flight->save();

// Alternatively, you may use the create method to "save" a new model using a single PHP statement.
$flight = Flight::create([
'name' => 'London to Paris',
]);

Updates

The save method may also be used to update models that already exist in the database.

1
2
3
$flight = Flight::find(1);
$flight->name = 'Paris to London';
$flight->save();

Mass Updates

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

When issuing a mass update via Eloquent, the saving, saved, updating, and updated 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

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$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.

1
2
3
4
5
6
7
8
9
10
11
12
13
$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:

1
2
3
4
5
6
7
8
9
10
$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

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.

You may do this using the $fillable property on the model.

1
2
3
4
5
6
7
protected $fillable = ['name'];

// 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']);

// The attributes that aren't mass assignable.
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

1
2
3
4
$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.

1
2
3
4
Flight::upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);

Deleting Models

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$flight = Flight::find(1);
$flight->delete();

// Deleting Models Using Queries, Like mass updates, mass deletes will not dispatch model events for the models that are deleted:
$deletedRows = Flight::where('active', 0)->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();

// if you know the primary key of the model, you may delete the model without explicitly retrieving it by calling the destroy method
Flight::destroy(1);

Flight::destroy(1, 2, 3);

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

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

The destroy method loads each model individually and calls the delete method so that the deleting and deleted events are properly dispatched for each model.

Soft Deleting

To enable soft deletes for a model, add the Illuminate\Database\Eloquent\SoftDeletes trait to the model, a deleted_at attribute is set on the model indicating the date and time at which the model was “deleted”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use SoftDeletes;

// The Laravel schema builder contains a helper method to create this column
Schema::table('flights', function (Blueprint $table) {
$table->softDeletes();
});

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

// To determine if a given model instance has been soft deleted, you may use the trashed method
if ($flight->trashed()) {
//
}

// Restoring Soft Deleted Models
$flight->restore();

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

// The restore method may also be used when building relationship queries
$flight->history()->restore();

Permanently Deleting Models

1
2
3
$flight->forceDelete();
// You may also use the forceDelete method when building Eloquent relationship queries:
$flight->history()->forceDelete();

Querying Soft Deleted Models

1
2
3
4
5
6
7
8
9
10
11
$flights = Flight::withTrashed()
->where('account_id', 1)
->get();

// The withTrashed method may also be called when building a relationship query
$flight->history()->withTrashed()->get();

// The onlyTrashed method will retrieve only soft deleted models
$flights = Flight::onlyTrashed()
->where('airline_id', 1)
->get();

Replicating Models

1
2
3
4
5
6
7
8
9
10
11
12
13
$shipping = Address::create([
'type' => 'shipping',
'line_1' => '123 Example Street',
'city' => 'Victorville',
'state' => 'CA',
'postcode' => '90001',
]);

$billing = $shipping->replicate()->fill([
'type' => 'billing'
]);

$billing->save();

Query Scopes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Writing Global Scopes
class AncientScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*/
public function apply(Builder $builder, Model $model)
{
$builder->where('created_at', '<', now()->subYears(2000));
}
}

// Applying Global Scopes
class User extends Model
{
/**
* The "booted" method of the model.
*/
protected static function booted()
{
static::addGlobalScope(new AncientScope);
}
}

// Anonymous Global Scopes
class User extends Model
{
/**
* The "booted" method of the model.
*/
protected static function booted()
{
static::addGlobalScope('ancient', function (Builder $builder) {
$builder->where('created_at', '<', now()->subYears(2000));
});
}
}

// Removing Global Scopes
User::withoutGlobalScope(AncientScope::class)->get();

// if you defined the global scope using a closure
User::withoutGlobalScope('ancient')->get();

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

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

Local Scopes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class User extends Model
{
/**
* Scope a query to only include popular users.
*/
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}

/**
* Scope a query to only include active users.
*/
public function scopeActive($query)
{
return $query->where('active', 1);
}
}

// Utilizing A Local Scope
$users = User::popular()->active()->orderBy('created_at')->get();

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

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

// Dynamic Scopes
class User extends Model
{
/**
* Scope a query to only include users of a given type.
*/
public function scopeOfType($query, $type)
{
return $query->where('type', $type);
}
}

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

Comparing Models

The is and isNot methods may be used to quickly verify two models have the same primary key, table, and database connection or not

1
2
3
4
5
6
7
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.

Events

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.

To start listening to model events, define a $dispatchesEvents property on your Eloquent model.

1
2
3
4
5
6
7
8
9
10
11
12
class User extends Authenticatable
{
use Notifiable;

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

Using Closures

1
2
3
4
5
6
7
8
9
10
11
12
class User extends Model
{
/**
* The "booted" method of the model.
*/
protected static function booted()
{
static::created(function ($user) {
//
});
}
}

Observers

1
2
// create a new observer class
php artisan make:observer UserObserver --model=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:

1
2
3
4
public function boot()
{
User::observe(UserObserver::class);
}

Muting Events

1
2
3
4
5
6
7
8
9
10
$user = User::withoutEvents(function () use () {
User::findOrFail(1)->delete();

return User::find(2);
});

// Saving A Single Model Without Events
$user = User::findOrFail(1);
$user->name = 'Victoria Faith';
$user->saveQuietly();

One To One
One To Many
Many To Many
Has One Through
Has Many Through
One To One (Polymorphic)
One To Many (Polymorphic)
Many To Many (Polymorphic)

Null object pattern

One To One

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class User extends Model
{
/**
* Get the phone associated with the user.
*/
public function phone()
{
return $this->hasOne(Phone::class);
// return $this->hasOne(Phone::class, 'foreign_key');
// return $this->hasOne(Phone::class, 'foreign_key', 'local_key');
}
}

$phone = User::find(1)->phone;

Defining The Inverse Of The Relationship

1
2
3
4
5
6
7
8
9
10
11
12
class Phone extends Model
{
/**
* Get the user that owns the phone.
*/
public function user()
{
return $this->belongsTo(User::class);
// return $this->belongsTo(User::class, 'foreign_key');
// return $this->belongsTo(User::class, 'foreign_key', 'owner_key');
}
}

One To Many

A one-to-many relationship is used to define relationships where a single model is the parent to one or more child models.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Post extends Model
{
/**
* Get the comments for the blog post.
*/
public function comments()
{
return $this->hasMany(Comment::class);
// return $this->hasMany(Comment::class, 'foreign_key');
// return $this->hasMany(Comment::class, 'foreign_key', 'local_key');
}
}

$comments = Post::find(1)->comments;

foreach ($comments as $comment) {
//
}

$comment = Post::find(1)->comments()
->where('title', 'foo')
->first();

One To Many (Inverse) / Belongs To

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Comment extends Model
{
/**
* Get the post that owns the comment.
*/
public function post()
{
return $this->belongsTo(Post::class);
// return $this->belongsTo(Post::class, 'foreign_key');
// return $this->belongsTo(Post::class, 'foreign_key', 'owner_key');
}
}

$comment = Comment::find(1);
return $comment->post->title;

Default Models

The belongsTo, hasOne, hasOneThrough, and morphOne relationships allow you to define a default model that will be returned if the given relationship is null. This pattern is often referred to as the Null Object pattern and can help remove conditional checks in your code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* Get the author of the post.
*/
public function user()
{
return $this->belongsTo(User::class)->withDefault();
}

/**
* Get the author of the post.
*/
public function user()
{
return $this->belongsTo(User::class)->withDefault([
'name' => 'Guest Author',
]);
}

/**
* Get the author of the post.
*/
public function user()
{
return $this->belongsTo(User::class)->withDefault(function ($user, $post) {
$user->name = 'Guest Author';
});
}

Has One Of Many

Sometimes a model may have many related models, yet you want to easily retrieve the “latest” or “oldest” related model of the relationship. For example, a User model may be related to many Order models, but you want to define a convenient way to interact with the most recent order the user has placed. You may accomplish this using the hasOne relationship type combined with the ofMany methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Get the user's most recent order.
*/
public function latestOrder()
{
return $this->hasOne(Order::class)->latestOfMany();
}

/**
* Get the user's oldest order.
*/
public function oldestOrder()
{
return $this->hasOne(Order::class)->oldestOfMany();
}

/**
* The ofMany method accepts the sortable column as its first argument and which aggregate function (min or max) to apply when querying for the related model
* Get the user's largest order.
*/
public function largestOrder()
{
return $this->hasOne(Order::class)->ofMany('price', 'max');
}

Advanced Has One Of Many Relationships

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Get the current pricing for the product.
*/
public function currentPricing()
{
return $this->hasOne(Price::class)->ofMany([
'published_at' => 'max',
'id' => 'max',
], function ($query) {
$query->where('published_at', '<', now());
});
}

Has One Through

1
2
3
4
5
6
7
8
9
10
class Mechanic extends Model
{
/**
* Get the car's owner.
*/
public function carOwner()
{
return $this->hasOneThrough(Owner::class, Car::class);
}
}

The first argument passed to the hasOneThrough method is the name of the final model we wish to access, while the second argument is the name of the intermediate model.

Key Conventions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Mechanic extends Model
{
/**
* Get the car's owner.
*/
public function carOwner()
{
return $this->hasOneThrough(
Owner::class,
Car::class,
'mechanic_id', // Foreign key on the cars table...
'car_id', // Foreign key on the owners table...
'id', // Local key on the mechanics table...
'id' // Local key on the cars table...
);
}
}

Has Many Through

1
2
3
4
5
6
7
8
9
10
class Project extends Model
{
/**
* Get all of the deployments for the project.
*/
public function deployments()
{
return $this->hasManyThrough(Deployment::class, Environment::class);
}
}

The first argument passed to the hasManyThrough method is the name of the final model we wish to access, while the second argument is the name of the intermediate model.

Key Conventions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Project extends Model
{
public function deployments()
{
return $this->hasManyThrough(
Deployment::class,
Environment::class,
'project_id', // Foreign key on the environments table...
'environment_id', // Foreign key on the deployments table...
'id', // Local key on the projects table...
'id' // Local key on the environments table...
);
}
}

Many To Many Relationships

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class User extends Model
{
/**
* The roles that belong to the user.
*/
public function roles()
{
return $this->belongsToMany(Role::class);
// return $this->belongsToMany(Role::class, 'role_user');
// return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id');
}
}

$user = User::find(1);
foreach ($user->roles as $role) {
//
}
$roles = User::find(1)->roles()->orderBy('name')->get();

Defining The Inverse Of The Relationship

the relationship is defined exactly the same as its User model

Retrieving Intermediate Table Columns

we may access the intermediate table using the pivot attribute on the models:

1
2
3
4
5
$user = User::find(1);

foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}

Notice that each Role model we retrieve is automatically assigned a pivot attribute. This attribute contains a model representing the intermediate table.

By default, only the model keys will be present on the pivot model. If your intermediate table contains extra attributes, you must specify them when defining the relationship:

return $this->belongsToMany(Role::class)->withPivot('active', 'created_by');

If you would like your intermediate table to have created_at and updated_at timestamps that are automatically maintained by Eloquent

return $this->belongsToMany(Role::class)->withTimestamps();

Customizing The pivot Attribute Name

1
2
3
4
5
6
7
8
9
return $this->belongsToMany(Podcast::class)
->as('subscription')
->withTimestamps();

$users = User::with('podcasts')->get();

foreach ($users->flatMap->podcasts as $podcast) {
echo $podcast->subscription->created_at;
}

Filtering Queries Via Intermediate Table Columns

You can also filter the results returned by belongsToMany relationship queries using the wherePivot, wherePivotIn, wherePivotNotIn, wherePivotBetween, wherePivotNotBetween, wherePivotNull, and wherePivotNotNull methods when defining the relationship

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
return $this->belongsToMany(Role::class)
->wherePivot('approved', 1);

return $this->belongsToMany(Role::class)
->wherePivotIn('priority', [1, 2]);

return $this->belongsToMany(Role::class)
->wherePivotNotIn('priority', [1, 2]);

return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']);

return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotNotBetween('created_at', ['2020-01-01 00:00:00', '2020-12-31 00:00:00']);

return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotNull('expired_at');

return $this->belongsToMany(Podcast::class)
->as('subscriptions')
->wherePivotNotNull('expired_at');

Defining Custom Intermediate Table Models

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Role extends Model
{
/**
* The users that belong to the role.
*/
public function users()
{
return $this->belongsToMany(User::class)->using(RoleUser::class);
}
}

class RoleUser extends Pivot
{

// Custom Pivot Models And Incrementing IDs
public $incrementing = true;
}

Pivot models may not use the SoftDeletes trait. If you need to soft delete pivot records consider converting your pivot model to an actual Eloquent model.

Polymorphic Relationships

A polymorphic relationship allows the child model to belong to more than one type of model using a single association. For example, imagine you are building an application that allows users to share blog posts and videos. In such an application, a Comment model might belong to both the Post and Video models.

One To One (Polymorphic)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Image extends Model
{
/**
* Get the parent imageable model (user or post).
*/
public function imageable()
{
return $this->morphTo();
// return $this->morphTo(__FUNCTION__, 'imageable_type', 'imageable_id');
}
}

class Post extends Model
{
/**
* Get the post's image.
*/
public function image()
{
return $this->morphOne(Image::class, 'imageable');
}
}

class User extends Model
{
/**
* Get the user's image.
*/
public function image()
{
return $this->morphOne(Image::class, 'imageable');
}
}

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

$image = Image::find(1);
$imageable = $image->imageable;

One To Many (Polymorphic)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Comment extends Model
{
/**
* Get the parent commentable model (post or video).
*/
public function commentable()
{
return $this->morphTo();
}
}

class Post extends Model
{
/**
* Get all of the post's comments.
*/
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}

class Video extends Model
{
/**
* Get all of the video's comments.
*/
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}

$post = Post::find(1);
foreach ($post->comments as $comment) {
//
}

$comment = Comment::find(1);
$commentable = $comment->commentable;

One Of Many (Polymorphic)

1
2
3
4
5
6
7
8
9
10
public function latestImage()
{
return $this->morphOne(Image::class)->latestOfMany();
}

// Get the user's most popular image.
public function bestImage()
{
return $this->morphOne(Image::class)->ofMany('likes', 'max');
}

Many To Many (Polymorphic)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Post extends Model
{
/**
* Get all of the tags for the post.
*/
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}

class Tag extends Model
{
/**
* Get all of the posts that are assigned this tag.
*/
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}

/**
* Get all of the videos that are assigned this tag.
*/
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
}

$post = Post::find(1);
foreach ($post->tags as $tag) {
//
}
$tag = Tag::find(1);
foreach ($tag->posts as $post) {
//
}
foreach ($tag->videos as $video) {
//
}

Custom Polymorphic Types

1
2
3
4
5
6
7
Relation::morphMap([
'post' => 'App\Models\Post',
'video' => 'App\Models\Video',
]);

$alias = $post->getMorphClass();
$class = Relation::getMorphedModel($alias);

Dynamic Relationships

1
2
3
Order::resolveRelationUsing('customer', function ($orderModel) {
return $orderModel->belongsTo(Customer::class, 'customer_id');
});

Querying Relations

Imagine a blog application in which a User model has many associated Post models

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class User extends Model
{
/**
* Get all of the posts for the user.
*/
public function posts()
{
return $this->hasMany(Post::class);
}
}

$user = User::find(1);
$user->posts()->where('active', 1)->get();

// Chaining orWhere Clauses After Relationships
$user->posts()
->where(function (Builder $query) {
return $query->where('active', 1)
->orWhere('votes', '>=', 100);
})
->get();

Relationship Methods Vs. Dynamic Properties

Dynamic relationship properties perform “lazy loading”, meaning they will only load their relationship data when you actually access them. Because of this, developers often use eager loading to pre-load relationships they know will be accessed after loading the model. Eager loading provides a significant reduction in SQL queries that must be executed to load a model’s relations.

Querying Relationship Existence

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Retrieve all posts that have at least one comment...
$posts = Post::has('comments')->get();

// Retrieve all posts that have three or more comments...
$posts = Post::has('comments', '>=', 3)->get();

// Retrieve posts that have at least one comment with images...
$posts = Post::has('comments.images')->get();

// Retrieve posts with at least one comment containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();

// Retrieve posts with at least ten comments containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
}, '>=', 10)->get();

Querying Relationship Absence

1
2
3
4
5
6
7
8
9
$posts = Post::doesntHave('comments')->get();

$posts = Post::whereDoesntHave('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();

$posts = Post::whereDoesntHave('comments.author', function (Builder $query) {
$query->where('banned', 0);
})->get();

Querying Morph To Relationships

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Retrieve comments associated to posts or videos with a title like code%...
$comments = Comment::whereHasMorph(
'commentable',
[Post::class, Video::class],
function (Builder $query) {
$query->where('title', 'like', 'code%');
}
)->get();

// Retrieve comments associated to posts with a title not like code%...
$comments = Comment::whereDoesntHaveMorph(
'commentable',
Post::class,
function (Builder $query) {
$query->where('title', 'like', 'code%');
}
)->get();

$comments = Comment::whereHasMorph(
'commentable',
[Post::class, Video::class],
function (Builder $query, $type) {
$column = $type === Post::class ? 'content' : 'title';

$query->where($column, 'like', 'code%');
}
)->get();

Querying All Related Models

1
2
3
$comments = Comment::whereHasMorph('commentable', '*', function (Builder $query) {
$query->where('title', 'like', 'foo%');
})->get();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$posts = Post::withCount('comments')->get();

foreach ($posts as $post) {
echo $post->comments_count;
}

$posts = Post::withCount(['votes', 'comments' => function (Builder $query) {
$query->where('content', 'like', 'code%');
}])->get();

echo $posts[0]->votes_count;
echo $posts[0]->comments_count;

$posts = Post::withCount([
'comments',
'comments as pending_comments_count' => function (Builder $query) {
$query->where('approved', false);
},
])->get();

echo $posts[0]->comments_count;
echo $posts[0]->pending_comments_count;

Deferred Count Loading

Using the loadCount method, you may load a relationship count after the parent model has already been retrieved

1
2
3
4
5
6
$book = Book::first();
$book->loadCount('genres');

$book->loadCount(['reviews' => function ($query) {
$query->where('rating', 5);
}])

Relationship Counting & Custom Select Statements

If you’re combining withCount with a select statement, ensure that you call withCount after the select method

1
2
3
$posts = Post::select(['title', 'body'])
->withCount('comments')
->get();

Other Aggregate Functions

In addition to the withCount method, Eloquent provides withMin, withMax, withAvg, withSum, and withExists methods. These methods will place a {relation}_{function}_{column} attribute on your resulting models

1
2
3
4
5
6
7
8
9
$posts = Post::withSum('comments', 'votes')->get();

foreach ($posts as $post) {
echo $post->comments_sum_votes;
}

// deferred versions of these methods
$post = Post::first();
$post->loadSum('comments', 'votes');

Counting Related Models On Morph To Relationships

let’s assume that Photo and Post models may create ActivityFeed models. We will assume the ActivityFeed model defines a “morph to” relationship named parentable that allows us to retrieve the parent Photo or Post model for a given ActivityFeed instance. Additionally, let’s assume that Photo models “have many” Tag models and Post models “have many” Comment models.

let’s imagine we want to retrieve ActivityFeed instances and eager load the parentable parent models for each ActivityFeed instance. In addition, we want to retrieve the number of tags that are associated with each parent photo and the number of comments that are associated with each parent post

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$activities = ActivityFeed::with([
'parentable' => function (MorphTo $morphTo) {
$morphTo->morphWithCount([
Photo::class => ['tags'],
Post::class => ['comments'],
]);
}])->get();

// Deferred Count Loading
$activities = ActivityFeed::with('parentable')->get();
$activities->loadMorphCount('parentable', [
Photo::class => ['tags'],
Post::class => ['comments'],
]);

Eager Loading

1
2
3
4
5
6
7
8
9
$comment = new Comment(['message' => 'A new comment.']);
$post = Post::find(1);
$post->comments()->save($comment);

// If you need to save multiple related models, you may use the saveMany method
$post->comments()->saveMany([
new Comment(['message' => 'A new comment.']),
new Comment(['message' => 'Another new comment.']),
]);

If you plan on accessing the relationship after using the save or saveMany methods, you may wish to use the refresh method to reload the model and its relationships

1
2
3
4
5
$post->comments()->save($comment);
$post->refresh();

// All comments, including the newly saved comment...
$post->comments;

Recursively Saving Models & Relationships

If you would like to save your model and all of its associated relationships, you may use the push method. In this example, the Post model will be saved as well as its comments and the comment’s authors

1
2
3
4
$post = Post::find(1);
$post->comments[0]->message = 'Message';
$post->comments[0]->author->name = 'Author Name';
$post->push();

The create Method

1
2
3
4
5
6
7
8
9
$post = Post::find(1);
$comment = $post->comments()->create([
'message' => 'A new comment.',
]);

$post->comments()->createMany([
['message' => 'A new comment.'],
['message' => 'Another new comment.'],
]);

You may also use the findOrNew, firstOrNew, firstOrCreate, and updateOrCreate methods to create and update models on relationships.

Belongs To Relationships

1
2
3
4
5
6
7
$account = Account::find(10);
$user->account()->associate($account);
$user->save();

// To remove a parent model from a child model, you may use the dissociate method. This method will set the relationship's foreign key to null
$user->account()->dissociate();
$user->save();

Many To Many Relationships

let’s imagine a user can have many roles and a role can have many users. You may use the attach method to attach a role to a user by inserting a record in the relationship’s intermediate table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$user = User::find(1);
$user->roles()->attach($roleId);

// When attaching a relationship to a model, you may also pass an array of additional data to be inserted into the intermediate table
$user->roles()->attach($roleId, ['expires' => $expires]);

// Detach a single role from the user...
$user->roles()->detach($roleId);

// Detach all roles from the user...
$user->roles()->detach();

$user->roles()->detach([1, 2, 3]);
$user->roles()->attach([
1 => ['expires' => $expires],
2 => ['expires' => $expires],
]);

You may also use the sync method to construct many-to-many associations. The sync method accepts an array of IDs to place on the intermediate table. Any IDs that are not in the given array will be removed from the intermediate table.

1
2
3
4
5
6
7
8
9
10
$user->roles()->sync([1, 2, 3]);

// You may also pass additional intermediate table values with the IDs
$user->roles()->sync([1 => ['expires' => true], 2, 3]);

// If you would like to insert the same intermediate table values with each of the synced model IDs, you may use the syncWithPivotValues method
$user->roles()->syncWithPivotValues([1, 2, 3], ['active' => true]);

// If you do not want to detach existing IDs that are missing from the given array, you may use the syncWithoutDetaching method:
$user->roles()->syncWithoutDetaching([1, 2, 3]);

The many-to-many relationship also provides a toggle method which “toggles” the attachment status of the given related model IDs. If the given ID is currently attached, it will be detached. Likewise, if it is currently detached, it will be attached

$user->roles()->toggle([1, 2, 3]);

If you need to update an existing row in your relationship’s intermediate table, you may use the updateExistingPivot method. This method accepts the intermediate record foreign key and an array of attributes to update

1
2
3
$user->roles()->updateExistingPivot($roleId, [
'active' => false,
]);

Touching Parent Timestamps

When a model defines a belongsTo or belongsToMany relationship to another model, such as a Comment which belongs to a Post, it is sometimes helpful to update the parent’s timestamp when the child model is updated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Comment extends Model
{
/**
* All of the relationships to be touched.
*/
protected $touches = ['post'];

/**
* Get the post that the comment belongs to.
*/
public function post()
{
return $this->belongsTo(Post::class);
}
}

电影配乐

闯将令

  • 如來神掌 (1964)
  • 功夫 (2004)

小刀会序曲

  • 龙门客栈 (1967)

The Blue Danube

  • 腿 (2020)

Double Dragon

  • 少林足球 (2001)

Daring General

  • 功夫 (2004)

The Last Rose of Summer

  • Three Billboards Outside Ebbing, Missouri (2017)
  • 腿 (2020)

粤语

当年情

  • 英雄本色 (1986)

友誼之光

  • 監獄風雲 (1987)

黎明不要来

  • 倩女幽魂 (1987)

佢有佢講

  • 雞同鴨講 (1989)

淺醉一生

  • 喋血双雄 (1989)

人间道

  • 倩女幽魂II:人間道 (1990)

男兒當自強

  • 黃飛鴻 (1991)

鐵窗紅淚

  • 逃學威龍 (1991)

美麗的花蝴蝶

  • 92黑玫瑰對黑玫瑰 (1992)

長路漫漫伴你闖

  • 武狀元蘇乞兒 (1992)

花好月圓夜

  • 花好月圆 (2004)

求神

  • 唐伯虎點秋香 (1993)

分分鐘需要你

  • 記得香蕉成熟時 (1993)

国语

只記今朝笑

  • 笑傲江湖Ⅱ东方不败 (1992)

明天會更好

  • 英雄本色 (1986)

新鸳鸯蝴蝶梦

  • 九品芝麻官 (1994)

去年烟花特别多

  • 去年烟花特别多 (1998)

男兒志

  • 少林足球 (2001)

知道不知道

  • 天下无贼 (2004)

茉莉花

  • 幼儿园 (2004)

菊花台

  • 满城尽带黄金甲 (2006)

還有我

  • 合約情人 (2007)

送别

  • 让子弹飞 (2010)

Don’t Break My Heart

  • 颐和园 (2006)

別問我是誰

  • 春嬌與志明 (2012)

我要你

  • 驴得水 (2016)

送别

  • 让子弹飞 (2010)

闽南语

大家免著驚

  • 逃學威龍2 (1992)

漏電的插頭

  • 同學麥娜絲 (2020)

英语

Auld Lang Syne (苏格兰)

  • The Last Emperor (1987)
  • 去年烟花特别多 (1998)

Red River Valley (红河谷)

I Wanna Be Free

  • 歲月神偷 (2010)

众里寻她

  • 非诚勿扰 (2008)

日语

軍艦進行曲

  • 鬼子来了 (2000)

其他类型电影

Spoof

  • The Naked Gun: From the Files of Police Squad! (1988)
  • 表姐,妳好嘢! (1990)
  • 黃飛鴻對黃飛鴻 (1993)
  • Mars Attacks! (1996)
  • Scary Movie (2000)

Shrew

  • The Taming of the Shrew (1967)
  • 最佳拍檔 (1982)
  • 10 Things I Hate About You (1999)
  • 我家有一隻河東獅 (2002)

Prison

  • 監獄風雲 (1987)
  • 黑獄斷腸歌之砌生豬肉 (1997)

Finance

  • The Big Short (2015)

Kinship

Sister

There is only one person an English girl hates more than she hates her elder sister; and that is her mother. (George Bernard Shaw)

  • 10 Things I Hate About You (1999)
  • In Her Shoes (2005)

Blended family

  • 第四張畫 (2010)

Adultery & Infidelity

Extramarital sex

  • 刺馬 (1973)
  • Little Children (2006)
  • 苹果 (2007)
  • The Invisible Guest (2016)
  • 为人民服务 인민을 위해 복무하라 (2022)

Incest

  • 老男孩 올드보이 (2003)
  • 满城尽带黄金甲 (2006)
  • 樓下的房客 (2016)

Fornication

  • 玉蒲團之偷情寶鑑 (1991)

Human Sexuality

Sexual dysfunction

  • Masters of Sex (2013-2016)

Paraphilia

  • Deranged: Confessions of a Necrophile (1974)
  • In the Realm of the Senses (1976)
  • Kink (2013)
  • The Little Death (2014)
  • Fifty Shades of Grey (2015)
  • Kiki, Love to Love (2016)
  • Professor Marston & the Wonder Women (2017)

Podophilia

  • 富美子之足 (2018)

Dominatrix

  • Maîtresse (1976)
  • Venus in Furs (1994)
  • Going Under (2004)
  • Venus In Fur (2013)
  • My Mistress (2014)
  • Dogs Don’t Wear Pants (2019)
  • 解禁男女 Love and Leashes (2022)

Femme fatale & Seductress

  • Basic Instinct (1992)
  • 赤裸羔羊 (1992)
  • The Last Seduction (1994)
  • 極度獸性 (1996)
  • 中國O記之血腥情人 (1996)

军阀 (Warlords)

  • 大軍閥 (1972)
  • 刀馬旦 (1986)
  • 大魔術師 (2011)

黑社会

  • 黑社會 (2005)
  • 同門 (2009)

Rebellion

  • 满城尽带黄金甲 (2006)

文革

  • 皇天后土 (1980)
  • 芙蓉镇 (1986)
  • 天浴 (1998)
  • 孔雀 (2005)
  • 芳香之旅 (2006)
  • 毛泽东时代的最后舞者(2009)
  • 山楂树之恋 (2010)
  • 我11 (2012)
  • 归来 (2014)
  • 闯入者 (2014)
  • 芳华 (2017)
  • 无问西东 (2018)
  • 一秒钟 (2020)
  • 邓小平小道 (2022)

南京大屠杀

  • 南京1937 (1995)
  • 黑太陽:南京大屠殺 (1995)
  • John Rabe (2009)
  • 南京!南京! (2009)
  • 金陵十三钗 (2011)

华语同性

  • 東宮西宮 (1996)
  • 藍宇 (2001)
  • 今年夏天 (2001)
  • Les filles du botaniste (2006)

小说改编

  • 林家铺子 (1959)
  • 董夫人 (1968)
  • 玉卿嫂 (1984)
  • 足本玉蒲團 (1987)
  • 大红灯笼高高挂 (1991)
  • 活着 (1994)
  • 大鸿米店 (1995)
  • 花橋榮記 (1998)
  • 天浴 (1998)
  • 太阳照常升起 (2007)
  • 金陵十三钗 (2011)
  • 喊·山 (2015)
  • 许三观 (2015)
  • 杀瓜 (2017)
  • 芳华 (2017)