Précédent   Forum des professionnels en informatique > PHP > Langage > Syntaxe
Syntaxe Forum d'entraide sur la syntaxe de PHP et la POO. Avant de poster -> FAQ syntaxe, Cours d'initiation et cours de POO
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 24/06/2008, 09h34   #1
Membre du Club
 
Inscription : février 2007
Messages : 117
Détails du profil
Informations forums :
Inscription : février 2007
Messages : 117
Points : 45
Points : 45
Par défaut [POO] Requêtes croisées

Bonjour,

Voici la situation dans laquelle je suis, l'état de mon raisonnement et mes questions ...

J'aimerai considérer chacune de mes tables comme étant des objets et donc, pour chacune de mes tables, créer une classe contenant une fonction pour le SELECT, une fonction pour le INSERT, etc.

Bien entendu, mes tables sont relationnelles. Exemple :

TABLE langue est composée des champs :
- id
- code
- langue
- statut

TABLE statut est composée des champs :
- id
- statut

Comme vous l'aurez comprit, le champ statut dans langue contient un ID renvoyant vers l'id de la table statut. Je n'ai par contre pas envie de trop lier mes 2 tables dans une seule et même classe.

Ma réflexion était donc la suivante :

- je crée une classe LANGUE avec une fonction SELECT qui fait (en gros) un SELECT * FROM langue et qui stocke le tout dans un array ;
- je crée une classe STATUT avec une fonction SELECT qui fait (en gros) un SELECT * FROM statut et qui stocke également le tout dans un array ;
- je crée une troisième classe avec une fonction qui effectuerai un "croisement" entre ces 2 tableaux pour permettre de générer un nouveau tableau qui donnerait comme résultat l'équivalent d'une requête comme celle-ci :

Code :
1
2
3
4
 
SELECT langue.id AS id, langue.code AS code, langue.langue AS langue, statut.statut AS statut 
FROM langue, statut 
WHERE langue.statut = statut.langue
et qui retournerait donc quelque chose comme :

1 FR Français OK
2 EN Anglais NOK
3 NL Néerlandais OK
...

Mes questions :

1. est-ce que ma réflexion "orientée objet" vous semble cohérente ou est-il tout simplement préférable de faire, dans ma fonction SELECT de ma classe LANGUE, la query complète ?

2. si ma réflexion est correcte, est-il possible de générer ce "croisement" de requête ?

3. en travaillant de la sorte (en gros, faire une query globale et faire un tri ou un filtre éventuel sur les résultats par la suite) ; n'est-ce pas un risque de ralentir le site puisqu'on ne fait aucun filtre dans la requête SQL elle-même ?

4. avez-vous une meilleure idée :-) ?

Merci d'avance,
Olivier
oranocha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 09h43   #2
Membre éprouvé
 
Inscription : février 2005
Messages : 401
Détails du profil
Informations personnelles :
Âge : 27

Informations forums :
Inscription : février 2005
Messages : 401
Points : 450
Points : 450
Question fort intéressante, je suis confronté au même dilemme chaque fois que je créé un nouveau site.

Une piste que j'ai peut être trouvé (mais pas encore exploitée à fond par manque de temps ) serait l'utilisation de requêtes écrites par un objet (voir le package Zend_Db_Select de Zend Framework).
ça permettrait de détecter dans la méthode de chargement qu'on essaye de faire une jointure sur une autre table et qu'on a donc besoin de remplir des attributs supplémentaires.

Bon dit comme ça ce n'est peut être pas très clair mais vu que c'est encore en réflexion dans ma tête j'arrive pas à faire mieux pour le moment
Sylvain71 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 09h56   #3
Membre du Club
 
Inscription : février 2007
Messages : 117
Détails du profil
Informations forums :
Inscription : février 2007
Messages : 117
Points : 45
Points : 45
Mais donc l'idée en POO est meilleure de ne pas inclure dans le SELECT de ma classe LANGUE des éléments de requête d'une autre table ... histoire de ne pas lier ces 2 tables ensemble et donc de permettre une modif de la structure des tables sans devoir passer dans chacune des classes ou peut-on tout de même le faire ?

Enoncé comme cela, ça me parait logique ...

