Pages

Date 21 octobre 2012

Jobeet ZF2 - Jour 4 - Controller et Vue

Après quelques jours sans nouvelle, me revoilà avec un nouveau tutoriel.

Dans le cours précédent, nous avons mis en place l'accès aux données.
Nous allons voir aujourd'hui comment mettre en place un contrôler et une vues.
Nous sommes enfin prêt à travailler sur les différents scénarios que nous avons présenté lors du jour 2:
  • En tant qu'utilisateur, j'accède à la page d'accueil du site
  • En tant qu'utilisateur, j'accède à la page d'une catégorie
  • En tant qu'utilisateur, je clique sur un emploi pour voir sa fiche détaillée

Au programme du jour (pas forcément dans cet ordre):
  • L'architecture MVC
  • Mise en place du Layout de l'application
  • Création de la page d'accueil du site Jobeet
  • Suppression du module Application

L'architecture MVC

Avant l’émergence des frameworks PHP, nous avions l'habitude de créer un fichier PHP par page: configurations diverses, récupération des données, traitements eventuelles de celles-ci puis affichage du code HTML. On se retrouvait donc avec du HTML mélangé avec du PHP, et plusieurs fichiers plus ou moins bien organisés

Puis les moteurs de template sont apparus: séparation de la logique et de la présentation. mais avec un effet pervers: beaucoup de code à maintenir, un "langage" de template à apprendre.

Aujourd'hui la plupart des solutions que nous utilisons sont basées sur l'architecture MVC (Modèle-Vue-Contrôleur). Pour résumer, l'architecture MVC  permet de mettre en place une organisation de notre code via une séparation en 3 couches.
Representation de l'architecture MVC

La couche Modèle

C'est elle qui contient la logique de nos données (les accès à la base de données se trouvent dans cette couche). Dans Zend Framework 2, les modèles se trouvent dans module/[nom-du-module]/src/[namespace]/Model

La couche Vue

C'est la partie présentation (ce que l'on voit sur le site). Dans ZF2, les vues sont situées dans module/[nom-du-module]/View

La couche Contrôleur

C'est cette couche qui récupère les données et les passe à la Vue pour le rendu.


Le Layout de l'application

Qu'est-ce que le layout ?
Il s'agit d'une vue particulière, qui englobe les autres vues de contenu de l'application.
La version 1.x du framework séparait la notion de layout et de vue. Ce n'est plus le cas avec la version 2. Zend Framework 2 propose une hiérarchie de vue, afin de déterminer la vue qui servira de layout. De plus, il est maintenant possible d'imbriquer une vue dans une vue.


Notre layout contiendra:

  • un entête (titre du site + logo)
  • un corps (qui contiendra les vues associées a nos controlleurs/actions)
  • un pied de page (avec différents liens)

Voici le code de notre layout:
// module/Front/view/layout/layout.phtml
<?php echo $this->doctype(); ?>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <?php
            echo $this->headLink(array('rel' => 'shortcut icon', 'type' => 'image/vnd.microsoft.icon', 'href' => $this->basePath() . '/favicon.ico'))
                      ->prependStylesheet($this->basePath() . '/css/main.css');
        ?>
        <title>ZF2 - Jobeet</title>
    </head>
    <body>
        <div id="container">
            <div id="header">
                <div class="content">
                    <h1>
                        <a href="#">
                            <img src="/images/logo.jpg" alt="Jobeet Job Board" />
                        </a>
                    </h1>
                    <div id="sub_header">
                        <div class="post">
                            <h2>Ask for people</h2>
                            <div>
                                <a href="#">Post a Job</a>
                            </div>
                        </div>
                        <div class="search">
                            <h2>Ask for a job</h2>
                            <form action="#" method="get">
                                <input type="text" name="keywords" id="search_keywords" />
                                <input type="submit" value="search" />
                                <div class="help">Enter some keywords (city, country, position, ...)</div>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
            <div id="content">
                <div class="content">
                    <?php echo $this->content; ?>
                </div>
            </div>
            <div id="footer">
                <div class="content">
                    <span class="symfony">
                        <img src="/images/jobeet-mini.png" alt="logo jobeet" /> powered by <a href=""><img src="/images/zf2-logo.png" alt="Zend Framework 2" /></a>
                    </span>
                    <ul>
                        <li><a href="">About Jobeet</a></li>
                        <li class="feed"><a href="">Full feed</a></li>
                        <li><a href="">Jobeet API</a></li>
                        <li class="last"><a href="">Affiliates</a></li>
                    </ul>
                </div>
            </div>
        </div>
    </body>
</html>

En regardant le code de plus près, vous aurez remarqué un peu de code PHP, que je vais vous expliqué brièvement ici:
echo $this->doctype();
Il s'agit d'une aide de vue: il s'agit d'une classe d'aide permettant d'effectuer des actions répétitives (formatter une date, générer des liens, ...) ou complexe. L'aide de vue Doctype affiche automatiquement le doctype.

echo $this->headLink(...)->prependStylesheet($this->basePath() . '/css/main.css');
Cette aide de vue permet ajouter une feuille de style.

Zend Framework 2 (comme le 1) possède un grand nombre d'aide de vue, que je vous invite à consulter ici. Nous seront amener à en utiliser d'autre ultérieurement.

