Back to Course |
PHP for Laravel Developers

Classes and Traits: Eloquent Model with "extends" and "use"

If you run the command php artisan make:model, here's a typical Model it would generate.

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
 
class Post extends Model
{
use HasFactory;
}

Let's look at it from the point of a totally new junior PHP developer.

Two questions they may ask:

  1. What does it mean extends Model? What's inside of that Model class?
  2. What does it mean use HasFactory? What's the difference between extends and use inside the class?

The answers are below, one by one.


Extending Classes

The keyword class ABC extends XYZ means that ABC automatically has all the properties/methods from the XYZ class but may add more of its own properties/methods on top.

It's a typical parent-child relationship in OOP, also called "inheritance".

In human language, the explanation would be this: "Post is a Model, and inside of the Post class we will describe in more details WHAT KIND of model".

And the point is that many classes may extend the same class. So, Laravel has many Models that extend the same Model class. Same with Controllers and other typical classes.

If you look inside the Model class inside the vendor, you will see it's a huge file with 2,000+ lines of code. It contains a lot of well-known Eloquent features that you may override or enable/disable in your individual models:

Illuminate/Database/Eloquent/Model.php

abstract class Model ...
{
 
// ...
 
// You know this property, right?
protected $table;
 
// You can set the pagination default per Model
protected $perPage = 15;
 
// Yes, this method is executed when you call Post::all()
public static function all($columns = ['*'])
{
return static::query()->get(
is_array($columns) ? $columns : func_get_args()
);
}
 
// ...
}

We cannot change any code in the /vendor folder. That's why Laravel Models are created outside of it, extending the framework classes from the /vendor. In other words, the framework defines the initial rules, but we may change them for our individual cases.

So, your individual Models "inherit" all the properties/methods of that Eloquent Model.

That also means that you can override their values:

app/Models/Post.php:

class Post extends Model
{
protected $table = 'my_posts';
 
// The new default if you call Post::paginate()
protected $perPage = 50;
}

Ok, now let's get to that use HasFactory thing.


Traits and "use"

Now, let's focus on these highlighted lines:

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
 
class Post extends Model
{
use HasFactory;
}

Is that HasFactory a PHP class?

No, it's a Trait. Here's its source.

Illuminate/Database/Eloquent/Factories/HasFactory.php:

namespace Illuminate\Database\Eloquent\Factories;
 
trait HasFactory
{
public static function factory($count = null, $state = [])
{
$factory = static::newFactory() ?: Factory::factoryForModel(get_called_class());
 
return $factory
->count(is_numeric($count) ? $count : null)
->state(is_callable($count) || is_array($count) ? $count : $state);
}
 
protected static function newFactory()
{
//
}
}

As you can see, it's not a big piece of code. What it does is allowing you to create 10 "fake" posts in the Seeder:

Post::factory(10)->create();

Now, from the OOP point of view, HasFactory is not a class, it's not "extended".

A trait is just a piece of functionality (methods/properties) that you can "glue into" any other PHP class.

In this case, many Eloquent models would benefit from this Model::factory() behavior, so it is added automatically to any new Model you generate with php artisan make:model command.

And that's the main point/benefit of traits: reusable code snippets in multiple PHP classes.

You can also add multiple traits into one PHP class, separating them by comma:

class User extends Model
{
use HasFactory, SomeOtherTrait, AndAnotherTrait;
}

This is exactly what is done in Laravel Model:

abstract class Model
{
use Concerns\HasAttributes,
Concerns\HasEvents,
Concerns\HasGlobalScopes,
Concerns\HasRelationships,
Concerns\HasTimestamps,
Concerns\HasUniqueIds,
Concerns\HidesAttributes,
Concerns\GuardsAttributes,
ForwardsCalls;

If you prefer, you may also separate them, one per line:

class User extends Model
{
use HasFactory;
use SomeOtherTrait;
use AndAnotherTrait;
}