Mais je ne vois pas comment faire pour, au départ de 2 tableaux :

- 1 contenant les infos de la table statut :
ID STATUT
1 OK
2 NOK

- 1 contenant les infos de la table langue
ID CODE LANGUE STATUT
1 FR Français 1
2 EN Anglais 2

Obtenir un seul tableau faisant la jointure ...

Et puis, toujours ce problème de performance DB ! Dans ces exemple, on fait un SELECT global sur des tables contenant peu d'info donc pas de souci ... mais que donnerait un SELECT global dans une table contenant des miliers de records ?

Doit-on dans ce cas, prévoir x fonctions de SELECT différente dans mes classes pour prévoir les différentes logiques applicatives (tantôt, j'aurai besoin de faire un filtre sur le code langue, tantôt sur le statut, etc.) ?

Désolé pour toutes ces questions, mais venant du monde "procédural" ... c'est un grand changement pour moi :-)
oranocha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 10h12   #4
Membre éprouvé
 
Inscription : février 2005
Messages : 401
Détails du profil
Informations personnelles :
Âge : 27

Informations forums :
Inscription : février 2005
Messages : 401
Points : 450
Points : 450
Oui c'est sûr que pour faire quelque chose de parfait, les éléments de ta table2 n'auraient rien à faire dans la classe correspondant à ta table1.

J'avais fais quelque chose comme ça à une époque mais niveau performances j'explosais tout (pas dans le bon sens...)

L'idée d'utiliser un composant tel que Zend_Db_Select pour l'écriture des requêtes permet justement de rendre celles-ci modulables. Ce qui peut déjà aider pour la réalisation de filtres simples.

Ensuite pour l'histoire des jointures, il faudrait que ta table1 ait un moyen de connaitre le nom des attributs de la table2 ($table2->getColNames() ???). Comme ça si tu détectes une jointure sur la table2 dans ta requête, tu vas chercher la liste des attributs nécessaires automatiquement.

Comme ça chaque classe conserve bien en son sein seulement ses attributs à elle et pas ceux des autres. Il faut juste qu'elle sache communiquer avec les autres pour leur demander des informations sur leur structure.

C'est marrant dit comme ça ça parait super simple mais quand je vais rentrer chez moi ce soir et me mettre à coder un truc comme ça je suis sûr que je vais encore me poser 200 questions à la seconde
Sylvain71 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 11h11   #5
Membre Expert
 
Homme
Inscription : janvier 2004
Messages : 1 238
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Secteur : Finance

Informations forums :
Inscription : janvier 2004
Messages : 1 238
Points : 1 421
Points : 1 421
Zend_Db_Select a l'air d'avoir ses limites... j'ai du mal a voir comment générer quelque chose comme :
Code :
1
2
3
4
5
select count(*)
from t1, t2
where t1.champ1 = 1
AND (t1.champ2 = 2 OR t1.champ3 = 3)
OR (t2.champ1 = t1.champ4)
Bref :

* Le parenthesage des conditions AND OR
* Les clauses de jointures dans un OR

Il est possible de faire un framework permettant de gérer tout ca... j'suis en train d'en faire un ^^ mais c'est chaud en effet.
Une idée intéressante c'est le concept de RELATION entre les objets.

2 objets(=table) "liés" le sont au niveau structurel (par une contrainte dans la base) ou au niveau fonctionnel (dans l'esprit du developpeur) mais dans les deux cas, ce lien peut etre exprimé dans le code.
Ainsi, il n'est pas necessaire d'indiquer explicitement la clause de jointure, on peut faire quelque chose comme :
Code :
1
2
3
$aSelect = new SelectQuery($monObject1);
$aSelect2 = new SelectQuery($monObject2);
$aSelect->join($monObject2);
voir :
Code :
$aSelect->join($monObject2, $monObject1::RELATION_1_WITH_OBJECT2);
dans le cas ou les objets ont plusieurs liens différents entre eux.

Le framework peut alors ajouter automatiquement la clause de jointure.

On peut aussi imaginer quelque chose comme :
Code :
$aSelect->WhereAnd($monObject1, $monObject2, NOM_RELATION);
pour gérer les clauses de jointures dans un OR
__________________
PHP :
Regle n°1 : mysql_query(...), mysql_connect(...) et mysq_select_db(...) doivent EN DEBUG etre suivies de or die(mysql_error()); (mais jamais en production)
Regle n°2 : Mieux encore : mysql_query($requete) or die("$requete<br/>".mysql_error());
Regle n°3 : echo '<pre>';var_dump($var);echo '</pre>'; affiche le contenu et le type d'une variable.
Publiez vos textes de fantasy et de science-fiction sur http://www.cercledefaeries.com/concours/
Fladnag est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 11h33   #6
Membre éprouvé
 
Avatar de SphynXz
 
Développeur Web
Inscription : mars 2008
Messages : 439
Détails du profil
Informations personnelles :
Âge : 26

Informations professionnelles :
Activité : Développeur Web

Informations forums :
Inscription : mars 2008
Messages : 439
Points : 474
Points : 474
je pense qu'avec une très bonne notion des fonctions de tableaux tu pourra arriver à tes fins très aisément .

La création d'une classe abstraite qui sera décliner pour toute tes tables. une jointures de deux select ne se ferait qu'avec un array_intersect() par exemple
__________________
I don't know what will be used in the next world war, but the 4th will be fought with stones. - Albert Einstein
Pour détourner un avion, il faut monter dedans - Frédéric beigbeder
SphynXz est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 11h39   #7
Membre éprouvé
 
Inscription : février 2005
Messages : 401
Détails du profil
Informations personnelles :
Âge : 27

Informations forums :
Inscription : février 2005
Messages : 401
Points : 450
Points : 450
Oui ça je te l'accorde Zend_Db_Select a ses limites. D'ailleurs je ne l'utilise pas, j'ai mon truc perso pour ça (qui n'est pas forcément mieux mais plus adapté à mes besoins). Je l'ai juste donné à titre d'exemple vu qu'il est assez répandu et qu'il y a une bonne doc dessus pour expliquer.

Ensuite pour le reste on est d'accords, ta réflexion semble rejoindre la mienne dans le fond mais tu as eu plus de courage que moi et tu es plus rentré dans les détails

Donc en gros si je comprend bien, tes objets seraient capables de fournir des informations sur la structure de leurs tables correspondantes (attributs, clé étrangères ...) et tu aurais un objet "SelectQuery" qui te monterait une requête à partir de ça. C'est ça ?

L'idée me semble pas mal mais la question que je me pose c'est qu'est ce que va te retourner ton objet SelectQuery après son exécution ? Un objet "ligneTable1", un objet "ligneTable2" ?
Sylvain71 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 11h51   #8
Membre éprouvé
 
Inscription : février 2005
Messages : 401
Détails du profil
Informations personnelles :
Âge : 27

Informations forums :
Inscription : février 2005
Messages : 401
Points : 450
Points : 450
Une petite chose au passage : ce qu'on est en train d'essayer de faire ne correspond il pas tout simplement au design pattern "active record" ?
Sylvain71 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 12h07   #9
Membre Expert
 
Homme
Inscription : janvier 2004
Messages : 1 238
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Secteur : Finance

Informations forums :
Inscription : janvier 2004
Messages : 1 238
Points : 1 421
Points : 1 421
en fait j'ai des select spécialisés du genre (les classes sont générées automatiquement avec un meta fichier contenant le schema ;o) :

$aSelect = new SelectObject1();
$aSelect2 = new SelectObject2();

// cas "simple"
$aSelect->whereAnd($aSelect->getChamp1(), OPERATEUR_EGAL, $aSelect2->getChamp1());

// join automatique
$aSelect->whereAndJoin($aSelect2->getRelation1());

Pour ce qui est du type de retour, ca depend de la methode utilisée pour interroger la base.
Ca peut etre un objet (completement ou partiellement chargé)
Ca peut etre un tableau indexé (si les données du select proviennent de plusieurs tables, ou contiennent des count(), max(), etc...)

Enfin rien ne m'empeche de "reconstruire" facilement un objet a partir du tableau indexé ensuite ;o)

Les parentheses sont gérés par l'ajout de select :

$aSelect = new SelectObject1();
$aSousSelect = new SelectObject1();
$aSousSelect->whereOr(..., ..., ...); // condition1
$aSousSelect->whereOr(..., ..., ...); // condition2
$aSelect->whereAnd(..., ..., ...); // condition3
$aSelect->whereAnd($aSousSelect);

va générer
WHERE condition3 AND (condition1 OR condition2)

je ne connais pas le pattern "active record", faudrait que je me renseigne ;o)
Edit : oui, c'est un peu ca (apres m'etre renseigné ^^)
__________________
PHP :
Regle n°1 : mysql_query(...), mysql_connect(...) et mysq_select_db(...) doivent EN DEBUG etre suivies de or die(mysql_error()); (mais jamais en production)
Regle n°2 : Mieux encore : mysql_query($requete) or die("$requete<br/>".mysql_error());
Regle n°3 : echo '<pre>';var_dump($var);echo '</pre>'; affiche le contenu et le type d'une variable.
Publiez vos textes de fantasy et de science-fiction sur http://www.cercledefaeries.com/concours/
Fladnag est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 14h53   #10
Membre du Club
 
Inscription : février 2007
Messages : 117
Détails du profil
Informations forums :
Inscription : février 2007
Messages : 117
Points : 45
Points : 45
et par rapport au fait de faire une query globale et d'ensuite faire un tri dans le tableau ...

qu'en penser au niveau performance ?

est-il préférable de créer des fonctions de SELECT différentes selon la logique applicative désirée ou peut-on imaginer taper dans un array, le résultat d'une query globale (genre : SELECT * FROM langue) et ensuite faire des recherches et des filtres dans le tableau ?

merci encore :-)
oranocha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 15h04   #11
Membre éprouvé
 
