Chaque fois que j'ajoute une logique supplémentaire aux modèles Eloquent, je finis par devoir en faire une méthode static
(c'est-à-dire moins qu'idéale) pour l'appeler depuis la façade du modèle. J'ai beaucoup essayé de trouver la bonne façon de procéder, et pratiquement tous les résultats parlent de la création de méthodes renvoyant des parties d'une interface Query Builder. J'essaie de comprendre comment ajouter des méthodes pouvant renvoyer n'importe quoi et s'appeler à l'aide de la façade du modèle.
Par exemple, disons que j'ai un modèle appelé Car
et que je veux tous les obtenir:
$cars = Car::all();
Génial, sauf que pour l'instant, disons que je veux trier le résultat dans un tableau multidimensionnel en le rendant ainsi, mon résultat peut ressembler à ceci:
$cars = array(
'Ford' => array(
'F-150' => '...',
'Escape' => '...',
),
'Honda' => array(
'Accord' => '...',
'Civic' => '...',
),
);
Prenant cet exemple théorique, je suis tenté de créer une méthode qui peut s'appeler comme:
$cars = Car::getAllSortedByMake();
Un instant, oublions le terrible nom de la méthode et le fait qu’elle est étroitement liée à la structure de données. Si je crée une méthode comme celle-ci dans le modèle:
public function getAllSortedByMake()
{
// Process and return resulting array
return array('...');
}
Et enfin, appelez-le dans mon contrôleur, je vais obtenir cette exception:
La méthode non statique Car :: getAllSortedByMake () ne devrait pas être appelée de manière statique, en supposant que $ this provient d'un contexte incompatible
TL; DR : Comment puis-je ajouter une fonctionnalité personnalisée qui a du sens pour être dans le modèle sans en faire une méthode statique et l'appeler en utilisant la façade du modèle?
Ceci est un exemple théorique. Peut-être qu'une reformulation de la question aurait plus de sens. Pourquoi certaines méthodes non statiques telles que all()
ou which()
sont-elles disponibles sur la façade d'un modèle Eloquent, mais pas de méthodes supplémentaires ajoutées au modèle? Cela signifie que la méthode magique __call
est utilisée, mais comment puis-je lui faire reconnaître mes propres fonctions dans le modèle?
Un meilleur exemple sur le "tri" est probablement si je devais exécuter un calcul ou un algorithme sur une donnée:
$validSPG = Chemical::isValidSpecificGravity(-1.43);
Pour moi, il est logique que quelque chose comme ça soit dans le modèle car il est spécifique à un domaine.
Ma question se situe à un niveau plus fondamental, tel que: pourquoi is all () accessible via la façade?
Si vous regardez le noyau de Laravel - all () est en fait une fonction statique
public static function all($columns = array('*'))
Vous avez deux options:
public static function getAllSortedByMake()
{
return Car::where('....')->get();
}
ou
public function scopeGetAllSortedByMake($query)
{
return $query->where('...')->get();
}
Les deux vous permettront de faire
Car::getAllSortedByMake();
pour un meilleur code dynamique, plutôt que d'utiliser le nom de classe Model "Car",
il suffit d'utiliser "statique" ou "soi"
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();
}
En fait, vous pouvez étendre Eloquent Builder et y placer des méthodes personnalisées.
Étapes pour prolonger le constructeur:
1.Créer un constructeur personnalisé
<?php
namespace App;
class CustomBuilder extends \Illuminate\Database\Eloquent\Builder
{
public function test()
{
$this->where(['id' => 1]);
return $this;
}
}
2.Ajouter cette méthode à votre modèle de base:
public function newEloquentBuilder($query)
{
return new CustomBuilder($query);
}
3.Exécuter une requête avec des méthodes dans votre générateur personnalisé:
User::where('first_name', 'like', 'a')
->test()
->get();
pour le code ci-dessus, la requête mysql sera:
select * from `users` where `first_name` like ? and (`id` = ?) and `users`.`deleted_at` is null
PS:
First Laurence example, le code est plus approprié pour votre référentiel, pas pour le modèle, mais vous ne pouvez pas non plus diriger davantage de méthodes avec cette approche:
public static function getAllSortedByMake()
{
return Car::where('....')->get();
}
Deuxième Laurence exemple est l'événement pire.
public function scopeGetAllSortedByMake($query)
{
return $query->where('...')->get();
}
Beaucoup de gens suggèrent d'utiliser des portées pour extend Laravel Builder, mais c'est en fait une mauvaise solution car les portées sont isolées par un constructeur éloquent et vous n'obtiendrez pas la même requête avec les mêmes commandes à l'intérieur ou à l'extérieur de scope. J'ai proposé des relations publiques pour changer si les portées devaient être isolées mais Taylor m'a ignoré.
Plus d'explications: Par exemple si vous avez des étendues comme celle-ci:
public function scopeWhereTest($builder, $column, $operator = null, $value = null, $boolean = 'and')
{
$builder->where($column, $operator, $value, $boolean);
}
et deux questions éloquentes:
User::where(function($query){
$query->where('first_name', 'like', 'a');
$query->where('first_name', 'like', 'b');
})->get();
contre
User::where(function($query){
$query->where('first_name', 'like', 'a');
$query->whereTest('first_name', 'like', 'b');
})->get();
Les requêtes générées seraient:
select * from `users` where (`first_name` like ? and `first_name` like ?) and `users`.`deleted_at` is null
contre
select * from `users` where (`first_name` like ? and (`id` = ?)) and `users`.`deleted_at` is null
à première vue, les requêtes se ressemblent mais il n'y en a pas. Pour cette requête simple, cela n'a peut-être pas d'importance, mais pour les requêtes complexes, évitez d'utiliser des étendues pour l'extension Builder :)