Zend FR

Consultez la FAQ sur le ZF avant de poster une question

Vous n'êtes pas identifié.

#1 22-10-2008 10:45:45

erwanpia
Membre
Lieu: Locronan - Quimper
Date d'inscription: 16-10-2007
Messages: 18
Site web

tables hiérarchiques

Bonjour

j'ai pas l'impression que le sujet a déjà été abordé : les tables hiérarchiques servent à pas mal de choses non (pages, catégories)

ci joint une extension de Zend_DB_Table pour gérer les hiérarchies

qui ajoute les fonctions countChildren, fetchChildren, utilisant l'algorithme tout simple basé sur parent_id.

commentaires bienvenus, reste à implémenter les fonctions fetchDescendents (tous les noeuds sous hiérarchie), fetchParent (le noeud parent), fetchAncestors (tous les noeuds parents)

Hors ligne

 

#2 24-10-2008 17:52:53

sekaijin
Membre
Date d'inscription: 17-08-2007
Messages: 1137

Re: tables hiérarchiques

J'ai dans mes carton pour la version 1.0.3 de une version de table hiérarchique basé sur la représentation intervallaire. donc recherche de tous les descendants ou ascendant sans récursivité

si ça dis je peux la mettre à dispo
A+JYT

Hors ligne

 

#3 27-10-2008 16:34:41

sekaijin
Membre
Date d'inscription: 17-08-2007
Messages: 1137

Re: tables hiérarchiques

table hiérarchique basé sur la représentation intervallaire. donc recherche de tous les descendants ou ascendant sans récursivité

Fast_Db_Table ma classe dérivée de Zend_Db_Table qui me sert de base à toute mes tables

Code:

<?php
Zend_Loader::loadClass('Zend_Db_Table_Abstract');
Zend_Loader::loadClass('Fast_Db_Exception');
require_once dirname(__FILE__).'/Row.php';

/**
 * Definition de base d'une table Fast
 * elle étend la classe Zend_Db_Table et lui adjoint un classe spécifique pour les enregistrement
 * ainsi que les méthode courantes d'accès au données
 *
 * @package Fast
 * @copyright  ftgroup
 * @author Jean-Yves Terrien
 * @subpackage Fast_Db
 * @see  Fast_Db_Exception
 * @see  Fast_Db_Row
 * @see  Zend_Db_Table_Row
 */
