Getting Started with Eloquent ORM

Waypoint WordPress plugin

Gateway includes the Eloquent ORM from Laravel. It is bundled into Gateway and preconfigured, so it “just works” with little or no configuration.

Gateway will try to establish the required PDO connection to the database using the default port 3306. While this is standard on many servers, it’s not always the correct port number. If you’re using Local (previously Local by Flywheel) it uses dynamic port numbers which you can find on the Database Tab in each Local site. Copy this port number and add it to the Gateway settings at WP Admin > Gateway > Settings in the custom port section.

Eloquent is a PHP based Object Relational Mapper (ORM). It is arguably one of, if not the best examples of PHP engineering. The amount of time and cost saved by Eloquent across millions of projects can easily be measured in billions, that’s based on millions of projects where significant cost savings were achieved compared to other options including hand-coding raw SQL.

In this tutorial we’ll cover the most common aspects of using Eloquent ORM in a WordPress context, and more specifically we’ll cover using Eloquent in Gateway projects.

PHP
// Fetch all events
$events = Event::all();

// Fetch a single event by ID
$event = Event::find(1);

// Fetch with a condition
$events = Event::where('status', 'published')->get();

// Fetch the first match
$event = Event::where('status', 'published')->first();

In the examples above, Event is a data model — a PHP class that represents a database table. Each instance of Event represents one row.

PHP
class Event extends \Illuminate\Database\Eloquent\Model
{
    protected $table = 'wp_events';
}

Eloquent is part of the Illuminate\Database package as you can see in the extend statement. It’s more common to write a use statement as shown in the next example. Note that because Model has excellent inference only the $table property is required. You will want to learn the other properties, but the creation of a model can be this simple.

PHP
use Illuminate\Database\Eloquent\Model;

class Event extends Model
{
    protected $table = 'wp_events';
}

Now let’s look at another model and how we would create a database row and then fetch:

PHP
// Define the model
class Vehicle extends \Illuminate\Database\Eloquent\Model
{
    protected $table = 'wp_vehicles';
}

// Create a vehicle
Vehicle::create(['make' => 'Toyota', 'model' => 'Camry', 'year' => 2024]);

// Fetch all vehicles
$vehicles = Vehicle::all();

// Fetch by ID
$vehicle = Vehicle::find(1);

// Fetch with a condition
$vehicles = Vehicle::where('make', 'Toyota')->get();

Notice that model methods are static and the naming is very intuitive. When you want to create, call create(). When you want to get all the rows, call all(). When you want to find one, just do find(). And how do we fetch data using parameters? We call where() and get().

Relationship Harmony with Eloquent

One of Eloquent’s most powerful features is the ability to define relationships between models. A Vehicle might have many Owners. An Order might belong to a Customer. These relationships are declared once on the model and Eloquent handles the rest.

PHP
class Make extends \Illuminate\Database\Eloquent\Model
{
    protected $table = 'wp_makes';

    public function vehicles()
    {
        return $this->hasMany(Vehicle::class);
    }
}

class Vehicle extends \Illuminate\Database\Eloquent\Model
{
    protected $table = 'wp_vehicles';

    public function make()
    {
        return $this->belongsTo(Make::class);
    }
}

Now we can fetch a make with its vehicles, or a vehicle with its make. The flexibility of Eloquent relationships is that they are defined unilaterally so there is no presumption that the return relationship is based on the same logic. In other words you can say “I love you” and she says in return “I like you too my friend”.

PHP
// Fetch a make with all its vehicles
$make = Make::find(1);
$vehicles = $make->vehicles;

// Fetch a vehicle with its make
$vehicle = Vehicle::find(1);
$make = $vehicle->make;

By default Eloquent uses lazy loading — the relationship is only fetched when you access it.

PHP
$make = Make::find(1);
$vehicles = $make->vehicles; // query runs here

This is convenient but can cause the N+1 problem — if you loop through 100 makes and access vehicles on each, that’s 101 queries.

Eager loading solves this by fetching everything in one go using with():

PHP
// Lazy - 1 query per make in the loop
$makes = Make::all();

// Eager - 2 queries total regardless of how many makes
$makes = Make::with('vehicles')->all();

Now for the brilliant part, accessing the related data works the same whether you use lazy/eager loading.

PHP
// Access works the same either way
foreach ($makes as $make) {
    echo $make->vehicles;
}