Les feuilles de style et les images

Ce tutoriel concernant Zend Framework 2, et non le web design, nous utiliserons les feuilles de style et les images du tutoriel Jobeet de Symfony pour donner un look à notre site. Décompresser cette archive dans le répertoire public de notre application (vous aurez donc un répertoire css, un répertoire images et un favicon dans ce répertoire public)

Création de la page d'accueil

Le layout et le design étant en place, nous allons enfin attaquer notre première page: la page d'accueil.

Créons maintenant un Contrôleur Index (dans module/Front/src/Front/Controller/):
namespace Front\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Front\Model\JobTable;

class IndexController extends AbstractActionController
{
    protected $jobTable;
    protected $categoryTable;
    
    public function getJobTable()
    {
        if (!$this->jobTable) {
            $sm = $this->getServiceLocator();
            $this->jobTable = $sm->get('Front\Model\JobTable');
        }
        return $this->jobTable;
    }
    
    public function getCategoryTable()
    {
     if (!$this->categoryTable) {
      $sm = $this->getServiceLocator();
      $this->categoryTable = $sm->get('Front\Model\CategoryTable');
     }
     return $this->categoryTable;
    }
    
    // Action index qui sera appelé par défaut
    public function indexAction()
    {
        $categories = $this->getCategoryTable()->fetchAll();
        $results = array();
        
        foreach ($categories as $category) {
            $jobs = $this->getJobTable()->fetchAllByIdCategory($category->idCategory);
            $results[$category->name] = $jobs;
        }
        
        return new ViewModel(
            array(
                'categories' => $results,
            )
        );
    }
}

Quelques explications sont nécessaires pour bien comprendre ce que vous venez de voir.
Dans Zend Framework 2, le contrôleur est une classe nommé par convention {Nom}Controler et doit commencer par une majuscule (IndexControler, CategoryControler, ...).
Notre contrôleur contient une action IndexAction (toutes les méthode doivent respecter la même convention de nommage {Nom}Action , doivent commencer par une majuscule et sont obligatoirement publique).

Les contrôleurs doivent implémenter l'interface Zend\Stdlib\Dispatchable. Pour cela, Zend Framework met à notre disposition deux classe abstraites:
  • Zend\Mvc\Controller\AbstractActionController: il s'agit du contrôleur standard
  • Zend\Mvc\Controller\AbstractRestfulController: pourra être utilisé pour écrire un WebService RESTful

Ce nouveau contrôleur doit être déclaré dans config/module.config.php de notre module Front.
return array(
    [...]
    'controllers' => array(
        'invokables' => array(
            'Front\Controller\Index' => 'Front\Controller\IndexController',
            'Front\Controller\Category' => 'Front\Controller\CategoryController',
            'Front\Controller\Job' => 'Front\Controller\JobController',
        ),
    ),
    [...]
);


Créons ensuite la vue index.phtml associée à l'action index de notre contrôleur Index dans (module/Front/view/front/index):
<?php
    $this->headLink()->appendStylesheet('/css/jobs.css');
?>
<div id="job_history">Recent viewed jobs:</div>
<?php foreach ($this->categories as $category => $jobs): ?>
    <h1><?php echo $category; ?></h1>
    <table class="jobs">
    <tbody>
        <?php foreach($jobs as $job): ?>
        <tr class="<?php echo $this->cycle(array("even", "odd"))->next();?>">
            <td><?php echo $this->escapeHtml($job->location); ?></td>
            <td>
                <a href="">
                    <?php echo $this->escapeHtml($job->position); ?>
                </a>
            </td>
            <td><?php echo $this->escapeHtml($job->company); ?></td>
        </tr>
        <?php endforeach;?>
    </tbody>
</table>
<?php endforeach;?>
Cette vue sera insérée dans notre layout. Vous pouvez voir l'utilisation de 2 aides de vue: appendStyleSheet() pour ajouter une nouvelle feuille de style et cycle() pour alterner une série de valeur (utilisé ici pour gérer deux classes CSS différentes, pour gérer l'alternance de couleur des lignes du tableau).

Suppression du module Application

Nous avons créé le premier contrôler de notre module Front.
Le squelette que nous avons utilisé contenait un module Application qui ne nous est pas utile.
Nous allons donc récupérer quelques bouts de code en les copiant dans le répertoire de notre module Front.

Copiez d'abord le répertoire error (dans view) du module Application pour le coller dans notre module Front (dans le répertoire view).

Nous allons aussi modifier notre fichier Module.php pour y ajouter ça:
use Zend\Mvc\MvcEvent;

use Front\Model\CategoryTable;
use Front\Model\JobTable;
use Front\Model\UserTable;
use Zend\ModuleManager\ModuleManager; // Ajoutez cette ligne
use Zend\Mvc\ModuleRouteListener;     // Et celle-là

