Obtenir des colonnes spécifiques à l'aide de la fonction «With ()» dans Laravel Eloquent

198

J'ai deux tables, Useret Post. On Userpeut en avoir plusieurs postset on postn'appartient qu'à un seul user.

Dans mon Usermodèle, j'ai une hasManyrelation ...

public function post(){
    return $this->hasmany('post');
}

Et dans mon postmodèle j'ai une belongsTorelation ...

public function user(){
    return $this->belongsTo('user');
}

Maintenant, je veux joindre ces deux tables en utilisant Eloquent with()mais je veux des colonnes spécifiques de la deuxième table. Je sais que je peux utiliser le Générateur de requêtes, mais je ne veux pas.

Quand dans le Postmodèle j'écris ...

public function getAllPosts() {
    return Post::with('user')->get();
}

Il exécute les requêtes suivantes ...

select * from `posts`
select * from `users` where `users`.`id` in (<1>, <2>)

Mais ce que je veux, c'est ...

select * from `posts`
select id,username from `users` where `users`.`id` in (<1>, <2>)

Quand j'utilise ...

Post::with('user')->get(array('columns'....));

Il ne renvoie que la colonne de la première table. Je veux des colonnes spécifiques à with()partir de la deuxième table. Comment puis je faire ça?

Awais Qarni
la source

Réponses:

349

Eh bien, j'ai trouvé la solution. Cela peut être fait en passant une closurefonction en with()tant que deuxième index du tableau comme

Post::with(array('user'=>function($query){
    $query->select('id','username');
}))->get();

Il sélectionnera uniquement idet à usernamepartir d'une autre table. J'espère que cela aidera les autres.


Souvenez-vous que la clé primaire (id dans ce cas) doit être le premier paramètre dans $ query-> select () pour récupérer réellement les résultats nécessaires. *

Awais Qarni
la source
2
C'est étrange, je n'ai pas pu faire fonctionner ça. Dès que j'ai ajouté $query->select('id','username');, je Trying to get property of non-object
recevais
5
Bizarre! renvoie toujours tous les champs de l'utilisateur. @AwaisQarni
justin
2
Merci de partager ceci. Où avez-vous découvert cette possibilité? Je n'ai pas trouvé cela dans la documentation Laravel.
Symen Timmermans
82
Pour ceux qui voient cela, rappelez-vous que la clé primaire ( iddans ce cas) est nécessaire dans le $query->select()pour récupérer réellement les résultats nécessaires. J'ai omis cela dans mon code et je ne pouvais pas comprendre pourquoi il ne trouverait aucun résultat. Que je suis bête! J'espère que cela aide quelqu'un d'autre avec le même problème
Azirius
4
super, juste besoin d'ajouter une clé étrangère, ici son post_id sinon le résultat sera vide. Mentionner la clé étrangère est important ici. Merci :)
Hashaam Ahmed
106

Vous pouvez le faire comme ceci depuis Laravel 5.5:

Post::with('user:id,username')->get();

Prenez soin du idterrain et foreign keyscomme indiqué dans la documentation :

Lorsque vous utilisez cette fonctionnalité, vous devez toujours inclure la colonne id et toutes les colonnes de clé étrangère pertinentes dans la liste des colonnes que vous souhaitez récupérer.

Par exemple, si l'utilisateur appartient à une équipe et a team_idcomme colonne de clé étrangère, alors $post->user->teamest vide si vous ne spécifiez pas team_id

Post::with('user:id,username,team_id')->get();
Adam
la source
13
N'oubliez pas d'inclure la clé étrangère (en supposant qu'il s'agit ici de post_id) pour résoudre la relation, sinon vous obtiendrez des résultats vides pour votre relation.
Hashaam Ahmed
2
Cela devrait vraiment être la réponse choisie. Fonctionne comme un charme :)
Graham
@Adam, Cela limitera les colonnes de la table enfant, comment puis-je restreindre les colonnes de la table Table parent avec la table enfant?
Sodium
@GauravGenius tout ce que vous voulez restreindre des belogns parants dans la get()méthodePost::with('user:id,username')->get(['age', 'color']);
Adam
82

Dans votre Postmodèle

public function user()
{
    return $this->belongsTo('User')->select(array('id', 'username'));
}

Le crédit original va à Laravel Eager Loading - Charger uniquement des colonnes spécifiques

user1669496
la source
14
Mais cette relation le rendra comme codé en dur. Dans toutes les conditions il me restituera toujours ces deux champs. Il se peut que j'aie besoin de plus de champs dans d'autres situations
Awais Qarni
Ensuite, vous devez utiliser le générateur de requêtes.
user1669496
5
Cela devrait être la réponse acceptée. C'est la bonne façon de procéder.
BugHunterUK
1
Existe-t-il un moyen de faire cela dans la version Laravel 5.3? Je pense qu'ils l'ont changé ... encore une fois ... et maintenant il retourne nul quand j'essaye avec cette approche
Andre F.
Cela fait un moment, mais vous pouvez ajouter un $fieldsparamètre.
JordyvD
70

En allant dans l'autre sens (hasMany):

User::with(array('post'=>function($query){
    $query->select('id','user_id');
}))->get();

N'oubliez pas d'inclure la clé étrangère (en supposant qu'il s'agit de user_id dans cet exemple) pour résoudre la relation, sinon vous n'obtiendrez aucun résultat pour votre relation.

Thijs
la source
6
Oui, exactement, "N'oubliez pas d'inclure la clé étrangère (en supposant qu'il s'agit de user_id dans cet exemple)"
aqingsao
vous devez inclure la colonne qui définit votre relation dans certains champs, dans ce cas user_id
alimfazeli
Beau frère de capture.
Shahrukh Anwar
génial frère, cette utilisation de la clé étrangère est vraiment importante, chanceux j'ai vu votre ans sinon j'aurais gratté la tête pendant des heures lol. Merci mec!
Hashaam Ahmed
35

