Besoin d'aide ?

Aide requête un peu particulière

  • Avatar de jsarda66
    Membre depuis :
    25/07/2016
    Messages :
    28

    Bonjour et merci de l'attention que vous allez porter à mon message.

    Voilà je suis en train de créer un site qui va lister et recenser les matches de l'équipe de france de rugby. Si je n'ai eu aucun soucis pour le moment, je bloque sur la construction d'une requête un peu particulière.
    En effet j'ai crée un controller statistiques pour remonter différentes statistiques (Nombre de sélections, nombre d'essais etc...).
    Je liste mes matches et les détails d'un match à l'aide de deux tables :

    • rencontres
    • details

    Dans rencontres il y a les différents éléments d'un match (date, score, nombre de spectateurs etc...)
    Dans détails on renseigne les différents détails de la rencontre :

    • rencontres_id (lien vers table rencontres)
    • personne_id (lien vers table personnes)
    • fonction_id (lien vers table fonctions (joueur, arbitre, entraineur...etc))
    • equipe_id (lien vers tables equipes)
    • NBMinutes
    • NumMaillot
    • NBEssai
    • NBTransf
    • ...(et toutes autres infos relatives à un match comme nombre pénalités, carton jaune, carton rouge etc...)

    J'ai crée une route qui me dirige vers ce lien : /statistiques/equipes/france

    Comment à partir de là puis-je faire une requête qui me permet de retourner le nombre de matches effectués par un joueur? Sachant que pour compter une sélection il faut que le joueur ait joué au moins une minute dans une rencontre soit ->where('NBMinutes', '>', 0)

    Avez vous la moindre idée de comment on pourrais faire cela?

    Quand je suis à la page d'un joueur je n'ai pas de soucis pour constuire cette requête puisque j'ai dans l'url et le routing les infos qui me permettent d'affiner ma requête.

    J'espère vraiment que vous pourrez me dépanner, j'avoue que je sèche complétement là.

    Merci à vous d'avoir lu cette demande.

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1414

    Bonsoir,

    Le schéma de la base n'est pas très clair par rapport aux joueurs, ils sont reliés où ?

    D'autre part pourquoi avoir divisé les informations en deux tables pour les rencontres ?

  • Avatar de jsarda66
    Membre depuis :
    25/07/2016
    Messages :
    28

    Merci de ta réponse BestMomo, les joueurs sont reliés à la table détails par personne_id

    Model Personne :

    public function details()
    {
    return $this->hasMany('App\Detail');
    }

    Model Detail :

    public function personne()
    {
    return $this->belongsTo('App\Personne');
    }

    Model Rencontre :

    public function details()
    {
    return $this->hasMany('App\Detail');
    }

    Alors j'ai divisé la table rencontre en deux pour ne pas répéter plusieurs enregistrements.
    Dans la table rencontres on a les infos de la rencontre à proprement parler (date_match, lieu, score, nombre spectateurs, Nombre essai équipe 1, Nombre essai équipe 2...etc)
    Dans la table détails c'est les détails d'un match pour une personne qui a participé (Nombre de minutes jouées, Essais marqués, Pénalités marqués etc...).

    Par exemple je crée la rencontre de la finale de la coupe du Monde 2011 dans la table rencontres.
    Date : 23/10/2011
    Equipe1 : Nouvelle-Zélande
    Equipe2 : France
    Score Equipe1 : 8
    Score Equipe2 : 7

    Maintenant dans ma table détails je rentre le détail joueur par joueur pour cette finale :
    Rencontre_id : liste déroulante qui recense toutes les rencontres
    Personne_id : liste déroulante qui recense tous les joueurs (ex: Dusautoir)
    Fonction_id : liste déroulante qui recense toutes les fonctions (joueur, arbitre, entraineur...)
    Equipe_id : liste déroulante qui recense les équipes
    NB minutes : 80
    Essais : 1
    Pénalités : 0
    etc...

    C'est un peu plus clair?

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1414

    Salut,

    Oui comme ça c'est plus clair !

    En fait la table details sert de pivot entre les rencontres et les personnes. Il faut donc établir une relation belongsToMany entre ces deux tables, avec withPivot pour ajouter les colonnes du pivot.

    Pour le compte des enregistrements enfants la méthode withCount est parfaite.

  • Avatar de jsarda66
    Membre depuis :
    25/07/2016
    Messages :
    28

    Salut,

    tout d'abord merci de tes réponses. Donc si je te suis bien il faut que dans mes modèles je fasse ceci :

    Model Personne :

    public function rencontre()

    {
    return $this->belongsToMany('App\Rencontre');
    }

    Model Rencontre :

    public function personne()

    {
    return $this->belongsToMany('App\Personne');
    }

    Je n'ai jamais utilisé with pivot, à quel moment il faut que je le mentionne? Dans un model? J'avoue que là je sèche un peu.

  • Avatar de jsarda66
    Membre depuis :
    25/07/2016
    Messages :
    28

    Bon du coup j'ai essayé en renseignant les model comme indiqué ci-dessus, et j'ai fait un withCount dans ma requête et ça fonctionne.

    $selections = Personne::withCount(['details' => function ($query) {
    $query->where('minute', '>', 0);
    }])->get();

    Comment je peux filtrer maintenant pour que cela me donne les joueurs avec le plus de sélections en premier?

    Un grand merci en tout cas.

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1414

    Il suffit d'ajouter le filtre sur la colonne générée :

    $selections = Personne::withCount(['details' => function ($query) {
    $query->where('minute', '>', 0);
    }])->latest('details_count')->get();
  • Avatar de jsarda66
    Membre depuis :
    25/07/2016
    Messages :
    28

    Un très grand merci! Maintenant quelque chose d'un peu plus compliqué dans le même genre.
    Comment je peux faire la même chose en donnant par exemple le nombre d'essais total inscrit par le joueur sachant que certaines fois il peut avoir inscrit 2 ou 3 essais?

    Si je fais la même chose en changeant 'minute' par 'essai', ça ne me retournera que le nombre de fois où la colonne essai est supérieure à zéro.

    Il y a bien "Sum", mais là je ne vois pas comment l'utiliser dans ce cas.

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1414

    Ah, là Eloquent ne sait plus faire, il faut une jointure dans ce genre :

    $joueurs = DB::table('personnes')
    ->join('details', 'personnes.id', '=', 'details.personne_id')
    ->orderByRaw("sum('details.NBessai')"))
    ->groupBy('personnes.id')
    ->get();

    Mais en prenant le problème dans l'autre sens on doit pouvoir utiliser Eloquent :

    $results = Details::with('personne')
    ->groupBy('personne_id')
    ->selectRaw('*, sum(NBessai) as sum')
    ->get();

    Il faut un peu tester tout ça ...

  • Avatar de jsarda66
    Membre depuis :
    25/07/2016
    Messages :
    28

    Merci BestMomo, les deux fonctionnent!
    C'est génial.

    Sinon d'une manière un peu plus général, tu conseilles Eloquent ou le QueryBuilder?

    Bonne journée

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1414

    C'est une question de goût personnel, avec Eloquent la syntaxe est plus propre et lisible.

    Bonne journée.

  • Avatar de jsarda66
    Membre depuis :
    25/07/2016
    Messages :
    28

    Salut bestmomo,

    est-ce que tu aurais une idée sur comment je peux remonter le nombre de victoires pour une équipe?
    Je m'explique dans ma table rencontres (je prends seulement les champs importants) j'ai :

    • domicile (lien vers table equipes)
    • exterieur (lien vers table equipes)
    • scoreFinalDomicile
    • scoreFinalExterieur

    Voici mon model Rencontre :

    public function equipe_domicile(){
    return $this->belongsTo('App\Equipe', 'domicile');
    }

    public function equipe_exterieur(){
    return $this->belongsTo('App\Equipe', 'exterieur');
    }

    Avec ces infos crois-tu qu'il est possible de retrouver le nombre de victoires de la France par exemple? Sachant que la France peut parfois être "domicile" ou "exterieur". En admettant par exemple que je sois à une page : www.monsite.fr/france où france est le slug de l'équipe.
    J'avoue que je sèche là aussi.

    Alors j'ai bien pensé créer une table resultats avec ces champs :
    id
    rencontre_id (lien vers table rencontres)
    equipe_id (lien vers table equipes)
    resultat (liste déroulante avec victoire, nul, défaite)

    Là dessus je pourrais requêter avec un withCount comme tu m'as indiqué plus haut, mais ça me fait créer une table en plus etc...

    Merci beaucoup par avance, bonne soirée,
    Jérémy

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1414

    Salut,

    Je suppose que tu as les relations réciproques dans le modèle Equipe. Tu peux discocier les victoires à l'extérieur et à domicile et pour chacune faire une requête dans ce genre :

    $victoires_exterieur = Equipe::whereSlug($equipe)->withCount(['rencontre_exterieur' => function ($query) {
    $query->where('scoreFinalDomicile', '<', 'scoreFinalExterieur');
    }])->first();

    Mais tu peux aussi les regrouper pour faire une seule requête.

  • Avatar de jsarda66
    Membre depuis :
    25/07/2016
    Messages :
    28

    Merci de ta réponse.
    Je t'avoue que non je n'avais pas fait les relations réciproques dans le modèle Equipe.
    D'ailleurs quelles sont-elles? Je viens bien de tenter de faire ça : (Model Equipe)

    public function rencontre_domicile(){
    return $this->belongsTo('App\Rencontre', 'domicile');
    }

    public function rencontre_exterieur(){
    return $this->belongsTo('App\Rencontre', 'exterieur');
    }

    ou encore ça :

    public function rencontre_domicile(){
    return $this->belongsTo('App\Rencontre');
    }

    public function rencontre_exterieur(){
    return $this->belongsTo('App\Rencontre');
    }

    Mais cela ne me semble pas logique.

    En tout cas un très grand merci encore une fois.

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1414

    Salut,

    Non la réciproque c'est du hasMany parce qu'une Equipe a plusieurs rencontres, et il faut préciser la clé étrangère puisque tu as deux relations.

  • Avatar de jsarda66
    Membre depuis :
    25/07/2016
    Messages :
    28

    Merci de ton aide,
    j'ai donc fait cela dans mon modèle Equipe :

    public function rencontre_domicile(){
    return $this->hasMany('App\Rencontre', 'domicile');
    }

    public function rencontre_exterieur(){
    return $this->hasMany('App\Rencontre', 'exterieur');
    }

    Et dans mon controller statistiques :

    $victoiresDom = Equipe::withCount(['rencontre_domicile' => function ($query) {
    $query->where('scoreFD', '>', 'scoreFE');
    }])
    ->get();

    Si cela ne me génère pas d'erreur, cela en fait ne me remonte simplement que le nombre de rencontres disputées à domicile par une équipe. Là on est ok. Ce qui est un peu plus bizarre en revanche, c'est que quand je change la condition de cette manière :

    $query->where('scoreFD', '<', 'scoreFE');

    Tous les 'rencontres_domicile_count' sont à 0

    Bon l'essentiel c'est qu'on s'en approche. Merci encore.

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1414

    Une autre manière de faire est de partir des rencontres et de compter les éléments de la collection obtenue :

    $nombre_rencontres_domicile = Rencontre::whereHas('equipe_domicile', function ($query) use ($slug_equipe) {
    $query->whereSlug($slug_equipe);
    })->where('scoreFD', '>', 'scoreFE')->get()->count();
  • Avatar de jsarda66
    Membre depuis :
    25/07/2016
    Messages :
    28

    Salut BestMomo,

    j'ai un problème bizarre. Quand je fais cette requête pour récupérer le total des points marqués par des joueurs et classer ce total par ordre décroissant (soit le joueur qui a inscrit le plus de points en premier), cela ne m'additionne pas tous les points de toutes les rencontres par joueur.

    $points = DB::table('personnes')
    ->join('details', 'personnes.id', '=', 'details.personne_id')
    ->join('equipe_personne', 'personnes.id', '=', 'equipe_personne.personne_id')
    ->orderByRaw("SUM('details.points')")
    ->groupBy('personnes.id')
    ->select('personnes.personne_lastname', 'personnes.personne_firstname', 'personnes.slug',
    'personnes.image_name', 'personnes.image_extension', 'personnes.id',
    'details.points')
    ->latest('details.points')
    ->take(10)
    ->get();

    On dirait que cela ne prend en compte qu'un certain nombre d'enregistrements de la table "details" et que cela n'additionne pas toutes les données de la table détails.

    Aurais-tu une idée de ce qui peut se passer?

    Sinon j'arrive à bien retourner les bons résultats en usant Eloquent de cette manière :

    $points = Detail::with('personne')
    ->groupBy('personne_id')
    ->selectRaw('*, sum(points) as sum')
    ->latest('sum')
    ->take(10)
    ->get();

    Mais là je bute sur la sélection de champs. Je voudrais récupérer les champs que je récupère comme avec le Query Builder mais je bloque.
    Pourrais-tu me dire comment faire?

    Merci beaucoup d'avance, excellente soirée.

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    1414

    Salut,

    Pour la version Eloquent quels sont les champs que tu peux pas récupérer ?

  • Avatar de Romu
    Membre depuis :
    23/02/2017
    Messages :
    25

    Salut,

    Par hasard, as tu essayé de faire un :

    ->selectRaw('*, sum(points) as sum')

    Et après le

    orderBy(sum)

    ?

    Sinon pour tester si tout tes resultat sont bien donné, tente la requete directement dans phpMyAdmin ou adminer,
    au moins tu sauras si les deux requete donne le même resultat que le soucis se passe au niveau de la syntaxe de la requete ? non ?

Vous ne pouvez pas répondre à ce sujet.