Class Fast_Db_Table
extends Zend_Db_Table_Abstract
{

    /**
    * The table name.
    *
    * @var array
    */
    protected $_name = null;

    /**
    * Classname for row
    *
    * @var string
    */
    protected $_rowClass = null;

    /**
    * Restriction for query
    *
    * @var string
    */
    protected $_restrict = NULL;

    /*master model de l'application */
    public $model;

   /**
    * Constructor.
    *
    * Supported params for $config are:
    * - db              = user-supplied instance of database connector,
    *                     or key name of registry instance.
    * - name            = table name.
    * - primary         = string or array of primary key(s).
    * - rowClass        = row class name.
    * - rowsetClass     = rowset class name.
    * - referenceMap    = array structure to declare relationship
    *                     to parent tables.
    * - dependentTables = array of child tables.
    * - metadataCache   = cache for information from adapter describeTable().
    *
    * @param  mixed $config Array of user-specified config options, or just the Db Adapter.
    * @return void
    * @throw     Fast_Db_Exception
    */
    public function __construct($config = array())
    {
        parent::__construct($config);
        if (null == $this->_rowClass) throw new Fast_Db_Exception(Fast_Db_Exception::UNDEFINED_ROW_CLASS);
        if (null == $this->_name)     throw new Fast_Db_Exception(Fast_Db_Exception::UNDEFINED_TABLE_NAME);
        if (isset($config['model'])) {
            $this->setModel($config['model']);
        }
        #Fast_Debug::show('$this', $this);
    }

    public function setModel($model)
    {
        $this->model = $model;
    }


    /**
     * Récupérer le nom du driver;
     */
    public function getDriver() {
        return basename(preg_replace('/_/', '/',get_class($this->getAdapter())));
    }
    /**
     * Initializes metadata.
     *
     * If metadata cannot be loaded from cache, adapter's describeTable() method is called to discover metadata
     * information. Returns true if and only if the metadata are loaded from cache.
     *
     * @return boolean
     * @throws Zend_Db_Table_Exception
     */
    protected function _setupMetadata()
    {
        // Assume that metadata will be loaded from cache
        $isMetadataFromCache = true;

        // If $this has no metadata cache but the class has a default metadata cache
        if (null === $this->_metadataCache && null !== self::$_defaultMetadataCache) {
            // Make $this use the default metadata cache of the class
            $this->_setMetadataCache(self::$_defaultMetadataCache);
        }

        // If $this has a metadata cache
        if (null !== $this->_metadataCache) {
            // Define the cache identifier where the metadata are saved
            $cacheId = md5("$this->_schema.$this->_name");
        }

        // If $this has no metadata cache or metadata cache misses
        if (null === $this->_metadataCache || !($metadata = $this->_metadataCache->load($cacheId))) {
            // Metadata are not loaded from cache
            $isMetadataFromCache = false;
            // Fetch metadata from the adapter's describeTable() method
            $metadata = $this->_db->describeTable($this->_name, $this->_schema);

            /* patch tous les champs en minuscule */
            foreach ($metadata as $key=>$value) {
                $value['COLUMN_NAME']= strtolower($key);
                unset($metadata[$key]);
                $metadata[strtolower($key)] = $value;
            }

            // If $this has a metadata cache, then cache the metadata
            if (null !== $this->_metadataCache && !$this->_metadataCache->save($metadata, $cacheId)) {
                /**
                 * @see Zend_Db_Table_Exception
                 */
                require_once 'Zend/Db/Table/Exception.php';
                throw new Zend_Db_Table_Exception('Failed saving metadata to metadataCache');
            }
        }

        // Assign the metadata to $this
        $this->_metadata = $metadata;

        // Update the columns
        $this->_cols = array_keys($this->_metadata);

        // Return whether the metadata were loaded from cache
        return $isMetadataFromCache;
    }

    /**
     * Fetches one row in an object of type Zend_Db_Table_Row_Abstract,
     * or returns Boolean false if no row matches the specified criteria.
     *
     * @param string|array $where         OPTIONAL An SQL WHERE clause.
     * @param string|array $order         OPTIONAL An SQL ORDER clause.
     * @param boolean $restrict           OPTIONAL use restrict SQL clause.
     * @return Fast_Db_Row  The row results per the
     *     Zend_Db_Adapter fetch mode, or null if no row found.
     */
   public function fetchRow($where = null, $order = null, $restrict = true)
   {
      if ($restrict&&isset($this->_restrict)&&is_string($this->_restrict))
      {
         if (is_array($where))
         {
            $where[] = $this->_restrict;
         } else {
            $where = '('.$this->_restrict.') AND ('.$where.')';
         }
      } elseif ($restrict&&isset($this->_restrict)&&is_array($this->_restrict))
      {
         if (is_array($where))
         {
            $where = array_merge($where, $this->_restrict);
         } else {
            foreach ($this->_restrict as $contraint) {
               $where = '('.$contraint.') AND ('.$where.')';
            }
         }
      }
      $res = parent::fetchRow($where,$order);
      return $res;
   }

   /**
    * Make new row associated to this table.
    *
    * @param StdClass|array $obj OPTIONAL object to cast
    * @return Fast_Db_Row
    */
    public function newRow($obj = null) {
      if ($obj)
      {
         if (is_object($obj))
            $obj = get_object_vars($obj);
         $row = $this->createRow($obj);
      } else {
         $row = $this->createRow();
      }
      //$row = new $this->_rowClass(array($config['table'] => $this,'data' => $obj));
      //$row->setTable($this);
      return $row;
   }

   /**
    * Select a row by primary key.
    *
    * @param integer $id value of primary key
    * @return Fast_Db_Row
    */
   public function getById($id) {
      $rows = $this->find($id);
      return $rows->current();
   }

   /**
    * Delete a row by primary key.
    *
    * @param integer $id value of primary key
    * @return integer the response of sql server
    */
   public function deleteById($id) {
      $row = $this->getById($id);
      $res = $row->delete();
      return $res;
   }

   /**
    * Update a row by primary key.
    *
    * @param array $data row value to update need to define $data[primary_key'
    * @return integer the response of sql server
    * @throw     Fast_Db_Exception
    */
   public function UpdateById($data) {
      if (!isset($data[$this->_primary[1]]))
         throw new Fast_Db_Exception(Fast_Db_Exception::UNDEFINED_PRIMARY_KEY);
      $res =  parent::update($data, $this->_primary[1].' = '.$data[$this->_primary[1]]);
      if (preg_match('/mysql/i', get_class($this->_db))) {
        if ($res <= 0) $res++;
      }
      return $res;
   }
 
   protected function _getList($select, $params=array(), $restrict = true)
   {
      if ($restrict&&isset($this->_restrict)&&is_string($this->_restrict)) {
         $select->where($this->_restrict);
      } elseif ($restrict&&isset($this->_restrict)&&is_array($this->_restrict)) {
         foreach ($this->_restrict as $contraint) {
            $select->where($contraint);
         }
      }
      $statement = $this->_db->prepare($select);
      $statement->setFetchMode(Zend_Db::FETCH_OBJ);
      $statement->execute($params);
      $list = $statement->fetchAll();
      #Fast_Debug::show('getList',$list);
      return $list;
   }


    /**
     * This is the find Zend_Db_Table Abstract method
     * But the where closes are prefixed by the table name
     *
     * Fetches rows by primary key.
     * The arguments specify the primary key values.
     * If the table has a multi-column primary key, you must
     * pass as many arguments as the count of column in the
     * primary key.
     *
     * To find multiple rows by primary key, the argument
     * should be an array.  If the table has a multi-column
     * primary key, all arguments must be arrays with the
     * same number of elements.
     *
     * The find() method always returns a Rowset object,
     * even if only one row was found.
     *
     * @param  mixed                         The value(s) of the primary key.
     * @return Zend_Db_Table_Rowset_Abstract Row(s) matching the criteria.
     * @throws Zend_Db_Table_Exception
     */
    public function find($key)
    {
        $args = func_get_args();
        $keyNames = array_values((array) $this->_primary);

        if (empty($args)) {
            require_once 'Zend/Db/Table/Exception.php';
            throw new Zend_Db_Table_Exception("No value(s) specified for the primary key");
        }

        if (count($args) != count($keyNames)) {
            require_once 'Zend/Db/Table/Exception.php';
            throw new Zend_Db_Table_Exception("Missing value(s) for the primary key");
        }

        $whereList = array();
        $numberTerms = 0;
        foreach ($args as $keyPosition => $keyValues) {
            // Coerce the values to an array.
            // Don't simply typecast to array, because the values
            // might be Zend_Db_Expr objects.
            if (!is_array($keyValues)) {
                $keyValues = array($keyValues);
            }
            if ($numberTerms == 0) {
                $numberTerms = count($keyValues);
            } else if (count($keyValues) != $numberTerms) {
                require_once 'Zend/Db/Table/Exception.php';
                throw new Zend_Db_Table_Exception("Missing value(s) for the primary key");
            }
            for ($i = 0; $i < count($keyValues); ++$i) {
                $whereList[$i][$keyPosition] = $keyValues[$i];
            }
        }

        $whereClause = null;
        if (count($whereList)) {
            $whereOrTerms = array();
            foreach ($whereList as $keyValueSets) {
                $whereAndTerms = array();
                foreach ($keyValueSets as $keyPosition => $keyValue) {
                    $type = $this->_metadata[$keyNames[$keyPosition]]['DATA_TYPE'];
                    $whereAndTerms[] = $this->_db->quoteInto(
                        $this->_db->quoteIdentifier($this->_name).'.'.
                        $this->_db->quoteIdentifier($keyNames[$keyPosition], true) . ' = ?',
                        $keyValue, $type);
                }
                $whereOrTerms[] = '(' . implode(' AND ', $whereAndTerms) . ')';
            }
            $whereClause = '(' . implode(' OR ', $whereOrTerms) . ')';
        }

        return $this->fetchAll($whereClause);
    }

}

