Zend FR

Consultez la FAQ sur le ZF avant de poster une question

Vous n'êtes pas identifié.

#1 17-03-2011 13:30:19

nuxwin
Membre
Lieu: Caen (14)
Date d'inscription: 17-03-2011
Messages: 66

API Widget

Bonjour  à tous ;

Il s'agit de mon premier post dans ce forum et j'espère donc que vous me tiendrez pas rigueur si je me trouve dans la mauvaise section.

Je travaille actuellement sur une API pour widget.

Actuellement, je procède comme suite:

J'ai une une classe abstraite qui doit être étendu par les widgets ainsi qu'une aide de vue qui est responsable de charger les widgets actives pour telle ou telle sidebar (zone de widget).


Pour exemple, cela me permet de faire ceci mon layout (ou script de vue):

Code:

[lang=php]
<?php echo $this->Widget('sidebar', array('Login', 'RecentComments'))?>

Ici, l'aide de vue Widget doit charger les widgets actives pour la zone de widget 'sidebar', et si aucune n'est trouvée (activer par l'utilisateur via le dashboard), celles par défaut (Login, RecentComments) sont chargées.

Ma question est la suivante:

Est-il correct, admit par le motif MVC de procéder comme ceci ? Je veux dire, est-il correct que cela soit une aide de vue qui invoque le modèle pour récupérer les Widget actives, exécuter certaines méthode sur chacune d'entre elle et enfin procéder à leur rendu en utilisant le code html que l'exécution de ces dernières retourne ?

J'ai pu voir plusieurs tutoriels notamment un qui s'appuie sur les aide d'action mais cela ne me convient pas vraiment. Aussi Je pourrais tous aussi bien factoriser un peu plus en créant un plugin qui chargerait les widgets et exécuterait certaines méthodes sur chacune d'entre elle et mettrait le résultat à disposition de la vue mais je me demande si c'est vraiment nécessaire.

Merci pour tout retour d'expérience en la matière.

Note: Je peux poster mon code si vous le souhaitez.

Dernière modification par nuxwin (04-04-2011 14:14:19)

Hors ligne

 

#2 17-03-2011 13:35:04

nuxwin
Membre
Lieu: Caen (14)
Date d'inscription: 17-03-2011
Messages: 66

Re: API Widget

je poste ici mon aide de vue t'elle qu'elle se trouve actuellement. Notez qu'il s'agit d'un premier jet.

Code:

[lang=php]

/**
 * @see iZend_View_Helper_Abstract
 */
require_once 'Zend/View/Helper/Abstract.php';

/**
 * Wiget view helper
 *
 * @category    iPMS
 * @package     iPMS_View
 * @subpackage  Helper
 * @author      Laurent Declercq <l.declercq@nuxwin.com>
 * @version     1.0.0
 */
class iPMS_View_Helper_Widget extends Zend_View_Helper_Abstract implements Zend_Loader_Autoloader_Interface
{
    /**
     * Widgets to be rendered
     *
     * @var array
     */
    protected $_widgets = array();

    /**
     * Render a widget sidebar
     *
     * @param  string $sidebar widget area where the widget will be rendered
     * @param array $default [OPTIONAL] list of widgets to render if none was set manually for the current sidebar
     * @return iPMS_View_Helper_Widget fluent interface, returns self
     */
    public function widget($sidebar, array $default = array())
    {
        $widgetsModel = new Model_DbTable_Widgets();
        $widgets = $widgetsModel->fetchAll(array(
            'is_active = ?' => 1,
            'sidebar = ?' => $sidebar
        ));

        if (!count($widgets)) { // Render the widgets defined by user via the dashboard
            $widgets = $widgets->toArray();
            $default = false;
        } elseif (!empty($default)) { // Render the default widgets set in layout
            $widgets = $default;
            $default = true;
        } else { // no widget to render, returns self
           return $this;
        }

        // Make the widget loader available
        $loader = Zend_Loader_Autoloader::getInstance();
        $loader->unshiftAutoloader($this);

        if ('development' != APPLICATION_ENV) {
            $loader->suppressNotFoundWarnings(true);
        }

        $instances = array();
        
        foreach ($widgets as $widget) {
            try {
                $name = ($default) ? ucfirst($widget): ucfirst($widget['name']);
                $className = 'Widget_' . $name . '_' . $name;

                /**
                 * @var $instance iPMS_Widget
                 */
                $instance = new $className($widget, $this->view);

                if($default) { // options are loaded from xml file
                    $instance->setOptionsFromXml(APPLICATION_PATH . '/widgets/' . $name . '/description.xml');
                } else { // options come from the database
                    $instance->setOptions(unserialize($widget['options']));
                }

                $hash = spl_object_hash($instance);
                $instances[$hash] = $instance;
            } catch (Exception $e) {
                if ('development' == APPLICATION_ENV) {
                    trigger_error($e->getMessage(), E_USER_ERROR);
                }
            }
        }

        $this->_widgets = $instances;

        // Widget loader is not longer needed
        Zend_Loader_Autoloader::getInstance()->removeAutoloader($this);

        return $this;
    }

    /**
     * Render a widget sidebar
     *
     * @return string HTML code that represent widget sidebar
     * @todo use iterator add add order feature
     */
    public function render()
    {
        $html = '';

        if(count($this->_widgets)) {
            /**
             * @var $widget iPMS_Widget
             */
            foreach($this->_widgets as $ref => $widget) {
                $beginTag = "<div id=\"$ref\" class=\"widget\">";
                $body = (string) $widget->_widget()->getContent();
                $endTag = "</div>";
                $html .= "$beginTag\n\t\t\t$body\n\t\t$endTag\n";
            }
        }

        return $html;
    }

    /**
     * Convenience method to render a widget sidebar
     *
     * @return string HTML representation of widget sidebar
     */
    public function __toString()
    {
        return $this->render();
    }

    /**
     * Widget autoloader
     *
     * @throws iPMS_Exception
     * @param  $class
     * @return
     */
    public function autoload($class)
    {
        if (class_exists($class, false) || interface_exists($class, false)) {
            return;
        }

        // Auto-discover the path from the class name
        // Implementation is PHP namespace-aware, and based on
        // Framework Interop Group reference implementation:
        // http://groups.google.com/group/php-standards/web/psr-0-final-proposal
        $className = ltrim($class, '\\');
        $file = '';
        $namespace = '';
        if ($lastNsPos = strripos($className, '\\')) {
            $namespace = substr($className, 0, $lastNsPos);
            $className = substr($className, $lastNsPos + 1);
            $file = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
        }

        $file .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
        $file = substr_replace($file, 'widgets', 0, 6);

        if (Zend_Loader_Autoloader::getInstance()->suppressNotFoundWarnings()) {
            @Zend_Loader::loadFile($file, APPLICATION_PATH, true);
        } else {
            Zend_Loader::loadFile($file, APPLICATION_PATH, true);
        }

        if (!class_exists($class, false) && !interface_exists($class, false)) {
            require_once 'iPMS/Exception.php';
            throw new iPMS_Exception("File \"$file\" does not exist or class \"$class\" was not found in the file");
        }
    }
}

Dernière modification par nuxwin (04-04-2011 14:15:00)

Hors ligne

 

#3 17-03-2011 21:14:44

probitaille
Membre
Lieu: Montréal
Date d'inscription: 20-04-2009
Messages: 336
Site web

Re: API Widget

Pour ma part, mon réflexe aurait été de prendre la même direction que toi. J'ai en effet développé certaines applications qui utilisaient les aides de vues dans le même but que toi. Donc selon moi c'est bon.

Par contre, je suis tout ouvert à voir d'autres propositions.

Peut-être existe-t-il une meilleure méthode de procéder ?

PS: J'aurais mis ton message dans "Motif de conception MVC"

Dernière modification par probitaille (17-03-2011 21:16:07)

Hors ligne

 

#4 18-03-2011 09:27:59

Dede
Membre
Date d'inscription: 26-06-2009
Messages: 99

Re: API Widget

Bonjour,
Je me suis intéressé à ce sujet et ma première idée étais de passer par un module ou contrôleur "widgets" qui se chargerais de la communication avec la BDD et de rendre ce qui doit l'être avec :
$this->view->action('index', 'widgets')
l'action index retournerais un rendu des actions représentant chacune un widget avec un rendu brut (sans layout).

C'est l'idée car je n'ai pas encore approfondis le sujet et je suis donc également intéresser par des retours d'expériences.
Dede


« Il ne faut pas lier un navire à une seule ancre, ni une vie à un seul espoir. »
Epictète
http://www.noumcreation.com

Hors ligne

 

#5 18-03-2011 10:36:19

nuxwin
Membre
Lieu: Caen (14)
Date d'inscription: 17-03-2011
Messages: 66

Re: API Widget

Bonjour ;

Merci pour ces deux réponses.

Créer un contrôleur spécifique pour gérer les widgets se défend mais il faut savoir qu'il devra être invoqué à chaque requête soit par l'intermédiaire de l'aide de vue action ou via l'action stack. Je penche plus pour l'utilisation de l'aide de vue action.

Ainsi, au lieu d'invoquer directement l'aide de vue Widgets dans le layout nous invoquerions l'aide de vue action comme suite:

Dans le layout:

Code:

[lang=php]
    <?php echo $this->action('sidebar', 'widgets', null, array('sidebar' => 'contentTop'));?>
    <p>The content</p>
    <?php echo $this->action('sidebar', 'widgets', null, array('sidebar' => 'contentBottom');?>
</div>
<div class="sidebar">
    <?php
        echo $this->action('sidebar', 'widgets', null,
                            array(sidebar' => 'sidebar','default' => array('Login', 'RecentComments')
                            ));
    ?>
</div>

Ici donc, nous invoquerions l'aide de vue 'action' pour chacune des zones de widgets à rendre. C'est la méthode d'action 'sidebar' du contrôleur 'Widgets' qui serait en charge d'invoquer le modèle 'Widgets' pour récupérer les widgets actives pour telle ou telle zone de widgets, créerait une instance de celles-ci, et enfin, passerais un tableau d'instances de widgets à une autre aide de vue (disons Widgets) qui serait en charge de procéder à leur rendu.

soit:

Code:

[lang=php]
class WidgetsController extends Zend_Controller_Action {

    public function sidebarAction()
    {
        $sidebar = $this->_request->getParam('sidebar');
        $widgets = $this->_getWidget($sidebar);

        if(!count($widgets)) {
            $widgets = $this->_request->getParam('default', array());
        }

        $instances = array();

        foreach($widgets as $widget) {
            ...
            $instance = new $Widget();
            $instance->run(); // On exécute le hook d'action de la widget
            $instances[spl_object_hash($instance] = $instance;
            ...
        }

        // Make the widgets available for the view via the Widgets view helper
        $this->view->Widgets()->setWidgets($instances);
    }

    protected function _getWidgets($sidebar)
    {
        $model = new Model_DbTable_Widgets();
        return $model->fetchAll(array('is_active = ?' => 1, 'sidebar = ?' =>  $sidebar))->toArray();
    }

}

L'aide de vue 'Widgets' serait invoquée dans un script de vue associé à l'action 'sidebar' comme suite:

/view/script/Widgets/sidebar.phtml

Code:

[lang=php]
<div class="widgets">
    <?php echo $this->Widgets()->render();
</div>

Bien entendu ici, l'idée est simplifiée au maximum. Ce qui m'ennuie c'est de devoir invoquer l'aide de vue 'action' plusieurs fois (pour chacune des sidebar (zone de widget) à rendre). Peut-être qu'utiliser une aide d'action ou un plugin en lieu est place du contrôleur 'Widgets' serait moins coûteux en performance, celui-ci n'étant instancié qu'une seule fois dans le bootstrap.

En ce qui concerne le passage du tableau d'instances de widgets à l'aide de vue 'Widgets', nous pourrions factoriser encore un peu plus en créant un conteneur de widgets un peu à la manière de ce qui existe déjà pour les pages (composant navigation). Cela permettrait notamment d'ordonner et afficher les widgets selon leur position (spécifiée par l'utilisateur via le dashboard). Ainsi, ce ne serait plus un tableau d'instances de widgets qui serait passé à l'aide de vue 'Widgets' mais une instance du conteneur de widgets (disons WidgetsContainer):

Code:

...
[lang=php]
$this->view->Widgets()->setContainer($container);
...

Je précise que le conteneur disposerait d'une méthode factory qui permettrait de créer les instances de widgets directement. Cette méthode prendrait en paramètre un tableau contenant le nom des widgets devant êtres instanciées.

Si vous avez d'autres idées, je suis preneur.

Dernière modification par nuxwin (04-04-2011 14:17:04)

Hors ligne

 

#6 19-03-2011 08:48:13

Dede
Membre
Date d'inscription: 26-06-2009
Messages: 99

Re: API Widget

Je suis d'accord avec ce développement,
effectivement il est possible d'initialiser un plugin qui instancierait l'object vue :

Code:

// Dans le plugin
...
$view->topWidget = $view->action('sidebar', 'widgets', null, array('sidebar' => 'contentTop'));
$view->leftWidget...
...
// dans le layout
$this->topWidget ; ...etc

J'aimerais beaucoup savoir quel serais les impact au niveau des performances de chacune des 2 méthode:
- Via un plugin
- Appel direct a la méthode action dans le layout.


« Il ne faut pas lier un navire à une seule ancre, ni une vie à un seul espoir. »
Epictète
http://www.noumcreation.com

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