JeuWeb - Crée ton jeu par navigateur
Une requête qui sélectionne un maximum de choses ou plusieurs petites ? - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : Une requête qui sélectionne un maximum de choses ou plusieurs petites ? (/showthread.php?tid=1381)

Pages : 1 2


Une requête qui sélectionne un maximum de choses ou plusieurs petites ? - Sephi-Chan - 24-03-2008

Salut tout le monde,

En réécrivant la classe métier de Seelies au sujet des Vols, je me suis posé une question… Mieux vaut-il pour moi faire une requête (dans mon cas le constructeur) qui récupère un maximum d'informations, où plutôt créer une méthode qui fait une requête pour chercher la liste des membres du Vol et leur rôle, une pour lister les territoires du Vol, etc.

Mon intuition me dit de privilégier la lisibilité du code et sa modularité, mais je voulais votre avis sur la question. Smile

Voilà le constructeur en l'état actuel. La requête est encore soft, mais j'ai de quoi bien la compliquer, mais ça me paraît pas bien propre de faire ça. Le coup du flag, ça va bien quand on n'a que peu de jointure, mais quand il y en aura 8, ça risque de devenir assez imbuvable…
Code PHP :
<?php 
/*
* Get informations about the given flight if it is specified.
*/
public function __construct($flight = 0){

if(
$flight == 0){
/* Coming soon… :) */
}
else {

$query = sprintf(
"SELECT F.*, R.*
FROM flights F
LEFT JOIN flight_has_role FHR ON FHR.flight_id = F.flight_id
LEFT JOIN roles R ON R.role_id = FHR.role_id
WHERE F.flight_id = %d;"
,
(int)
$flight
);
$sql = mysql_query($query);

if(
mysql_num_rows($sql) >= 1){

$flag = NULL;
while(
$result = mysql_fetch_assoc($sql)){

/* These informations are the same for each row returned. */
$this->id = $result['flight_id'];
$this->name = $result['flight_name'];
$this->status = $result['status'];
$this->invitationDelay = $result['invitation_delay'];

/*
* These ones are different for each row,
* so we use a flag to differentiate each other.
*/
if($flag != $result['role_id']){

$this->roles[] = array(
'name' => $result['role_name'],
'description' => $result['role_description'],
'hierarchy' => $result['hierarchy'],
'quantityConstraint' => $result['quantity_constraint'],
'canEditPrivileges' => $result['can_edit_privileges'],
'canInvite' => $result['can_invite'],
'canDischarge' => $result['can_discharge'],
'votingValue' => $result['voting_value'],
);
$flag = $result['role_id'];
}
}
}
else { throw new
Exception("Chosen Flight doesn't exist."); }
}

}


Je vous remercie d'avance, Smile


Sephi-Chan, qui va faire plusieurs petites méthodes en attendant…


RE: Une requête qui sélectionne un maximum de choses ou plusieurs petites ? - denisc - 24-03-2008

Si tout est nécessaire pour la création de ton/tes objets, une seule requete. Moins tu auras de requêtes, plus tu ira vite...

Par contre, s'il te plait, supprime-moi ces saloperies de *... Nomme tous tes champs !!! Là, tu gagnera en lisibilité, et tu pourra même ne pas récupérer les champs qui, finalement, ne sont pas nécessaires à la construction!


P.S.: Exception("Chosen Flight doesn't exist.");


RE: Une requête qui sélectionne un maximum de choses ou plusieurs petites ? - Sephi-Chan - 24-03-2008

Oui, les * sont remplacés (même si au final je prends tout quand même, mais ça me permet de garder en tête le nom de mes champs), c'était pour ne pas prendre trop de place inutile sur ce sujet. Smile

J'ai quand même opté pour plusieurs requêtes car c'était vraiment le bordel de traiter un jeu de résultat issu de 3 ou 4 jointures. Le temps que je perds à faire plusieurs requêtes SQL, je le gagne en n'ayant pas à traiter le jeu de résultat avec PHP. En plus, il y a probablement moyen d'optimiser ça en utilisant des requêtes préparées.

