Courses

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

I love the traits explanation.

👍 10
avatar

Well explained

👍 6
avatar

Nice explanation on trait

avatar

Nice tutorial

avatar

Thanks for the explanation. Have a question. When to use trait instead of extending a class. In fact If the factory method was present in the base class Model, then we could also have it available in the child classes right ?

avatar

If you are extending a class - you will have all of parent class methods available (of course, that depends on function visibility too). So in your case, you should have it as long as it is public or protected.

As for when to use traits - that really depends. For me personally it's small repeating parts of code. It can be as simple as relationships, functions that are needed for more than 2 classes

avatar

Hoooo it makes sense. Thanks a lot

avatar

Great!

avatar

Brilliant course idea, Povilas. Many of the young (and those us who are old in years but new to coding) are attempting to master skills in reverse. We shoot for Laravel first via auto install, cut and paste, git download, etc and then revisit OOP basics. Modern IDEs and AI make it temptingly fast and productive to sequence this reverse way, and some of these principles were not even around when we started. Consequently, your idea to reference advanced usage patterns to reinforce OOP/PHP basics is timely and appreciated.

👍 7
avatar

Thanks for the explanation.

avatar

big thanks