Zend FR

Consultez la FAQ sur le ZF avant de poster une question

Vous n'êtes pas identifié.

#1 17-04-2008 03:11:40

apsy
Membre
Date d'inscription: 25-06-2007
Messages: 50

Et vous ? Comment sont générés vos menus ?

Bonsoir à tous,

Le titre est assez explicite. Je souhaiterais savoir comment générer vous vos menus ? Sont ils dynamiques ? Statiques ? Existe-il une méthode "élégante" de générer un menu suivant le rôle de l'utilisateur ? Et comment faites vous pour savoir quel est le "menu actif" ?

J'ai beau parcourir le web, je trouve rien de bien consistant à ce sujet... J'ai tenté une approche avec un menu basé sur un fichier XML et un controller "Navigation" dotn les actions sont appelés dans le Layout masi je suis pas convaincu du résultat. Des idées ?

Hors ligne

 

#2 17-04-2008 07:57:16

whitespirit
Membre
Date d'inscription: 25-01-2008
Messages: 393

Re: Et vous ? Comment sont générés vos menus ?

Déjà, comme beaucoup de personne ici, j'utilise le Layout de ZF. A partir de là, mon code est géré à partir d'une vue (les vues des actions s'afichent dans cette vue). Donc à partir de là c'est toi qui décide :
- soit tu gère en html / php à partir d'un fichier (le plus simple si tes menus sont statiques)
- soit tu les fais en dynamique et là à toi de voir comment gérer, ce n'est pas vraiment ZF qui va t'aider mais tes compétences. Moi j'ai fait un controlleur spécial qui me génère mes partie dynamique du menu.

Pour savoir sur quel menu tu es, c'est toi qui voit. Moi pour ne pas me prendre la tête, j'aurai créer une variable global type statut_menu contenant l'id du menu actif que j'aurai lachement abandonné dans le Registry de ZF. Après t'imagine la suite, après chaque clique sur un menu, tu récupères cette variable et tu changes sont état.

Quoi qu'il en soit, je te conseil d'utiliser des menus dynamique que si tu en as besoin. Dés fois, vaut mieux ajouter une ligne dans un fichier php que codé un gros menu dynamique qui restera pour la plupart du temps statique. Moi j'ai fini la semaine dernière mon menu dynamique, associé à un rôle, aux Acls et à une liste de menu, c'était pénible. En fait, j'ai une table menu contenant tous les menus, une table role_menu contenant l'id du menu et l'id du role associé (une relation n-n). Dans mes acls je calcul tous les menus dispo pour ce rôle et je range d'une manière élégante dans un tableau... que je balance vulgairement au Registry. Ensuite une action afficheMenu() récupère ce tableau et l'affiche selon la charte graphique adapté.

C'était mon premier post du matin smile

Hors ligne

 

#3 17-04-2008 10:47:07

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

Re: Et vous ? Comment sont générés vos menus ?

j'utilise une table menu et une table menu_role
je génère automatiquement le menu en fonction de ça
un menu qui n'est associé à aucun rôle et autorisé pour tous
un menu associé au moins à un rôle est visible que pour les rôles associés

c'est ma classe dérivée de Zend_Controler_Action qui capitalise tous les éléments commun à tous mes contrôleur qui porte les méthodes nécessaires

j'ai un paramètre dans ma config qui permet de dire au framwork d'utiliser ou non le menu
et chaque action peut de désactiver au besoin

le framework le rajoute seul à la vue.
A+JYT

Hors ligne

 

#4 17-04-2008 16:18:29

lethak
Membre
Lieu: France
Date d'inscription: 05-04-2008
Messages: 168

Re: Et vous ? Comment sont générés vos menus ?

j'ai créé des objet Menu_Block, Menu_Pool, etc..

ou que je soit dans mon application je fait

Code:

$menuDroite = new Menu();
$menuDroite->add(....);
$menuDroite->add(....);
$menuDroite->add(....);

$this->menuPool->add($menuDroite);

ensuite dans mon layout j'utilise

Code:

echo $this->menuPool->toString();

évidement toString() me génère un menu sous la forme

Code:

<ul>
   <li> <a href=.....   <li/>
   <li> <a href=.....   <li/>
<ul/>

IT Dev @Twitter

Hors ligne

 

#5 17-04-2008 20:50:52

ziedb
Membre
Lieu: Tunis
Date d'inscription: 24-03-2008
Messages: 224

Re: Et vous ? Comment sont générés vos menus ?

Bonjour
est-ce que vous pouvez nous aider plus en expliquant un peu la classe menu?
Merci

Hors ligne

 

#6 18-04-2008 12:05:22

lethak
Membre
Lieu: France
Date d'inscription: 05-04-2008
Messages: 168

Re: Et vous ? Comment sont générés vos menus ?

la classe menu est tout simplement un container avec une variable $menuArray

quand je fait appel à

Code:

$menu->Add( $href, $target, $options );

ma variable $menuArray, dans la classe Menu, prend $menuArray[]['href'] = $href; etc...


ensuite dans la classe menuPool qui est en fait un container pour empiler plusieurs menu, j'ai la fonction toString() qui va lire chaque $menuArray de chaque menu et qui va générer le html en fonction de ce qui est dans le tableau.

voila pour les grandes lignes, après si j'ai le temps de clarifier & commenter  le code et si cela intéresse, je peux publier ces classes, mais je dois aussi revoir leur fonctionnement car actuellement je n'ai pas encore testé avec ZF, mais la théorie reste la bonne.


pour le moment c'est la seule manière que j'ai vu pour faire un menu de manière programmable depuis un contrôleur, et de manière souple, sans avoir a utiliser des fichiers de templates ou des vues pour chaque menus; c'est le CSS qui en définit l'apparence; le tout sans avoir a faire du HTML dans le contrôleur.

Dernière modification par lethak (18-04-2008 12:14:06)


IT Dev @Twitter

Hors ligne

 

#7 18-04-2008 12:21:02

slaughter
Membre
Date d'inscription: 01-04-2008
Messages: 217

Re: Et vous ? Comment sont générés vos menus ?

Je suis débutant dans la manipulation du ZF et je cherche également à faire la même chose. (d'ailleurs, tout le monde doit passer par là, étonnant qu'il n'y ai pas plus de doc là dessus).

Donc si je comprend ton principe, tu utilises le MVC pour faire ton application mais pas du tout quand tu inclus tes menus. Ça me parait étrange comme façon de faire.

Est-ce que ce n'est pas les "layout" qui doivent remplir cette fonction?

Hors ligne

 

#8 18-04-2008 12:26:00

lethak
Membre
Lieu: France
Date d'inscription: 05-04-2008
Messages: 168

Re: Et vous ? Comment sont générés vos menus ?

echo $this->menuPool->toString();


c'est dans le layout wink

mon idée c'était d'avoir un objet générique réutilisable peux importe le site ou le framework.

d'ailleurs je ne l'ai pas créé pour ZF à la base, je l'ai codé pour un TP scolaire a l'arrache, c'est pour ça que je doit revoir quelques trucs avant de le publier

Dernière modification par lethak (18-04-2008 12:28:19)


IT Dev @Twitter

Hors ligne

 

#9 18-04-2008 18:22:39

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

Re: Et vous ? Comment sont générés vos menus ?

voilà ma classe menu

Code:

<?php
/** Definit le menu de l'application.
 */ 
class Fast_Menu
{
   
   public function getMenus() {
      /*$bouchon = array(
         (object)array('url' => 'adm/user/showList/',
                       'label' => 'Admin Users', 
                       'help' => 'Rechercher, modifier, ajouter des utilisateurs', 
                       'actif' => 'true'),
         (object)array('url' => 'adm/group/showList/', 
                       'label' => 'Admin Groups', 
                       'help' => 'Gestion de la structure organisationnelle de l\'application', 
                       'actif' => 'true')
      );
      return $bouchon*/
      if ($parameters = Fast_Registry::getParameters()) {
         $auth = $parameters->fast->get('auth', false);
         if ($auth) $user = Zend_Auth::getInstance()->getIdentity();
         if($auth&&$user&&isset($user->profile)) {
            $currentRoleId = $user->profile->rol_id;
            // return menu for role
            $menus = $this->_getMenusByRoleId($currentRoleId);
            return $menus;
         }
         // no menu for user without profile
      }
      // return menu for all users
      return $this->_getMenusByRoleId();
   }

   protected function _getMenusByRoleId($id = null)
   {
      Zend_Loader::loadClass('Zend_Db_Expr');
      $db = Fast_Registry::get('dbAdapter');
      // requête préparée de recherche des menus de
      // l'utilisateur selon son profil.
      $select = $db->select(); /* @var $select Zend_Db_Select */
      $select
      ->from('menu', array(
            'label' => 'mnu_label', 
            'url'   => 'mnu_url', 
            'help'  => 'mnu_help',
            'actif' => new Zend_Db_Expr('"true"')))
      ->joinLeft('menu_role', 'menu.mnu_id = menu_role.mnu_id', array())
      ->joinLeft('role', 'menu_role.rol_id = role.rol_id', array())
      ->where('menu.mnu_id IS NOT NULL')
      ->order('menu.mnu_order');
      if (null == $id) {
         $select->where('(role.rol_id IS NULL)');
      } else {
         $select->where('role.rol_id = :_rol_id OR role.rol_id IS NULL');
         $select->where('role.rol_valid = 1 OR role.rol_id IS NULL');
      }
      $statement = $db->prepare($select);
      $statement->setFetchMode(Zend_Db::FETCH_OBJ);
      if (null == $id) {
         $statement->execute();
      } else {
         $statement->execute(array('_rol_id'=>$id));
      }
      $menus = $statement->fetchAll();
      #Fast_Debug::show('menus', "$select");
      return $menus;
   }

}

dans la méthode init commune à tous mes contrôleurs

Code:

         $menu = $parameters->fast->get('menu', false);
         $db=$parameters->fast->get('db', false);
         if(!($eh&&$eh->exception instanceOf Zend_Db_Exception)&&$menu&&$db) {
            Zend_Loader::loadClass('Fast_Menu');
            $this->menu = new Fast_Menu();
         }

et ma méthode render commune à tous mes contrôleurs

Code:

   public function render($action = null, $name = null, $noController = false)
   {
      if ($parameters = Fast_Registry::getParameters()) {
         if($parameters->fast->get('menu', false)&&isset($this->menu)) {
            $this->view->menus = $this->menu->getMenus();
         }
      }
      // on ajoute à la vue le tableau de messages à diffuser
      $this->view->messages = $this->_messenger->getAll();
      // on passe la main au renderer pour afficher la vue
      parent::render($action, $name, $noController);
   }

A+JYT

Hors ligne

 

#10 19-04-2008 03:00:16

apsy
Membre
Date d'inscription: 25-06-2007
Messages: 50

Re: Et vous ? Comment sont générés vos menus ?

Pour ceux que ça intéresse (pourquoi pas le mettre dans la section "codes" du site...), voici la solution que j'ai retenu pour avoir un menu ainsi qu'un sous menu basé sur un rôle utilisateur :

Une classe "Menu" dans un fichier "Menu.php"

Code:

class Menu {

    private $_items = array();
    private $_userRole;
    
    /**
     * @param string $role
     */
    public function setUserRole($role)
    {
        $this->_userRole = $role;
    }
    
    /**
     * @param Menu_Item $item
     */
    public function addItem(Menu_Item $item)
    {
         $this->_items[] = $item;
    }
    
    /**
     * Retourne un tableau d'items de menu
     * 
     * @return array
     */
    public function getItems()
    {
       return $this->_items;
    }
    
    /**
     * @return array
     */
    public function getAllowedItems()
    {
        $allowedItems = array();
        
        foreach($this->_items as $item) {
            if ($item->isAllowed($this->_userRole)) {
                $allowedItems[] = $item;
            }
        }
        
        return $allowedItems;
    }
    
    /**
     * @return Menu_Item
     */
    public function getActiveItem()
    {
        foreach ($this->_items as $item)
        {
            if ($item->isActive()) {
                return $item;
            }
        }
        return null;
    }
}

Une deuxième classe qui est "Menu_Item" dans un fichier "Menu/Item.php" :

Code:

<?php
class Menu_Item
{
    private $_href = '';
    private $_title = '';
    private $_description = '';
    private $_active = false;
    private $_roles = array();
    private $_submenu = null;
    
    /**
     * @param string $href
     */
    public function setHref($href)
    {
        $href = '/' . ltrim($href, '/');
        $this->_href = $href;
    }

    /**
     * @return string
     */
    public function getHref()
    {
        return BASE_URI . $this->_href;
    }
    
    /**
     * @param string $title
     */
    public function setTitle($title)
    {
        $this->_title = $title;
    }
    
    /**
     * @return string
     */
    public function getTitle()
    {
        return $this->_title;
    }
    
    /**
     * @param string $description
     */
    public function setDescription($description)
    {
         $this->_description = $description;
    }
    
    /**
     * @return string
     */
    public function getDescription()
    {
        return $this->_description;
    }
    
    /**
     * @param bool
     */
    public function setActive($active)
    {
        $this->_active = (bool) $active;
    }

    /**
     * @return bool
     */
    public function isActive()
    {
        return $this->_active;
    }
    
    /**
     * @param array|string $roles
     * @param string $seperator 
     */
    public function setRoles($roles, $seperator = '|')
    {
        if (is_array($roles)) {
            $this->_roles = $roles;
        }
        else {
            $this->_roles = explode($seperator, $roles);
        }
    }
    
    /**
     * @return array
     */
    public function getRoles()
    {
        return $this->_roles;
    }
    
    /**
     * @return bool
     */
    public function isAllowed($role)
    {
        if (in_array($role, $this->_roles)) {
            return true;
        }
        else {
            return false;
        }
    }
    
    /**
     * @param $submenu Menu
     */
    public function addSubMenu(Menu $submenu)
    {
        $this->_submenu = $submenu;
    }
    
    /**
     * @return Menu
     */
    public function getSubMenu()
    {
        return $this->_submenu;
    }
}

Je n'ai pas tout documenté encore, mais si des personnes sont intéressés par ces classes, je le ferais.

Voici maintenant le XML contenant les données (fichier "menudata.xml") :

Code:

<?xml version="1.0" encoding="UTF-8"?>
<menus>
    <item href="/" roles="guest">
        <title>Retour à l'accueil</title>
        <description>Accueil</description>
    </item>
    <item href="/become-user" roles="guest">
        <title></title>
        <description>Devenez un utilisateur</description>
    </item>
    <item href="/become-client" roles="guest">
        <title></title>
        <description>Devenez client</description>
    </item>
    <item href="/who-are-we" roles="guest">
        <title>Découvrez qui nous sommes</title>
        <description>Qui sommes-nous ?</description>
    </item>
    <item href="/faq" roles="guest">
        <title>Foire aux questions</title>
        <description>FAQ</description>
        <submenu>
            <item href="/test" roles="guest">
                <title>TEST !</title>
                <description>Test</description>
            </item>
        </submenu>
    </item>
</menus>

Maintenant j'utilise deux fichiers de vues différents "primarymenu.phtml" ainsi que "secondarymenu.phtml" (un pour chaque menu) :
primarymenu.phtml :

Code:

<?php if (isset($this->items) && count($this->items) > 0) : ?>
    <ul id="primary-menu">
    <?php foreach($this->items as $item) : ?>
        <?php if ($item->isActive() === true) : ?>
        <li class="active"><a href="<?php echo $item->getHref(); ?>" title="<?php echo $item->getTitle() ?>"><?php echo $item->getDescription(); ?></a></li>
        <?php else : ?>
        <li><a href="<?php echo $item->getHref(); ?>" title="<?php echo $item->getTitle() ?>"><?php echo $item->getDescription(); ?></a></li>
        <?php endif; ?>
    <?php endforeach; ?>
    </ul>
<?php endif; ?>

secondarymenu.phtml

Code:

<?php if (isset($this->items) && count($this->items) > 0) : ?>
    <ul id="secondary-menu">
    <?php foreach($this->items as $item) : ?>
        <?php if ($item->isActive() === true) : ?>
        <li class="active"><a href="<?php echo $item->getHref(); ?>" title="<?php echo $item->getTitle() ?>"><?php echo $item->getDescription(); ?></a></li>
        <?php else : ?>
        <li><a href="<?php echo $item->getHref(); ?>" title="<?php echo $item->getTitle() ?>"><?php echo $item->getDescription(); ?></a></li>
        <?php endif; ?>
    <?php endforeach; ?>
    </ul>
<?php endif; ?>

Une classe controller "NavigationController" contenant les actions à appeler dans le layout :

Code:

<?php
/**
 * Controller permettant l'affichage des menus suivant le role de l'utilisateur
 *
 */
class NavigationController extends Zend_Controller_Action
{
    /**
     * @var Menu
     */
    private $_menu = null;
    
    private $_userRole = null;
    
    public function init()
    {
        define("BASE_URI", "http://www.monsite.com");
        $auth = Zend_Auth::getInstance();
        
        if ($auth->hasIdentity()) {
            $this->_userRole = $auth->getIdentity()->role;
        }
        else {
            $this->_userRole = 'guest';
        }
        
        $primaryMenu = new Menu();
        $primaryMenu->setUserRole($this->_userRole);
        
        $xml = simplexml_load_file('menusdata.xml');

        foreach ($xml->children() as $noeud) {
            $primaryItem = new Menu_Item();
            $primaryItem->setTitle($noeud->title);
            $primaryItem->setDescription($noeud->description);
            $primaryItem->setHref($noeud['href']);
            $primaryItem->setRoles($noeud['roles']);
            
            if ($noeud['href'] == $_SERVER['REQUEST_URI']) {
                $primaryItem->setActive(true);
            }
            
            $primaryMenu->addItem($primaryItem);
            
            if (isset($noeud->submenu)) {
                $secondaryMenu = new Menu();
                $secondaryMenu->setUserRole($this->_userRole);
                
                foreach ($noeud->submenu->children() as $subitem) {
                    $secondaryItem = new Menu_Item();
                    $secondaryItem->setTitle($subitem->title);
                    $secondaryItem->setDescription($subitem->description);
                    $secondaryItem->setHref($subitem['href']);
                    $secondaryItem->setRoles($subitem['roles']);
                    
                    if ($subitem['href'] == $_SERVER['REQUEST_URI']) {
                        $secondaryItem->setActive(true);
                        $primaryItem->setActive(true);
                    }
                    
                    $secondaryMenu->addItem($secondaryItem);
                }
                $primaryItem->addSubMenu($secondaryMenu);
            }
        }
        
        $this->_menu = $primaryMenu;
    }
    
    /**
     * Construit le menu primaire suivant les rôles définit 
     * dans le fichier XML des menus
     * 
     * @return void
     */
    public function primarymenuAction ()
    {
        $this->view->items = $this->_menu->getAllowedItems();
    }
    
    public function secondarymenuAction ()
    {
        $submenu = $this->_menu->getActiveItem()->getSubMenu();
        
        if ($submenu !== null) {
            $this->view->items = $submenu->getAllowedItems();
        }
    }
    
    public function breadcrumbAction() 
    {
    }
}

Puis l'appel aux actions de mon controller dans le fichier de layout :

Code:

<?php echo $this->doctype('XHTML1_TRANSITIONAL'); ?>
<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <?php echo $this->headTitle() ?>
        <base href="<?php echo BASE_URI ?>" />
        <?php echo $this->headScript() ?>
        <?php echo $this->headMeta() ?>
        <?php echo $this->headLink()->appendStylesheet('/styles/global.css'); ?>
        <?php echo $this->headStyle() ?>
    </head>
    <body>
        <div id="main-content">
            <?php echo $this->action('primarymenu', 'navigation'); ?>
            <?php echo $this->action('secondarymenu', 'navigation'); ?>
            
            <?php echo $this->layout()->header ?>
            <?php echo $this->layout()->content?>
            <?php echo $this->layout()->footer ?>
        </div>
    </body>
</html>

Aller, un extra !! Le fichier css (/styles/global.css) :p

Code:

/**********************
 * Menu
 **********************/
ul#primary-menu, ul#secondary-menu {
    text-transform: uppercase;
    background-color: #b0d5f7;
    color: white;
    line-height: 2em;
    clear: both;
    margin-bottom: 1em;
}

ul#secondary-menu {
    background-color: #DFF0FF;
}

