worksome/model-attributes

Model attributes are dynamically generated data on models added as a relationship.

v0.1.0 2022-02-24 11:32 UTC

This package is auto-updated.

Last update: 2022-04-25 08:28:03 UTC


README

run-tests PHPStan

Model attributes are dynamically generated values for models. They are used as eloquent relationships which can be eager loaded.

Installation

You can install the package via composer:

composer require worksome/model-attributes

Usage

Assuming we have the following table structure:

users:
- id

reviews:
- id
- user_id
- stars

And the following models:

class User extends Model 
{
    public function reviews()
    {
        $this->hasMany(Review::class);
    }
}
class Review extends Model 
{
    public function user()
    {
        $this->belongsTo(User::class);
    }
}

We can add a rating property to each user that is the calculated average of a users reviews. First we're going to create a Model attribute:

class Rating extends \Worksome\ModelAttributes\ModelAttribute
{
    protected $casts = [
        'id' => 'int',
        'user_id' => 'int',
        'rating' => 'float',
    ];

    public static function attributeGlobalScope(Builder $query): void
    {
        $ratingModel = new Rating();

        $query
            ->groupBy($ratingModel->user()->getForeignKeyName())
            ->addSelect([
                $ratingModel->getKeyName(), // id
                $ratingModel->user()->getForeignKeyName(), // user_id
                \DB::raw('avg(stars) as rating'), // rating
            ]);
    }
}

and add it as a relationship to the user model

public function rating(): \Worksome\ModelAttributes\AttributeRelation|\Illuminate\Database\Eloquent\Relations\HasOne
{
    return new \Worksome\ModelAttributes\AttributeRelation(
        $this->hasOne(Rating::class)
    );
}

So that it can be used like so:

$user = User::first();
$rating = $user->rating; // The rating model attribute created above
$rating->rating // the actual rating

Since model attributes are essentially dynamically generated data, and the rating is a scalar, we can override the getValue() method of a model in order to "reach" the rating faster:

public function getValue()
{
    return $this->rating;
}

And now the rating can be accessed like so:

User::first()->rating;

And because it's a relationship it can be eager loaded:

User::with('rating')->get()->map->rating;

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Credits

License

The MIT License (MIT). Please see License File for more information.