• Reading time ~ 8 min
  • 31.05.2021

Eloquent ORM technology seems simple enough, but under the hood it hides many hidden features and little-known ways to achieve greater efficiency. In this article, I'll show you a few tricks.

 

01: Zoom in and out

Instead of:

$article = Article::find($article_id);
$article->read_count++;
$article->save();

You can do this:

$article = Article::find($article_id);
$article->increment('read_count');

And so too:

Article::find($article_id)->increment('read_count');
Article::find($article_id)->increment('read_count', 10); // +10
Product::find($produce_id)->decrement('stock'); // -1

 

02: XorY Methods

Eloquent has quite a few features that combine both methods, such as “do X, if it doesn't work, do Y”.

EXAMPLE 1 - FINDORFAIL ():

Instead of:

$user = User::find($id);
if (!$user) { abort (404); }

Do:

$user = User::findOrFail($id);

EXAMPLE 2 - FIRSTORCREATE ():

Instead of:

$user = User::where('email', $email)->first();
if (!$user) {
  User::create([
    'email' => $email
  ]);
}

Do:

$user = User::firstOrCreate(['email' => $email]);

03: Model boot ()

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);
        });
    }
}

One of the most popular examples is setting values ​​for some fields at the moment of creating a model object.
For example, we generate a UUID field at this moment.

public static function boot()
{
  parent::boot();
  self::creating(function ($model) {
    $model->uuid = (string)Uuid::generate();
  });
}

04: Relationships with conditions and sorting

This is the standard way of defining relationships:

public function users() {
    return $this->hasMany(User::class);   
}

Did you know that we can immediately add where and orderBy methods?

For example, if you want to define specific relationships for users of a specific type, sorted by email, you can do this:

public function approvedUsers() {
    return $this->hasMany('App\User')->where('approved', 1)->orderBy('email');
}

05: Model properties: timestamps, additions, and the like

There are several "parameters" of an Eloquent model in the form of properties of this class.
The most popular ones are:

class User extends Model {
    protected $table = 'users';
    protected $fillable = ['email', 'password']; // поля, которые можно заполнять при User::create()
    protected $dates = ['created_at', 'deleted_at']; // поля, которые должны быть преобразованы к датам
    protected $appends = ['field1', 'field2']; // дополнительные значения, возвращаемые в JSON
}

But wait, that's not all:

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 is even more, I just listed the most interesting ones. To learn more, see abstract Model class code

06: Find multiple values ​​

Everyone knows the find () method, right?

$user = User::find(1);

I was quite surprised how few people know that this method can accept multiple identifiers as an array:

$users = User::find([1,2,3]);

 

07: WhereX

There is an elegant way to turn this around:

$users = User::where('approved', 1)->get();

into this:

$users = User::whereApproved(1)->get();

Yes, you can change the name of any field and add it as a suffix to where and it will work.

In addition, Eloquent has several predefined methods related to date and time:

User::whereDate('created_at', date('Y-m-d'));
User::whereDay('created_at', date('d'));
User::whereMonth('created_at', date('m'));
User::whereYear('created_at', date('Y'));

 

08: Sort by Relationship

A slightly more complex trick. For example, you have topics in a forum and you want to sort them by the most recent post. A common thing for forums, isn't it?

First, make a separate relationship for the last post in the topic:

public function latestPost()
{
    return $this->hasOne(\App\Post::class)->latest();
}

Now, in our controller, we'll do some magic:

$users = Topic::with('latestPost')->get()->sortByDesc('latestPost.created_at');

 

09: Eloquent :: when () - no if-else

Many of us write conditional statements using 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 there is a better way - 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'));
});

It may not look shorter and more elegant, but it is cooler in passing parameters:

$query = User::query();
$query->when(request('role', false), function ($q, $role) {
    return $q->where('role_id', $role);
});
$authors = $query->get();

 

10: BelongsTo default model

Let's say you have a Post owned by an Author and this is the code in your Blade template:

{{ $post->author->name }}

