Page 1 sur 1

Problème d'optimisation

Posté : 22 avr. 2009, 12:16
par supercanard
Bonjour,

Je viens de tomber sur un cas pratique pas terrible, qui concerne l'utilisation des méthodes de mes classe, qui est peut être mauvaise, car je constate que ma façon de faire est assez brutale question optimisation.

Pour la faire courte, j'ai une classe article avec ces méthodes :

- creer() ( qui permet de creer un objet article depuis la base de données )
- creerListe() ( qui permet de créer une liste d'article depuis la base de données )
Il faut savoir que creerListe boucle une liste d'id préalablement récupéré et apelle la méthode créer(). C'est lourd en nombre de requête mais évite d'avoir une redondance de code entre ces deux méthodes.

Enssuite, j'ai une classe rss avec une méthode

- creerRssArticle() qui permet de générer un flix rss des articles

Le problème est le suivant :
creerRssArticle fait en fait appel à la méthode creerListe() de la classe article, toujours pour éviter une redondance de code, et parce que elle rempli bien ce rôle, pourquoi donc réécrire du code inutile.
Mais niveau optimisation c'est pas terrible :
On lance en effet une requete, on boucle les résultats, on lance une autre requete au sein de la boucle, le tout pour creer un objet au complet, c'est à dire avec un tas de propriétés que l'on à pas besoin dans la classe rss...


Donc voilà, je suis assez perdu. D'un coté je pense qu'il est logique de faire comme celà, mais d'un autre je me dit que c'est lourd et que je fait les choses mal ?


Je laisse le code de ces 3 méthodes :

Article
public static function creer($value){
		$val = array(':id'=>$value);
		$req = "SELECT * FROM blog_article WHERE idArticle = :id";
		$res = pdo2::getInstance()->prepare($req);
		$res->execute($val);
		$row = $res->fetch(pdo2::FETCH_OBJ);
		$article = new article($row->idArticle);
		$article->idCategorie = $row->idCategorie;
		$article->etat = $row->etat;
		$article->titre = $row->titre;
		$article->titreForUrl = $row->titreForUrl;
		$article->date = $row->date;
		$article->chapo = $row->chapo;
		$article->contenu = $row->contenu;
		$article->optionMarque = $row->marque;
		return $article;
	}
	public static function creerListe($idCategorie, $limitX, $limitY, $etat){
		$val = array(':etat'=>$etat);
		if($idCategorie == 'all' || $idCategorie == null){ // toute catégories confondues
			$req = "SELECT blog_article.*idArticle FROM blog_article 
			JOIN blog_categorie ON  blog_article.idCategorie = blog_categorie.idCategorie 
			WHERE etat = :etat ORDER BY date DESC LIMIT ".$limitX.",".$limitY."";
		}
		else{ // selon catégorie
			$val[':idCategorie'] = $idCategorie;
			$req = "SELECT idArticle FROM blog_article 
			JOIN blog_categorie ON  blog_article.idCategorie = blog_categorie.idCategorie 
			WHERE blog_article.idCategorie = :idCategorie AND etat = :etat ORDER BY date DESC LIMIT ".$limitX.",".$limitY."";
		}
		$res = pdo2::getInstance()->prepare($req);
		$res->execute($val);
		//var_dump($res->execute($val));
		$liste = array();
		while($row = $res->fetch(pdo2::FETCH_OBJ)){
			$liste[] = article::creer($row->idArticle);
		}
		return $liste;
	}
rss
private function genererXmlArticle(){
$this->setHeader();
$article= article::creerListe('all', 0, 10, 1);
// suite boucle

Posté : 23 avr. 2009, 10:54
par Hywan
Hey :),

Tu as plusieurs solutions qui s'offrent à toi.

Tout d'abord, il est bon de bien comprendre le fonctionnement de la relation entre le serveur applicatif (i.e. celui qui fait tourner PHP) et le serveur de base de données (i.e. celui qui fait tourner MySQL). En général, c'est le même serveur physique (pour des petites et moyennes applications), mais pas virtuel.

Le serveur applicatif peut avoir un cache pour les résultats de la base de données, mais des caches existent aussi du côté base de données. Par exemple, si tu utilises PDO, il va déjà forcer une mise en cache des résultats. Qui plus est, si tu utilises des requêtes paramétrées, le serveur de base de données va mettre en cache la requête et ses possibles résultats. Tous les RDBMS (Relational DataBase Manager System) ne supportent pas ces fonctionnalités, mais dans ce cas, PDO va les émuler.

Donc utiliser les bons outils va déjà améliorer les performances.

Maintenant, voyons ce qu'on peut faire côté serveur applicatif. L'idéal serait d'avoir une classe ORM pour créer les requêtes, et ta classe Article utiliserait l'ORM. Pas de redondance de code, et des requêtes optimisées. Hmm, sinon niveau cache, ça risque d'être un peu lourd, donc on va s'appuyer sur le serveur de base de données. Par contre, tu peux mettre en cache tes résultats affichés (je veux dire, les outputs, le code HTML et RSS généré).

Essaye ça : ORM qui génère des requêtes paramétrées et basé sur PDO. Ça pourrait être pas mal :-).

