IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Zend_Db PHP Discussion :

[Zend_Db] ORM avec jointures SQL ?


Sujet :

Zend_Db PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    ovh
    ovh est déconnecté
    Rédacteur
    Avatar de ovh
    Homme Profil pro
    Architecte devops web full stack
    Inscrit en
    Mai 2002
    Messages
    3 842
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Architecte devops web full stack

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 842
    Par défaut [Zend_Db] ORM avec jointures SQL ?
    Bonjour

    Je débute avec le ZF, et je bute déjà sur un éceuil En effet les possibilités d'ORM sont séduisantes mais... c'est absolument nul point de vue requêtes SQL puisque ça fait un SELECT pour chaque table liée, que ce soit en lazy ou full loading. (la différence entre les 2 modes se situe juste au niveau du moment où on exécute les SELECT secondaires)
    C'est pour moi inacceptable sur une base mysql avec des tables comportant des dizaines de milliers de lignes sur un petit serveur.

    Je cherche donc le moyen d'optimiser l'ORM basique de ZF en créant mes objets sur base des champs adéquats, que je sélectionnerais au moyen d'une requête SQL propre (et surtout unique ).

    Prenons un exemple abstrait, mettons que j'ai 2 tables SQL, t1 et t2. Je crée 2 classes dérivées de Zend_Db_Table_Abstract, et à partir de t1 je voudrais récupérer les informations de la table t2 qui est liée.
    En SQL je ferais :
    SELECT t1.*, t2.* FROM t1 INNER JOIN t2 ON t2.id_toto = t1.id_toto
    Je me vois donc faire une petite fonction genre "myFetch()" dans la classe t1, qui utilise le db adapter pour passer la requête SQL; mais ensuite tout le noeud du problème consiste à lui dire comment transformer le résultat en objets t1 et t2...

    Je ne sais pas si je suis clair

    Y a-t-il une méthode magique pour fabriquer son mapping objet à la main, ou bien il faut créer des classes t1_ovh et t2_ovh en mettant tous les champs t1 et t2 en attributs "protected" et en gérant tout ça à la main ? Mais alors bénéfice-t-on encore de tous les avantages du ZF pour les opérations élémentaires sur la table (insert, update, delete, etc.) ? De quels types devraient être ces classes ?

    Un grand d'avance à ceux qui pourront m'aider
    J'ai cherché au préalable ici et sur google, mais je n'ai pas trouvé de réponse satisfaisante

    Tutoriels sur les UPS, e-commerce, PHP, critiques de livres...

    Pensez à consulter les FAQs et les cours et tutoriels.
    FAQ Linux - Cours et tutoriels Linux - FAQ PHP - Cours et tutoriels PHP
    Ce forum est fait pour vous et surtout par vous, merci d'en respecter les règles.

    Je n'ai rien à voir avec la société www.ovh.com !

  2. #2
    ovh
    ovh est déconnecté
    Rédacteur
    Avatar de ovh
    Homme Profil pro
    Architecte devops web full stack
    Inscrit en
    Mai 2002
    Messages
    3 842
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Architecte devops web full stack

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 842
    Par défaut
    Grâce à un post de gorgo, il semblerait que la méthode magique soit fetchObject Ca vient de PDO apparemment, et je ne connaissais pas

    Enfin ça n'a pas l'air si simple que ça quand il s'agit de séparer différents champs d'un même select dans plusieurs objets différents...

    Dans ce cas, c'est peut-être plutôt la fonction bindColumn qu'il faut exécuter, pour faire pointer chaque champ du résultat vers un attribut de la classe ?
    http://fr.php.net/manual/en/function...bindColumn.php

    Suis-je dans la bonne direction ?

    Tutoriels sur les UPS, e-commerce, PHP, critiques de livres...

    Pensez à consulter les FAQs et les cours et tutoriels.
    FAQ Linux - Cours et tutoriels Linux - FAQ PHP - Cours et tutoriels PHP
    Ce forum est fait pour vous et surtout par vous, merci d'en respecter les règles.

    Je n'ai rien à voir avec la société www.ovh.com !

  3. #3
    ovh
    ovh est déconnecté
    Rédacteur
    Avatar de ovh
    Homme Profil pro
    Architecte devops web full stack
    Inscrit en
    Mai 2002
    Messages
    3 842
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Architecte devops web full stack

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 842
    Par défaut
    Bon hé bien je n'étais pas dans la bonne direction et j'ai trouvé une solution Pour cela j'ai dû regarder le code source du ZF pour voir comment ils font leur mapping, et en adaptant ça à mes propres classes ça marche \o/

    Je ferai une doc un de ces jours pour expliciter cela (après avoir un peu joué avec et voir si ça marche bien pour tous les cas, et aussi optimisé un peu le code)

    Tutoriels sur les UPS, e-commerce, PHP, critiques de livres...

    Pensez à consulter les FAQs et les cours et tutoriels.
    FAQ Linux - Cours et tutoriels Linux - FAQ PHP - Cours et tutoriels PHP
    Ce forum est fait pour vous et surtout par vous, merci d'en respecter les règles.

    Je n'ai rien à voir avec la société www.ovh.com !

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    21
    Détails du profil
    Informations personnelles :
    Âge : 48
    Localisation : France, Doubs (Franche Comté)

    Informations forums :
    Inscription : Août 2007
    Messages : 21
    Par défaut
    Oui, tiens-nous au courant.

    Ta solution a l'air intéressante. Moi aussi, je trouve innacceptable la charge induite sur le serveur web par le full loading. C'est une des raisons pour lesquelles je n'utilise pas (encore) l'ORM. Mais avec ta méthode, peut-être... ?

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Août 2007
    Messages
    95
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 95
    Par défaut
    Bonjour,
    Cela fait plusieurs heures que je cherche a faire une jointure entre 2 classes en utilisant l'ORM mais je n'y arrive pas

    quelqu'un peut me donner un exemple simple sil vous plait ?

  6. #6
    ovh
    ovh est déconnecté
    Rédacteur
    Avatar de ovh
    Homme Profil pro
    Architecte devops web full stack
    Inscrit en
    Mai 2002
    Messages
    3 842
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Architecte devops web full stack

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 842
    Par défaut
    OK désolé, je n'ai pas abandonné mais j'ai eu d'autres choses à faire au boulot, mais je m'y réattelle parce que j'ai un projet à terminer pour janvier 2008 donc j'ai intérêt à mettre les bouchées doubles

    Alors je vais tenter d'expliquer le principe

    Avant d'attaquer le code il faut comprendre basiquement le fonctionnement de l'ORM du ZF. Lorsqu'on fait appel aux méthodes findXX(), findParentRow() etc. pour récupérer un objet lié (jointure), le framework effectue une requête SQL, et sur base de l'array du résultat (renvoyé par l'objet Zend_Db), ZF va créer un objet Zend_Db_Table_Rowset contenant un ensemble d'objets Zend_Db_Table_Row pour chaque ligne de résultat.
    Ceci est expliqué dans l'excellent tuto de Julien.

    Notre but est de ne plus devoir faire appel aux méthodes findXX(), sans non plus faire du full loading puisque cette dernière méthode ne résoud pas notre problème (effectue autant de requêtes select qu'il y a de jointures, donc très gourmand en ressources). Pour y arriver, nous créerons manuellement notre rowset de résultats. Détails ci-dessous.

    Considérons pour notre exemple un modèle en 2 tables : Document et Auteur. Pour simplifier, on considérera qu'un document ne peut avoir qu'un seul auteur et un auteur peut avoir plusieurs documents bien sûr, donc la table Document aura une clé étrangère id_auteur pointant sur la table Auteur.

    Nous allons créer une classe Document de manière classique, donc dérivée de Zend_Db_Table_Abstract. Dans cette classe nous pouvons définir les liens ORM au moyen du tableau $_referenceMap au cas où nous aurions besoin d'utiliser l'ORM de base du framework dans certaines situations. Mais pour faire son ORM personnalisé, ce n'est pas nécessaire. Par contre, ce qui est absolument vital c'est de remplacer la classe résultat par défaut par une que nous allons créer, ce qui se fait en renseignant l'attribut $_rowClass (par défaut = Zend_Db_Table_Row).

    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Document extends Zend_Db_Table_Abstract {
     
    	protected $_name = "document";
    	protected $_schema = "mabelledb";   // schema = nom de la db (facultatif, uniquement si différente de celle par défaut dans $db)
    	protected $_rowClass = "DocumentRow";
     
    	protected $_referenceMap = array (
    			"auteur" => array(
    				"columns" => array("id_auteur"),
    				"refTableClass" => "Auteur",
    				"refColumns" => "id_auteur"
    			)
    		);

    Ensuite dans notre classe Document nous allons créer une fonction pour extraire les résultats qui nous intéressent, appelons-la liste(). Là en considérant que nous construisons la requête SQL dans la variable $sql voici le code intéressant qui va générer les résultats sous forme d'objets :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public function liste() {
    		$db = $this->getAdapter();
    		// ici : on génère la requête SQL dans $query
    		$sql = $db->quoteInto($query, null);
    		$result = $db->query($sql)->fetchAll();
     
    		// crée une classe avec tous les résultats (rowset)
    		$data  = array(
             'table'    => $this,
             'data'     => $result,
             'rowClass' => $this->_rowClass,
             'stored'   => true
          );
     
          Zend_Loader::loadClass($this->_rowsetClass);
          return new $this->_rowsetClass($data);
    	}
    Ceci crée un objet classique Zend_Db_Table_Rowset, mais qui au lieu de contenir des Zend_Db_Table_Row contient une classe dérivée de celle-ci pour chaque ligne. Et c'est dans cette classe (plus précisément dans le constructeur) que nous allons créer les objets nécessaires sur base des champs provenant de la jointure SQL.
    Le chargement explicite de la classe n'est pas nécessaire si vous utilisez l'auto-loader de ZF.
    Illustration :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class DocumentRow extends Zend_Db_Table_Row {
     
    	public $auteur;
     
    	public function __construct($param) {
    		// constructeur par défaut
    		parent::__construct($param);
     
    		// crée une classe pour l'auteur :
    		$fields["id_auteur"] = $this->id_auteur;
    		$fields["nom"] = $this->anom;
    		$data  = array(
             'table'    => $this,
             'data'     => $fields,
             'stored'   => true
          );
          $this->auteur = new Zend_Db_Table_Row($data);
    	}
     
    };
    Je crée un tableau $fields qui contient uniquement les champs constituant un auteur (ici id_auteur et nom), et je les passe en paramètre pour créer un Zend_Db_Table_Row normal que j'associe à l'attribut "auteur" de ma classe. "anom" est le nom de l'auteur tel que je l'ai nommé dans ma jointure SQL. A noter que l'objet Document contient tous les champs accessibles directement ($objet->attribut), même ceux provenant de la jointure.

    Si on veut créer d'autres liaisons, on peut procéder de la même manière, en prenant soin de réinitialiser à chaque fois le tableau $fields.
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // crée une classe pour le fournisseur :
          unset($fields);
    		$fields["nom"] = $this->fnom;
    		$data  = array(
             'table'    => $this,
             'data'     => $fields,
             'stored'   => true
          );
          $this->fourn = new Zend_Db_Table_Row($data);

    Voyons maintenant l'utilisation :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $doc = new Document();
    $listedoc = $doc->liste();
    foreach ($listedoc as $undoc) {
      echo 'Doc '$undoc->id_doc.' écrit par '.$undoc->auteur->nom.'<br>';
    }

    Voilà j'espère que j'ai été +/- clair ?

    Dites-moi quoi, que je puisse améliorer s'il le faut, et en faire probablement une Q/R de FAQ ou un mini-article.


    Tutoriels sur les UPS, e-commerce, PHP, critiques de livres...

    Pensez à consulter les FAQs et les cours et tutoriels.
    FAQ Linux - Cours et tutoriels Linux - FAQ PHP - Cours et tutoriels PHP
    Ce forum est fait pour vous et surtout par vous, merci d'en respecter les règles.

    Je n'ai rien à voir avec la société www.ovh.com !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [SQL 2000] Optimisation requête avec jointure multiple
    Par zooffy dans le forum Développement
    Réponses: 5
    Dernier message: 18/09/2007, 15h38
  2. [SQL]Problème avec jointure de tables
    Par benjisan dans le forum Requêtes et SQL.
    Réponses: 16
    Dernier message: 29/03/2007, 20h43
  3. Réponses: 5
    Dernier message: 06/02/2007, 13h46
  4. [MySQL] Erreur SQL 1064 : Requête imbriquée avec jointure !
    Par patchankito dans le forum Langage SQL
    Réponses: 5
    Dernier message: 31/01/2006, 10h37
  5. [PL/SQL] update avec jointure
    Par Fox_magic dans le forum Oracle
    Réponses: 6
    Dernier message: 09/12/2004, 12h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo