DEV Community

david duymelinck
david duymelinck

Posted on

Laravel: reimagining the Eloquent model for PHP 8.4

A part of the The problem with indirections post was about the behaviour of the Eloquent Model class. This got me thinking, what if it was rewritten with PHP 8.4 as a minimum requirement.

The fields

Now

At the moment it is not required to add the fields as a property to get the values from the database.

For mass assignment you need the protected fillable property. But there is also a protected guarded property, which is the opposite of the fillable attribute.

The default value can be added with the protected attributes property.

The relations are always functions.
And also accessors and mutators are functions.

class Person extends Model
{
    protected $fillable = ['first_name'];

    protected $attributes = ['first_name' => 'nobody'];

    public function children(): HasMany
    {
       return $this->hasMany(Person::class, 'parent_id');
    }

    protected function firstName(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => ucfirst($value),
            set: fn (string $value) => strtolower($value),
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Future

Because PHP 8.4 introduced property hooks the Model class could be:

class Person extends Model
{
    public string $firstName = 'nobody' {
       get => ucfirst($this->firstName);
       set => strtolower($this->firstname)
    }

    public HasMany $children {
       get => $this->hasMany(Person::class, 'parent_id');
       set => $this->hasMany(Person::class, 'parent_id');
    }
}
Enter fullscreen mode Exit fullscreen mode

Metadata

Now

A lot of the metadata is set by a mix of public and protected properties.
And a few are constants.

class Person extends Model
{
   const CREATED_AT = 'creation_date';

   protected $table = 'custom_persons';
   public $incrementing = false;

}
Enter fullscreen mode Exit fullscreen mode

Future

Because we now use properties for the fields, using properties for metadata is asking for problems. Attributes would be a more logical choice.

use Illuminate\Database\Eloquent\Attributes as Eloquent;

#[Eloquent\Table('custom_persons')]
#[Eloquent\NoAutoIncrement]
#[Eloquent\CreationColumn('creation_date')]
class Person extends Model
{

}
Enter fullscreen mode Exit fullscreen mode

Serialisation

Now

There are the protected hidden and visible properties to manipulate the output of the toArray and toJson methods.

There is also a functionality to add computed fields to the output.

class Person extends Model
{
   protected hidden = ['fingerprint']

   protected $appends = ['full_name'];

   protected function fullName(): Attribute
   {
       return new Attribute(
            get: fn () => $this->firstName . ' ' . $this->lastName,
        );
   }
}
Enter fullscreen mode Exit fullscreen mode

Future

This is a mix of a property hook and an attribute.

#[Eloquent\HiddenFromSerialisation(['fingerprint'])]
class Person extends Model
{
   public string $fullname { 
     get => $this->firstName . ' ' . $this->lastName; 
   }
}
Enter fullscreen mode Exit fullscreen mode

Because the $fullname property is a part of the class, it is not needed to configure it.

Conclusion

I am sure I'm missing a lot of options of the Model class. I wanted to have a glance of what the class could be if it evolved with PHP.

What already can happen is moving the properties to attributes.

Are there changes you want to see to make the Model class more in tune with the new PHP functionality?

Top comments (0)