Laravel: Eloquent ORM


Eloquent, the ORM used by Laravel, has some features that can facilitate access to data under specific conditions.

Eager Loading

Let’s assume a relationship between users and videos, in which we want to obtain the users who have videos on the platform.

User.php

namespace App\Models;

use App\Models\Video;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class User extends Model
{
  public function videos(): HasMany
  {
    return $this->hasMany(Video::class);
  }
}

Video.php

namespace App\Models;

use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Video extends Model
{
  public function user(): BelongsTo
  {
    return $this->belongsTo(User::class);
  }
}

We can access users who have videos in the following way:

use App\Models\User;

$usersWithVideos = User::with('videos')->get();

Query Scopes

Suppose we want to obtain the videos that have been uploaded to the platform during this month. We can use query scope to define the logic within the model itself:

Video.php

namespace App\Models;
 
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class Video extends Model
{
  ...

  public scopeCurrentMonth(Builder $query): void
  {
    $query
      ->where('created_at', '>=', now()->firstOfMonth())
      ->where('created_at', '<', now()->lastOfMonth());
  }
}
use App\Models\Video;

$newestVideos = Video::query()->currentMonth()->get();

Events

Events provide a simple form of the Observer pattern. It allows us to subscribe and listen to events that occur in the application.

Eloquent models generate several events that allow us to hook at different moments in the life cycle of a model. Among them are:

We can consult the complete list of events in the official documentation.

A typical event use case is to notify a user when something happens.

  1. We create the notification that will be sent.
    The toArray() method indicates what data will be saved in the notifications table of the database.
    The toMail() method indicates the email message to be sent.
    The via() method indicates the channels through which the notification will be sent.

app/Notifications/OrderCreatedNotification.php

namespace App\Notifications;

use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;

class OrderCreatedNotification extends Notification implements ShouldQueue
{
  use Queueable;

  public function __construct(public Order $order)
  {}

  public function toArray(object $notifiable): array
  {
    return [
      'order_id' => $this->order->id,
    ];
  }

  public function toMail(object $notifiable): MailMessage
  {
    $url = url('/orders/' . $this->order->id);

    return (new MailMessage)
      ->greeting('New Order Created')
      ->line('There is a new order to be processed.')
      ->line('The client will be notified once the order is processed and the products are sent.')
      ->action('View order', $url);
  }

  public function via()
  {
    return ['database', 'mail'];
  }
}
  1. We create the event class. This should expect to receive an instance of the affected model through its constructor and has no further behavior.

app/Events/OrderCreated.php

namespace App\Events;

use App\Models\Order;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class OrderCreated
{
  use Dispatchable, InteractsWithSockets, SerializesModels;

  public function __construct(public Order $order)
  {}
}
  1. We create the listener class for the event. The handle() method indicates what should happen when the event happens.

app/Listeners/SendOrderCreatedNotification.php

namespace App\Listeners;

use App\Events\OrderCreated;
use App\Notifications\OrderCreatedNotification;

class SendOrderCreatedNotification
{
  public function __construct()
  {}

  public function handle(OrderCreated $event): void
  {
    $event->order->seller->notify(new OrderCreatedNotification($event->order));
  }
}
  1. We define the dispatchesEvents property in the model in question, associating the event that we have created.

app/Models/Order.php

namespace App\Models;

use App\Events\OrderCreated;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Order extends Model
{
  public function buyer(): BelongsTo
  {
    return $this->belongsTo(User::class);
  }

  public function seller(): BelongsTo
  {
    return $this->belongsTo(User::class);
  }

  protected $dispatchesEvents = [
    'created' => OrderCreated::class,
  ];
}
  1. Finally, we register the event and its listener in the EventServiceProvider class.

app/Providers/EventServiceProvider.php

namespace App\Providers;

use App\Events\OrderCreated;
use App\Listeners\SendOrderCreatedNotification;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;

class EventServiceProvider extends ServiceProvider
{
  protected $listen = [
    OrderCreated::class => [
        SendOrderCreatedNotification::class,
    ],
  ];
}

With this, every time a new Order model is created, a notification will be created in the notifications table that can be displayed in the application and the corresponding email will also be sent.

Conclusion

Laravel makes use of Eloquent ORM to provide us with great power when building applications with advanced features, saving us from having to implement a large amount of logic.

2024-03-07
Written by Samuel de Vega.
Tags