class Module
{
    // Ajoutez cette méthode
    public function onBootstrap(MvcEvent $e)
    {
        $e->getApplication()->getServiceManager()->get('translator');
 $eventManager        = $e->getApplication()->getEventManager();
 $moduleRouteListener = new ModuleRouteListener();
 $moduleRouteListener->attach($eventManager);
    }
    [...]
}
Cette méthode permettra de gérer les traductions de l'application (que nous aborderons plus tard). Nous l'ajoutons maintenant car les vues error que nous venons de copier en ont besoin (si vous ouvrez une des vues error, vous verrez $this->translate(...) qui permet de traduire une chaine de caractères.

Nous devons aussi ajouter une route pour que notre module soit chargé par défaut. Nous verrons les route dans le jour 5. Modifiez juste le fichier /config/module.config.php de notre module:
return array(
    // La route dont nous avons besoin
    'router' => array(
        'routes' => array(
            'home' => array(
                'type' => 'Zend\Mvc\Router\Http\Literal',
                'options' => array(
                    'route'    => '/',
                    'defaults' => array(
                        '__NAMESPACE__' => 'Front\Controller',
                        'controller' => 'Index',
                        'action'     => 'index',
                    ),
                ),
            ),
        ),
    ),
    // Translator, qui permettra plus tard de gérer les traduction de notre application (plus tard)
    'service_manager' => array(
        'factories' => array(
            'translator' => 'Zend\I18n\Translator\TranslatorServiceFactory',
        ),
    ),
    'translator' => array(
        'locale' => 'en_US',
        'translation_file_patterns' => array(
            array(
                'type'     => 'gettext',
                'base_dir' => __DIR__ . '/../language',
                'pattern'  => '%s.mo',
            ),
        ),
    ),
    // Nos contrôleurs
    'controllers' => array(
        'invokables' => array(
            'Front\Controller\Index' => 'Front\Controller\IndexController',
            'Front\Controller\Category' => 'Front\Controller\CategoryController',
            'Front\Controller\Job' => 'Front\Controller\JobController',
        ),
    ),
    // Quelques paramètres pour notre application
    'view_manager' => array(
        'display_not_found_reason' => true,
        'display_exceptions'       => true,
        'doctype'                  => 'HTML5',       // On défini notre doctype
        'not_found_template'       => 'error/404',   // On indique la page 404
        'exception_template'       => 'error/index', // On indique la page en cas d'exception
        'template_map' => array(
            'layout/layout'           => __DIR__ . '/../view/layout/layout.phtml',
        ),
        'template_path_stack' => array(
            __DIR__ . '/../view',
        ),
    ),
);

Vous pouvez maintenant effacer le répertoire du module Application.

Si tout va bien, et que je n'ai rien oublié (ce qui serait possible, car j'écrit ces cours presque en même temps que j'avance le code de Jobeet et que je découvre Zend), vous devriez pouvoir accéder à votre page d'accueil (allez insérer quelques données, telles que 2 ou 3 catégories et 2-3 emplois, dans la base MySQL) :

Page d'accueil de Jobeet


Si cela ne fonctionne pas chez vous, téléchargez le code source du jour 4 sur mon compte Github et comparez avec ce que vous avez (ou remplacez).

C'est fini pour ce jour 4.

N'hésitez pas à me laissez des commentaires si vous avez des questions ou si vous avez des remarques !

56 commentaires:
  1. Les recommandations de ZF ont changées entre ZF1 et ZF2 ? Car je vois de variables en protected ne commençant pas par _.
    Attention, le schéma pour énoncé le MVC est faux, attention aux liaisons.
    L'imbrication des vues existait dans ZF1.
    Tu n'as pas rencontré de problème pour le moment avec la traduction ? Tu optes pour du gettext ? Car tu vas voir au niveau de la détection par le navigateur c'est bien moins intuitif.

    RépondreSupprimer
  2. L'utilisation du underscore dans des attributs protected ou private n'est pas obligatoire: "For variables that are declared with private or protected visibility, the first character of the variable name MAY be a single underscore." => le MAY est important

    Tu peux aller voir les codings standards pour ZF2 ici: http://framework.zend.com/wiki/display/ZFDEV2/Coding+Standards

    Pour les traductions, je ne passerais pas par la détection du navigateur, mais par un choix de l'utilisateur (flag, selectbox).

    RépondreSupprimer
    Réponses
    1. La raison pour ne pas mettre de underscore: "This is the only acceptable application of an underscore in a variable name, and is discouraged (as it makes refactoring to public visibility more difficult)."

      Supprimer
  3. Merci pour le lien (c'est justement pour ça que je posais la question). Je continuerai donc d'utiliser le _ car j'aime bien savoir si telle ou telle variable est public directement.
    Pour la traduction tu fais bien, car la détection du navigateur ne semble pas tout à fait au point encore.
    Sinon tu as essayé de créer ta propre librairie ? (attention je parle pas de module) car j'ai pas trouvé ça très intuitif.

    RépondreSupprimer
    Réponses
    1. Pour la création d'une librairie (pour l'instant, je n'en ai pas fait pour Jobeet), mais créer une lib est presque identique à la création d'un module (la structure est très proche) => l'essentiel étant de bien comprendre le fichier Module.php (et du fichier autoload_classmap.php)

      Supprimer
  4. Loïc tu es obsolète pour le coup ;) les underscore c'était utile en php4 quand il n'y avait pas les private et protected sur les attributs de classe. Maintenant ce n'est plus du tout utilisé et absolument pas considéré comme une bonne pratique.

    RépondreSupprimer
  5. Cyprien tu fais erreur, cela est frequemment utilisé en PHP5. Cela n'est plus obligatoire dans ZF mais cela ne le rend pas pour autant deprecated. Quand tu bosses dans une classe contenant pas mal de méthodes, tu sais rapidement si ta variable est public ou non. Prouve moi que cela n'est pas une mauvaise pratique et en suite nous verrons ;).
    Romain, c'est justement pour ça que j'ai bien précisé "pas comme les modules", car il est possible de créer une librairie permettant la surcharge de méthode ZF, tout à fait différent de la notion de module.

    Cyprien, tu devrais regarder comment sont utilisés les annotations sur SF, j'espère qu'il est possible de les désactiver ;).

    RépondreSupprimer
  6. De manière générale je suis contre ces pratiques qui consistent à contourner les "limitations" d'un langage avec des conventions de code ou de commentaire. Et les underscore sont exactement ça à la base : un moyen visuel de s'empêcher d'utiliser des propriétés que l'on considère comme internes à la classe (puisque php4 ne permettait pas de protéger ces propriétés au niveau du code). Maintenant si tu l'utilises et trouves ça pratique libre à toi, moi je dis juste que ce n'est pas une bonne pratique, d'après moi, et d'après les codings standards de nombreuses librairies php un tant soit peu modernes (donc pas ZF1 :p). D'ailleurs comme le fait remarquer Romain (et ZF2) si tu veux changer la visibilité d'une propriété, ça impose d'en modifier le nom partout où tu l'as utilisée.
    Et si tu veux troller sur Symfony, tu peux choisir ton format de configuration en annotation, yml, xml, ...(waaa un framework flexible et découplé !)
    ;)

    RépondreSupprimer
  7. Pour info: La norme PSR-2 indique (point 4.2) que l'underscore ne devrait pas être utilisé comme préfixe du nom de la propriété pour indiquer sa visibilité private ou protected :-)

    Property names SHOULD NOT be prefixed with a single underscore to indicate protected or private visibility.

    RépondreSupprimer
  8. @Cyprien: D'un coté, il y a les "préconisations" et de l'autre les préférences personnelles :-)
    @Loïc: Avec un bon IDE, si tu commente bien tes classes, tes propriétés, etc, avec des commentaire style PHPDoc, tu peux voir rapidement la visibilité :-)

    Après c'est une question de goût et d'habitude. Il y a bien des personnes qui developpent en PHP en suivant la notation hongroise...C'est aussi un choix personnel :-)

    RépondreSupprimer
  9. @Romain, oui tout à fait d'accord pour l'IDE mais je parlais de façon générale.
    @Cyprien, si nous t'coutons, les conventions de nommages serait totalement obsolète.
    Je rappelle quand même quelque chose qui va outre les convetions de tel ou tel framework, ce qui est important comme le signale Romain au niveau de certaines conventions, c'es de garder les même convetions et quelles soient connus.
    (Il ne faut pas être borné, sinon le jour où tu arrives dans une nouvelle boite tu vas pas dire les convetions ce n'est pas bon, ce qui compte c'est que tous les développeurs de la boite utilisent les même convetions et que celles ci soient explicite.

    La plupart des frameworks de nos jours sont flexible et heureusement (SF1 n'était pas flexible tout l'opposé de ZF ^^). Je sais bien qu'il est possible de faire en yaml ou xml. D'ailleurs c'est exactement ce qui est fait sous ZF2 mais pas les même formats.
    Ce que je veux dire, c'est que c'est bien de prévoir des annotations dans les fichiers PHP mais quand tu vois comment c'est géré, ça fait peur tout de même.

    Il ne faut pas vouloir faire ce que Java fait obligatoirement, car les annotations en Java sont traitées lors de la compilation.
    Du coup je réitère ma question, est il possible de désactivés le système d'annotations de SF2, car ne pas utiliser les annotations n'empêche pas le mécanisme d'être présent et de vérifier si des annotations sont présentes ou non.

    Et je ne suis pas la pour critiquer SF2, bien au contraire, je vais faire des deux conjointement car ils sont tout de même des références, même si je trouve que ZF2 n'est pas complètement fini pour le moment.

    RépondreSupprimer
  10. Bonjour Romain,

    Est ce que le projet Jobeet sur ZF2 est toujours d'actualité. Si je ne me trompe pas je n'ai pas vu de nouvelles publications depuis cet article. Attention, ce n'est absolument pas un reproche ni une critique. Je souhaiterai juste savoir si les autres articles sont prévus dans ton planning.

    Merci de tes retours.

    Cordialement,
    Fred

    RépondreSupprimer
    Réponses
    1. Bonjour Frédéric,

      Effectivement, il y a eu un petit temps d'arrêt. Depuis un mois, je n'ai pas eu beaucoup de temps libre pour moi.
      Je pense reprendre la suite de ce tuto prochainement:certainement un nouvel article en début (ou milieu) de semaine prochaine.

      En tout cas, ça fait plaisir de voir que des personnes sont intéressés pour lire la suite :-)

      Cordialement,
      Romain

      Supprimer
    2. Avec impatience je dirai même :)
      Courage !!

      Supprimer
    3. Bon, encore pas mal occupé cette semaine...
      Je vais essayer de finir ce weekend (j'ai un petit soucis avec les tests unitaires et l'injection de dépendance dans les controleurs).

      Merci pour le soutien :-)

      Supprimer
  11. Dans la méthode de 'IndexController::indexAction', tu utilises 'JobTable::fetchAllByIdCategory' qui n'existe pas. Il faut donc ajouter dans module/Front/src/Front/Model/Jobtable:

    public function fetchAllByIdCategory($id_category) {
    $resultSet = $this->select(array('id_category'=>$id_category));
    return $resultSet;
    }

    RépondreSupprimer
  12. Coucou Romain !

    J'ai un problème avec ce jour 4, et voici le message d'erreur :

    Additional information:
    Zend\ServiceManager\Exception\ServiceNotFoundException

    File:
    D:\eclipse-php\workspace\jobeet\vendor\zendframework\zendframework\library\Zend\ServiceManager\ServiceManager.php:456

    Message:
    Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Zend\Db\Adapter\Adapter


    Même en comparant avec l'ariche que tu a mis à disposition, je ne trouve pas d'où vient le problème. Saurais-tu d'où cela vient ?

    Merci d'avance

    RépondreSupprimer
    Réponses
    1. l'ariche = l'archive :/

      Supprimer
    2. Salut Biloute,
      Tu n'as pas eu de soucis avec les tutos précédents ?

      A priori, ton message d'erreur indiquerait un pb de création de l'adapter pour la base de données.
      Regarde dans le fichier /config/autoload/database.local.php (il te faudra certainement changer le nom de la base, login ou password en fonction de ce que tu as reellement dans MySQL).

      Supprimer
  13. une question qui me revient souvent depuis la version 1 de Zend.
    j'ai souvent ces erreurs dans view

    "NetworkError: 404 Not Found - http://localhost/images/logo.jpg"
    "NetworkError: 404 Not Found - http://localhost/images/zf2-logo.png"
    "NetworkError: 404 Not Found - http://localhost/images/jobeet-mini.png"
    "NetworkError: 404 Not Found - http://localhost/public/images/jobeet-mini.png"

    Est t'il nécessaire de faire un $this->baseUrl() (ou $this->basePath() dans Zend 2) dans tous les liens des view? ou bien il y'a une configuration à faire, et c'est pour cette raison que tu l'as pas ajouté dans l'application jobeet que tu as partagé?!

    RépondreSupprimer
  14. Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Zend\Db\Adapter\Adapter

    comment je fais avec ce type d'erreur

    RépondreSupprimer
  15. Bonjour

    il faut ajouter le fichier /config/autoload/database.local.php

    $dbParams = array(
    'database' => 'jobeet',
    'username' => 'jobeet',
    'password' => 'jobeet',
    'hostname' => 'localhost'
    );

    return array(
    'service_manager' => array(
    'factories' => array(
    'Zend\Db\Adapter\Adapter' => function ($sm) use ($dbParams) {
    $adapter = new BjyProfiler\Db\Adapter\ProfilingAdapter(
    array(
    'driver' => 'pdo',
    'dsn' => 'mysql:dbname='.$dbParams['database'].';host='.$dbParams['hostname'],
    'database' => $dbParams['database'],
    'username' => $dbParams['username'],
    'password' => $dbParams['password'],
    'hostname' => $dbParams['hostname'],
    'driver_options' => array(
    PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
    ),
    )
    );

    $adapter->setProfiler(new BjyProfiler\Db\Profiler\Profiler);
    $adapter->injectProfilingStatementPrototype();
    return $adapter;
    },
    ),
    ),
    );

    RépondreSupprimer
    Réponses
    1. Merci Fadel pour la réponse pour Thity14 :-)
      Il me semblait que le fichier était dispo dans l'archive

      Supprimer
    2. ;)

      et ce qu'on peux créer une communauté pour faire avancer les tutos ? mm creer des articles sur ZF2 ?

      Supprimer
  16. Fatal error: Uncaught exception 'RuntimeException' with message 'Unable to load ZF2. Run `php composer.phar install` or define a ZF2_PATH environment variable.' in C:\wamp\www\zendJobeet\init_autoloader.php on line 48

    et pourtan j'ai suivie le tuto des album et ca marché bien??

    RépondreSupprimer
  17. An error occurred during execution; please try again later.
    Additional information:
    Zend\ServiceManager\Exception\ServiceNotFoundException
    File:
    C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\ServiceManager\ServiceManager.php:495
    Message:
    Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Zend\Db\Adapter\Adapter
    Stack trace:
    #0 C:\wamp\www\zendJobeet\module\Front\Module.php(46): Zend\ServiceManager\ServiceManager->get('Zend\Db\Adapter...')
    #1 [internal function]: Front\{closure}(Object(Zend\ServiceManager\ServiceManager), 'frontmodelcateg...', 'Front\Model\Cat...')
    #2 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\ServiceManager\ServiceManager.php(852): call_user_func(Object(Closure), Object(Zend\ServiceManager\ServiceManager), 'frontmodelcateg...', 'Front\Model\Cat...')
    #3 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\ServiceManager\ServiceManager.php(984): Zend\ServiceManager\ServiceManager->createServiceViaCallback(Object(Closure), 'frontmodelcateg...', 'Front\Model\Cat...')
    #4 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\ServiceManager\ServiceManager.php(597): Zend\ServiceManager\ServiceManager->createFromFactory('frontmodelcateg...', 'Front\Model\Cat...')
    #5 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\ServiceManager\ServiceManager.php(556): Zend\ServiceManager\ServiceManager->doCreate('Front\Model\Cat...', 'frontmodelcateg...')
    #6 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\ServiceManager\ServiceManager.php(480): Zend\ServiceManager\ServiceManager->create(Array)
    #7 C:\wamp\www\zendJobeet\module\Front\src\Front\Controller\IndexController.php(26): Zend\ServiceManager\ServiceManager->get('Front\Model\Cat...')
    #8 C:\wamp\www\zendJobeet\module\Front\src\Front\Controller\IndexController.php(33): Front\Controller\IndexController->getCategoryTable()
    #9 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\Mvc\Controller\AbstractActionController.php(83): Front\Controller\IndexController->indexAction()
    #10 [internal function]: Zend\Mvc\Controller\AbstractActionController->onDispatch(Object(Zend\Mvc\MvcEvent))
    #11 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\EventManager\EventManager.php(468): call_user_func(Array, Object(Zend\Mvc\MvcEvent))
    #12 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\EventManager\EventManager.php(207): Zend\EventManager\EventManager->triggerListeners('dispatch', Object(Zend\Mvc\MvcEvent), Object(Closure))
    #13 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\Mvc\Controller\AbstractController.php(117): Zend\EventManager\EventManager->trigger('dispatch', Object(Zend\Mvc\MvcEvent), Object(Closure))
    #14 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\Mvc\DispatchListener.php(114): Zend\Mvc\Controller\AbstractController->dispatch(Object(Zend\Http\PhpEnvironment\Request), Object(Zend\Http\PhpEnvironment\Response))
    #15 [internal function]: Zend\Mvc\DispatchListener->onDispatch(Object(Zend\Mvc\MvcEvent))
    #16 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\EventManager\EventManager.php(468): call_user_func(Array, Object(Zend\Mvc\MvcEvent))
    #17 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\EventManager\EventManager.php(207): Zend\EventManager\EventManager->triggerListeners('dispatch', Object(Zend\Mvc\MvcEvent), Object(Closure))
    #18 C:\wamp\www\zendJobeet\vendor\ZF2\library\Zend\Mvc\Application.php(309): Zend\EventManager\EventManager->trigger('dispatch', Object(Zend\Mvc\MvcEvent), Object(Closure))
    #19 C:\wamp\www\zendJobeet\public\index.php(17): Zend\Mvc\Application->run()
    #20 {main}

    RépondreSupprimer
    Réponses
    1. Message:
      Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Zend\Db\Adapter\Adapter


      => La réponse est juste dans le message du dessus :-)

      Supprimer
  18. Bonjour, j'ai suivis ton tuto jusqu'a ce jour(Jour 4) et j'obtiens l'erreur suivante : " Fatal error: Uncaught exception 'Zend\ModuleManager\Exception\RuntimeException' with message 'Module (Application) could not be initialized.' in C:\wamp\www\Zend_test\vendor\zendframework\zendframework\library\Zend\ModuleManager\ModuleManager.php on line 175" après avoir recu une erreur qui disais que le module Application avais un problème et que j'ai résolu en supprimant application de la liste des module dans config/applicatio.config.php
    Je suis vraiment bloqué svp

    RépondreSupprimer
    Réponses
    1. Bonjour,

      C'est avec le code trouvé dans l'archive ?

      Supprimer
    2. oui, j'ai telechargé ton archive et il me génèrz toujours ces erreurs

      Supprimer
    3. Ok, je regarderais aujourd'hui. Là, je n'ai pas le temps du tout :-)

      Supprimer
  19. Bonjour Romain,

    Je confirme il ya une erreur de connexion et de création d'instance, c'est vraiment dommage de suivre 4 pages et de tomber sur une erreurs insoluble pour les débutants ... :(

    RépondreSupprimer
  20. Bonjour,

    Le bug en question viens d'un problème du module bjyProfiler qui n'était pas à jour avec la version Zend Framework2.
    Il a été fixé sur le repo Git : https://github.com/bjyoungblood/BjyProfiler

    Grosse prise de tête pour trouver mais j'ai trouvé !

    RépondreSupprimer
  21. et je fais comment pour le resoudre?

    RépondreSupprimer
  22. Arnaud, tu télécharges la version sur le repository Git du projet BjyProfiler et tu le remplaces avec celui existant dans le dossier vendor

    RépondreSupprimer
  23. Salut, qui a pu traiter l'erreur du BjyProfiler ? je l'ai telecharger et mis dans le dossier vendor mais ça ne marche pas :( help !

    RépondreSupprimer
  24. BONJOUR ,

    j'ai une erreur de type " Uncaught exception 'RuntimeException' with message 'Unable to load ZF2. Run `php composer.phar install` or define a ZF2_PATH environment variable.' in C:\wamp\www\ZF2\init_autoloader.php"

    RépondreSupprimer
  25. salut,
    j ai une erreur de ce type " Informations complémentaires:
    Zend\View\Exception\RuntimeException

    Fichier:

    /var/www/reseausocial1/vendor/zendframework/zendframework/library/Zend/View/Renderer/PhpRenderer.php:499

    Message:

    Zend\View\Renderer\PhpRenderer::render: Unable to render template"

    RépondreSupprimer
  26. A quoi sa sert de faire ce tuto si tu le fais pas intégralement?
    Pourtant ce tuto était génial, tu expliques bien mais c'est carrément incomplet.

    RépondreSupprimer
    Réponses
    1. Bonjour,

      Comme tout le monde, j'ai des priorités, et le tuto n'en était pas une depuis quelques mois :-)
      Le tuto est là pour donner les bases du framework. Rien ne t’empêche de suivre le début et de te débrouiller pour la suite.

      Je t'invite cependant à revenir voir régulièrement. Je devrais pouvoir poster de nouveaux tutos prochainement.

      Supprimer
  27. Bonjour,
    Je suis débutant sur ZF2 :

    J'aimerais faire une requête : comme ceci :
    "SELECT `item_quantity` FROM `vw_my_asn_unit_sum` WHERE `message_id` = '000098b0-5896-44db-834f-c504937f513e';"
    l'affichage :
    ++++++++++++++
    + item_quantity +
    ++++++++++++++
    + 54 +
    ++++++++++++++

    ma view est :
    ++++++++++++++++++++++++++++++++++++++++++++++
    + message_id + item_quantity +
    ++++++++++++++++++++++++++++++++++++++++++++++
    +000098b0-5896-44db-834f-c504937f513e + 54 +
    +00009d98-b130-4343-b8ad-b1dcdbeff8de + 720 +
    +0001919e-e902-48a4-8d8e-3d769d752e07 + 144 +
    +0005baa5-1ba4-4da3-9e7c-a9005390ca08 + 435 +
    +00086b6d-f359-43ab-a0b4-cb055de8a466 + 0 +
    +000bb737-c8f5-4152-865f-c990dbc90b9b + 15609 +
    +000dff41-baf3-4feb-bdcf-c31c35a8060f + 1 +
    +00101b13-4a19-4360-be96-a556d29b7ca7 + 31 +
    +001bdcd1-99fd-4bf4-804f-02ddf968f927 + 166 +
    +001dbfa3-9b99-4004-82ec-aba951c0186d + 0 +
    +001e9ee2-eb93-4541-b731-3a9fcbe3142d + 77 +
    ++++++++++++++++++++++++++++++++++++++++++++++
    J'ai fait une fonction suivante :
    public function getSumUnit($message_id)
    {
    $message_id = (string)$message_id ;
    $select = $this->tableGateway->getSql()->select();
    $select->where->equalTo('message_id', $message_id);
    $resultSet = $this->tableGateway->selectWith($select);
    return $resultSet;
    }
    mais cela me retourne une erreur :
    Object of class Zend\Db\ResultSet\ResultSet could not be converted to string
    Or je souhaite juste l'item_quantity et non le message_id et l'item_quantity.

    Mes question sont les suivantes :
    - 1) comment faire un select avec juste item_quantity en retour
    - 2) comment tu l'affiche sur la page xxxx.phtml sachant que j'ai un retour sur le Controller pour la page comme celui-ci :
    return array(
    'asn_header' => $asn_header,
    'recap' => $array_recap,
    'summaries' => $summaries,
    'result' => $result,
    'language' => $language,
    'form' => $form,
    'systematic_consumption' => $systematic_consumption,
    );

    RépondreSupprimer
    Réponses
    1. Bonjour,

      Pour repondre à tes questions:
      1/ Tu dois pouvoir faire:

      $select->columns(array('l'item_quantity'));

      2/ Si tu passe 'result' à ta vue, ca correspond au resultat de ton select ?

      Tu dois pouvoir faire ça dans ta vue:

      foreach ($this->result as $result)

      echo $result->'item_quantity



      Supprimer
    2. merci de ta réponse !
      ça marche !

      Supprimer
    3. Bonjour,

      je sais pas si tu as déjà fait des requêtes avec PredicatSet :
      je bloque sur une bêtise qu'en SQL ça prend 10 secondes à corriger mais sur Zend je sais pas faire, voilà le code PHP :

      Supprimer
    4. Et le SQL :
      SELECT * FROM `vw_my_asn_header` where `username` = 'toto' and ( `shipment_number` like '20151106052811' or `shipment_number` like '20151110053250' or `shipment_number` like '20151116054359') ORDER BY `message_id` ASC

      Mon problème est de mettre les parenthèses au bon endroit.
      La requête zend :
      ["queryString"] => string(181) "SELECT `vw_my_asn_header`.* FROM `vw_my_asn_header` WHERE (username = :where1 AND `shipment_number` LIKE :where2 OR `shipment_number` LIKE :where3 OR `shipment_number` LIKE :where4)"

      Je met comment les parenthèses.
      Cordialement,

      Supprimer
  28. public function searchSitesDeliveries($username, Search $search)
    {
    $select = $this->tableGateway->getSql()->select();
    $predicate_set = new Predicate\PredicateSet(
    array(new Predicate\Expression('username = ?', $username)),
    Predicate\PredicateSet::COMBINED_BY_AND
    );
    if (!empty($search->get_shipment_number()))
    {

    $valeur = $search->get_shipment_number();
    if(is_array($valeur))
    {
    $valeur = array_unique($valeur);
    \Zend\Debug\Debug::dump($valeur);
    foreach ($valeur as $key => $value)
    {
    if($key == 0)
    {
    $predicate_set->andPredicate(new Predicate\Like('shipment_number', '%'.$value.'% ('));
    }
    else
    {
    if($key == end($key))
    {
    $predicate_set->orPredicate(new Predicate\Like('shipment_number', '%'.$value.'%)'));
    }
    else
    {
    $predicate_set->orPredicate(new Predicate\Like('shipment_number', '%'.$value.'%'));
    }
    }
    }
    }
    else
    {
    $predicate_set->andPredicate(new Predicate\Like('shipment_number', '%'.$valeur.'%'));
    }
    }
    $select->where($predicate_set);
    \Zend\Debug\Debug::dump($this->tableGateway->selectWith($select));
    $resultSet = $this->tableGateway->selectWith($select);

    RépondreSupprimer
  29. salut,
    j ai une erreur
    Fatal error: Uncaught exception 'Zend\ModuleManager\Exception\RuntimeException' with message 'Module (projet1) could not be initialized.' in C:\wamp\www\projet1\vendor\zendframework\zend-modulemanager\src\ModuleManager.php:195 Stack trace: #0 C:\wamp\www\projet1\vendor\zendframework\zend-modulemanager\src\ModuleManager.php(169): Zend\ModuleManager\ModuleManager->loadModuleByName(Object(Zend\ModuleManager\ModuleEvent)) #1 C:\wamp\www\projet1\vendor\zendframework\zend-modulemanager\src\ModuleManager.php(96): Zend\ModuleManager\ModuleManager->loadModule('projet1') #2 [internal function]: Zend\ModuleManager\ModuleManager->onLoadModules(Object(Zend\ModuleManager\ModuleEvent)) #3 C:\wamp\www\projet1\vendor\zendframework\zend-eventmanager\src\EventManager.php(444): call_user_func(Array, Object(Zend\ModuleManager\ModuleEvent)) #4 C:\wamp\www\projet1\vendor\zendframework\zend-eventmanager\src\EventManager.php(205): Zend\EventManager\EventManager->triggerListeners('loadModules', Object(Zend\ModuleManager\ModuleEvent), NULL) #5 C:\w in C:\wamp\www\projet1\vendor\zendframework\zend-modulemanager\src\ModuleManager.php on line 195

    RépondreSupprimer
    Réponses
    1. Salut,

      Ton module existe bien ?
      Il est declaré dans application.config.php ?

      Autre erreur possible => tu as bien un fichier Module.php dans ton module projet1, avec une classe appelé Module ?

      Supprimer
  30. ouiii voila mon fichier application.config.php
    'modules' => array(
    'Application','projet1'
    ),
    et voila le contenu de mon fichier module.php
    array(__DIR__.'/autoload_classmap.php',),
    'zendframework\zend-loader\src\ClassMapAutoloader' => array('namespaces' => array(__NAMESPACE__=>
    __DIR__.'/src/'.__NAMESPACE__,),),);
    }
    public function getConfig (){
    return include __DIR__ .'/config/module.config.php';
    }

    je pense que le probleme vient de composer.json !??

    RépondreSupprimer
  31. Composer could not find a composer.json file in C:\ProgramData\ComposerSetup\bin

    To initialize a project, please create a composer.json file as described in the
    https://getcomposer.org/ "Getting Started" section

    RépondreSupprimer
  32. Bonjour!Merci pour ce tutoriel!
    J'essai de refaire les mêmes étapes pour mon propre projet.Mes deux tables sont service(id_service,nom_service) et personnel(id_prsonnel,id_service,matricule,nom_personnel,prenom_personnel,date_naissance).
    en compilation,j'ai l'erreur suivante qui s'affiche en haut mais les information dans la base de données s'affiche correctement.
    Array ( [id_service] => 1 [nom_service] => Moderateur ) Array ( [id_service] => 2 [nom_service] => Developpeur ) Array ( [id_service] => 3 [nom_service] => Secretaire ) Array ( [id_service] => 4 [nom_service] => Administrateur )

    RépondreSupprimer
  33. Bonjour apres avoir suivie ce tuto et rajouté le fichier database.local.php j'ai cette erreur Fatal error: Class 'BjyProfiler\Db\Adapter\ProfilingAdapter' not found in /Users/Shared/MAMP/WWW/ZendComp/config/autoload/database.local.php on line 13 aurrez vous une idée svp ? merci bien

    RépondreSupprimer
  34. Bonjour apres avoir suivie ce tuto et rajouté le fichier database.local.php j'ai cette erreur Fatal error: Class 'BjyProfiler\Db\Adapter\ProfilingAdapter' not found in /Users/Shared/MAMP/WWW/ZendComp/config/autoload/database.local.php on line 13 aurrez vous une idée svp ? merci bien

    RépondreSupprimer