ul#primary-menu li, ul#secondary-menu li {
    display: inline;
    padding: 0 1em;
    line-height: 2em;
}

ul#primary-menu a, ul#secondary-menu a {
    color: #113356;
    font-weight: bold;
}

ul#primary-menu a:hover, ul#secondary-menu a:hover {
    color: white;
}

ul#primary-menu .active, ul#secondary-menu .active {
    font-weight: bold;
    background-color: #113356;
    padding: 0 2em;
}

ul#secondary-menu .active {
    background-color: #294C6F;
}

ul#primary-menu .active a, ul#secondary-menu .active a {
    color: white;
    text-decoration: none;
}

A penser : à chaque page le script parse le fichier de données XML, je pense que la meilleure utilisation et pour un soucis de performance, serait d'utiliser Zend_Cache pour éviter cette opération.

Je suis ouvert à toutes propositions d'amélioration de mon système car je viens de le finir et je suis sur que je vais être amené à le modifier smile

Dernière modification par apsy (19-04-2008 03:42:32)

Hors ligne

 

#11 21-04-2008 10:00:54

phpman
Membre
Date d'inscription: 20-03-2008
Messages: 138

Re: Et vous ? Comment sont générés vos menus ?

Merci beaucoup pour ton module, je pense que ça va servir à beaucoup (dont moi big_smile, je vais m'en inspirer surement big_smile)

Hors ligne

 

#12 21-04-2008 19:33:29

lethak
Membre
Lieu: France
Date d'inscription: 05-04-2008
Messages: 168

Re: Et vous ? Comment sont générés vos menus ?

je pense que c'est bien pensé wink
il ressemble pas mal a ce que j'ai de mon coté mais pas totalement, si avec tout ca les gens n'ont pas d'idées ... wink


IT Dev @Twitter

Hors ligne

 

#13 21-06-2008 14:04:44

alien7
Membre
Date d'inscription: 29-04-2007
Messages: 447

Re: Et vous ? Comment sont générés vos menus ?

Bonjour,
Avez vous déjà crée un menu hiérarchique (arbre) avec mysql ? J'arrive à en faire un mais avec des récursion de partout, c'est pas très propre je dois dire.
Est ce que vous avez une bonne methode pour gerer mes menus ?
Merci d'avance


ZF 2.3 - Twitter Bootstrap 3.2
Local: Ubuntu  -> Apache2 2.4 - MariaDB 10 - PHP 5.6

Hors ligne

 

#14 21-06-2008 15:08:57

whitespirit
Membre
Date d'inscription: 25-01-2008
Messages: 393

Re: Et vous ? Comment sont générés vos menus ?

Qu'est ce que tu appelles un menu hiérarchique ?

Hors ligne

 

#15 21-06-2008 15:54:44

alien7
Membre
Date d'inscription: 29-04-2007
Messages: 447

Re: Et vous ? Comment sont générés vos menus ?

Un menu du genre ;

Code:

* 1
   * 1.1
   > 1.2
      * 1.2.1
       * 1.2.2
    * 1.3
* 2
* 3

En fait je veux représenter un arbre dans ma table menu, je veux pouvoir ajouter n sou sous menu, supprimer un noeud...

Dernière modification par alien7 (21-06-2008 15:55:51)


ZF 2.3 - Twitter Bootstrap 3.2
Local: Ubuntu  -> Apache2 2.4 - MariaDB 10 - PHP 5.6

Hors ligne

 

#16 22-06-2008 11:30:59

whitespirit
Membre
Date d'inscription: 25-01-2008
Messages: 393

Re: Et vous ? Comment sont générés vos menus ?

ok, je vois mieux ce que tu appelles menu hiérarchique mais pas contre, comment est représenté ton menu dans ta table ? La récursivité, si tu gères bien, ce n'est pas mal pour tout ça. Sinon dans ta table tu gères comment les dépendances entre le menu et son parent ?

Une fois j'ai créé un de ces types de menus, dans ma table j'avais ces champs : ID_MENU, ID_MENU_PARENT, NOM. Dans 'id_menu_parent' je mettais l'identifiant 'id_menu' du parent s'il existe, '0' sinon. Ca me permettais de gérer une arborescence en profondeur. Par contre je n'avais pas besoin d'afficher de numérotations. Mais étant donné que tu voudrais ajouter des parents/enfants à volonté, je ne crois pas que tu devrais stocker cette numérotation, car à chaque modification tu devras tout recalculer.

Mais avant d'en écrire plus, c'est bien qq chose comme ça que tu souhaites ?

Hors ligne

 

#17 22-06-2008 11:33:16

lethak
Membre
Lieu: France
Date d'inscription: 05-04-2008
Messages: 168

Re: Et vous ? Comment sont générés vos menus ?

a tout hasard:

une colonne 'id' et une colonne 'parent_id' et même une colone 'order' qui permet de choisir l'ordre d'affichage dans le noeud ?

Dernière modification par lethak (22-06-2008 11:33:47)


IT Dev @Twitter

Hors ligne

 

#18 22-06-2008 11:47:09

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

Re: Et vous ? Comment sont générés vos menus ?

pour les table hiérarchique j'utilise la représentation intervalaire c'est pas très compliqué une fois qu'on l'a mise en place, c'est un peu plus dur de comprendre au début.
voici ma classe générique table hiérarchique pour l'utiliser il faut la dériver comme avec une zend_db_table

Code:

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

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_Exception_Db(Fast_Exception_Db::UNDEFINED_LEFT_KEY);
      if (null == $this->_right)  throw new Fast_Exception_Db(Fast_Exception_Db::UNDEFINED_RIGHT_KEY);
      if (null == $this->_level)  throw new Fast_Exception_Db(Fast_Exception_Db::UNDEFINED_LEVEL_KEY);
      if (null == $this->_parent) throw new Fast_Exception_Db(Fast_Exception_Db::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());
      $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());

      $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);
    }


}

