Laravel, ¿cómo agregar una función personalizada en un modelo Eloquent?

4 minutos de lectura

Tengo un modelo de Producto

class Product extends Model
{
    ...

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

    ...
}

Quiero agregar una función que devolverá el precio más bajo, y en el controlador puedo obtener el valor usando:

Product::find(1)->lowest;

Agregué esto en el modelo del producto:

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

pero tengo un error que dice:

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

y si uso Product::find(1)->lowest();, funcionará. ¿Es posible conseguir Product::find(1)->lowest; ¿trabajar?

Cualquier ayuda sería apreciada.

  • no ofc porque está llamando a un método, creo que tgat funcionará solo para una relación hasone, ¿y cuál es la diferencia? ¡con o sin!

    – Achraf Khouadja

    8 de junio de 2016 a las 3:15


Avatar de usuario de Rahul M
Raúl M

Cuando intenta acceder a una función en el modelo como una variable, laravel asume que está tratando de recuperar un modelo relacionado. Las llaman propiedades dinámicas. Lo que necesita en su lugar es un atributo personalizado.

Antes de Laravel 9

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

agregue el siguiente método a su modelo:

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

Ahora debería poder acceder a él de esta manera:

Product::find(1)->lowest;

EDITAR: Nuevo en Laravel 9

Laravel 9 ofrece una nueva forma de manejar los atributos:

Documentos: 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;
      * });
      */
}

  • No me di cuenta de que podías hacer eso con atributos que no estaban definidos como columnas en la tabla subyacente; la documentación no mencionó eso (que pude encontrar).

    – Aarón Wallentine

    29 de noviembre de 2016 a las 6:21

  • Hola, también me interesaría saber dónde podría encontrar esto en la documentación de Laravel. Gracias.

    – lijadoras

    24 de enero de 2017 a las 21:26

  • creo que esto debe ser el más bajo (); — recibo un error de relación sin que termine ‘()’

    – caro

    16 abr 2017 a las 21:14

  • @sanders puedes encontrarlo debajo Elocuente: Mutadores

    – egvaldes

    20 de abril de 2017 a las 7:36

  • Así que básicamente, tienes que hacer get + nombre-de-la-propiedad-con-la-primera-letra-en-mayuscula + Attribute() para conseguir lo que quieres? Eso es confuso, pero funciona. Gracias.

    – Hartley San

    19 de marzo de 2019 a las 21:11

Usar elocuente accesorios

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

Después

$product->lowest;

puede usar los métodos anteriores o usar el siguiente método para agregar una función directamente al modelo existente:

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();
    }
}

O use Illuminate\Support\Facades\DB; dentro de su función. Espero que esto ayude a otros.

  • ¿Cómo se accede a estos métodos? Lo es Company::detail(2); o Company::list('Company 1')?

    – Hmerman6006

    17 de julio de 2020 a las 9:59

  • @HalfMens sí, intenta hacer esto

    – May’Habit

    18 de julio de 2020 a las 10:16

  • Gracias. Lo principal es que el método debe ser static de lo contrario obtienes un error: Non-static method App\Models\Company::detail(2) should not be called statically

    – Hmerman6006

    18 de julio de 2020 a las 11:47

  • Claro, si quieres llamarlo directamente. De lo contrario, Company::where()->detail() o algún otro método estático está disponible en laravel.

    – May’Habit

    20 de julio de 2020 a las 3:00

Avatar de usuario de Achraf Khouadja
Achraf Khouadja

¿Por qué simplemente no haces esto? Lo sé, no es lo que pediste específicamente y, a veces, puede ser una mala práctica. pero en tu caso supongo que es bueno.

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

cambiar el código de seguimiento

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

a

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

¿Ha sido útil esta solución?