Eloquent ORM seems simple, but under the hood there are many half-hidden functions and less well-known methods. In this article I will show you a few tricks.
Instead of this:
$article = Article::find($article_id); $article->read_count++; $article->save();
You can do this:
$article = Article::find($article_id); $article->increment('read_count');
So it will also work:
Article::find($article_id)->increment('read_count'); Article::find($article_id)->increment('read_count', 10); // +10 Product::find($produce_id)->decrement('stock'); // -1
Eloquent has several functions that combine two methods, for example “please make X, otherwise make Y”.
Example 1 - findOrFail()
:
Instead of this:
$user = User::find($id); if (!$user) { abort (404); }
We do it:
$user = User::findOrFail($id);
Example 2 - firstOrCreate()
:
Instead of this:
$user = User::where('email', $email)->first(); if (!$user) { User::create([ 'email' => $email ]); }
We do it:
$user = User::firstOrCreate(['email' => $email]);
The Eloquent model has a magic boot()
method, where you can override the default behavior:
class User extends Model { public static function boot() { parent::boot(); static::updating(function($model) { // - // - , $model->something = transform($something); }); } }
Probably one of the most popular examples is setting a field value at the time the model object is created. Suppose you want to generate a UUID field at this moment.
public static function boot() { parent::boot(); self::creating(function ($model) { $model->uuid = (string)Uuid::generate(); }); }
This is the typical way to define relationships:
public function users() { return $this->hasMany('App\User'); }
But did you know that we can add where
or orderBy
?
For example, if you need a special relationship for certain types of users, organized by email, you can do this:
public function approvedUsers() { return $this->hasMany('App\User')->where('approved', 1)->orderBy('email'); }
There are several “parameters” of the Eloquent model in the form of class properties. The most popular ones are probably the following:
class User extends Model { protected $table = 'users'; protected $fillable = ['email', 'password']; // User::create() protected $dates = ['created_at', 'deleted_at']; // Carbon protected $appends = ['field1', 'field2']; // JSON }
But wait, there is more:
protected $primaryKey = 'uuid'; // "id" public $incrementing = false; // protected $perPage = 25; // , ( 15) const CREATED_AT = 'created_at'; const UPDATED_AT = 'updated_at'; // , public $timestamps = false; //
And there are even more, I listed the most interesting of them, for more information, read the default code of the abstract Model class and see all the used traits.
Everyone knows the find ()
method, right?
$user = User::find(1);
I was surprised how few people know that it can accept multiple IDs as an array:
$users = User::find([1,2,3]);
There is an elegant way to transform this:
$users = User::where('approved', 1)->get();
In it:
$users = User::whereApproved(1)->get();
Yes, you can change the name of any field and add it as a suffix to the “where”, and it will work like magic.
Also in the Eloquent ORM there are predefined methods related to the date and time:
User::whereDate('created_at', date('Ym-d')); User::whereDay('created_at', date('d')); User::whereMonth('created_at', date('m')); User::whereYear('created_at', date('Y'));
A little more than a "trick." What if you have forum threads, but you want to sort them, by their latest posts ? Quite a popular requirement in the forums with the latest updated topics at the top, right?
First describe a separate link for the last post in the topic:
public function latestPost() { return $this->hasOne(\App\Post::class)->latest(); }
And then, in your controller, you can perform this "magic":
$users = Topic::with('latestPost')->get()->sortByDesc('latestPost.created_at');
Many of us write conditional requests with "if-else", something like this:
if (request('filter_by') == 'likes') { $query->where('likes', '>', request('likes_amount', 0)); } if (request('filter_by') == 'date') { $query->orderBy('created_at', request('ordering_rule', 'desc')); }
But the best way is to use when()
:
$query = Author::query(); $query->when(request('filter_by') == 'likes', function ($q) { return $q->where('likes', '>', request('likes_amount', 0)); }); $query->when(request('filter_by') == 'date', function ($q) { return $q->orderBy('created_at', request('ordering_rule', 'desc')); });
This example may not seem shorter or more elegant, but it would be more correct to forward parameters:
$query = User::query(); $query->when(request('role', false), function ($q, $role) { return $q->where('role_id', $role); }); $authors = $query->get();
Suppose you have a post belonging to the author, and the Blade code:
{{ $post->author->name }}
But what if the author is removed or not installed for any reason? You will get an error like "property of non-object".
Of course, you can prevent this by doing the following:
{{ $post->author->name ?? '' }}
But you can do it at the level of an eloquent relationship:
public function author() { return $this->belongsTo('App\Author')->withDefault(); }
In this example, the relationship author()
returns an empty App\Author
model, if the author is not attached to the post.
In addition, we can assign default property values ​​for this model.
public function author() { return $this->belongsTo('App\Author')->withDefault([ 'name' => 'Guest Author' ]); }
Imagine that you have such a converter:
function getFullNameAttribute() { return $this->attributes['first_name'] . ' ' . $this->attributes['last_name']; }
Do you need to sort entries by the full_name
field? This solution will not work:
$clients = Client::orderBy('full_name')->get(); //
The solution is quite simple. We need to sort the records after we receive them.
$clients = Client::get()->sortBy('full_name'); // !
Please note that the function name is different - this is not orderBy , this is sortBy .
What if you want User::all()
always be sorted by the name
field? You can assign a global stub (Global Scope). Let us return to the boot ()
method, which we have already mentioned above.
protected static function boot() { parent::boot(); // name static::addGlobalScope('order', function (Builder $builder) { $builder->orderBy('name', 'asc'); }); }
Sometimes we need to add raw expressions to our Eloquent request.
Fortunately, there are functions for this.
// whereRaw $orders = DB::table('orders') ->whereRaw('price > IF(state = "TX", ?, 100)', [200]) ->get(); // havingRaw Product::groupBy('category_id')->havingRaw('COUNT(*) > 1')->get(); // orderByRaw User::where('created_at', '>', '2016-01-01') ->orderByRaw('(updated_at - created_at) desc') ->get();
Without a deep explanation, here is the best way to make a copy of the record in the database:
$task = Tasks::find(1); $newTask = $task->replicate(); $newTask->save();
Not really about Eloquent, it's more about collections, but still a powerful method — to handle large data sets. You can break them into pieces.
Instead of this:
$users = User::all(); foreach ($users as $user) { // ...
You can do it:
User::chunk(100, function ($users) { foreach ($users as $user) { // ... } });
We all know the Artisan team:
php artisan make:model Company
But did you know that there are three useful flags for creating additional model files?
php artisan make:model Company -mcr
-r indicates that the controller must be a resource ( resourceful )
Did you know that the method ->save()
can take parameters? As a result, we can “ignore” the updated_at
functionality, which by default should have set the current timestamp.
Look at the following example:
$product = Product::find($id); $product->updated_at = '2019-01-01 10:00:00'; $product->save(['timestamps' => false]);
Here we rewrote updated_at
our preset value.
Have you ever thought about what this code returns?
$result = $products->whereNull('category_id')->update(['category_id' => 2]);
I mean that the update is running in the database, but what will this $result
contain?
Answer: affected lines . Therefore, if you need to check how many rows have been affected, you do not need to call anything - the update()
method will return this number for you.
What to do if you have AND and OR in your SQL query, like this:
... WHERE (gender = 'Male' and age >= 18) or (gender = 'Female' and age >= 65)
How to convert this query to an eloquent query? This is the wrong way:
$q->where('gender', 'Male'); $q->orWhere('age', '>=', 18); $q->where('gender', 'Female'); $q->orWhere('age', '>=', 65);
The order will be wrong. The correct way is a little more complicated, using closures as subqueries:
$q->where(function ($query) { $query->where('gender', 'Male') ->where('age', '>=', 18); })->orWhere(function($query) { $query->where('gender', 'Female') ->where('age', '>=', 65); })
You can pass an array of parameters in orWhere()
.
“Normal” method:
$q->where('a', 1); $q->orWhere('b', 2); $q->orWhere('c', 3);
You can do it like this:
$q->where('a', 1); $q->orWhere(['b' => 2, 'c' => 3]);
Source: https://habr.com/ru/post/354036/
All Articles