jeremy harris
Cada vez que agrego lógica adicional a los modelos Eloquent, termino teniendo que convertirlo en un static
(es decir, menos que ideal) para llamarlo desde la fachada del modelo. Intenté buscar mucho sobre cómo hacer esto de la manera correcta y casi todos los resultados hablan sobre la creación de métodos que devuelven partes de una interfaz de Query Builder. Estoy tratando de descubrir cómo agregar métodos que puedan devolver cualquier cosa y ser llamados usando la fachada del modelo.
Por ejemplo, digamos que tengo un modelo llamado Car
y quiero conseguirlos todos:
$cars = Car::all();
Genial, excepto por ahora, digamos que quiero ordenar el resultado en una matriz multidimensional por make para que mi resultado se vea así:
$cars = array(
'Ford' => array(
'F-150' => '...',
'Escape' => '...',
),
'Honda' => array(
'Accord' => '...',
'Civic' => '...',
),
);
Tomando ese ejemplo teórico, tengo la tentación de crear un método que pueda llamarse así:
$cars = Car::getAllSortedByMake();
Por un momento, olvidemos el terrible nombre del método y el hecho de que está estrechamente relacionado con la estructura de datos. Si hago un método como este en el modelo:
public function getAllSortedByMake()
{
// Process and return resulting array
return array('...');
}
Y finalmente llámelo en mi controlador, obtendré esta excepción:
El método no estático Car::getAllSortedByMake() no debe llamarse estáticamente, asumiendo que $this proviene de un contexto incompatible
TL;DR: ¿Cómo puedo agregar una funcionalidad personalizada que tenga sentido para estar en el modelo sin convertirlo en un método estático y llamarlo usando la fachada del modelo?
Editar:
Este es un ejemplo teórico. Tal vez una reformulación de la pregunta tendría más sentido. ¿Por qué ciertos métodos no estáticos como all()
o which()
disponible en la fachada de un modelo Eloquent, pero no se agregaron métodos adicionales al modelo? Esto significa que el __call
se está utilizando el método mágico, pero ¿cómo puedo hacer que reconozca mis propias funciones en el modelo?
Probablemente un mejor ejemplo sobre la “clasificación” es si necesito ejecutar un cálculo o algoritmo en una pieza de datos:
$validSPG = Chemical::isValidSpecificGravity(-1.43);
Para mí, tiene sentido que algo así esté en el modelo, ya que es específico del dominio.
Mi pregunta es más de un nivel fundamental, como ¿por qué se puede acceder a all() a través de la fachada?
Si observa el Laravel Core, all() es en realidad una función estática
public static function all($columns = array('*'))
Tienes dos opciones:
public static function getAllSortedByMake()
{
return Car::where('....')->get();
}
o
public function scopeGetAllSortedByMake($query)
{
return $query->where('...')->get();
}
Ambos te permitirán hacer
Car::getAllSortedByMake();
-
Ahh, había oído hablar de los ámbitos en Eloquent, pero aún no los he usado. ¡Gracias! veo que
all()
es estático ahora (eso es lo que obtengo por no verificar). Siempre había escuchado que estos métodos no eran y solo usaba patrones de diseño para Aparecer de esa manera para la usabilidad mientras se mantiene la capacidad de prueba.–Jeremy Harris
14 mayo 2014 a las 15:40
-
Sí, la mayoría de las funciones no son estáticas, solo algunas de las del modelo por alguna razón, por lo que estoy seguro de que Taylor tiene una buena razón, pero está por encima de mi conocimiento.
– Laurence
14 mayo 2014 a las 15:43
-
Tenga en cuenta que “los ámbitos siempre deben devolver una instancia del generador de consultas”. Así que si usas
->get()
debe usar un método estático y no la variante de alcance. También tenga en cuenta que los ámbitos se pueden encadenar:Car::GetAllSortedByMake()->GetAllSortedByMake()->otherScopeYouHaveDefined()->where(...)->get()
.– aserrado
3 de enero de 2016 a las 12:36
-
no use ámbitos porque la consulta no es la misma porque los ámbitos están aislados
– fico7489
7 ago. 2018 a las 6:00
En realidad, puede extender Eloquent Builder y poner métodos personalizados allí.
Pasos para extender el constructor:
1.Crear constructor personalizado
<?php
namespace App;
class CustomBuilder extends \Illuminate\Database\Eloquent\Builder
{
public function test()
{
$this->where(['id' => 1]);
return $this;
}
}
2. Agregue este método a su modelo base:
public function newEloquentBuilder($query)
{
return new CustomBuilder($query);
}
3. Ejecute la consulta con métodos dentro de su generador personalizado:
User::where('first_name', 'like', 'a')
->test()
->get();
para el código anterior generado, la consulta mysql será:
select * from `users` where `first_name` like ? and (`id` = ?) and `users`.`deleted_at` is null
PD:
primer lorenzo El ejemplo es un código más adecuado para su repositorio, no para el modelo, pero tampoco puede canalizar más métodos con este enfoque:
public static function getAllSortedByMake()
{
return Car::where('....')->get();
}
segundo lorenzo el ejemplo es el evento peor.
public function scopeGetAllSortedByMake($query)
{
return $query->where('...')->get();
}
Mucha gente sugiere usar ámbitos para extender el generador de laravel, pero esa es en realidad una mala solución porque los ámbitos están aislados por el generador elocuente y no obtendrá la misma consulta con los mismos comandos dentro y fuera del alcance. Propuse relaciones públicas para cambiar si los alcances deberían estar aislados, pero Taylor me ignoró.
Más explicación: por ejemplo, si tiene ámbitos como este:
public function scopeWhereTest($builder, $column, $operator = null, $value = null, $boolean = 'and')
{
$builder->where($column, $operator, $value, $boolean);
}
y dos preguntas elocuentes:
User::where(function($query){
$query->where('first_name', 'like', 'a');
$query->where('first_name', 'like', 'b');
})->get();
contra
User::where(function($query){
$query->where('first_name', 'like', 'a');
$query->whereTest('first_name', 'like', 'b');
})->get();
Las consultas generadas serían:
select * from `users` where (`first_name` like ? and `first_name` like ?) and `users`.`deleted_at` is null
contra
select * from `users` where (`first_name` like ? and (`id` = ?)) and `users`.`deleted_at` is null
a primera vista, las consultas parecen iguales, pero no las hay. Para esta consulta simple, tal vez no importe, pero para consultas complicadas sí, así que no use ámbitos para extender el generador 🙂
para un mejor código dinámico, en lugar de usar el nombre de clase de modelo “Car”,
simplemente use “estático” o “auto”
public static function getAllSortedByMake()
{
//to return "Illuminate\Database\Query\Builder" class object you can add another where as you want
return static::where('...');
//or return already as collection object
return static::where('...')->get();
}
Métodos personalizados del modelo Laravel -> la mejor manera es usar rasgos
- Paso #1: Crea un rasgo
- Paso #2: Agrega el rasgo al modelo
- Paso #3: Usa el método
User::first()->confirmEmailNow()
aplicación/Modelo/Usuario.php
use App\Traits\EmailConfirmation;
class User extends Authenticatable
{
use EmailConfirmation;
//...
}
app/Traits/EmailConfirmation.php
<?php
namespace App\Traits;
trait EmailConfirmation
{
/**
* Set email_verified_at to now and save.
*
*/
public function confirmEmailNow()
{
$this->email_verified_at = now();
$this->save();
return $this;
}
}
Comience por tener dos tablas de datos:
manufacturers
ymodels
asi quemanufacturers
contiene “Ford”, “Honda”, etc ymodels
con unmanufacturer_id
vinculando elmodel
haciamanufacturer
y que contiene “F-150”, “Escape”, “Accord”, “Civic”, etc.– Marcos Baker
14 mayo 2014 a las 15:09
@MarkBaker Este fue un ejemplo teórico. Mi pregunta es más de un nivel fundamental, como por qué es
all()
accesible por la fachada? No es un método estático, lo que significa que el__call
se está utilizando el método mágico. Debido a eso, ¿por qué esarbitraryMethodICreate()
¿Inaccesible?–Jeremy Harris
14 mayo 2014 a las 15:30
ver
Illuminate\Database\Eloquent\Model
,all
es un método estático–Jeff Lambert
14 mayo 2014 a las 15:34
cilosis: la razón de eso es simple:
all()
es de hecho un método estático en elModel
y__call
no se llama en esta situación. Hay más métodos estáticos en elModel
clase, y otros que puedes usar de la maneraModel::method()
son procesados por__callStatic
después__call
métodos mágicos y pasó a laEloquent Builder
clase.– Jarek Tkaczyk
14 mayo 2014 a las 15:34
Esto podría saludarte codezen.io/most-utility-laravel-collection-methods
– Sapnesh Naik
8 de mayo de 2020 a las 6:07