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:
- What does it mean
extends Model
? What's inside of thatModel
class? - What does it mean
use HasFactory
? What's the difference betweenextends
anduse
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;}
I love the traits explanation.
Well explained
Nice explanation on trait
Nice tutorial
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 ?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
orprotected
.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
Hoooo it makes sense. Thanks a lot
Great!
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.
Thanks for the explanation.
big thanks