Consultez la FAQ sur le ZF avant de poster une question
Vous n'êtes pas identifié.
Pages: 1
je suis en train de travailler à la réalisation d'un contrôleur CRUD générique.
j'en attends beaucoup, notamment le fait qu'il soit utilisable en cascade pour traiter les cas où j'aurai à renseigner des tables reliées de façon "1-à-plusieurs" dans la base de données.
Cela suppose donc la gestion d'un mécanisme de type "fil d'Ariane" qui permettrait de tracer quels sont les enregistrements visités et à renseigner dans la base. Par exemple :
Dossier n°1234 - Fiche n°56 - Intervenant n°78
pourrait être un exemple de trace d'un tel fil (les relations 1 vers N pointent bien sur de gauche à droite). Ce mécanisme s'implémente avec une pile bien entendu, et les traces de visite en cours sont stockées au moyen de la session.
Tout irait bien dans le meilleur des mondes si j'avais les droits sur toute la base ! pour être plus précis le niveau "Dossier" de mon schéma, n'est pas une table, mais une vue d'une base externe.
Et là patatras ! fini le contrôleur générique ! Car au lieu de pouvoir s'appuyer sur de bien gentilles Zend_Db_Table qui vous procurent des Zend_Db_Table_Rowset remplies de Zend_Db_Table_Row avec qui on peut se promener, consulter, modifier et enregistrer à souhait, voilà qu'on se retrouve avec des résultats de requête (sur la vue) qui sont des Zend_Db_Pdo_Statement et tous les fetch() qu'on pourra n sortir.
Comment faire pour tendre vers quelque chose de générique ? tout passer par des stdClass ?
meilleurs voeux à tous et merci à qui aura une idée
Héfeust
Dernière modification par hefeust (21-01-2008 12:13:34)
Hors ligne
tu peux très bien faire cela sur une Zend_Db_table
la première chose à faire est de créer une classe dérivé de zend_Db_Table et d'ajouter les champs adéquat dans le constructeur
Class Model_Facture_Table extends Zend_Db_Table_Abstract { protected $_name = 'facture'; protected $_rowClass = ' Model_Facture_Row'; public function __construct($config = array()) { parent::__construct($config); $this->_cols[] = 'fac_total'; } }
une fois cela fait il faut surcharger la methode _fetch
/** * Support method for fetching rows. * * @param string|array $where OPTIONAL An SQL WHERE clause. * @param string|array $order OPTIONAL An SQL ORDER clause. * @param int $count OPTIONAL An SQL LIMIT count. * @param int $offset OPTIONAL An SQL LIMIT offset. * @return array The row results, in FETCH_ASSOC mode. */ protected function _fetch($where = null, $order = null, $count = null, $offset = null) { // selection tool $select = $this->_db->select(); // the FROM clause $select->from($this->_name, $this->_cols, $this->_schema); // the WHERE clause $where = (array) $where; foreach ($where as $key => $val) { // is $key an int? if (is_int($key)) { // $val is the full condition $select->where($val); } else { // $key is the condition with placeholder, // and $val is quoted into the condition $select->where($key, $val); } } // the ORDER clause if (!is_array($order)) { $order = array($order); } foreach ($order as $val) { $select->order($val); } // the LIMIT clause $select->limit($count, $offset); // return the results $stmt = $this->_db->query($select); $data = $stmt->fetchAll(Zend_Db::FETCH_ASSOC); return $data; }
et mettre le necessaire pour la ou les jointure
$select->from($this->_name, $this->_cols, $this->_schema) ->join('lignes',’ligne.fac_id = facture.fac_id’, array('fac_total' =>Zend_Db_Exp('SUM(lig_prix * lig_qte)'))) ->group('fac_id');
une fois cela fait tu remonte avec les méthode standard de Zend_Db_Table des Row de ta jointure.
reste l'enregistrement.
il te faut aussi surcharger les méthode delete update et insert pour tenir compte des champs que tu as ajouté à ta classe table et qui ne sont pas dans la table. (et eventuellement gérer une transaction pour enregistrer les éléments liées)
A+JYT
Hors ligne
et ca va traiter un Pdo_Statement comme un Rowset ?
pour mémoire je cherche à me rapprocher de ce qui se fait avec MS-Access programmé avec VB (j'ai commencé avec ça
--> si on requête avec un SELECT on obtient un jeu d'enregistrements (Recorset) en lecture seule (normal) et si on requête sur une table, on obtient AUSSI un jeu d'enregistrements (Recorset) cette fois en lecture et écriture. D'ailleurs le mauvais positionnement des indicateurs de lecture/écriture pouvait provoquer des résultats inattendus de l'application, notamment en utilisant directement l'interface graphique.
Bon c'est du commentaire, merci de ta réponse rapide, je vais étudier ça cet aprem' ;-)
Hors ligne
je ne dirais même plus :
le hic vient du fait que le fetchAll() d'un Zend_Db_Table est un Zend_Db_Table_Rowset tandis que le fetchAll() d'un Zend_Db_Pdo_Statement est un tableau de... [ ce qu'on a défini comme type de retour pour cet objet (tableau de colonnes, objets stdClass, etc) ] donc ce n'est pas la même chose :-( !!!!!
Hors ligne
Mais c'est bien sûr !
en relisant le fond du code (tout au fond) de la classe Zend_Db_Table_Abstract j'ai bien retrouvé cette méthode _fetch(), je vais voir ce que je peux en faire...
Hors ligne
non ca va pas traiter un Pdo_Statement comme un Rowset.
ça va utiliser des rowset et non plus des Pdo_Statement
si tu passe par une Zend_Db_Table tu as toute les fonctionnalités d'une Zend_Db_Table
il faut juste veillier à faire correctement les jointure sur le _fetch et redispatcher les données sur les insert update et delete
tu as donc un objet table comme si tu avait une table derrière et ça lit et enregistre dans plusieurs tables.
A+JYT
Hors ligne
super j'espère que ca marchera avec les vues cette idée de "Table qui peut en cacher une autre". je vais tester ca en vrai d'ici la fin de la semaine.
un grand merci à toi.
au fait lecture en passant : (pour ceux qui ne connaissent pas, moi non plus avant de le lire!!!)
http://en.wikipedia.org/wiki/Object-rel … e_mismatch
ca parle de l'incompréhension fondamentale (ou divvergence de vues) qui peut exister entre la programmation objet (UML, etc) et le monde des bases de données relationnelles (Merise, etc)
très complet mais en anglais
bonne lecture !
Hors ligne
voilà les résultats de mes essais :
- on peut planquer une vue derrière une Zend_Db_Table, par contre il vaut mieux cacher les warnings du constructeur car il en envoie plein, notamment quand il essaie de trouver la clé primaire avec describeTable() notamment en utilisant Zend_Db_Adapter_Pdo_Mssql.php
donc :
@parent::__construct($config);
avec un arobase devant règle l'affaire.
par contre : je suis ok pour placer de la logique supplémentaire dans une méthode _fetch() surchargée. Par contre, comment éviter d'envoyer les champs ajoutés à $this->_cols[] dans la requête faite par le _fetch en standard ? je ne vais quand même pas y aller à coups de unset($this->_cols[''fac_total]) avant la requête pour ensuite remplir le tableau $data avec une boucle derrière ?
c'est à dire : insérer ma logique quelque part entre les lignes terminant le _fetch
// return the results $stmt = $this->_db->query($select); $data = $stmt->fetchAll(Zend_Db::FETCH_ASSOC); ///// // on fait une requete qui fabrique du contenu pour $data foreach($data as $index => $ligne) { $data[$index] = array_merge($ligne, $champ_calcules[$index]) } ///// return $data;
ou alors il y a une astuce que je n'ai pas vu ! la je seche !
bon week end
Hors ligne
Oui il faut effectivement nettoyer le tableau des cols et le restituer après
le mieux est de garder dans la tables un tableau des cols d'origine et celui qu'on a enrichie
ainsi dans le insert et le update
on peut subsitiuer les tableau de cols en fonction du besoin.
pour le constructeur si on mappe une Zend_Db8table sur une vue il suffit de surcharger le constructeur pour qu'il n'appelle plus le describeTable en le remplaçant par sont équivalent sur la vue
Je suis en train de réfléchir pour faire une classe abstraite qui permette de mapper un classe table sur une table qui joint automatiquement ses tables de référence.
je pense qu'on doit pouvoir faire le même genre de chose avec une table mappé sur une vue.
A+JYT
Hors ligne
ca marche mieux comme ca, merci ;-)
j'ajouterai que je vais sûrement être obligé de redéfinir fetchAll() afin qu'elle évite de me générer TOUS les champs calculés (= extraits par jointures) quand je demande TOUS les enregistrements, car sinon il y aura trop de consommation CPU.
OU alors je vais devoir jouer correctement avec les COUNT et OFFSET qu'on peut intercaler dans cette méthode
Hors ligne
ca s'arrange en effet...
et ca marche !
donc : une classe de modele qui hérite de Zend_Db_Table_Abstract avec dedans
- des méthodes (mon_fetchRow, mon_fetchAll, mon_find et protected:_mon_fetch) avec un paramètre booleen en plus pour dire si on doit calculer les champs 'rapportés'
- une méthode protégée _ajouterChampEtendu($nom, $type, $ressource, $params) qui permet, au niveau du init post construction d'ajouter dans les Row ou Rowset générés soit : 1/ une valeur constante, 2/ le résultat de l'appel d'une fonction interne à la classe (qui peut éventuellement faire une requête), 3/ un Rowset retourné à partir d'un appel magique find ou findParent...
- enfin deux méthodes lister(array $criteres) qui renvoie une liste d'enregistrements SANS champs calculés (sinon c'est gourmand :-) et une autre, détailler($primary_key_value) qui renvoie un enregistrement sur la base de mon_find($primary_key_value) avec les champs calculés dedans
Les fonctions mon_fetch...() ne font que reprendre le code des fonctions de Zend, laissées inchangées et qui sont donc toujours utilisables.
Ca permet, si on a une liste d'Albums de musique, d'avoir juste la liste, et si on affiche le détail d'un Album, d'avoir la liste de ses titres : ce n'est donc plus du Lazy Loading, ce n'est pas du Full Loading, c'est du "[suffisant] Loading"
;-)
Hors ligne
salut je viens de publier un article sur ce sujet illustrant ce que j'ai posté le 07-01-2008
http://sekaijin.ovh.org/?p=18
A+JYT
Hors ligne
Pages: 1