La magia de Laravel que ya conoces, ahora aplicada a las uniones.
Las uniones son muy útiles en muchos sentidos. Si está aquí, lo más probable es que los conozca y los utilice. Eloquent es muy poderoso, pero le falta un poco del "estilo Laravel" cuando usa uniones. Este paquete hace que sus uniones sean más Laravel, más legibles con menos código y al mismo tiempo oculta los detalles de implementación en lugares donde no es necesario exponerlos.
Consideramos que faltan algunas cosas cuando se utilizan uniones, que son características muy potentes de Eloquent:
Puede leer una explicación más detallada sobre los problemas que resuelve este paquete en esta publicación de blog.
Puede instalar el paquete a través del compositor:
composer require kirschbaum-development/eloquent-power-joins
Para versiones de Laravel <10, utilice la versión 3.*. Para versiones de Laravel <8, use la versión 2.*:
composer require kirschbaum-development/eloquent-power-joins:3. *
Este paquete proporciona algunas características.
Digamos que tiene un modelo User
con una relación hasMany
con el modelo Post
. Si quieres unir las tablas, normalmente escribirías algo como:
User:: select ( ' users.* ' )-> join ( ' posts ' , ' posts.user_id ' , ' = ' , ' users.id ' );
Este paquete le proporciona un nuevo método joinRelationship()
, que hace exactamente lo mismo.
User:: joinRelationship ( ' posts ' );
Ambas opciones producen los mismos resultados. En términos de código, no ahorraste TANTO, pero ahora estás usando la relación entre los modelos User
y Post
para unir las tablas. Esto significa que ahora está ocultando cómo funciona esta relación detrás de escena (detalles de implementación). Tampoco es necesario cambiar el código si cambia el tipo de relación. Ahora tiene un código más legible y menos abrumador.
Pero mejora cuando necesita unirse a relaciones anidadas . Supongamos que también tiene una relación hasMany
entre los modelos Post
y Comment
y necesita unir estas tablas, simplemente puede escribir:
User:: joinRelationship ( ' posts.comments ' );
Mucho mejor, ¿no te parece? También puede unirse a las relaciones left
o right
según sea necesario.
User:: leftJoinRelationship ( ' posts.comments ' );
User:: rightJoinRelationship ( ' posts.comments ' );
Imaginemos que tienes un modelo Image
que es una relación polimórfica ( Post -> morphMany -> Image
). Además de la unión normal, también deberá aplicar la condición where imageable_type = Post::class
; de lo contrario, podría obtener resultados complicados.
Resulta que si te unes a una relación polimórfica, Eloquent Power Joins aplica automáticamente esta condición por ti. Simplemente necesitas llamar al mismo método.
Post:: joinRelationship ( ' images ' );
También puedes unirte a relaciones MorphTo.
Image:: joinRelationship ( ' imageable ' , morphable: Post::class);
Nota: La consulta de transformación en relaciones solo admite un tipo transformable a la vez.
Aplicar condiciones y devoluciones de llamada a las uniones
Ahora, digamos que desea aplicar una condición a la unión que está realizando. Simplemente necesita pasar una devolución de llamada como segundo parámetro al método joinRelationship
.
User:: joinRelationship ( ' posts ' , fn ( $ join ) => $ join -> where ( ' posts.approved ' , true ))-> toSql ();
También puedes especificar el tipo de unión que deseas realizar en la devolución de llamada:
User:: joinRelationship ( ' posts ' , fn ( $ join ) => $ join -> left ());
Para llamadas anidadas , simplemente necesita pasar una matriz que haga referencia a los nombres de las relaciones.
User:: joinRelationship ( ' posts.comments ' , [
' posts ' => fn ( $ join ) => $ join -> where ( ' posts.published ' , true ),
' comments ' => fn ( $ join ) => $ join -> where ( ' comments.approved ' , true ),
]);
Para pertenecer a muchas llamadas, debe pasar una matriz con la relación y luego una matriz con los nombres de las tablas.
User:: joinRelationship ( ' groups ' , [
' groups ' => [
' groups ' => function ( $ join ) {
// ...
},
// group_members is the intermediary table here
' group_members ' => fn ( $ join ) => $ join -> where ( ' group_members.active ' , true ),
]
]);
Consideramos que esta es una de las características más útiles de este paquete. Digamos que tiene un alcance published
en su modelo Post
:
public function scopePublished ( $ query )
{
$ query -> where ( ' published ' , true );
}
Al unir relaciones, puede utilizar los ámbitos definidos en el modelo que se une. ¿Qué tan genial es esto?
User:: joinRelationship ( ' posts ' , function ( $ join ) {
// the $join instance here can access any of the scopes defined in Post
$ join -> published ();
});
Cuando se utilizan ámbitos modelo dentro de una cláusula de unión, no puede escribir sugerencia en el parámetro $query
en su ámbito. Además, tenga en cuenta que está dentro de una unión, por lo que está limitado a utilizar únicamente las condiciones admitidas por las uniones.
A veces, necesitará utilizar alias de tabla en sus combinaciones porque se unirá a la misma tabla más de una vez. Una opción para lograr esto es utilizar el método joinRelationshipUsingAlias
.
Post:: joinRelationshipUsingAlias ( ' category.parent ' )-> get ();
En caso de que necesites especificar el nombre del alias que se va a utilizar, puedes hacerlo de dos maneras diferentes:
Post:: joinRelationshipUsingAlias ( ' category ' , ' category_alias ' )-> get ();
as
dentro de la devolución de llamada de unión. Post:: joinRelationship ( ' category.parent ' , [
' category ' => fn ( $ join ) => $ join -> as ( ' category_alias ' ),
' parent ' => fn ( $ join ) => $ join -> as ( ' category_parent ' ),
])-> get ()
Para pertenece a muchos o tiene muchos llamadas directas , debe pasar una matriz con la relación y luego una matriz con los nombres de las tablas.
Group:: joinRelationship ( ' posts.user ' , [
' posts ' => [
' posts ' => fn ( $ join ) => $ join -> as ( ' posts_alias ' ),
' post_groups ' => fn ( $ join ) => $ join -> as ( ' post_groups_alias ' ),
],
])-> toSql ();
Al realizar uniones, usar select * from ...
puede ser peligroso ya que los campos con el mismo nombre entre las tablas principal y unida podrían entrar en conflicto. Pensando en eso, si llamas al método joinRelationship
sin seleccionar previamente ninguna columna específica, Eloquent Power Joins la incluirá automáticamente. Por ejemplo, eche un vistazo a los siguientes ejemplos:
User:: joinRelationship ( ' posts ' )-> toSql ();
// select users.* from users inner join posts on posts.user_id = users.id
Y, si especifica la declaración de selección:
User:: select ( ' users.id ' )-> joinRelationship ( ' posts ' )-> toSql ();
// select users.id from users inner join posts on posts.user_id = users.id
Al unir cualquier modelo que utilice el rasgo SoftDeletes
, la siguiente condición también se aplicará automáticamente a todas sus uniones:
and " users " . " deleted_at " is null
En caso de que desee incluir modelos descartados, puede llamar al método ->withTrashed()
en la devolución de llamada de unión.
UserProfile:: joinRelationship ( ' users ' , fn ( $ join ) => $ join -> withTrashed ());
También puedes llamar al modelo onlyTrashed
:
UserProfile:: joinRelationship ( ' users ' , ( $ join ) => $ join -> onlyTrashed ());
Si tiene condiciones adicionales en las definiciones de su relación, se aplicarán automáticamente.
class User extends Model
{
public function publishedPosts ()
{
return $ this -> hasMany (Post::class)-> published ();
}
}
Si llama User::joinRelationship('publishedPosts')->get()
, también aplicará el alcance publicado adicional a la cláusula de unión. Produciría un SQL más o menos como este:
select users. * from users inner join posts on posts . user_id = posts . id and posts . published = 1
Si su modelo tiene alcances globales aplicados, puede habilitar los alcances globales llamando al método withGlobalScopes
en su cláusula de unión, de esta manera:
UserProfile:: joinRelationship ( ' users ' , fn ( $ join ) => $ join -> withGlobalScopes ());
Sin embargo, hay un problema aquí. Su alcance global no puede escribir sugerencias sobre la clase EloquentBuilder
en el primer parámetro del método apply
; de lo contrario, obtendrá errores.
Consultar la existencia de relaciones es una característica muy poderosa y conveniente de Eloquent. Sin embargo, utiliza la sintaxis where exists
, que no siempre es la mejor y puede que no sea la opción más eficaz, dependiendo de cuántos registros tenga o de la estructura de sus tablas.
Este paquete implementa la misma funcionalidad, pero en lugar de usar la sintaxis where exists
, usa combinaciones . A continuación, puede ver los métodos que implementa este paquete y también el equivalente de Laravel.
Tenga en cuenta que, aunque los métodos son similares, no siempre obtendrá los mismos resultados al utilizar combinaciones, según el contexto de su consulta. Debe tener en cuenta las diferencias entre consultar los datos con where exists
y con joins
.
Métodos nativos de 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 ' );
Paquete equivalente, pero usando uniones
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 ' );
Cuando use el método powerJoinWhereHas
con relaciones que involucran más de una tabla (uno a muchos, muchos a muchos, etc.), use la sintaxis de matriz para pasar la devolución de llamada:
User:: powerJoinWhereHas ( ' commentsThroughPosts ' , [
' comments ' => fn ( $ query ) => $ query -> where ( ' body ' , ' a ' )
])-> get ());
También puede ordenar los resultados de su consulta usando una columna de otra tabla usando el método orderByPowerJoins
.
User:: orderByPowerJoins ( ' profile.city ' );
Si necesita pasar algunos valores sin procesar para la función ordenar por, puede hacer esto:
User:: orderByPowerJoins ([ ' profile ' , DB :: raw ( ' concat(city, ", ", state) ' ]);
Esta consulta ordenará los resultados según la columna city
en la tabla user_profiles
. También puede ordenar sus resultados por agregaciones ( COUNT
, SUM
, AVG
, MIN
o MAX
).
Por ejemplo, para ordenar los usuarios con la mayor cantidad de publicaciones, puede hacer esto:
$ users = User:: orderByPowerJoinsCount ( ' posts.id ' , ' desc ' )-> get ();
O bien, para obtener la lista de publicaciones donde los comentarios contienen el promedio más alto de votos.
$ posts = Post:: orderByPowerJoinsAvg ( ' comments.votes ' , ' desc ' )-> get ();
También tienes métodos para SUM
, MIN
y MAX
:
Post:: orderByPowerJoinsSum ( ' comments.votes ' );
Post:: orderByPowerJoinsMin ( ' comments.votes ' );
Post:: orderByPowerJoinsMax ( ' comments.votes ' );
En caso de que desee utilizar combinaciones izquierdas para ordenar, también puede:
Post:: orderByLeftPowerJoinsCount ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsAvg ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsSum ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsMin ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsMax ( ' comments.votes ' );
Consulte CONTRIBUCIÓN para obtener más detalles.
Si descubre algún problema relacionado con la seguridad, envíe un correo electrónico a [email protected] en lugar de utilizar el rastreador de problemas.
El desarrollo de este paquete está patrocinado por Kirschbaum Development Group, una empresa impulsada por desarrolladores centrada en la resolución de problemas, la formación de equipos y la comunidad. ¡Conozca más sobre nosotros o únase a nosotros!
La Licencia MIT (MIT). Consulte el archivo de licencia para obtener más información.