le principe consiste à numéroter les noeuds avec deux bornes une borne droite et une borne gauche
les numéros permettent alors de trouver tous les élément par rapport à un couple de borne.
un exemple de table

Code:

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

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();
      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(
               'id'=>$this->_primary[1], 
               'ident'=>'wkg_code', 
               'name'=>'wkg_label',
               'level'=>$this->_level
            ))
      ->order($this->_left, 'wkg_label')
      ;
      $user = Zend_Auth::getInstance()->getIdentity();
      if ($forParent&&$user) {
         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');
         #Fast_Debug::show("$select",false);
         return $this->_getList($select, null, false);
      } else {
         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(
                  'value'=>'wgt_id', 
                  '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 = :_currentRole)')
         ->where('rol_right <= (SELECT rol_right FROM role WHERE rol_id = :_currentRole)')
         ->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 = :_currentParent))')
            ->where('wgt_right <= (SELECT wgt_right FROM workgroup_type WHERE wgt_id = (SELECT wgt_id FROM workgroup WHERE wkg_id = :_currentParent))')
            ->where('wgt_level = (SELECT wgt_level + 1 FROM workgroup_type WHERE wgt_id = (SELECT wgt_id FROM workgroup WHERE wkg_id = :_currentParent))');
         }
         #Fast_Debug::show("$select",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('_currentRole' => $currentRoleId, '_currentParent' => $parent_id), false);
      } else {
         return array();
      }
   } 

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

      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
         ->from('workgroup', array(
            'value'    => 'wkg_id',
            'label'  => 'wkg_label',
            'selected' => '(workgroup.wkg_id = :_selected)'
            ))
         ->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 = :_id')
         ->where('rol_left >= (SELECT rol_left FROM role WHERE rol_id = :_currentRole)')
         ->where('rol_right <= (SELECT rol_right FROM role WHERE rol_id = :_currentRole)')
         ;