Inscription : février 2005
Messages : 401
Détails du profil
Informations personnelles :
Âge : 27

Informations forums :
Inscription : février 2005
Messages : 401
Points : 450
Points : 450
Comme je l'ai dis, un query object est bien utile pour faire ce genre de chose. Parce que tu peux très bien imaginer une méthode de chargement qui prend en paramètre des "parts de requête" contenant des instructions de filtre ou de tri.

Par exemple quelque chose dans le style :

Code :
1
2
3
4
5
$options = new SelectPart();
$options->orderBy('nom', 'DESC');
$options->limit(0, 10);
 
$mesObjets = MaClasse::getList($options);
ça offre un peu plus de modularité. Bon ensuite je ne suis pas sûr que ce soit conceptuellement correct d'avoir des traitements direct sur la couche données au milieu du reste comme ça. Donc il va peut être falloir rajouter quelque chose entre tout ça, mais dans les grandes lignes ça doit ressembler à mon petit bout de code là.

En tout cas tout charger dans un tableau et faire les filtres après je pense que c'est très très mal. Si tu as 10 enregistrements voire 1000 ça va, mais si tu as une base de données gigantesque ça va faire très mal
Sylvain71 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 16h06   #12
Membre du Club
 
Inscription : février 2007
Messages : 117
Détails du profil
Informations forums :
Inscription : février 2007
Messages : 117
Points : 45
Points : 45
Bon j'avoue, j'utilise quasi pas les tableaux et j'ai dur dur au début ...