mes exception sur les base Fast_Db_Exception

Code:

<?php
Zend_Loader::loadClass('Fast_Exception');
/**
 * Exception levée par la librairie Fast
 * Définit les exception de base de donnée de la library Fast
 *
 * @package Fast
 * @copyright  ftgroup
 * @author Jean-Yves Terrien
 * @subpackage Fast_Db
 */
class Fast_Db_Exception extends Fast_Exception {
    // DB Exceptions
    const PARAMETERS_NOT_FOUND = 'Database configuration parameters not founds';
    // Table Exceptions
    const UNDEFINED_ROW_CLASS   = 'Undefined row class name';
    const UNDEFINED_TABLE_NAME  = 'Undefined database table name';
    const UNDEFINED_PRIMARY_KEY = 'Undefined primary key';
    // Row Exceptions
    const INVALID_TABLE_OBJECT = 'Attempt a Zend_Db_Table object';
    const UNDEFINED_KEY        = 'Undefined field on this row';
    // Hiérarchical Table Exceptions
    const UNDEFINED_LEFT_KEY  = 'Undefined left field name';
    const UNDEFINED_RIGHT_KEY = 'Undefined right field name';
    const UNDEFINED_LEVEL_KEY = 'Undefined level field name';
    const UNDEFINED_PARENT    = 'Undefined parent virtual field name';
}

ma classe de basepour mes rows

Code:

<?php
Zend_Loader::loadClass('Fast_Collectionable_Interface');
Zend_Loader::loadClass('Zend_Db_Table_Row_Abstract');
/**
 * Definition de base d'un enregistrement d'une table Fast
 * elle étend la classe Zend_Db_Table_Row
 *
 * @package Fast
 * @copyright  ftgroup
 * @author Jean-Yves Terrien
 * @subpackage Fast_Db
 * @see  Fast_Db_Exception
 * @see  Zend_Db_Table_Row
 */