/*Fast_Debug::show("$select",array('_id' => $roleId,
                                               '_selected' => $selectedGroupId,
                                               '_currentRole' => $currentRoleId));*/

         return $this->_getList($select, array('_id' => $roleId, 
                                               '_selected' => $selectedGroupId, 
                                               '_currentRole' => $currentRoleId));
      } else {
         return array();
      }
   } 

}

je ne vous mets pas ma classe Fast_Db_Table qui contient un mécanisme permettant d'ajouter une condition supplémentaire sur la table à partir du membre _restrict et qui contient la définition de la méthode _getList qui retourne un tableau d'objet à partir d'un objet select.
vous pouvez voir dans cet exemple l'utilisation de plusieurs table hiérarchique jointes workgroup role et workgroupe_type

enfin cette classe table hiérarchique introduit un élément non enregistré en base en effet pour facilité l'usage en php cette classe ajoute un champ dans l'objet qui n'est pas enregistré cet objet est l'id du parent nommé parent_id. les bornes et level ne sont pas et ne doivent pas être gérée par le php appelant c'est du ressort de la classe elle même. d'un point de vus php on a donc une hérarchie à base d'id avec le parent_id et dans la base une hiérarchie par représentation intervalaire.
les méthodes de la classe Fast_Db_Herrachical prennent en charge la gestion particulière de ce champs.
elle est écrite pour la version 1.0.3 de ZF JE l'ai porté pour test en 1.5 mais ZF 1.5 introduit une nouvelle classe
Zend_Db_Table_Select et mon développement demanderait à être repris pour être mieux conforme à cette nouvelle approche. je ne l'ai pas encore fait car ZF 1.5 n'est toujours pas d'actualité dans mon entreprise.

