Harrison
Harrison

Reputation: 2670

Laravel how to add a custom function in an Eloquent model?

I have a Product model

class Product extends Model
{
    ...

    public function prices()
    {
        return $this->hasMany('App\Price');
    }

    ...
}

I want to add a function which will return the lowest price, and in controller I can get the value using:

Product::find(1)->lowest;

I added this in Product model:

public function lowest()
{
    return $this->prices->min('price');
}

but I got an error saying:

Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation

And if I use Product::find(1)->lowest();, it will work. Is it possible to get Product::find(1)->lowest; to work?

Any help would be appreciated.

Upvotes: 31

Views: 115467

Answers (5)

Achraf Khouadja
Achraf Khouadja

Reputation: 6269

Why you just don't do this? I know, it's not what you asked for specifically and it might be a bad practice sometimes. but in your case, I guess it's good.

$product = Product::with(['prices' => function ($query) {
   $query->min('price');
}])->find($id);

Upvotes: 2

Rahul M
Rahul M

Reputation: 1509

When you try to access a function in the model as a variable, laravel assumes you're trying to retrieve a related model. They call them dynamic properties. What you need instead is a custom attribute.

Before Laravel 9

Laravel 6 docs: https://laravel.com/docs/6.x/eloquent-mutators

add following method to your model:

public function getLowestAttribute()
{
    //do whatever you want to do
    return 'lowest price';
}

Now you should be able to access it like this:

Product::find(1)->lowest;

EDIT: New in Laravel 9

Laravel 9 offers a new way of dealing with attributes:

Docs: https://laravel.com/docs/9.x/eloquent-mutators#accessors-and-mutators

// use Illuminate\Database\Eloquent\Casts\Attribute;

public function lowest(): Attribute
{
     return new Attribute(
        get: function( $originalValue ){
         //do whatever you want to do
         //return $modifiedValue;
      });

     /**
      * Or alternatively:-
      *
      * return Attribute::get( function( $originalValue ){
      *    // do whatever you want to do
      *    // return $modifiedValue;
      * });
      */
}

Upvotes: 56

Rahman Rezaee
Rahman Rezaee

Reputation: 2165

change follow code

public function lowest()
{
    return $this->prices->min('price');
}

to

// add get as prefix and add posfix Attribute and make camel case function
public function getLowestAttribute()
{
    return $this->prices->min('price');
}

Upvotes: 2

May'Habit
May'Habit

Reputation: 1372

you can use above methods or use following method to add a function direct into existing model:

class Company extends Model
{
    protected $table = 'companies';

    // get detail by id
    static function detail($id)
    {
        return self::find($id)->toArray();
    }

    // get list by condition
    static function list($name = '')
    {
        if ( !empty($name) ) return self::where('name', 'LIKE', $name)->get()->toArray();
        else return self::all()->toArray();
    }
}

Or use Illuminate\Support\Facades\DB; inside your function. Hope this help others.

Upvotes: 9

huuuk
huuuk

Reputation: 4795

Use Eloquent accessors

public function getLowestAttribute()
{
    return $this->prices->min('price');
}

Then

$product->lowest;

Upvotes: 17

Related Questions