La magie Laravel que vous connaissez, désormais appliquée aux jointures.
Les jointures sont très utiles à bien des égards. Si vous êtes ici, vous les connaissez probablement et les utilisez. Eloquent est très puissant, mais il lui manque un peu de "la manière Laravel" lors de l'utilisation des jointures. Ce package rend vos jointures plus Laravel, avec plus de lisibilité avec moins de code tout en masquant les détails d'implémentation aux endroits où ils n'ont pas besoin d'être exposés.
Selon nous, quelques éléments manquent lors de l'utilisation de jointures qui sont des fonctionnalités Eloquent très puissantes :
Vous pouvez lire une explication plus détaillée sur les problèmes que ce package résout sur ce billet de blog.
Vous pouvez installer le package via composer :
composer require kirschbaum-development/eloquent-power-joins
Pour les versions de Laravel < 10, utilisez la version 3.*. Pour les versions Laravel < 8, utilisez la version 2.* :
composer require kirschbaum-development/eloquent-power-joins:3. *
Ce package fournit quelques fonctionnalités.
Supposons que vous ayez un modèle User
avec une relation hasMany
avec le modèle Post
. Si vous souhaitez rejoindre les tables, vous écrivez généralement quelque chose comme :
User:: select ( ' users.* ' )-> join ( ' posts ' , ' posts.user_id ' , ' = ' , ' users.id ' );
Ce package vous fournit une nouvelle méthode joinRelationship()
, qui fait exactement la même chose.
User:: joinRelationship ( ' posts ' );
Les deux options produisent les mêmes résultats. En termes de code, vous n'avez pas économisé TELLEMENT, mais vous utilisez désormais la relation entre les modèles User
et Post
pour joindre les tables. Cela signifie que vous cachez désormais le fonctionnement de cette relation en coulisses (détails de mise en œuvre). Vous n'avez pas non plus besoin de modifier le code si le type de relation change. Vous disposez désormais d’un code plus lisible et moins écrasant.
Mais cela s’améliore lorsque vous devez rejoindre des relations imbriquées . Supposons que vous ayez également une relation hasMany
entre les modèles Post
et Comment
et que vous deviez joindre ces tables, vous pouvez simplement écrire :
User:: joinRelationship ( ' posts.comments ' );
C'est tellement mieux, n'est-ce pas ?! Vous pouvez également rejoindre les relations left
ou right
selon vos besoins.
User:: leftJoinRelationship ( ' posts.comments ' );
User:: rightJoinRelationship ( ' posts.comments ' );
Imaginons que vous ayez un modèle Image
qui est une relation polymorphe ( Post -> morphMany -> Image
). Outre la jointure régulière, vous devrez également appliquer la where imageable_type = Post::class
, sinon vous pourriez obtenir des résultats désordonnés.
Il s'avère que si vous rejoignez une relation polymorphe, Eloquent Power Joins applique automatiquement cette condition pour vous. Il vous suffit d'appeler la même méthode.
Post:: joinRelationship ( ' images ' );
Vous pouvez également rejoindre des relations MorphTo.
Image:: joinRelationship ( ' imageable ' , morphable: Post::class);
Remarque : L'interrogation de relations de transformation en relations ne prend en charge qu'un seul type morphable à la fois.
Application de conditions et de rappels aux jointures
Supposons maintenant que vous souhaitiez appliquer une condition à la jointure que vous effectuez. Il vous suffit de passer un rappel comme deuxième paramètre à la méthode joinRelationship
.
User:: joinRelationship ( ' posts ' , fn ( $ join ) => $ join -> where ( ' posts.approved ' , true ))-> toSql ();
Vous pouvez également spécifier le type de jointure que vous souhaitez effectuer dans le rappel :
User:: joinRelationship ( ' posts ' , fn ( $ join ) => $ join -> left ());
Pour les appels imbriqués , il vous suffit de transmettre un tableau référençant les noms des relations.
User:: joinRelationship ( ' posts.comments ' , [
' posts ' => fn ( $ join ) => $ join -> where ( ' posts.published ' , true ),
' comments ' => fn ( $ join ) => $ join -> where ( ' comments.approved ' , true ),
]);
Pour appartient à de nombreux appels, vous devez transmettre un tableau avec la relation, puis un tableau avec les noms des tables.
User:: joinRelationship ( ' groups ' , [
' groups ' => [
' groups ' => function ( $ join ) {
// ...
},
// group_members is the intermediary table here
' group_members ' => fn ( $ join ) => $ join -> where ( ' group_members.active ' , true ),
]
]);
Nous considérons cela comme l’une des fonctionnalités les plus utiles de ce package. Disons que vous disposez d'une portée published
sur votre modèle Post
:
public function scopePublished ( $ query )
{
$ query -> where ( ' published ' , true );
}
Lorsque vous joignez des relations, vous pouvez utiliser les étendues définies dans le modèle à joindre. C'est pas cool ?
User:: joinRelationship ( ' posts ' , function ( $ join ) {
// the $join instance here can access any of the scopes defined in Post
$ join -> published ();
});
Lorsque vous utilisez des étendues de modèle dans une clause de jointure, vous ne pouvez pas saisir le paramètre $query
dans votre étendue. Gardez également à l’esprit que vous êtes dans une jointure, vous êtes donc limité à utiliser uniquement les conditions prises en charge par les jointures.
Parfois, vous devrez utiliser des alias de table sur vos jointures car vous rejoignez la même table plusieurs fois. Une option pour y parvenir consiste à utiliser la méthode joinRelationshipUsingAlias
.
Post:: joinRelationshipUsingAlias ( ' category.parent ' )-> get ();
Dans le cas où vous devez spécifier le nom de l'alias qui va être utilisé, vous pouvez le faire de deux manières différentes :
Post:: joinRelationshipUsingAlias ( ' category ' , ' category_alias ' )-> get ();
as
dans le rappel de jointure. Post:: joinRelationship ( ' category.parent ' , [
' category ' => fn ( $ join ) => $ join -> as ( ' category_alias ' ),
' parent ' => fn ( $ join ) => $ join -> as ( ' category_parent ' ),
])-> get ()
Pour appartient à plusieurs ou en a plusieurs via des appels, vous devez transmettre un tableau avec la relation, puis un tableau avec les noms de table.
Group:: joinRelationship ( ' posts.user ' , [
' posts ' => [
' posts ' => fn ( $ join ) => $ join -> as ( ' posts_alias ' ),
' post_groups ' => fn ( $ join ) => $ join -> as ( ' post_groups_alias ' ),
],
])-> toSql ();
Lors de la réalisation de jointures, l'utilisation select * from ...
peut être dangereuse car les champs portant le même nom entre les tables parent et les tables jointes pourraient entrer en conflit. En pensant à cela, si vous appelez la méthode joinRelationship
sans sélectionner au préalable de colonnes spécifiques, Eloquent Power Joins l'inclura automatiquement pour vous. Par exemple, jetez un œil aux exemples suivants :
User:: joinRelationship ( ' posts ' )-> toSql ();
// select users.* from users inner join posts on posts.user_id = users.id
Et, si vous spécifiez l'instruction select :
User:: select ( ' users.id ' )-> joinRelationship ( ' posts ' )-> toSql ();
// select users.id from users inner join posts on posts.user_id = users.id
Lorsque vous rejoignez des modèles utilisant le trait SoftDeletes
, la condition suivante sera également automatiquement appliquée à toutes vos jointures :
and " users " . " deleted_at " is null
Si vous souhaitez inclure des modèles supprimés, vous pouvez appeler la méthode ->withTrashed()
dans le rappel de jointure.
UserProfile:: joinRelationship ( ' users ' , fn ( $ join ) => $ join -> withTrashed ());
Vous pouvez également appeler le modèle onlyTrashed
:
UserProfile:: joinRelationship ( ' users ' , ( $ join ) => $ join -> onlyTrashed ());
Si vous avez des conditions supplémentaires dans vos définitions de relation, elles seront automatiquement appliquées pour vous.
class User extends Model
{
public function publishedPosts ()
{
return $ this -> hasMany (Post::class)-> published ();
}
}
Si vous appelez User::joinRelationship('publishedPosts')->get()
, cela appliquera également la portée publiée supplémentaire à la clause de jointure. Cela produirait un SQL plus ou moins comme ceci :
select users. * from users inner join posts on posts . user_id = posts . id and posts . published = 1
Si des étendues globales sont appliquées à votre modèle, vous pouvez activer les étendues globales en appelant la méthode withGlobalScopes
dans votre clause de jointure, comme ceci :
UserProfile:: joinRelationship ( ' users ' , fn ( $ join ) => $ join -> withGlobalScopes ());
Il y a cependant un piège ici. Votre portée globale ne peut pas indiquer la classe EloquentBuilder
dans le premier paramètre de la méthode apply
, sinon vous obtiendrez des erreurs.
Interroger l'existence d'une relation est une fonctionnalité très puissante et pratique d'Eloquent. Cependant, il utilise la syntaxe where exists
qui n'est pas toujours la meilleure et peut ne pas être le choix le plus performant, selon le nombre d'enregistrements dont vous disposez ou la structure de vos tables.
Ce package implémente la même fonctionnalité, mais au lieu d'utiliser la syntaxe where exists
, il utilise joins . Ci-dessous, vous pouvez voir les méthodes implémentées par ce package ainsi que l'équivalent Laravel.
Veuillez noter que même si les méthodes sont similaires, vous n'obtiendrez pas toujours les mêmes résultats lors de l'utilisation de jointures, selon le contexte de votre requête. Vous devez être conscient des différences entre l'interrogation des données avecwhere where exists
et joins
.
Méthodes natives Laravel
User:: has ( ' posts ' );
User:: has ( ' posts.comments ' );
User:: has ( ' posts ' , ' > ' , 3 );
User:: whereHas ( ' posts ' , fn ( $ query ) => $ query -> where ( ' posts.published ' , true ));
User::whereHas( ' posts.comments ' , [ ' posts ' => fn ( $ query ) => $ query -> where ( ' posts.published ' , true ));
User:: doesntHave ( ' posts ' );
Package équivalent, mais utilisant des jointures
User:: powerJoinHas ( ' posts ' );
User:: powerJoinHas ( ' posts.comments ' );
User:: powerJoinHas ( ' posts.comments ' , ' > ' , 3 );
User:: powerJoinWhereHas ( ' posts ' , function ( $ join ) {
$ join -> where ( ' posts.published ' , true );
});
User:: powerJoinDoesntHave ( ' posts ' );
Lorsque vous utilisez la méthode powerJoinWhereHas
avec des relations qui impliquent plus d'une table (un à plusieurs, plusieurs à plusieurs, etc.), utilisez la syntaxe du tableau pour transmettre le rappel :
User:: powerJoinWhereHas ( ' commentsThroughPosts ' , [
' comments ' => fn ( $ query ) => $ query -> where ( ' body ' , ' a ' )
])-> get ());
Vous pouvez également trier les résultats de votre requête à l'aide d'une colonne d'une autre table à l'aide de la méthode orderByPowerJoins
.
User:: orderByPowerJoins ( ' profile.city ' );
Si vous devez transmettre des valeurs brutes pour la fonction order by, vous pouvez procéder comme ceci :
User:: orderByPowerJoins ([ ' profile ' , DB :: raw ( ' concat(city, ", ", state) ' ]);
Cette requête triera les résultats en fonction de la colonne city
de la table user_profiles
. Vous pouvez également trier vos résultats par agrégations ( COUNT
, SUM
, AVG
, MIN
ou MAX
).
Par exemple, pour trier les utilisateurs ayant le plus grand nombre de publications, vous pouvez procéder comme suit :
$ users = User:: orderByPowerJoinsCount ( ' posts.id ' , ' desc ' )-> get ();
Ou encore, pour obtenir la liste des publications dont les commentaires contiennent la moyenne de votes la plus élevée.
$ posts = Post:: orderByPowerJoinsAvg ( ' comments.votes ' , ' desc ' )-> get ();
Vous disposez également de méthodes pour SUM
, MIN
et MAX
:
Post:: orderByPowerJoinsSum ( ' comments.votes ' );
Post:: orderByPowerJoinsMin ( ' comments.votes ' );
Post:: orderByPowerJoinsMax ( ' comments.votes ' );
Si vous souhaitez utiliser des jointures gauches dans le tri, vous pouvez également :
Post:: orderByLeftPowerJoinsCount ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsAvg ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsSum ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsMin ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsMax ( ' comments.votes ' );
Veuillez consulter CONTRIBUER pour plus de détails.
Si vous découvrez des problèmes liés à la sécurité, veuillez envoyer un e-mail à [email protected] au lieu d'utiliser l'outil de suivi des problèmes.
Le développement de ce package est sponsorisé par Kirschbaum Development Group, une société axée sur les développeurs et axée sur la résolution de problèmes, la constitution d'équipes et la communauté. Apprenez-en davantage sur nous ou rejoignez-nous !
La licence MIT (MIT). Veuillez consulter le fichier de licence pour plus d'informations.