A+JYT

Dernière modification par sekaijin (22-06-2008 11:59:58)

Hors ligne

 

#19 22-06-2008 12:27:10

alien7
Membre
Date d'inscription: 29-04-2007
Messages: 447

Re: Et vous ? Comment sont générés vos menus ?

lethak ->
C'est pas très pratique, si mes sous-level etait defini à 3 ca passait encore, mais là je peux avoir 5 ou 6 sous level (ex: 1.2.2.3.5.6).

Pour l'instant j'utilise cette méthode :
champs : categories_id | label | children
exemple : 1 | root | 2:3:4:5:6:7

Dans le champs children je met la liste de ces children et chaque children à aussi ces children. Pour afficher je fai sune récursion sur mon Helper pour afficher mon menu.J'ai à peu près 400 menu. Dans la partie admin les perfs se recent. Pour le site je peux encore mettre en cache le résultat.

sekaijin ->
Plutot que de reprendre betement tes class, j'aimerais bien essayé de comprendre et de faire ma propre class, tu utilise cette approche là ?
http://sql.developpez.com/arborescence/


ZF 2.3 - Twitter Bootstrap 3.2
Local: Ubuntu  -> Apache2 2.4 - MariaDB 10 - PHP 5.6

Hors ligne

 

#20 22-06-2008 15:30:34