But, what if the author is deleted or not installed for some other reason? Then you get an error, something like "property of non-object"
You can of course prevent this:

{{ $post->author->name }}

But you can do it at the Eloquent relationship level:

public function author()
{
    return $this->belongsTo('App\Author')->withDefault();
}

In this example, the author () relation will return an empty App \ Author model if no Authors are assigned to the Post.

In addition, we can assign a default value to this default model.

public function author()
{
    return $this->belongsTo('App\Author')->withDefault([
        'name' => 'Guest Author'
    ]);
}

 

11: Sort by Transformer

Let's imagine that we have:

function getFullNameAttribute()
{
    return $this->attributes['first_name'] . ' ' . $this->attributes['last_name'];
}

Now try sorting by full_name. Will not work:

$clients = Client::orderBy('full_name')->get(); // не работает

The solution is pretty simple. We need to sort the results AFTER receiving them:

$clients = Client::get()->sortBy('full_name'); // заработало!

 

12: Default sort in the global scope

For example, you want User :: all () to always be sorted by the name field. To do this, you can assign a global Scope. Let's go back to the boot () method you mentioned above.

protected static function boot()
{
    parent::boot();


    // Сортировка поля name по возрастанию (ASC)
    static::addGlobalScope('order', function (Builder $builder) {
        $builder->orderBy('name', 'asc');
    });
}

Learn more about Global Scope here .

 

13: Raw Query Methods

Sometimes we need to add raw queries to our Eloquent calls. Fortunately, there is functionality for that.

// 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();

 

14. Replicate: copy line

Without in-depth explanation - here's the best way to make a copy of a database record:

$task = Tasks::find(1);
$newTask = $task->replicate();
$newTask->save();

 

15: The chunk () method for large tables

Not really about Eloquent, more about Collection, however, here's a powerful way to handle large datasets: you can split them into chunks.

Instead of:

$users = User::all();
foreach ($users as $user) {
// ...

You can do:

User::chunk(100, function ($users) {
foreach ($users as $user) {
// ...
}
});

 

16: Additional steps when creating a model

We all know this Artisan command

php artisan make:model Company

But did you know that there are three useful flags for generating model-related files?

php artisan make:model Company -mcr

-m will create a migration file
-c will create a controller
-r specifying that the controller should contain ready resources

 

17: Override updated_at on save

Did you know that the -> save () method can take parameters?
As a result, we can force it to "ignore" the default function of filling the updated_at field with the current timestamp. See:

$product = Product::find($id);
$product->updated_at = '2019-01-01 10:00:00';
$product->save(['timestamps' => false]);

Here we override the default updated_at with our own.

 

18: What is the result of update ()?

Have you ever wondered what this code actually returns?

$result = $products->whereNull('category_id')->update(['category_id' => 2]);

I mean, the update is done on the database, but what will the $ result variable end up with?

The response is the number of lines affected. Therefore, if you need to check how many rows were affected, you do not need to call anything else - the update () method will return that number to you.

 

19: Converting parentheses in an Eloquent query

What to do if you have mixed AND and OR in your SQL query, for example:

... WHERE (gender = 'Male' and age >= 18) or (gender = 'Female' and age >= 65)

How do I translate this into Eloquent? Wrong way:

$q->where('gender', 'Male');
$q->orWhere('age', '>=', 18);
$q->where('gender', 'Female');
$q->orWhere('age', '>=', 65);
Правильный способ немного сложнее, через замыкания в качестве подзапросов:
$q->where(function ($query) {
    $query->where('gender', 'Male')
        ->where('age', '>=', 18);
})->orWhere(function($query) {
    $query->where('gender', 'Female')
        ->where('age', '>=', 65);
})

 

20: orWhere with multiple parameters

You can pass an array of parameters to orWhere ().
"Normal" way:

$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]);

 

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

ABOUT

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

About author CrazyBoy49z
WORK EXPERIENCE
Contact
Ukraine, Lutsk
+380979856297