class Fast_Db_Row
extends Zend_Db_Table_Row_Abstract
implements Fast_Collectionable_Interface
{
   //public $data = array();

    public function __construct($config = null) {
        if (null != $config) {
            if (is_array($config)&&($config['table'] instanceof Fast_Db_Table)) {
                /*foreach ($config['data'] as $key=>$value) {
                    unset($config['data'][$key]);
                    $config['data'][strtolower($key)] = $value;
                }*/

                $config['data'] = array_change_key_case($config['data'], CASE_LOWER);

                foreach ($this->_data as $key=>$value) {
                    if ((!isset($config['data'][$key]))||(null === $config['data'][$key])) {
                        $config['data'][$key] = $value;
                    }
                }

            }
        }
        parent::__construct($config);
    }
   /*
   public function setTable($table) {
      $this->table = $table;
   }
   */

    /**
     * Constructs where statement for retrieving row(s).
     *
     * @return array
     */
    protected function _getWhereQuery($dirty = true)
    {
        $where = array();
        $db = $this->_getTable()->getAdapter();
        $primaryKey = $this->_getPrimaryKey($dirty);

        $info = $this->getTable()->info();
        // retrieve rec$this->tableently updated row using primary keys
        foreach ($primaryKey as $columnName => $val) {
            $where[] = $db->quoteInto($db->quoteIdentifier($info[Zend_Db_Table::NAME]).'.'.$db->quoteIdentifier($columnName, true) . ' = ?', $val);
        }

        return $where;
    }



   public function add() {
      return $this->_doInsert();
   }

   public function update() {
      foreach ($this->_primary as $pKey) {
         $this->_cleanData[$pKey] = $this->_data[$pKey];
      }
      return $this->_doUpdate();
   }

   public function toStdClass() {
      return (object) $this->toArray();
   }

    /**
     * Allows pre-insert logic to be applied to row.
     * Subclasses may override this method.
     *
     * @return void
     */
    protected function _insert()
    {
        $this->_data = $this->_prepareData($this->_data);
    }

    /**
     * Allows post-insert logic to be applied to row.
     * Subclasses may override this method.
     *
     * @return void
     */
    protected function _postInsert()
    {
    }


    /**
     * Allows pre-update logic to be applied to row.
     * Subclasses may override this method.
     *
     * @return void
     */
    protected function _update()
    {
        $this->_data = $this->_prepareData($this->_data);
    }

    /**
     * Allows post-update logic to be applied to row.
     * Subclasses may override this method.
     *
     * @return void
     */
    protected function _postUpdate()
    {
    }

    /**
     * Force les données en Zend_Db_Expr
     * Zend_Db produit sinon des requêtes avec des parametres
     * positionnels qui ne sont pas supportés par oracle (entre autre)
     */
    protected function _prepareData($data) {
        Zend_Loader::loadClass('Zend_Db_Expr');
        $db = $this->_getTable()->getAdapter();
        $tbInfo = $this->_getTable()->info();
        foreach ($tbInfo['primary'] as $pkey) {
            if ('' == $data[$pkey]) {
                $data[$pkey] = null;
            }
        }
        foreach ($data as $key=>$value) {
            //TODO voir s'il faut traiter les null différemement
            if (isset($data[$key])) {
                $data[$key] = new Zend_Db_Expr($db->quote($value));
            }
        }
        return $data;
    }
}

et enfin Fast_Db_Hierarchical ma classe qui gère l'héritage

Code:

<?php
Zend_Loader::loadClass('Fast_Db_Table');

/**
 * Definition de base d'une table hiérarchique Fast
 *
 * Cette Classe défini un acces à tune table hiérarchique par représentation intervalaire.
 * Cette représentation permet de manipuler les notions hiérarchiques sans avoir à faire de
 * fonctions récurcives.
 *
 * @package Fast
 * @copyright  ftgroup
 * @author Jean-Yves Terrien
 * @subpackage Fast_Db
 * @see  Fast_Db_Exception
 * @see  Fast_Db_Table
 * @see  Zend_Db_Table_Row
 */