alien7
Membre
Date d'inscription: 29-04-2007
Messages: 447

Re: Et vous ? Comment sont générés vos menus ?

Le problème est que je dois developpé une section admin, je ne m'occuperai peut etre pas du site après son developpement, donc je dois prévoir dans la partie admin l'ajout et la suppression de menu.  Donc je ne peux pas mettre vraiment en cache vu qu'il y'aura des changements. Je cherche aussi à faire ca bien et apprendre, d'ou ma question.

Edit : j'ai posté à un post que whitespirit a effacer je pense.

Dernière modification par alien7 (22-06-2008 15:32:57)


ZF 2.3 - Twitter Bootstrap 3.2
Local: Ubuntu  -> Apache2 2.4 - MariaDB 10 - PHP 5.6

Hors ligne

 

#21 22-06-2008 16:04:54

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

Re: Et vous ? Comment sont générés vos menus ?

alien7 a écrit:

sekaijin ->
Plutot que de reprendre betement tes class, j'aimerais bien essayé de comprendre et de faire ma propre class, tu utilise cette approche là ?
http://sql.developpez.com/arborescence/

oui j'utilise cette approche et je l'encapsule dans la classe de base pour la masque à php

A+JYT

Hors ligne

 

#22 22-06-2008 17:08:39

alien7
Membre
Date d'inscription: 29-04-2007
Messages: 447