Dans Laravel 5.7, vous pouvez appeler un champ spécifique comme celui-ci

$users = App\Book::with('author:id,name')->get();

Il est important d'ajouter un foreign_keychamp dans la sélection.

hendra1
la source
12
n'oubliez pas d'ajouter le champ de clé étrangère
hendra1
2
N'oubliez pas de noter le commentaire de @ hendra1, tous les champs de clé étrangère sont également nécessaires avec la clé primaire, sinon vous obtiendrez une collection vide. Mec a sauvé mon temps. Merci
Rajesh Vishnani
14

Dans votre Postmodèle:

public function userWithName()
{
    return $this->belongsTo('User')->select(array('id', 'first_name', 'last_name'));
}

Vous pouvez maintenant utiliser $post->userWithName

Duy Hoang
la source
2
Vraiment? Pour moi, cela ne renvoie rien, c'est à dire qu'il le casse?
Sjwdavies
12

Si vous souhaitez obtenir des colonnes spécifiques en utilisant with()dans laravel éloquent, vous pouvez utiliser le code ci-dessous auquel @Adam a répondu à l'origine dans sa réponse ici en réponse à cette même question:

Post::with('user:id,username')->get();

Donc je l'ai utilisé dans mon code mais cela me donnait une erreur de 1052: Column 'id' in field list is ambiguous, donc si vous êtes également confronté au même problème

Ensuite, pour le résoudre, vous devez spécifier le nom de la table avant la colonne id dans la with()méthode comme code ci-dessous :

Post::with('user:user.id,username')->get();
Haritsinh Gohil
la source
Cela renvoie null pour moi mais je ne savais pas que c'était possible avant! Ok, donc j'ai posté prématurément mais j'ai trouvé un résultat. Pour utiliser cette méthode (qui semble beaucoup plus propre), vous devez inclure le lien de relation, par exemple la colonne ID lors de l'extraction des données avec. Sans ce spécificateur, vous obtenez un retour nul.
Adsy2010
ouais @ Adsy2010, pour obtenir des données de relation connexes, vous devez également obtenir la colonne d'identifiant ou la clé primaire ou tout identifiant responsable de cette relation.
Haritsinh Gohil
10

Je suis tombé sur ce problème mais avec une deuxième couche d'objets connexes. La réponse de @Awais Qarni tient avec l'inclusion de la clé étrangère appropriée dans l'instruction select imbriquée. Tout comme un identifiant est requis dans la première instruction select imbriquée pour référencer le modèle associé, la clé étrangère est requise pour référencer le deuxième degré des modèles associés; dans cet exemple, le modèle d'entreprise.

Post::with(['user' => function ($query) {
        $query->select('id','company_id', 'username');
    }, 'user.company' => function ($query) {
        $query->select('id', 'name');
    }])->get();

De plus, si vous souhaitez sélectionner des colonnes spécifiques du modèle Post, vous devez inclure la colonne user_id dans l'instruction select afin de la référencer.

Post::with(['user' => function ($query) {
        $query->select('id', 'username');
    }])
    ->select('title', 'content', 'user_id')
    ->get();
jwarshaw
la source
4

Notez que si vous n'avez besoin que d'une seule colonne de la table, l'utilisation de «listes» est plutôt bien. Dans mon cas, je récupère les articles préférés d'un utilisateur mais je ne veux que les identifiants d'article:

$favourites = $user->favourites->lists('id');

Renvoie un tableau d'identifiants, par exemple:

Array
(
    [0] => 3
    [1] => 7
    [2] => 8
)
omar j
la source
ça retourne une collection!
Giovanni Far
si vous voulez un tableau alors appelez toArray()!!! $user->favourites->lists('id')->toArray();
Giovanni Far
La requête obtiendra toujours les autres colonnes car la méthode list () ne fait que modifier le tableau des résultats, donc si vous avez juste besoin de l'ID de cette table, vous voudrez peut-être le spécifier dans la requête. Il est toujours judicieux de garder à l'esprit les performances lorsque vous effectuez des requêtes. Profitez du codage!
Ervin Llojku
-1 $user->favouritessera renvoyé Collectionavec tous les champs sélectionnés. L'utilisation correcte est: $user->favourites()->lists('id').
Wallace Maxters
Tout sélectionner pour une utilisation ultérieure du filtrage PHP est une mauvaise idée. Il est préférable de n'utiliser que les champs nécessaires dans Query. Il est important de connaître la différence entre Query\Builder::listsméthode et Collection::listsméthode.
Wallace Maxters
1

Vous pouvez également spécifier des colonnes sur le modèle associé au moment d'y accéder.

Post::first()->user()->get(['columns....']);

Shreyansh Panchal
la source
0

Vous pouvez maintenant utiliser la pluckméthode sur une Collectioninstance:

Cela ne renverra que l' uuidattribut duPost model

App\Models\User::find(2)->posts->pluck('uuid')
=> Illuminate\Support\Collection {#983
     all: [
       "1",
       "2",
       "3",
     ],
   }
Giovanni Far
la source
0

Essayez avec des conditions.

$id = 1;
Post::with(array('user'=>function($query) use ($id){
    $query->where('id','=',$id);
    $query->select('id','username');
}))->get();
Shirjeel Ahmed Khan
la source
-3
EmployeeGatePassStatus::with('user:id,name')->get();
kush
la source
2
C'est la même syntaxe que dans cette réponse (juste des noms différents pour le modèle et la colonne)
barbsan