Je fais donc le choix de la lisibilité plutôt que de la performance (et encore, je ne suis pas sûr d'y perdre beaucoup).


Sephi-Chan, Merci pour la faute sur does, c'est une erreur de con que j'ai fais là… Smile


RE: Une requête qui sélectionne un maximum de choses ou plusieurs petites ? - Valter - 24-03-2008

Ce n'est pas la seule : These ones are different.

Quant à ton code, moi je ferais une approximation en pourcentage mégacomplexe, lol Big Grin Big Grin

Si tu utiliseras plus de 75% des éléments, fait une grosse sinon une petite.
C'est comme ça que je fais Wink

Citation :Je fais donc le choix de la lisibilité plutôt que de la performance (et encore, je ne suis pas sûr d'y perdre beaucoup).

Tu es donc fondamentalement opposé de moi, alors ?

Valter,


RE: Une requête qui sélectionne un maximum de choses ou plusieurs petites ? - Sephi-Chan - 24-03-2008

Valter a écrit :Ce n'est pas la seule : These ones are different.
Exact, il semble que l'anglais nocturne ne me réussis pas…

Valter a écrit :Quant à ton code, moi je ferais une approximation en pourcentage mégacomplexe, lol Big Grin Big Grin

Si tu utiliseras plus de 75% des éléments, fait une grosse sinon une petite.
C'est comme ça que je fais Wink
Terrible le calcul ! Tiens, vengeance ! "Si tu utiliseras" n'est-il pas un barbarisme ? :p

Valter a écrit :
Citation :Je fais donc le choix de la lisibilité plutôt que de la performance (et encore, je ne suis pas sûr d'y perdre beaucoup).

Tu es donc fondamentalement opposé de moi, alors ?
T'es super contrariant comme mec, à faire l'inverse de ce que je fais ! Big Grin

Plus sérieusement, je préfère toujours favoriser la lisibilité, surtout pour un script qui est amené à beaucoup évoluer. Et je pense de manière générale que c'est la bonne démarche, même si je n'irai pas jusqu'à me considérer comme une référence malgré mon génie et ma modestie… Ou pas.


Sephi-Chan


RE: Une requête qui sélectionne un maximum de choses ou plusieurs petites ? - naholyr - 24-03-2008

Faites des benchmarks pour tester, vous seriez parfois surpris.
Il n'est pas rare que faire plusieurs petites requêtes au lieu d'une grosse (qu'il faut décortiquer derrière) soit bien plus performant.

Ça dépend de la situation, mais dans le doute : testez (c'est tout con hein : echo microtime(); for ($i=0; $i<100; $i++) { /* mon code ici */ } echo microtime(); et on regarde la différence entre les deux versions de "mon code ici" Tongue).

Sinon, pour le problème de la lisibilité et des requêtes, j'ai définitivement réglé le problème ainsi : Toute requête devrait pouvoir être placée dans une fonction, et si je n'arrive pas à trouver un nom à cette fonctoin, c'est que le rôle de ma requête n'est pas clair et qu'elle doit donc être revue (quite à être divisée).
Et d'ailleurs je ne m'en prive pas, j'ai réellement pris pour habitude de mettre mes requêtes dans des fonctions (en réalité, des méthodes statiques) à part, ainsi dans mes actions (pages, méthodes d'objet, etc...) aucune requête SQL n'apparait, seulement de la logique "métier".


RE: Une requête qui sélectionne un maximum de choses ou plusieurs petites ? - Sephi-Chan - 24-03-2008

J'avoue ne pas avoir fais de benchmarks… Mon MacBook Pro est si puissant qu'il me donne des résultats négatifs… Confusediffle:

Plus sérieusement, je pense que les résultats trouvés en local ne sont pas fiable, et ensuite ça dépend des hébergeurs. Certains font tourner MySQL sur la même machine que celle qui fait tourner Apache, alors que d'autres ont deux serveurs différents. M'enfin, ça permet toujours de se faire une idée globale. Smile

Par contre, Naholyr, je ne comprends pas l'utilité de sortir les requêtes SQL des méthodes de tes objets, ni la façon de le faire concrètement, puisque ces requêtes nécessitent bien souvent des paramètres liés à l'objet en lui même. Peux-tu expliquer ça plus en détail (en privé, si tu estimes que ça sort du cadre de ce sujet) ?

Merci bien,


Sephi-Chan


RE: Une requête qui sélectionne un maximum de choses ou plusieurs petites ? - naholyr - 25-03-2008

Et bien dans ton exemple, au lieu de faire la requête directement dans le constructeur :
Code PHP :
<?php 
$query
= sprintf(
"SELECT F.*, R.*
FROM flights F
LEFT JOIN flight_has_role FHR ON FHR.flight_id = F.flight_id
LEFT JOIN roles R ON R.role_id = FHR.role_id
WHERE F.flight_id = %d;"
,
(int)
$flight
);
$sql = mysql_query($query);

if(
mysql_num_rows($sql) >= 1) {

$flag = NULL;
while(
$result = mysql_fetch_assoc($sql)){
...
}
}

On appelle une méthode qui fait est chargée de faire cette requête :
Code PHP :
<?php 
$results
= getFlightInfo($flight);

if (
count($results)) {
foreach (
$results as $result) {
...
}
}

Et la requête est donc déportée dans une tierce fonction :
Code PHP :
<?php 
function getFlightInfo($flight_id) {
return
SQL_Select(sprintf(
"SELECT F.*, R.*
FROM flights F
LEFT JOIN flight_has_role FHR ON FHR.flight_id = F.flight_id
LEFT JOIN roles R ON R.role_id = FHR.role_id
WHERE F.flight_id = %d;"
,
(int)
$flight
));
}

// Et éventuellement, au cas où tu n'aies pas compris :
function SQL_Select($query) {
$sql = mysql_query($query);

$results = array();
while(
$result = mysql_fetch_assoc($sql)){
$results[] = $result;
}

return
$results;
}



Quels intérêts ?
-> Les requêtes ne sont pas éparpillées partout dans le code, elles sont toutes dans le même set de fonctions (plus facile de faire la mise à jour en cas de modification de la structure des tables).
-> Le code "métier" n'est pas pollué par du SQL, on appelle une fonction avec un nom clair, et on a donc un code plus lisible (et au passage, le jour où "getFlightInfo" doit aller chercher dans du SQL ou autre connerie, on peut le faire en changeant juste cette fonction).
-> Les requêtes SQL réutilisables le sont réellement, pas de vilain copier-coller de SQL d'un bout de projet à l'autre Smile


This is named "refactoring" Wink


RE: Une requête qui sélectionne un maximum de choses ou plusieurs petites ? - Sephi-Chan - 25-03-2008

Ouais… Ça commence à faire vraiment beaucoup de séparation. L'idée est bonne et intéressante, mais où se trouvent ces fonctions ? Elles appartiennent à un genre d'objet global ?


Sephi-Chan


RE: Une requête qui sélectionne un maximum de choses ou plusieurs petites ? - naholyr - 25-03-2008

En réalité je travaille toujours avec des objets qui représentent mes tables (des "Data Access Objects") et ces fonctions sont donc des méthodes statiques des classes correspondantes.
Dans Symfony, ce serait une méthode de la classe FlightPeer Wink

La couche d'abstraction SQL, elle, est une librairie tierce.


Note :
La séparation en fonctions atomiques comme ça peut faire peur au début, on trouve ça chiant à débugger parce qu'il faut sauter de fichier en fichier pour trouver la source, et on se dit que ça craint parce qu'il y a un milliard de fichiers à charger et que ça plombe les ressources.
En réalité on finit par utiliser un vrai éditeur (genre Eclipse) qui permet de simplifier ces "sauts" (ctrl+click sur nom d'une fonction et hop on arrive à sa déclaration), et le problème du nombre de fichiers est un faux problème car entre 3 petits fichiers et un gros, le disque ne travaille pas bien plus que ce soit dans un cas ou dans l'autre...

Mais surtout à la fin on se rend compte combien une fonction de 10 lignes est plus simple à débugger qu'une fonction de 50 lignes Smile C'est simple chez moi mon éditeur m'affiche 25 lignes de code sur une page, si je n'arrive pas à afficher toute ma fonction sur une page alors je sais qu'il y a de grandes chances qu'il faille refactoriser tout ça Tongue (pas toujours hein, juste très souvent), c'est à dire éclater ma grosse fonction en de petites fonctions "atomiques" (ne fais qu'une chose, mais fais-la bien, ça vous rappelle quelque chose au niveau philosophie de développement ?).