Re: Et vous ? Comment sont générés vos menus ?

Ok merci je vais travailler dessus en m'aidant de tes class.
Merci sekaijin


ZF 2.3 - Twitter Bootstrap 3.2
Local: Ubuntu  -> Apache2 2.4 - MariaDB 10 - PHP 5.6

Hors ligne

 

#23 22-06-2008 18:40:22

lethak
Membre
Lieu: France
Date d'inscription: 05-04-2008
Messages: 168

Re: Et vous ? Comment sont générés vos menus ?

alien7 a écrit:

Donc je ne peux pas mettre vraiment en cache vu qu'il y'aura des changements.

pourquoi pas ? avec une durée de vie limitée.


IT Dev @Twitter

Hors ligne

 

#24 23-06-2008 09:08:10

whitespirit
Membre
Date d'inscription: 25-01-2008
Messages: 393

Re: Et vous ? Comment sont générés vos menus ?

alien7: oui j'ai effacé mon post car ma question ne faisait pas avancer tes recherches (tu es trop rapide à répondre smile). Actuellement, j'ai donc une gestion basique de mes menus (ID_MENU, ID_PARENT, LABEL...) et effectivement je met tout en cache pour éviter de charger à chaque fois. Dans ma logique, quand les menus sont au points, on ne les modifies plus (ce n'est pas un cms, mais un crm dont il s'agit). Quoi qu'il en soit, j'ai bien regardé ton lien sur concernant la hiérarchie de menu et j'ai été totalement conquis par cette approche qui est super bien pensé !!!! En plus, ce n'est pas très compliqué à comprendre. Je vais modifier ma gestion de menu à l'occaz.

Hors ligne

 

#25 02-07-2008 06:53:03

legal101205
Nouveau membre
Date d'inscription: 29-10-2007
Messages: 1

Re: Et vous ? Comment sont générés vos menus ?

apsy a écrit:

à chaque page le script parse le fichier de données XML, je pense que la meilleure utilisation et pour un soucis de performance, serait d'utiliser Zend_Cache pour éviter cette opération.

Je suis ouvert à toutes propositions d'amélioration de mon système car je viens de le finir et je suis sur que je vais être amené à le modifier smile

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