Understanding Laravel Eloquent Through Advanced OOP in PHP

October 14, 2026

Introduction

When you write a short line in Laravel to fetch data from the database, it looks simple:

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

Behind this single line, several object-oriented programming (OOP) concepts in PHP are at work: classes, inheritance, traits, and special methods that run automatically.

Eloquent is not something separate from PHP. It is a practical application of OOP concepts. Once you understand these concepts, Eloquent's behavior becomes logical and clear.


Part One: The OOP Concepts Behind Eloquent

1. Encapsulation and Access Modifiers

Encapsulation means protecting an object's data from careless modification. It is done with three keywords known as access modifiers:

  • public: accessible from anywhere.
  • protected: accessible from the class itself and from classes that inherit from it.
  • private: accessible only from inside the class itself.

The benefit is that changes to an object's data go through specific methods that control them, so the data is not corrupted by an accidental change from another part of the application.

In Eloquent, properties such as $fillable and $hidden are declared as protected, because they are internal settings of the model and should not be changed by outside code.

2. Interfaces and Abstract Classes

Both help standardize the shape of classes, but each has a different role:

  • An interface is a contract: it defines the method names that any class implementing it must provide, without defining how they are implemented. A single class can implement multiple interfaces.
  • An abstract class is a base template that cannot be instantiated directly. It contains shared code ready for its child classes, and it may leave some methods unimplemented so each child can complete them in its own way. A class can extend only one abstract class.

In Laravel, the base Model class is what every model (such as User and Post) extends. Model holds the shared logic, and each model adds its own details.

3. Traits

PHP allows a class to extend only one class. But sometimes you need to share functionality between classes that have no inheritance relationship.

This is where traits come in: a set of methods and properties that you write once, then add to any class you want using the use keyword.

class Post extends Model
{
    use SoftDeletes;
}

In the example above, we gave the model the soft delete (SoftDeletes) feature without changing anything in the inheritance tree. Another common trait is HasFactory, which connects the model to factories.

4. Special Methods That Start With Two Underscores

PHP has methods with reserved names that start with __, and the language calls them automatically when a certain situation happens. They are officially known as magic methods. The most important ones are:

  • __construct: runs when the object is created.
  • __get and __set: run when reading or writing a property that does not exist or is not accessible.
  • __call and __callStatic: run when calling a method that does not exist in the class.

These methods are why reading $user->name works in Eloquent even though the User class does not define a property with that name. The model catches the read through __get and fetches the value from its internal data array.

Similarly, User::where(...) works because __callStatic forwards the call to the query builder.

5. Late Static Binding

When you call an inherited static method, the method may need to know which child class it was actually called from, not the class where it was written. This idea is called late static binding.

  • self:: always refers to the class where the code was written.
  • static:: refers to the class that started the call.
class Model
{
    public static function create()
    {
        return new static();
    }
}

class User extends Model {}

$user = User::create(); // the result is a User object, not a Model

This is why, when you call User::create(...), Eloquent returns a User object, even though the create method is written in the parent class.


Part Two: How Eloquent Applies These Concepts

1. The Active Record Pattern

Eloquent follows the Active Record pattern:

  • Each database table corresponds to a class called a Model.
  • Each row in the table corresponds to an object of that class.
  • The object knows how to save itself, update its data, and delete itself, through methods such as save(), update(), and delete().

In other words, the logic for working with the database lives inside the object itself, rather than being scattered across different parts of the application.

2. Relationships

In SQL, connecting tables requires writing JOINs and matching conditions. In Eloquent, a relationship is defined as a method inside the model, with a name that expresses its meaning.

class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

// Access the related data as if it were a regular property
$posts = $user->posts;

Here, a user has many posts, and Eloquent builds the right query automatically. The common relationship types are one-to-one, one-to-many, and many-to-many.

3. Method Chaining

Each method in the query builder returns the same object, so you can link conditions one after another: filter, then sort, then limit the number of results, then run the query.

The result is code that reads from left to right like a connected sentence, and the actual query only runs at the final method, such as get() or first().

4. Automatic Data Handling and Scopes

  • Accessors: change how a value looks when it is read from the model, such as formatting a date or combining first and last names.
  • Mutators: change a value when it is saved, such as hashing a password or converting text to lowercase.
  • Scopes: gather a repeated condition in one place inside the model, such as "published posts only", and call it by a clear name instead of repeating the same condition everywhere.

The benefit here is separating data logic from the presentation layer and maintaining the code from a single place.


Conclusion

Understanding advanced OOP is not only useful for technical interviews. It explains why Laravel is designed the way it is:

  • Inheritance and abstract classes: every model extends Model.
  • Access modifiers: they appear in $fillable and $hidden.
  • Traits: they appear in SoftDeletes and HasFactory.
  • Special methods: __get reads properties, and __callStatic makes User::where() work.
  • Late static binding: it makes User::create() return a User object.

With this understanding, Eloquent changes from a tool you use without knowing how it works into one whose inner workings you understand, which makes it easier to track down errors and customize model behavior.