Posté : 24 avr. 2009, 23:55
par supercanard
Oula, alors comment te dire...
Le lien de mon profil représente en gros mon niveau en POO :D donc je ne sais même pas de quoi tu parle. Enfin j'arrive peut être a faire un rapprochement avec le terme classe abstraite que j'ai déjà entendu, sans être sur que tu parle bien de ça.

Avant de franchir un tel cap, il n'y aurait pas une option un peu plus adapté à mon niveau ?

Car tu vois je me rapelle un jour avoir téléchargé ton framawork par curiosité, bon ben j'ai abandonné au bout de 5 minutes. Dailleur félicitation, je n'ai pas réussi à comprendre quelque chose, mais la vidéo de présentation sur ton site me laisse penser que ça à l'air vraiment bien et que plus tard je pourrais peut être m'en servir :wink:

Posté : 25 avr. 2009, 09:43
par naholyr
Ce qu'il faut ce n'est pas créer des objets à partir d'une requête directement, mais à partir du résultat de cette requête ! Cette méthode pourra par exemple (cas de Propel) s'appeler "hydrate", et prendre en paramètre un tableau ou même un "PDOStatement" et te renvoyer l'objet "rempli" par ces données.



Cela ressemblera à ça pour la méthode "creer" :
public function hydrate(PDOStatement $stmt)
{
  $row = $stmt->fetch(pdo2::FETCH_OBJ);

  if (!$row) 
  {
    return false;
  }

  $this->idArticle = $row->idArticle;
  $this->idCategorie = $row->idCategorie;
  $this->etat = $row->etat;
  $this->titre = $row->titre;
  $this->titreForUrl = $row->titreForUrl;
  $this->date = $row->date;
  $this->chapo = $row->chapo;
  $this->contenu = $row->contenu;
  $this->optionMarque = $row->marque;

  return true;
}

public static function creer($value)
{
  $val = array(':id'=>$value);
  $req = "SELECT * FROM blog_article WHERE idArticle = :id";
  $res = pdo2::getInstance()->prepare($req);
  $res->execute($val);

  $article = new article();
  if ($article->hydrate($res))
  {
    return $article;
  }
  else 
  {
    return null;
  }
}
Tu vois qu'on a découpé la fonction "creer" en deux aspects distincts : la requête elle-même d'un côté, et la création de l'objet à partir du résultat de cette requête de l'autre.

Il y aurait mille façon d'écrire cette méthode hydrate, en l'occurrence elle prend en paramètre un "PDOStatement", essaie de l'avancer d'un cran dans la liste des résultats, remplit l'objet et renvoie true si un résultat est trouvé, et sinon renvoie false. Ce qui permet au passage de gérer un cas que tu n'avais pas prévu actuellement : si l'article n'est pas trouvée, tu renvoyais un objet "vide", là on renvoie "NULL".

Ce qui te permet maintenant très simplement tout en gardant à l'idée de ne pas répéter du code inutilement, de pouvoir remplir une liste avec une seule requête :
public static function creerListe($idCategorie, $limitX, $limitY, $etat)
{
  $val = array(':etat'=>$etat);

  if($idCategorie == 'all' || $idCategorie == null)
  { // toute catégories confondues
    $req = "SELECT blog_article.* FROM blog_article 
      JOIN blog_categorie ON  blog_article.idCategorie = blog_categorie.idCategorie 
      WHERE etat = :etat ORDER BY date DESC LIMIT ".$limitX.",".$limitY."";
  }
  else
  { // selon catégorie
    $val[':idCategorie'] = $idCategorie;
    $req = "SELECT blog_article.* FROM blog_article 
      JOIN blog_categorie ON  blog_article.idCategorie = blog_categorie.idCategorie 
      WHERE blog_article.idCategorie = :idCategorie AND etat = :etat ORDER BY date DESC LIMIT ".$limitX.",".$limitY."";
  }

  $res = pdo2::getInstance()->prepare($req);
  $res->execute($val);

  $liste = array();
  do {
    $article = new Article();
    $has_results = $article->hydrate($res);
    if ($has_results)
    {
      $liste[] = $article;
    }
  }
  while ($has_results);

  return $liste;
}
L'API n'est pas encore idéale, avec ton système il faudrait sans doute plutôt se baser sur le résultat d'un PDOStatement::fetch() vu que ton constructeur prend en paramètre l'ID, il faudrait déjà avoir cette donnée en dehors de la fonction hydrate(). Mais bon, l'idée est là :)

Posté : 25 avr. 2009, 12:55
par supercanard
Ah oui on revient donc à ce que l'on m'avait expliqué sur un autre topic.
JE vais étudier celà merci

Posté : 05 mai 2009, 10:03
par Hywan
Sinon, tu utilises APCache, EAccelerator ou d'autre système de cache et hop :).