Class Fast_Db_Hierarchical extends Fast_Db_Table {

   /**
   * left field neme in table
   *
   * @var string
   */
   protected $_left = NULL;

   /**
   * right field neme in table
   *
   * @var string
   */
   protected $_right = NULL;

   /**
   * level field neme in table
   *
   * @var string
   */
   protected $_level = NULL;

   /**
   * virtual field name used has id of parent
   *
   * @var string
   */
   protected $_parent = NULL;

   public function __construct($config = array())
   {
      parent::__construct($config);
      if (null == $this->_left)   throw new Fast_Db_Exception(Fast_Db_Exception::UNDEFINED_LEFT_KEY);
      if (null == $this->_right)  throw new Fast_Db_Exception(Fast_Db_Exception::UNDEFINED_RIGHT_KEY);
      if (null == $this->_level)  throw new Fast_Db_Exception(Fast_Db_Exception::UNDEFINED_LEVEL_KEY);
      if (null == $this->_parent) throw new Fast_Db_Exception(Fast_Db_Exception::UNDEFINED_PARENT);
      $this->_cols[] = $this->_parent;
   }

   public function getById($id) {
      $rows = $this->find($id);
      if ($rows) {
         return $rows->current();
      }
      return false;
   }

   public function deleteById($id) {
      if ($id == 1) return false; // on ne peut supprimer la racine

      $this->_db->beginTransaction();
      $parent = $this->_db->select();
      $parent->from($this->_name, array('delete_left' => $this->_left, 'delete_right' => $this->_right))
             ->where($this->_primary[1].' = :_deleteId');
      $statement = $this->_db->prepare($parent);
      $statement->execute(array('_deleteId' => $id));
      list($deleteLeft, $deleteRight) = array_values($statement->fetch());
      $statement->closeCursor();
      $res = false;
      if ($deleteLeft) {
         $row = $this->getById($id);
         $res = $row->delete();

         if ($res) {
            $statement = $this->_db->prepare('UPDATE '.$this->_name.' SET '.$this->_left.' = '.$this->_left.' - 1 WHERE '.$this->_left.' >= '.$deleteLeft.' AND '.$this->_right.' < '.$deleteRight.';');
            $statement->execute();
         }

         if ($res) {
            $statement = $this->_db->prepare('UPDATE '.$this->_name.' SET '.$this->_left.' = '.$this->_left.' - 2 WHERE '.$this->_left.' >= '.$deleteLeft.' AND '.$this->_right.' > '.$deleteRight.';');
            $statement->execute();
         }

         if ($res) {
            $statement = $this->_db->prepare('UPDATE '.$this->_name.' SET '.$this->_right.' = '.$this->_right.' - 1 WHERE '.$this->_right.' >= '.$deleteLeft.' AND '.$this->_right.' < '.$deleteRight.';');
            $statement->execute();
         }

         if ($res) {
            $statement = $this->_db->prepare('UPDATE '.$this->_name.' SET '.$this->_right.' = '.$this->_right.' - 2 WHERE '.$this->_right.' >= '.$deleteLeft.' AND '.$this->_right.' > '.$deleteRight.';');
            $statement->execute();
         }

         if ($res) {
            $this->_db->commit();
         } else {
            $this->_db->rollback();
         }
      }
      return $res;
   }

   public function UpdateById($data) {
      // on ne peut mettre à jour les donnée hiérarchique
      // ie on ne peut déplacer un noeud dans l'arbre.
      unset($data[$this->_parent]); // ne fait pas partie de la table
      unset($data[$this->_left]);   //ne peut être changé
      unset($data[$this->_right]);  //ne peut être changé
      unset($data[$this->_level]);  //ne peut être changé
      $res =  parent::UpdateById($data);
      return $res;
   }

    public function insert(array $data) {
      # select left and level of parent
      $parentId = $data[$this->_parent];

      $this->_db->beginTransaction();
      $parent = $this->_db->select();
      if (null != $this->_level) {
         $fields = array('parent_left' => $this->_left, 'parent_level' => $this->_level);
      } else {
         $fields = array('parent_left' => $this->_left,);
      }

      $parent->from($this->_name, $fields)
             ->where($this->_primary[1].' = :_parentId');
      $statement = $this->_db->prepare($parent);
      $statement->execute(array('_parentId' => $parentId));
      list($parentLeft, $parentLevel) = array_values($statement->fetch());
      $statement->closeCursor();
      $res = false;
      if ($parentLeft) {
         #update tree
         $statement = $this->_db->prepare('UPDATE '.$this->_name.' SET '.$this->_left.' = '.$this->_left.' + 2 WHERE '.$this->_left.' > '.$parentLeft.';');
         $res = $statement->execute();
         if ($res) {
            $statement = $this->_db->prepare('UPDATE '.$this->_name.' SET '.$this->_right.' = '.$this->_right.' + 2 WHERE '.$this->_right.' > '.$parentLeft.';');
            $statement->execute();
         }

         #insert node
         if ($res) {
            unset($data[$this->_parent]);
            $data[$this->_left] = $parentLeft + 1;
            $data[$this->_right] = $parentLeft + 2;
            if (null != $this->_level)
               $data[$this->_level] = $parentLevel + 1;
            $res =  parent::insert($data);
           }
         if ($res) {
            $this->_db->commit();
         } else {
            $this->_db->rollback();
         }
      }
      return $res;
    }

    /**
     * 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();

        //no _parent col on master table
        $cols = $this->_cols;
        unset($cols[array_search($this->_parent,$cols)]);

        // the FROM clause
        $select->from($this->_name, $cols, $this->_schema);
        // add the parent col
        $select->join(array('parent' => $this->_name),
                      '(parent.'.$this->_left.' < workgroup.'.$this->_left.') AND
                       (parent.'.$this->_right.' > workgroup.'.$this->_right.') AND
                       (parent.'.$this->_level.' = workgroup.'.$this->_level.' -1)',
                      array('parent_id' => 'parent.'.$this->_primary[1].''));

        // 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;
    }

    public function update(array $data, $where)
    {
      unset($this->_cols[array_search($this->_parent,$this->_cols)]);
      unset($data[$this->_parent]);
      $res = parent::update($data, $where);
      $this->_cols[] = $this->_parent;
      return $res;
    }

   protected function _parent($row, $fiels) {
      $parent = $this->_parents($row, $fiels)
             ->order($this->_right)
             ->limit(1);
      return $parent;
   }
   protected function _parents($row, $fiels) {
      $parent = $this->_db->select();
      $parent->from($this->_name, $fiels)
             ->where($this->_left.'  < '.$row->{$this->_left})
             ->where($this->_right.' > '.$row->{$this->_right});
      return $parent;
   }
   protected function _childs($row, $fiels) {
      $childs = $this->_db->select();
      $childs->from($this->_name, $fiels)
             ->where($this->_left.'  > '.$row->{$this->_left})
             ->where($this->_right.' < '.$row->{$this->_right});
      return $childs;
   }

    /**
     * This is the find Zend_Db_Table Abstract method
     * But the where closes are prefixed by the table name
     *
     * Fetches rows by primary key.
     * The arguments specify the primary key values.
     * If the table has a multi-column primary key, you must
     * pass as many arguments as the count of column in the
     * primary key.
     *
     * To find multiple rows by primary key, the argument
     * should be an array.  If the table has a multi-column
     * primary key, all arguments must be arrays with the
     * same number of elements.
     *
     * The find() method always returns a Rowset object,
     * even if only one row was found.
     *
     * @param  mixed                         The value(s) of the primary key.
     * @return Zend_Db_Table_Rowset_Abstract Row(s) matching the criteria.
     * @throws Zend_Db_Table_Exception
     */
    public function find($key)
    {
        $args = func_get_args();
        $keyNames = array_values((array) $this->_primary);

        if (empty($args)) {
            require_once 'Zend/Db/Table/Exception.php';
            throw new Zend_Db_Table_Exception("No value(s) specified for the primary key");
        }

        if (count($args) != count($keyNames)) {
            require_once 'Zend/Db/Table/Exception.php';
            throw new Zend_Db_Table_Exception("Missing value(s) for the primary key");
        }

        $whereList = array();
        $numberTerms = 0;
        foreach ($args as $keyPosition => $keyValues) {
            // Coerce the values to an array.
            // Don't simply typecast to array, because the values
            // might be Zend_Db_Expr objects.
            if (!is_array($keyValues)) {
                $keyValues = array($keyValues);
            }
            if ($numberTerms == 0) {
                $numberTerms = count($keyValues);
            } else if (count($keyValues) != $numberTerms) {
                require_once 'Zend/Db/Table/Exception.php';
                throw new Zend_Db_Table_Exception("Missing value(s) for the primary key");
            }
            for ($i = 0; $i < count($keyValues); ++$i) {
                $whereList[$i][$keyPosition] = $keyValues[$i];
            }
        }
        $whereClause = null;
        if (count($whereList)) {
            $whereOrTerms = array();
            foreach ($whereList as $keyValueSets) {
                $whereAndTerms = array();
                foreach ($keyValueSets as $keyPosition => $keyValue) {
                    $whereAndTerms[] = $this->_db->quoteInto(
                        $this->_db->quoteIdentifier($this->_name).'.'.$this->_db->quoteIdentifier($keyNames[$keyPosition], true) . ' = ?',
                        $keyValue
                    );
                }
                $whereOrTerms[] = '(' . implode(' AND ', $whereAndTerms) . ')';
            }
            $whereClause = '(' . implode(' OR ', $whereOrTerms) . ')';
        }

        return $this->fetchAll($whereClause);
    }


}

