TIPS, TRICKS, AND GOOD
PRACTICES
WITH
LARAVEL'S ELOQUENT
PRESENTED BY CHRIS GMYR
Slide 2
Slide 2 text
No content
Slide 3
Slide 3 text
WHAT IS LARAVEL?
Laravel is a modern PHP framework that helps you create
applications using simple, expressive syntax as well as
offers powerful features like an ORM, routing, queues,
events, notifications, simple authentication...
...and so much more!
3 — Twitter/GitHub: @cmgmyr
Slide 4
Slide 4 text
WHAT IS ELOQUENT?
The Eloquent ORM included with Laravel provides a
beautiful, simple ActiveRecord implementation for working
with your database. Each database table has a
corresponding "Model" which is used to interact with that
table. Models allow you to query for data in your tables,
as well as insert new records into the table.
https://laravel.com/docs/5.6/eloquent
4 — Twitter/GitHub: @cmgmyr
Slide 5
Slide 5 text
THE BASICS
Slide 6
Slide 6 text
A MODEL
class Post extends Model
{
// look Ma, no code!
}
- id
- title
- created_at
- updated_at
$post = Post::find(1);
6 — Twitter/GitHub: @cmgmyr
ARTISAN GOODIES
php artisan make:model Product -mcr
-m will create a migration file
-c will create a controller
-r will indicate that controller should be resourceful
8 — Twitter/GitHub: @cmgmyr
CHUNKING
User::chunk(50, function ($users) {
foreach ($users as $user) {
//
}
});
20 — Twitter/GitHub: @cmgmyr
Slide 21
Slide 21 text
COLLECTIONS
For Eloquent methods like all() and get() which
retrieve multiple results, an instance of
Illuminate\Database\Eloquent\Collection
will be returned.
$admins = $users->filter(function ($user) {
return $user->is_admin;
});
21 — Twitter/GitHub: @cmgmyr
RELATIONSHIPS
hasOne() // User has one Address
belongsTo() // Address belongs to User
hasMany() // Post has many Comment
belongsToMany() // Role belongs to many User
hasManyThrough() // Country has many Post through User
// Use single table
morphTo() // Comment can be on Post, Video, Album
morphMany() // Post has many Comment
// Use pivot table
morphToMany() // Post has many Tag
morphedByMany() // Tag has many Post
24 — Twitter/GitHub: @cmgmyr
Slide 25
Slide 25 text
RELATIONSHIPS
class Video extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
$video = Video::find(1);
foreach ($video->comments as $comment) {
// $comment->body
}
25 — Twitter/GitHub: @cmgmyr
Slide 26
Slide 26 text
RELATIONSHIPS
class Video extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
$video = Video::find(1);
foreach ($video->comments()->where('approved', true)->get() as $comment) {
// $comment->body
}
26 — Twitter/GitHub: @cmgmyr
Slide 27
Slide 27 text
DEFAULT CONDITIONS AND ORDERING
class Video extends Model
{
public function comments()
{
return $this->hasMany(Comment::class)
->where('approved', true)
->latest();
}
}
27 — Twitter/GitHub: @cmgmyr
Slide 28
Slide 28 text
DEFAULT CONDITIONS AND ORDERING
class Video extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
public function publicComments()
{
return $this->comments()
->where('approved', true)
->latest();
}
}
28 — Twitter/GitHub: @cmgmyr
Slide 29
Slide 29 text
DEFAULT MODELS
Default models can be used with belongsTo(),
hasOne(), and morphOne() relationships.
29 — Twitter/GitHub: @cmgmyr
Slide 30
Slide 30 text
DEFAULT MODELS
{{ $post->author->name }} // error if author not found
class Post extends Model
{
public function author()
{
return $this->belongsTo(User::class);
}
}
30 — Twitter/GitHub: @cmgmyr
Slide 31
Slide 31 text
DEFAULT MODELS
{{ $post->author->name ?? '' }} // meh
class Post extends Model
{
public function author()
{
return $this->belongsTo(User::class);
}
}
31 — Twitter/GitHub: @cmgmyr
Slide 32
Slide 32 text
DEFAULT MODELS
{{ $post->author->name }} // better!
class Post extends Model
{
public function author()
{
return $this->belongsTo(User::class)->withDefault();
}
}
32 — Twitter/GitHub: @cmgmyr
Slide 33
Slide 33 text
DEFAULT MODELS
class Post extends Model
{
public function author()
{
return $this->belongsTo(User::class)->withDefault([
'name' => 'Guest Author',
]);
}
}
33 — Twitter/GitHub: @cmgmyr
Slide 34
Slide 34 text
EVENTS
The retrieved event will fire 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 fire.
If a model already existed in the database and the save()
method is called, the updating / updated events will fire.
However, in both cases, the saving / saved events will fire.
https://laravel.com/docs/5.6/eloquent#events
34 — Twitter/GitHub: @cmgmyr
Slide 35
Slide 35 text
EVENTS
class User extends Model
{
protected $dispatchesEvents = [
'saved' => UserSaved::class,
'deleted' => UserDeleted::class,
];
}
35 — Twitter/GitHub: @cmgmyr
Slide 36
Slide 36 text
OBSERVERS
php artisan make:observer UserObserver --
model=User
class ModelObserverServiceProvider extends ServiceProvider
{
public function boot()
{
User::observe(UserObserver::class);
}
}
36 — Twitter/GitHub: @cmgmyr
Slide 37
Slide 37 text
OBSERVERS
class UserObserver
{
public function created(User $user)
{
}
public function updated(User $user)
{
}
public function deleted(User $user)
{
}
}
37 — Twitter/GitHub: @cmgmyr
Slide 38
Slide 38 text
boot() METHOD
class Post extends Model
{
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$model->uuid = (string) Uuid::generate();
});
}
}
38 — Twitter/GitHub: @cmgmyr
Slide 39
Slide 39 text
BOOTABLE TRAIT
class Post extends Model
{
use HasUuid;
}
trait HasUuid
{
public static function bootHasUuid()
{
self::creating(function ($model) {
$model->uuid = (string) Uuid::generate();
});
}
// more uuid related methods
}
39 — Twitter/GitHub: @cmgmyr
CHECK IF RECORDS EXIST
Instead of count(), you could use...
User::where('username', 'cmgmyr')->exists();
User::where('username', 'cmgmyr')->doesntExist();
44 — Twitter/GitHub: @cmgmyr
Slide 45
Slide 45 text
MODEL STATE
$model->isDirty($attributes = null);
$model->isClean($attributes = null);
$model->wasChanged($attributes = null);
$model->hasChanges($changes, $attributes = null);
$model->getDirty();
$model->getChanges();
//Indicates if the model exists.
$model->exists;
//Indicates if the model was inserted during the current request lifecycle.
$model->wasRecentlyCreated;
45 — Twitter/GitHub: @cmgmyr
ACCESSORS/MUTATORS
class User extends Model
{
public function setFirstNameAttribute($value)
{
$this->attributes['first_name'] = strtolower($value);
}
public function setLastNameAttribute($value)
{
$this->attributes['last_name'] = strtolower($value);
}
}
57 — Twitter/GitHub: @cmgmyr
Slide 58
Slide 58 text
ACCESSORS/MUTATORS
class User extends Model
{
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
public function getLastNameAttribute($value)
{
return ucfirst($value);
}
public function getEmailAttribute($value)
{
return new Email($value);
}
public function getFullNameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
}
58 — Twitter/GitHub: @cmgmyr
TO ARRAY/JSON
$user = User::find(1);
return $user->toArray();
return $user->toJson();
You can also return $user from a controller method
and it will automatically return JSON.
60 — Twitter/GitHub: @cmgmyr
Slide 61
Slide 61 text
APPENDING VALUES TO JSON
class User extends Model
{
protected $appends = ['full_name']; // adds to toArray()
public function getFullNameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
}
// or...
return $user->append('full_name')->toArray();
return $user->setAppends(['full_name'])->toArray();
61 — Twitter/GitHub: @cmgmyr
LOCAL SCOPES
class Post extends Model
{
public function scopePublished($query)
{
return $query->whereNotNull('published_at')
->where('published_at', '<=', Carbon::now())
->latest('published_at');
}
}
63 — Twitter/GitHub: @cmgmyr
Slide 64
Slide 64 text
LOCAL SCOPES
$posts = Post::published()->get();
64 — Twitter/GitHub: @cmgmyr
SINGLE TABLE INHERITANCE
class User extends Model
{
public function scopeAdmin($query)
{
return $query->where('is_admin', true);
}
public function scopeCustomer($query)
{
return $query->where('is_admin', false);
}
}
$admins = User::admin()->get();
$customers = User::customer()->get();
67 — Twitter/GitHub: @cmgmyr
Slide 68
Slide 68 text
SINGLE TABLE INHERITANCE
class Admin extends User
{
protected static function boot()
{
parent::boot();
static::addGlobalScope(function ($query) {
$query->where('is_admin', true);
});
}
}
68 — Twitter/GitHub: @cmgmyr
Slide 69
Slide 69 text
SINGLE TABLE INHERITANCE
class Customer extends User
{
protected static function boot()
{
parent::boot();
static::addGlobalScope(function ($query) {
$query->where('is_admin', false);
});
}
}
69 — Twitter/GitHub: @cmgmyr
DEFAULT MODEL DATA
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->string('role')->default('user'); // moderator, admin, etc
$table->rememberToken();
$table->timestamps();
});
73 — Twitter/GitHub: @cmgmyr
Slide 74
Slide 74 text
DEFAULT MODEL DATA
class User extends Model
{
protected $fillable = [
'name', 'email', 'password', 'role'
];
}
74 — Twitter/GitHub: @cmgmyr
Slide 75
Slide 75 text
DEFAULT MODEL DATA
$user = new User();
$user->name = 'Chris';
$user->email = '[email protected]';
$user->password = Hash::make('p@ssw0rd');
// $user->role is currently NULL
$user->save();
$user->role; // 'user'
75 — Twitter/GitHub: @cmgmyr
Slide 76
Slide 76 text
DEFAULT MODEL DATA
Remove ->default('user');
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->string('role'); // moderator, admin, etc
$table->rememberToken();
$table->timestamps();
});
76 — Twitter/GitHub: @cmgmyr
Slide 77
Slide 77 text
DEFAULT MODEL DATA
Set $attributes
class User extends Model
{
protected $fillable = [
'name', 'email', 'password', 'role'
];
protected $attributes = [
'role' => 'user',
];
}
77 — Twitter/GitHub: @cmgmyr
Slide 78
Slide 78 text
DEFAULT MODEL DATA
$user = new User();
$user->name = 'Chris';
$user->email = '[email protected]';
$user->password = Hash::make('p@ssw0rd');
// $user->role is currently 'user'!
$user->save();
$user->role; // 'user'
78 — Twitter/GitHub: @cmgmyr
Slide 79
Slide 79 text
DEFAULT MODEL DATA
$user = new User();
$user->name = 'Chris';
$user->email = '[email protected]';
$user->password = Hash::make('p@ssw0rd');
$user->role = 'admin'; // can override default
$user->save();
$user->role; // 'admin'
79 — Twitter/GitHub: @cmgmyr
Slide 80
Slide 80 text
DEFAULT MODELS
Remember our previous example?
class Post extends Model
{
public function author()
{
return $this->belongsTo(User::class)->withDefault([
'name' => 'Guest Author',
]);
}
}
80 — Twitter/GitHub: @cmgmyr
Slide 81
Slide 81 text
DEFAULT MODELS
We no longer need to provide a name, use the User
$attributes property!
class Post extends Model
{
public function author()
{
return $this->belongsTo(User::class)->withDefault();
}
}
81 — Twitter/GitHub: @cmgmyr
Slide 82
Slide 82 text
DEFAULT MODEL DATA
class User extends Model
{
protected $fillable = [
'name', 'email', 'password', 'role'
];
protected $attributes = [
'name' => 'Guest Author',
'role' => 'user',
];
}
82 — Twitter/GitHub: @cmgmyr
Slide 83
Slide 83 text
DEFAULT MODEL DATA
Watch Colin DeCarlo's - Keeping Eloquent Eloquent from
Laracon US 2016
https://streamacon.com/video/laracon-us-2016/colin-
decarlo-keeping-eloquent-eloquent
83 — Twitter/GitHub: @cmgmyr
Slide 84
Slide 84 text
SUB-QUERIES
$customers = Customer::with('company')
->orderByName()
->paginate();
Get latest interactions?