A magia do Laravel que você conhece, agora aplicada a junções.
As junções são muito úteis de várias maneiras. Se você está aqui, provavelmente os conhece e os usa. O Eloquent é muito poderoso, mas falta um pouco do "jeito Laravel" ao usar joins. Este pacote torna suas junções de uma forma mais Laravel, mais legíveis e com menos código, ao mesmo tempo que esconde detalhes de implementação de locais onde eles não precisam ser expostos.
Algumas coisas que consideramos que faltam ao usar junções, que são recursos muito poderosos do Eloquent:
Você pode ler uma explicação mais detalhada sobre os problemas que este pacote resolve nesta postagem do blog.
Você pode instalar o pacote via compositor:
composer require kirschbaum-development/eloquent-power-joins
Para versões do Laravel <10, use a versão 3.*. Para versões do Laravel <8, use a versão 2.*:
composer require kirschbaum-development/eloquent-power-joins:3. *
Este pacote fornece alguns recursos.
Digamos que você tenha um modelo User
com um relacionamento hasMany
com o modelo Post
. Se você quiser juntar as tabelas, normalmente escreveria algo como:
User:: select ( ' users.* ' )-> join ( ' posts ' , ' posts.user_id ' , ' = ' , ' users.id ' );
Este pacote fornece um novo método joinRelationship()
, que faz exatamente a mesma coisa.
User:: joinRelationship ( ' posts ' );
Ambas as opções produzem os mesmos resultados. Em termos de código, você não economizou TANTO, mas agora está usando o relacionamento entre os modelos User
e Post
para unir as tabelas. Isso significa que agora você está escondendo como esse relacionamento funciona nos bastidores (detalhes de implementação). Você também não precisa alterar o código se o tipo de relacionamento for alterado. Agora você tem um código mais legível e menos complicado.
Mas fica melhor quando você precisa ingressar em relacionamentos aninhados . Vamos supor que você também tenha um relacionamento hasMany
entre os modelos Post
e Comment
e precise unir essas tabelas, basta escrever:
User:: joinRelationship ( ' posts.comments ' );
Muito melhor, você não concorda?! Você também pode ingressar nos relacionamentos left
ou right
, conforme necessário.
User:: leftJoinRelationship ( ' posts.comments ' );
User:: rightJoinRelationship ( ' posts.comments ' );
Vamos imaginar que você tem um modelo Image
que é um relacionamento polimórfico ( Post -> morphMany -> Image
). Além da junção regular, você também precisaria aplicar a condição where imageable_type = Post::class
, caso contrário, você poderia obter resultados confusos.
Acontece que, se você ingressar em um relacionamento polimórfico, o Eloquent Power Joins aplicará automaticamente essa condição para você. Você simplesmente precisa chamar o mesmo método.
Post:: joinRelationship ( ' images ' );
Você também pode ingressar em relacionamentos MorphTo.
Image:: joinRelationship ( ' imageable ' , morphable: Post::class);
Nota: A consulta de metamorfose para relacionamentos oferece suporte apenas a um tipo morfável por vez.
Aplicando condições e retornos de chamada às junções
Agora, digamos que você queira aplicar uma condição à junção que está fazendo. Você simplesmente precisa passar um retorno de chamada como segundo parâmetro para o método joinRelationship
.
User:: joinRelationship ( ' posts ' , fn ( $ join ) => $ join -> where ( ' posts.approved ' , true ))-> toSql ();
Você também pode especificar o tipo de junção que deseja fazer no retorno de chamada:
User:: joinRelationship ( ' posts ' , fn ( $ join ) => $ join -> left ());
Para chamadas aninhadas , você simplesmente precisa passar um array referenciando os nomes dos relacionamentos.
User:: joinRelationship ( ' posts.comments ' , [
' posts ' => fn ( $ join ) => $ join -> where ( ' posts.published ' , true ),
' comments ' => fn ( $ join ) => $ join -> where ( ' comments.approved ' , true ),
]);
Para pertencer a muitas chamadas, você precisa passar um array com o relacionamento e depois um array com os nomes das tabelas.
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 este um dos recursos mais úteis deste pacote. Digamos que você tenha um escopo published
em seu modelo Post
:
public function scopePublished ( $ query )
{
$ query -> where ( ' published ' , true );
}
Ao unir relacionamentos, você pode usar os escopos definidos no modelo que está sendo unido. Quão legal é isso?
User:: joinRelationship ( ' posts ' , function ( $ join ) {
// the $join instance here can access any of the scopes defined in Post
$ join -> published ();
});
Ao usar escopos de modelo dentro de uma cláusula de junção, você não pode digitar o parâmetro $query
no seu escopo. Além disso, lembre-se de que você está dentro de uma junção e, portanto, está limitado a usar apenas condições suportadas por junções.
Às vezes, você precisará usar aliases de tabela em suas junções porque estará ingressando na mesma tabela mais de uma vez. Uma opção para fazer isso é usar o método joinRelationshipUsingAlias
.
Post:: joinRelationshipUsingAlias ( ' category.parent ' )-> get ();
Caso seja necessário especificar o nome do alias que será utilizado, você pode fazê-lo de duas maneiras diferentes:
Post:: joinRelationshipUsingAlias ( ' category ' , ' category_alias ' )-> get ();
as
dentro do retorno de chamada de junção. Post:: joinRelationship ( ' category.parent ' , [
' category ' => fn ( $ join ) => $ join -> as ( ' category_alias ' ),
' parent ' => fn ( $ join ) => $ join -> as ( ' category_parent ' ),
])-> get ()
Para pertencer a muitos ou possuir muitos através de chamadas, você precisa passar um array com o relacionamento, e depois um array com os nomes das tabelas.
Group:: joinRelationship ( ' posts.user ' , [
' posts ' => [
' posts ' => fn ( $ join ) => $ join -> as ( ' posts_alias ' ),
' post_groups ' => fn ( $ join ) => $ join -> as ( ' post_groups_alias ' ),
],
])-> toSql ();
Ao fazer junções, usar select * from ...
pode ser perigoso, pois campos com o mesmo nome entre as tabelas pai e unidas podem entrar em conflito. Pensando nisso, se você chamar o método joinRelationship
sem selecionar previamente nenhuma coluna específica, o Eloquent Power Joins incluirá isso automaticamente para você. Por exemplo, dê uma olhada nos seguintes exemplos:
User:: joinRelationship ( ' posts ' )-> toSql ();
// select users.* from users inner join posts on posts.user_id = users.id
E, se você especificar a instrução select:
User:: select ( ' users.id ' )-> joinRelationship ( ' posts ' )-> toSql ();
// select users.id from users inner join posts on posts.user_id = users.id
Ao ingressar em qualquer modelo que use a característica SoftDeletes
, a seguinte condição também será aplicada automaticamente a todas as suas junções:
and " users " . " deleted_at " is null
Caso queira incluir modelos descartados, você pode chamar o método ->withTrashed()
no retorno de chamada de junção.
UserProfile:: joinRelationship ( ' users ' , fn ( $ join ) => $ join -> withTrashed ());
Você também pode chamar o modelo onlyTrashed
:
UserProfile:: joinRelationship ( ' users ' , ( $ join ) => $ join -> onlyTrashed ());
Se você tiver condições extras em suas definições de relacionamento, elas serão aplicadas automaticamente para você.
class User extends Model
{
public function publishedPosts ()
{
return $ this -> hasMany (Post::class)-> published ();
}
}
Se você ligar User::joinRelationship('publishedPosts')->get()
, ele também aplicará o escopo publicado adicional à cláusula join. Produziria um SQL mais ou menos assim:
select users. * from users inner join posts on posts . user_id = posts . id and posts . published = 1
Se o seu modelo tiver escopos globais aplicados a ele, você poderá habilitar os escopos globais chamando o método withGlobalScopes
na sua cláusula join, assim:
UserProfile:: joinRelationship ( ' users ' , fn ( $ join ) => $ join -> withGlobalScopes ());
Há, porém, uma pegadinha aqui. Seu escopo global não pode digitar a classe EloquentBuilder
no primeiro parâmetro do método apply
, caso contrário você obterá erros.
Consultar a existência de um relacionamento é um recurso muito poderoso e conveniente do Eloquent. No entanto, ele usa a sintaxe where exists
que nem sempre é a melhor e pode não ser a escolha de melhor desempenho, dependendo de quantos registros você possui ou da estrutura de suas tabelas.
Este pacote implementa a mesma funcionalidade, mas em vez de usar a sintaxe where exists
, ele usa joins . Abaixo você pode ver os métodos que este pacote implementa e também o equivalente do Laravel.
Observe que embora os métodos sejam semelhantes, você nem sempre obterá os mesmos resultados ao usar junções, dependendo do contexto da sua consulta. Você deve estar ciente das diferenças entre consultar os dados com where exists
e joins
.
Métodos nativos do 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 ' );
Equivalente ao pacote, mas usando junções
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 ' );
Ao usar o método powerJoinWhereHas
com relacionamentos que envolvem mais de 1 tabela (Um para Muitos, Muitos para Muitos, etc.), use a sintaxe de array para passar o retorno de chamada:
User:: powerJoinWhereHas ( ' commentsThroughPosts ' , [
' comments ' => fn ( $ query ) => $ query -> where ( ' body ' , ' a ' )
])-> get ());
Você também pode classificar os resultados da consulta usando uma coluna de outra tabela usando o método orderByPowerJoins
.
User:: orderByPowerJoins ( ' profile.city ' );
Se você precisar passar alguns valores brutos para a função ordenar por, você pode fazer assim:
User:: orderByPowerJoins ([ ' profile ' , DB :: raw ( ' concat(city, ", ", state) ' ]);
Esta consulta classificará os resultados com base na coluna city
na tabela user_profiles
. Você também pode classificar seus resultados por agregações ( COUNT
, SUM
, AVG
, MIN
ou MAX
).
Por exemplo, para classificar os usuários com o maior número de postagens, você pode fazer o seguinte:
$ users = User:: orderByPowerJoinsCount ( ' posts.id ' , ' desc ' )-> get ();
Ou, para obter a lista de postagens onde os comentários contêm a maior média de votos.
$ posts = Post:: orderByPowerJoinsAvg ( ' comments.votes ' , ' desc ' )-> get ();
Você também tem métodos para SUM
, MIN
e MAX
:
Post:: orderByPowerJoinsSum ( ' comments.votes ' );
Post:: orderByPowerJoinsMin ( ' comments.votes ' );
Post:: orderByPowerJoinsMax ( ' comments.votes ' );
Caso queira usar junções à esquerda na classificação, você também pode:
Post:: orderByLeftPowerJoinsCount ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsAvg ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsSum ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsMin ( ' comments.votes ' );
Post:: orderByLeftPowerJoinsMax ( ' comments.votes ' );
Consulte CONTRIBUINDO para obter detalhes.
Se você descobrir algum problema relacionado à segurança, envie um e-mail para [email protected] em vez de usar o rastreador de problemas.
O desenvolvimento deste pacote é patrocinado pelo Kirschbaum Development Group, uma empresa voltada para desenvolvedores focada na resolução de problemas, formação de equipes e comunidade. Saiba mais sobre nós ou junte-se a nós!
A licença MIT (MIT). Consulte Arquivo de licença para obter mais informações.