voilà pour le code générique un exemple de table et de row

Code:

<?php
Zend_Loader::loadClass('Fast_Db_Hierarchical');
require_once dirname(__FILE__).'/Row.php';
/**
 * @package Fast_Module
 * @subpackage Adm_Module
 */
Class Adm_Model_Group_Table extends Fast_Db_Hierarchical {

    /**
   * The table name.
   *
   * @var array
   */
    protected $_name = 'WORKGROUP';

    /**
   * Classname for row
   *
   * @var string
   */
    protected $_rowClass = 'Adm_Model_Group_Row';

    /**
   * Restriction for query
   *
   * @var string
   */
    protected $_restrict = array('workgroup.wkg_level >= 0');

    /**
   * left field neme in table
   *
   * @var string
   */
    protected $_left = 'wkg_left';

    /**
   * right field neme in table
   *
   * @var string
   */
    protected $_right = 'wkg_right';

    /**
   * level field neme in table
   *
   * @var string
   */
    protected $_level = 'wkg_level';

    /**
   * field neme used has id of parent
   *
   * @var string
   */
    protected $_parent = 'parent_id';

    public function __construct($config = array())
    {
        parent::__construct($config);
        $user = Zend_Auth::getInstance()->getIdentity();
        #Fast_Debug::show('$user', $user);

        if ($user) {
            //L’utilisateur ne peut voir que les groupes fils de celui dans lequel in exerce sont rôle.
            $this->_restrict[] = $this->_name.'.wkg_left >= (SELECT wkg_left FROM workgroup WHERE wkg_id = '.$user->profile->wkg_id.')';
            $this->_restrict[] = $this->_name.'.wkg_right <= (SELECT wkg_right FROM workgroup WHERE wkg_id = '.$user->profile->wkg_id.')';
        } else {
            $this->_restrict = 'false'; //sans identité on ne peut rien voir dans la base
        }
    }

    public function getList($forParent=false, $withParent = false) {
        $select = $this->_db->select(); // @var $select Zend_Db_Select
        $select
        ->from($this->_name, array(
            $this->_db->quoteIdentifier('id') => $this->_primary[1],
            $this->_db->quoteIdentifier('ident') => 'wkg_code',
            $this->_db->quoteIdentifier('name') => 'wkg_label',
            $this->_db->quoteIdentifier('level') => $this->_level
        ))
        ->order($this->_left, 'wkg_label')
        ;
        $user = Zend_Auth::getInstance()->getIdentity();
        if ($forParent&&$user) {
            $today = array(
                'Oci' => 'sysdate',
                'Mysql' => 'NOW()'
            );
            $driver = basename(preg_replace('/_/', '/',get_class($this->_db)));
            if ($withParent) {
                $curentWorkGroup = $this->getById($user->profile->wkg_id);
                $parent_id = $curentWorkGroup->parent_id;
            } else {
                $parent_id = $user->profile->wkg_id;
            }
            $select
            ->join('workgroup_type', 'workgroup_type.wgt_id = workgroup.wgt_id', array())
            ->where($this->_name.'.wkg_left >= (SELECT wkg_left FROM workgroup WHERE wkg_id = '.$parent_id.')')
            ->where($this->_name.'.wkg_right <= (SELECT wkg_right FROM workgroup WHERE wkg_id = '.$parent_id.')')
            ->where('wgt_right > wgt_left + 1')
            ->where('wkg_begin_dt <= ' . $today[$driver])
            ->where('wkg_end_dt >= ' . $today[$driver]);
            #Fast_Debug::show("$select",false);
            return $this->_getList($select, null, false);
        } else {
            #Fast_Debug::show("$select",false);
            return $this->_getList($select);
        }
    }

    public function getGroupTypes($parent_id = NULL) {
        $user = Zend_Auth::getInstance()->getIdentity();
        if ($user) {
            //L’utilisateur ne peut voir que les groupes pour lesquels il peut gérer un rôle.
            $currentRoleId = $user->profile->rol_id;
            $select = $this->_db->select(); // @var $select Zend_Db_Select
            $select->distinct()
            ->from('workgroup_type', array(
                $this->_db->quoteIdentifier('value') => 'wgt_id',
                $this->_db->quoteIdentifier('label') => 'wgt_label'
            ))
            ->join('workgroup_type_role', 'workgroup_type.wgt_id = workgroup_type_role.wgt_id', array())
            ->join('role', 'workgroup_type_role.rol_id = role.rol_id', array())
            ->where('wgt_valid = 1')
            ->where('rol_left >= (SELECT rol_left FROM role WHERE rol_id = :currentRole1)')
            ->where('rol_right <= (SELECT rol_right FROM role WHERE rol_id = :currentRole2)')
            ->order('wgt_label')
            ;
            if (NULL != $parent_id) {
                $select
                ->where('wgt_left >= (SELECT wgt_left FROM workgroup_type WHERE wgt_id = (SELECT wgt_id FROM workgroup WHERE wkg_id = :currentParent1))')
                ->where('wgt_right <= (SELECT wgt_right FROM workgroup_type WHERE wgt_id = (SELECT wgt_id FROM workgroup WHERE wkg_id = :currentParent2))')
                ->where('wgt_level = (SELECT wgt_level + 1 FROM workgroup_type WHERE wgt_id = (SELECT wgt_id FROM workgroup WHERE wkg_id = :currentParent3))');
            }
            #Fast_Debug::show($select->__toString(),array('currentRole' => $currentRoleId, 'currentParent' => $parent_id));
            //on n'utilise pas les contraintes définies sur la table workgroup
            //  d'où le false en dernier paramètres
            return $this->_getList($select, array(
                'currentRole1' => $currentRoleId,
                'currentRole2' => $currentRoleId,
                'currentParent1' => $parent_id,
                'currentParent2' => $parent_id,
                'currentParent3' => $parent_id), false);
        } else {
            return array();
        }
    }

    public function getAvailableGroupByRoleId($roleId, $selectedGroupId) {
        $user = Zend_Auth::getInstance()->getIdentity();
        //Fast_Debug::show('$user', $user);

        if ($user) {
            $today = array(
                'Oci' => 'sysdate',
                'Mysql' => 'NOW()'
            );
            $driver = basename(preg_replace('/_/', '/',get_class($this->_db)));
            //L’utilisateur ne peut voir que les groupes pour lesquels il peut gérer un rôle.
            $currentRoleId = $user->profile->rol_id;

            $slectedExpression = new Zend_Db_Expr('(CASE WHEN workgroup.wkg_id = :selected THEN 1 ELSE 0 END)');

            $select = $this->_db->select(); // @var $select Zend_Db_Select
            $select
            ->from('workgroup', array(
                $this->_db->quoteIdentifier('value')     => 'wkg_id',
                $this->_db->quoteIdentifier('label')     => 'wkg_label',
                $this->_db->quoteIdentifier('selected')  => $slectedExpression
            ))
            ->join('workgroup_type_role', 'workgroup.wgt_id = workgroup_type_role.wgt_id', array())
            ->join('role', 'workgroup_type_role.rol_id = role.rol_id', array())
            ->where('workgroup_type_role.rol_id = :roleId')
            ->where('rol_left >= (SELECT rol_left FROM role WHERE rol_id = :currentRole1)')
            ->where('rol_right <= (SELECT rol_right FROM role WHERE rol_id = :currentRole2)')
            ->where('wkg_begin_dt <= ' . $today[$driver])
            ->where('wkg_end_dt >= ' . $today[$driver]);
            ;
            //TODO Redéfinir les table et relations de la base
            /*Fast_Debug::show("$select",array('roleId' => $roleId,
                'selected' => $selectedGroupId,
                'currentRole' => $currentRoleId));*/
            return $this->_getList($select, array('roleId' => $roleId,
                'selected' => $selectedGroupId,
                'currentRole1' => $currentRoleId,
                'currentRole2' => $currentRoleId));
        } else {
            return array();
        }
    }

    public function getByCode($id) {
        return $this->fetchRow("workgroup.wkg_code = '".$id."'", null, false);
    }

}