j'ai ceci :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
class Database {
 
	var $query;
	var $result;
	var $result_arr;
 
	function query($q) {
		$this -> query = mysql_query($q);
 
		return $this -> query;
	}
 
	function result($r) {
		while ($result = mysql_fetch_array($r)) {
			$result_arr = $result;
		}
 
		return $result_arr;
	}
 
}
et ceci est mon fichier test :

Code :
1
2
3
4
5
6
7
 
$database = new Database();
 
$select = "SELECT * FROM langue";
$result = $database -> result($database -> query($select));
 
print_r($result);
le résultat du print_r donne ceci :

Array ( [0] => 1 [id] => 1 [1] => FR [code] => FR [2] => Français [langue] => Français [3] => Y [defaut] => Y [4] => 1 [statut] => 1 )

Pq ai-je chaque fois les trucs en double ... j'ai donc [0] => 1 et ensuite [id] => 1 ... idem, j'ai d'abord [1] => FR et ensuite [code] => FR

comment faire pour avoir uniquement le [id] => 1, [code] => FR, etc. ?

merci
oranocha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 16h11   #13
Membre Expert
 
Homme
Inscription : janvier 2004
Messages : 1 238
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Secteur : Finance

Informations forums :
Inscription : janvier 2004
Messages : 1 238
Points : 1 421
Points : 1 421
Citation:
Code :
1
2
3
4
5
6
7
	function result($r) {
		while ($result = mysql_fetch_array($r)) {
			$result_arr = $result;
		}
 
		return $result_arr;
	}
