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 26/01/2011, 23h14   #1
Invité de passage
 
Inscription : avril 2010
Messages : 15
Détails du profil
Informations forums :
Inscription : avril 2010
Messages : 15
Points : 4
Points : 4
Par défaut POO, sql et optimisation : mauvais ménage ?

Bonjour,

J'ai eu l'occasion d'intervenir, pendant un stage, sur une application existante en PHP associant POO et base de données.

En gros (en simplifiant), on a une classe par table, chaque classe représentant une table. Par exemple, on a les tables News(id_news, titre, contenu, #id_categorie) et Categorie(id_categorie, libelle).

On se retrouve donc avec des classes de ce type :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class News{
	private $id_news;
	private $titre;
	private $contenu;
	private $id_categorie;
	// méthodes habituelles
	public static function getTousLesArticles()
	// fait une requête dans la base de données,
	// récupère tous les articles
	// instancie chaque objet, le stocke dans un tableau
	// retourne une collection d'articles (=tableau)
}
class Categorie{
	private $id_categorie;
	private $libelle;
	// méthodes habituelles
        // le constructeur prend en paramètre l'id de la catégorie et renseigne les attributs à partir d'une requête sql
}
A côté, on a notre page afficher_articles.php, avec un truc du genre :
Code :
1
2
3
4
5
$maCollection = News::getTousLesArticles();
foreach($maCollection as $monArticle){
	$maCategorie = new Categorie($monArticle->getIdCategorie());
        echo $monArticle->getTitre().' - rubrique : '.$maRubrique->getLibelle();
}
Ce qui me choque : le nombre de requêtes qui seront effectuées dans notre fichier "afficher_articles.php" : on aura, en effet, nbArticles*1 requête pour récupérer les catégories de chaque article ! Et encore, mon cas est simplifié, car dans ce que j'ai pu voir, on avait parfois l'instanciation de bien plus d'objets (dans cet exemple, on avait par exemple l'instanciation de l'auteur... soit x requêtes de plus).

En procédural, on se serait contenté d'une seule requête avec jointure...

Comment procédez-vous sur des cas comme ça ?

Bonne soirée.
floriann est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 26/01/2011, 23h43   #2
Expert Confirmé
 
Avatar de rawsrc
 
Homme Martin
Dev indep
Inscription : mars 2004
Messages : 1 461
Détails du profil
Informations personnelles :
Nom : Homme Martin
Âge : 35
Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

Informations professionnelles :
Activité : Dev indep

Informations forums :
Inscription : mars 2004
Messages : 1 461
Points : 2 548
Points : 2 548
Envoyer un message via Skype™ à rawsrc
Salut floriann,

Ai première vue je dirais un peu comme toi : c'est vachement lourd pour rien.
Je pense que la fonction getTousLesArticles() doive se charger de récupérer en une seule passe tous les articles et les catégories correspondantes.
En sortie tu récupère un tableau associatif et le tour est joué.

Donc je serais toi, je modifierai la requête sous jacente de getTousLesArticles().
rawsrc est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 26/01/2011, 23h44   #3
Membre chevronné
 
Inscription : mars 2005
Messages : 583
Détails du profil
Informations forums :
Inscription : mars 2005
Messages : 583
Points : 651
Points : 651
Hello,

Question de logique. Une catégorie est un ensemble d'éléments et ces éléments sont des articles. C'est un peu l'inverse qui est implémenté ici.

Il serait donc plus logique d'avoir quelque chose de ce genre :

Code :
$mesArticles = $maCategorie->getArticles();
Maintenant, pour la suite de ton questionnement des classes du style Catégorie, News, Auteur ont elles "besoin" d'un rapport quelconque avec le système d'enregistrement ? Si tu implémentes tes classes pour qu'elles tapent dans une DB X et que demain tu dois réutiliser le même système pour taper dans une DB Y ou un fichier plat, tu réécris tes classes ?

Idéalement l'accès aux données devrait se faire de l'"extérieur" de ce genre de classe. Ce qui te permettrait de limiter le nombre de requêtes de la même manière qu'en procédural.
__________________
Pourfendeur de singletons en croisade
Petibidon est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/01/2011, 00h09   #4
Modérateur
 
Avatar de Benjamin Delespierre
 
Benjamin Delespierre
Développeur Web
Inscription : février 2010
Messages : 2 984
Détails du profil
Informations personnelles :
Nom : Benjamin Delespierre
Âge : 24
Localisation : France

Informations professionnelles :
Activité : Développeur Web
Secteur : High Tech - Opérateur de télécommunications

Informations forums :
Inscription : février 2010
Messages : 2 984
Points : 5 015
Points : 5 015
Mon grain de sel: utilise des adaptateur
http://badger.developpez.com/tutorie...ns/adaptateur/

Le but du jeu c'est d'avoir des objets modèles indifférents au type de média qu'ils utilisent.

Donc en d'autres termes, quand tu vas construire ta couche modèle, il faut voir ça comme une pile: les objets de la couche supérieure ne savent pas comment travaillent les objets de la couche en dessous d'eux et ainsi de suite, chacun s'occupe de ce qu'il à a faire et délègue ce qu'il ne sait pas faire à ses copains du dessous.

Si tu ne veux pas te lancer la dedans, tu as de très bon projets qui te mâchent le travail: les ORM (Object Relationnal Mapper) qui sont des couches d'abstraction à destination des SGBD relationnelles (99.99% des usages de PHP) capables de comprendre la structure de ton modèle de données relationnel et de te construire les objets qui vont bien. On citera les 2 gagnants du moment:
- Propel http://www.propelorm.org/
- Doctrine http://www.doctrine-project.org/

Après y'a toujours la solution "je-suis-un-goret-qui-mets-des-requêtes-sql-dans-des-vues" mais vu que tu a l'air de montrer un intérêt pour la POO en PHP je crois comprendre que tu n'en est pas

@Petibidon
Citation:
Pourfendeur de singletons en croisade
Arise my minions, let's __destruct his instances !
__________________
A la recherche d'un framework MVC facile a prendre en main ? Essayez Axiom
Nouveau: la référence d'Axiom est disponible sur GitHub (je la peaufine en ce moment même).

Un problème correctement identifié est à moitié résolu, évitez de poster l'intégralité de votre code avec pour seule explication "ça ne marche pas...".
Pour identifier correctement vos problèmes PHP, utilisez la gestion des erreurs et xdebug.

Les boutons et existent, servez-vous en
Benjamin Delespierre est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/01/2011, 08h43   #5
Expert Confirmé
 
Avatar de grunk
 
Homme Olivier
Développeur Web
Inscription : août 2003
Messages : 1 837
Détails du profil
Informations personnelles :
Nom : Homme Olivier
Âge : 27
Localisation : France, Côte d'Or (Bourgogne)

Informations professionnelles :
Activité : Développeur Web
Secteur : Industrie

Informations forums :
Inscription : août 2003
Messages : 1 837
Points : 3 318
Points : 3 318
La solution de Benjamin Delespierre est la plus en adéquation avec ton code , mais nécessitera pas mal de modification pour un gain en performance pas fulgurant. Les ORM quoi que l'on en dise restent très lent comparé à une simple requête bien conçue.

Je suis plus partisan d'avoir une méthode qui retourne tous tes articles avec les jointures adéquates pour avoir toutes les infos. Surtout que dans ton cas si le but est d'afficher le nom de la catégorie associée ça ne vaut peut être pas la peine de créer un objet, faire une requête et appeler une méthode pour chaque élément.
grunk est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/01/2011, 19h08   #6
Invité de passage
 
Inscription : avril 2010
Messages : 15
Détails du profil
Informations forums :
Inscription : avril 2010
Messages : 15
Points : 4
Points : 4
Tout d'abord, merci d'avoir pris le temps de me répondre

@Petibidon
D'accord avec toi sur le manque de logique de l'emplacement de la méthode Mais même avec cette organisation, on en reviendra de toute façon au même point, car on aura toujours besoin d'un $monUtilisateur = new Utilisateur($monArticle->getIdUtilisateur()); pour avoir les informations que chaque utilisateur
Par contre, tu soulèves un point intéressant sur la séparation DB/classes (voir fin de mon message)

@Benjamin Delespierre
J'ai jeté un coup d'œil sur les adaptateurs mais j'avoue que ça reste très flou pour moi, surtout pour l'implémenter dans une problématique de base de données et d'économie de requêtes. Si tu avais un exemple, ça serait super !

@grunk
Dans le code auquel je fais référence, le fonctionnement est parfois comme décrit dans mon premier message, et parfois comme ce que tu préconises (une méthode qui retourne toutes les informations de la requête). J'avoue que ça permet d'économiser les requêtes, mais je trouve que c'est ne pas respecter l'esprit de la POO... et que ça diminue un peu l'intérêt d'utiliser de la POO selon moi.

...

Cet après-midi, j'ai un peu réfléchi à la façon dont j'aurais personnellement construit le code (je rappelle que le code présenté ci-dessus n'est pas de moi ; j'avais juste dû intervenir dessus et ça m'avait donné envie de réfléchir sur la "bonne" façon de faire...).
L'idée est de se servir des associations. Ainsi, on rajoutera un objet "Categorie" et un objet "Utilisateur" dans notre classe News. Les constructeurs ne chargent plus les données depuis la base de données :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class News{
	private $id_news;
	private $titre;
	private $contenu;
	private $id_utilisateur;
	private $id_categorie;
	private $utilisateur;
	private $categorie;
	// accesseurs
	// mutateurs
	function __construst($unId_news, $unTitre, $unContenu, $unId_utilisateur, $unId_categorie, $unUtilisateur = null, $uneCategorie = null)
	// ...
}
 
class Categorie{
	private $id_categorie;
	private $libelle;
	// accesseurs
	// mutateurs
	function __construct($unId_categorie, $unLibelle)
	// ...
}
Je rajoute la classe Utilisateur et entreprise pour que mon exemple soit plus compréhensible :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
class Utilisateur{
	private $id_utilisateur;
	private $pseudo;
	private $id_entreprise; // un utilisateur appartient à une entreprise
	private $entreprise // comme sur les autres classes, j'ajoute un objet entreprise
	//...
}
class Entreprise{
	private $id_entreprise;
	private $nom_entreprise;
	//...
}
A côté, on ajoute une classe ManagerArticle, par exemple :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 
class Manager{
	// renvoit une collection d'objets News construite depuis la BD
	public static function getLesArticles(){
		$requete = mysql_query('SELECT id_news, titre, contenu, utilisateur.id_utilisateur, categorie.id_categorie, libelle, pseudo, entreprise.id_entreprise, nom_entreprise FROM news INNER JOIN categorie ON categorie.id_categorie=news.id_categorie
INNER JOIN utilisateur ON utilisateur.id_utilisateur=news.id_utilisateur
INNER JOIN entreprise ON entreprise.id_entreprise=utilisateur.id_entreprise') or die(mysql_error());
		$collectionArticles = array();
		while($mesDonnees = mysql_fetch_object($requete)){
			$entreprise = new clEntreprise($mesDonnees->id_entreprise, $mesDonnees->nom_entreprise);
			$utilisateur = new clUtilisateur($mesDonnees->id_utilisateur, $mesDonnees->pseudo, $mesDonnees->id_entreprise, $entreprise);
			$collectionAnomalies[] = new Article($mesDonnes->id_news, $mesDonnes->titre, $mesDonnes->contenu, $mesDonnes->id_utilisateur, $mesDonnes->id_categorie, $utilisateur);
		}
		return $collectionArticles;
	}
 
	// renvoit un objet News correspondant à la news demandée en paramètre
	public static function getUneNews($unId){
		$requete = mysql_query('SELECT id_news, titre, contenu, utilisateur.id_utilisateur, categorie.id_categorie, libelle, pseudo, entreprise.id_entreprise, nom_entreprise FROM news INNER JOIN categorie ON categorie.id_categorie=news.id_categorie
INNER JOIN utilisateur ON utilisateur.id_utilisateur=news.id_utilisateur
INNER JOIN entreprise ON entreprise.id_entreprise=utilisateur.id_entreprise
WHERE id_news='.$unId);
		$mesDonnees = mysql_fetch_object($requete)){
		$entreprise = new clEntreprise($mesDonnees->id_entreprise, $mesDonnees->nom_entreprise);
		$utilisateur = new clUtilisateur($mesDonnees->id_utilisateur, $mesDonnees->pseudo, $mesDonnees->id_entreprise, $entreprise);
		return new Article($mesDonnes->id_news, $mesDonnes->titre, $mesDonnes->contenu, $mesDonnes->id_utilisateur, $mesDonnes->id_categorie, $utilisateur);
}
// veuillez excuser l'absence de contrôles poussés, et les éventuelles erreurs, j'ai fait ça en rédigeant ce message
// je ne me suis pas occupé des Catégories dans ces exemples, c'était déjà assez parlant
Avec cette méthode, on garde un nombre requêtes identique à ce qu'on avait en procédural et on manipule vraiment des objets.

Néanmoins, plusieurs points me laissent perplexe :
- Si on décide de changer de constructeur demain, il faudra modifier toutes ces méthodes statiques... Sur un gros projet, ça pourrait devenir complexe.
- Je trouve qu'il y a une certaine redondance dans l'instanciation des objets. Demain, si je crée une classe Message, où chaque "message" sera posté par un Utilisateur, on aura encore le même travail à faire sur l'instanciation d'Entreprise, puis d'Utilisateur, puis de Message...
- Je me dis aussi que ça risque de devenir compliqué quand on aura X objets à instancier, et qu'il faudra remonter toute la hiérarchie (là, on a déjà entreprise -> utilisateur -> news... on peut imaginer des cas plus complexe où ça risque de devenir une usine à gaz ?)
- On a un structure des classes légèrement différente du modèle relationnel de la BD puisqu'on ajoute les objets correspond aux différents ID (id_catégorie, id_utilisateur...).

Bref, qu'en pensez-vous ?

J'espère avoir été un minimum clair.

Bonne soirée et merci d'avancer pour vos avis toujours très intéressants.
floriann est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 27/01/2011, 19h20   #7
Modérateur
 
Inscription : septembre 2010
Messages : 7 101
Détails du profil
Informations forums :
Inscription : septembre 2010
Messages : 7 101
Points : 8 466
Points : 8 466
avec PDO tu pourrais faire du FETCH_CLASS direct meme avec un FETCH_ALL
__________________
http://blog.stealth35.com/
stealth35 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/01/2011, 09h59   #8
Modérateur
 
Avatar de Benjamin Delespierre
 
Benjamin Delespierre
Développeur Web
Inscription : février 2010
Messages : 2 984
Détails du profil
Informations personnelles :
Nom : Benjamin Delespierre
Âge : 24
Localisation : France

Informations professionnelles :
Activité : Développeur Web
Secteur : High Tech - Opérateur de télécommunications

Informations forums :
Inscription : février 2010
Messages : 2 984
Points : 5 015
Points : 5 015
Heu... Un exemple d'utilisation des adaptateur ? Vu que je suis en train de le réaliser ça va être assez tendax là...

Y'en avait pas dans la doc que je t'ai filé ??

Je peux te fournir quelques signatures si ça peut t'aider :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
 
interface Adaptable { ... }
interface Adapter extends Adaptabe { ... }
 
class MySQLReader extends PDOStatement implements Adapter{ .... }
class FileReader extends File implements Adapter { ... }
 
class MyModelClass implements Adaptable {
   public function attach (Adapter $a) { ... }
   public function refresh () { $this->_adapter->read($config); }
   ....
}
On a besoin de deux interfaces de base, Adaptable, pour les classes pouvant recevoir un (ou plusieurs) adaptateurs, et Adapter pour les classe d'adaptateur (on notera qu'un adaptateur est en fait également un objet adaptable - le cas d'usage c'est un parseur de fichier: on a une classe qui s'occupe de lire le fichier et une autre qui s'occupe de parser son contenu)

Ensuite on crées un Adaptateur de bdd et un adaptateur de fichier, à toi de voir en fonction de tes besoins comment implémenter tout ça.
Et au final on a une class MyModelClass qui peut se voir attacher un adaptateur pour effectuer ses lectures / écritures. Cette classe ne devra porter aucune information relative au média qu'elle utilise sinon l'adaptateur pour ce média.
__________________
A la recherche d'un framework MVC facile a prendre en main ? Essayez Axiom
Nouveau: la référence d'Axiom est disponible sur GitHub (je la peaufine en ce moment même).

Un problème correctement identifié est à moitié résolu, évitez de poster l'intégralité de votre code avec pour seule explication "ça ne marche pas...".
Pour identifier correctement vos problèmes PHP, utilisez la gestion des erreurs et xdebug.

Les boutons et existent, servez-vous en
Benjamin Delespierre est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 29/01/2011, 15h57   #9
Invité de passage
 
Inscription : avril 2010
Messages : 15
Détails du profil
Informations forums :
Inscription : avril 2010
Messages : 15
Points : 4
Points : 4
Hum, c'est vague pour un débutant en POO comme moi. Faut que je réfléchisse à tout ça.

Sinon, personne n'a de remarques/avis sur la proposition de solution que j'ai faite ?
floriann est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 04h45.


 
 
 
 
Partenaires

Hébergement Web