Applications Are Just a Web of Related Models

Whether you’re planning a small but important website feature or the next big SAAS app, all you need as the foundation of your app is models related to models. Just like front-end development is boxes in boxes in more boxes, apps are models related to models that relate to other models.

PHP
class User extends Model
{
    protected $table = 'wp_users';
    
    public function likedArticles() { return $this->belongsToMany(Article::class, 'wp_user_likes'); }
}

class Article extends Model
{
    protected $table = 'wp_articles';

    public function likedBy() { return $this->belongsToMany(User::class, 'wp_user_likes'); }
}

class UserLike extends Model
{
    protected $table = 'wp_user_likes';
    // columns: user_id, article_id
}

In the example above we have rapidly created User, Article and UserLike. What app are we building where a user has a relationship to an Article? This is a simple “liking” or favoriting system. In real world applications it is very rare to use only 2 models, and even more rare to only have 1 standing alone. Most solutions require 3+ models.

CRUD with Eloquent

CRUD — Create, Read, Update, Delete — covers every operation you’ll perform on your data. You’ve already seen Create and Read. Here’s the full picture.

PHP
// Create
Vehicle::create(['make' => 'Toyota', 'model' => 'Camry', 'year' => 2024]);

// Read
$vehicles = Vehicle::all();
$vehicle = Vehicle::find(1);
$vehicles = Vehicle::where('make', 'Toyota')->get();

// Update
$vehicle = Vehicle::find(1);
$vehicle->year = 2025;
$vehicle->save();

// Or update directly
Vehicle::where('make', 'Toyota')->update(['year' => 2025]);

// Delete
$vehicle = Vehicle::find(1);
$vehicle->delete();

// Or delete directly
Vehicle::where('make', 'Toyota')->delete();

Eloquent gives you two patterns for Update and Delete — fetch the record first and operate on the instance, or operate directly on a query. The first is safer when you need to confirm the record exists. The second is more efficient when updating or deleting multiple rows at once.

Using Eloquent in Gateway

Gateway’s Collection class extends Eloquent’s Model directly — everything covered in this tutorial works on any Gateway Collection out of the box.

PHP
use Gateway\Collection;

class Vehicle extends Collection
{
    protected $table = 'wp_vehicles';
}

Collections add Gateway-specific features on top — fields, API routes, and admin UI. If you just need a plain model without any of that, skip Collection and extend Eloquent directly. A model is a model.

PHP
use Gateway\Collection;

class Vehicle extends Collection
{
    protected $table = 'wp_vehicles';

    protected $fields = [
        ['name' => 'make',  'type' => 'text'],
        ['name' => 'model', 'type' => 'text'],
        ['name' => 'year',  'type' => 'number'],
    ];
}

From this single class Gateway automatically provides:

  • 5 REST API routes — index, show, store, update, delete at /gateway/v1/vehicles
  • Admin UI — a records list and form with fields for make, model and year
  • Full Eloquent — every query method covered in this tutorial

While we might want Eloquent models for cases where we’re powering a fully custom UI, in most CRM usage the \Gateway\Collection gives you more mileage with no added cost. You can still switch to code based querying for example:

PHP
$make = Make::where('name', 'Toyota')->first();
$vehicles = Vehicle::where('make_id', $make->id)->get();

Performance and Scaling

Eloquent is not magic — it generates SQL queries and those queries need to be efficient. A poorly structured query in plain SQL is equally poorly structured in Eloquent, just harder to see.

A few principles worth keeping in mind as your app grows:

  • Eager load relationships when you know you need them. Lazy loading in a loop is the most common performance mistake in Eloquent applications.
  • Select only what you need. Vehicle::all() fetches every column. Vehicle::select('id', 'make_id', 'year')->get() does not.
  • Index your database tables. Eloquent has no opinion on your database structure — that’s your responsibility.

Gateway’s custom tables give you a significant advantage over WordPress’s default post table architecture. Your data lives in purpose-built tables with proper columns, proper relationships, and proper indexes. As your dataset grows that difference compounds. A query against a structured table with an index is a fundamentally different operation than sifting through wp_postmeta with a meta key lookup.

Eloquent scales as far as your database scales. Most WordPress applications will never hit a ceiling.

What Can/Will You Make?

Making something cool with WP/Gateway? Let us know.

WHEN PRECISION MATTERS
© 2026 ARC Software. All rights reserved.