Tu écrase $result_arr a chaque fois, ce n'est pas correct.
fetch_array renvoie 2 fois le resultat, une fois avec un index numérique, une fois avec un index correspondant au nom de la colonne. _fetch_assoc est mieux ;o) (uniquement le nom de la colonne)

Code :
1
2
3
4
5
6
7
8
	function result($r) {
		$result_arr = array();
		while ($result = mysql_fetch_assoc($r)) {
			$result_arr[] = $result;
		}
 
		return $result_arr;
	}
__________________
PHP :
Regle n°1 : mysql_query(...), mysql_connect(...) et mysq_select_db(...) doivent EN DEBUG etre suivies de or die(mysql_error()); (mais jamais en production)
Regle n°2 : Mieux encore : mysql_query($requete) or die("$requete<br/>".mysql_error());
Regle n°3 : echo '<pre>';var_dump($var);echo '</pre>'; affiche le contenu et le type d'une variable.
Publiez vos textes de fantasy et de science-fiction sur http://www.cercledefaeries.com/concours/
Fladnag est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 24/06/2008, 16h12   #14
Membre éprouvé
 
Inscription : février 2005
Messages : 401
Détails du profil
Informations personnelles :
Âge : 27

Informations forums :
Inscription : février 2005
Messages : 401
Points : 450
Points : 450
Bon alors je vois carrément pas le lien entre ton bout de code et le sujet mais c'est pas grave

Remplace :
mysql_fetch_array

par
mysql_fetch_assoc
Sylvain71 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 26/06/2008, 12h29   #15
Expert Confirmé Sénior
 
Avatar de GrandFather
 
Inscription : mai 2004
Messages : 4 490
Détails du profil
Informations personnelles :
Âge : 42

Informations forums :
Inscription : mai 2004
Messages : 4 490
Points : 5 049
Points : 5 049
Citation:
Envoyé par Fladnag Voir le message
Zend_Db_Select a l'air d'avoir ses limites... j'ai du mal a voir comment générer quelque chose comme :
Code :
1
2
3
4
5
select count(*)
from t1, t2
where t1.champ1 = 1
AND (t1.champ2 = 2 OR t1.champ3 = 3)
OR (t2.champ1 = t1.champ4)
Ca ne pose pas de soucis particuliers, le framework prend soin de placer des parenthèses autour de chaque composant de la clause where. Ta requête générant un Zend_Db_Select s'écrira donc :
Code :
1
2
3
4
5
6
$select = $db->select()
             ->from(array('t1', 't2'),
                    array('COUNT(*)'))
             ->where('t1.champ1 = 1')
             ->where('t1.champ2 = 2 OR t1.champ3 = 3')
             ->orWhere('t2.champ1 = t1.champ4');
La façon dont Zend a implémenté le pattern ActiveRecord dans Zend Framework vaut le coup qu'on y regarde précisément avant de se lancer dans le développement de son propre framework DB, car c'est tout sauf une tâche triviale.
__________________
FAQ XML
------------
« Le moyen le plus sûr de cacher aux autres les limites de son savoir est de ne jamais les dépasser »
Giacomo Leopardi
GrandFather est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/07/2008, 14h39   #16
Membre du Club
 
Inscription : février 2007
Messages : 117
Détails du profil
Informations forums :
Inscription : février 2007
Messages : 117
Points : 45
Points : 45
Voila ...

Ce point est résolu pour ma part en tout cas ;-)

J'ai développé 2 classes qui me permettent de gérer et de générer des requêtes SELECT de manière "automatisée" et surtout qui sont capables de prendre en compte la structure de ma DB.

Un peu long à expliquer ici mais j'essayerai de prendre le temps un jour ;-)
oranocha est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 03h25.


 
 
 
 
Partenaires

Hébergement Web