et les rows de la table

Code:

<?php
/**
 * @package Fast_Module
 * @subpackage Adm_Module
 */
class Adm_Model_Group_Row extends Fast_Db_Row {
    public $_data = array(
    'wkg_id' => null,
    'wkg_code' => '',
    'wkg_label' => '',
    'parent_id' => null,
    'wkg_begin_dt' => null,
    'wkg_end_dt'   => null);

    public function __construct($config = null) {
        parent::__construct($config);
        $dbParameters = $this->getTable()->model->getParameters();

        if ('' == $this->_data['wkg_begin_dt']) {
            $this->_data['wkg_begin_dt'] = $dbParameters['firstDate'];//date('Y-m-d');
        }
        if ('' == $this->_data['wkg_end_dt']) {
            $this->_data['wkg_end_dt'] = $dbParameters['lastDate'];
        }
    }
}

pour fonctionner la table doit posséder une borne droite, une borne gauche, et un niveau hiérarchique.
ces trois champs sont à placer dans les membre de la classe table
    protected $_left = 'wkg_left';
    protected $_right = 'wkg_right';
    protected $_level = 'wkg_level';

ils servent à gérer la hierarchie par représentation intervalaire.
c'est la classe qui s'occupe de tout
Attention il faut oblicatoirement un élément racine dans la table pourque cela fonctione.

les attribut left right et level sont géré automatiquement.
le champs _parent n'est lui pas présent dans la base mais il est remonté et représent l'id du parent.

vus des objet php on a donc une liaison par id alors qu'en SQL on a une représentation intervalaire.

pour finir cet ensemble de classe fonction sour ZF1.0.4

Je ne l'ai pas porté sur la 1.5 mais j'ai déjà vérifié la faisabilité. il est necéssaire pour cela d'apporter des modification la  1.0.4 ne connait pas la classe Zend_Db_Tbale_Select

pour voir comment s'y prendre vous pouvez vous reporter à mon blog pour voir comment ajouter un champ calculé à une table dans la 1.5 c'est nécéssaire pour le _parrent.

vous aurrez surement remarqué que j'ai surchargé la méthode find.
en fait ZF ne préfixe pas els champs dans les clause where et lorsqu'on fait une jointure sur la même table ça plante

je ne sais pas si cela à été corrigé depuis.
A+JYT

Hors ligne

 

Pied de page des forums

Propulsé par PunBB
© Copyright 2002–2005 Rickard Andersson
Traduction par punbb.fr

Graphisme réalisé par l'agence Rodolphe Eveilleau
Développement par Kitpages