ZF2: Skeleton App - Module not found?

Hello,

I’m having a problem with the ZF2 Skeleton Application.

I’ve followed every step of the tutorial, the homepage (http://zf2-tutorial.localhost) works fine on my computer, but there seems to be information missing on this part of instructions about module.

Problem lies within this specific page:
https://framework.zend.com/manual/2.3/en/user-guide/routing-and-controllers.html

"Note

Make sure to register the new Album module in the “modules” section of yourconfig/application.config.php. You also have to provide a [Module Class] for the Album module to be recognized by the MVC."
(2/3 of the page)

I’ve found no indication saying how to do this, what changes have to be made to the config/application.config.phpfile, and what, where and how I must add this “Module” class. It seems very unclear to me.

Consequently, when I type the module address in my browser (http://zf2-tutorial.localhost/album), I’ve got a 404 error: “The requested URL could not be matched by routing.”.

Coud you help me with this ? I’m losing my hair right now :slightly_smiling_face:

Many thanks!

Here is my “config/application.config.php” file:

<?php
/**
 * If you need an environment-specific system or application configuration,
 * there is an example in the documentation
 * @see https://docs.zendframework.com/tutorials/advanced-config/#environment-specific-system-configuration
 * @see https://docs.zendframework.com/tutorials/advanced-config/#environment-specific-application-configuration
 */
return [
 // Retrieve list of modules used in this application.
 'modules' => require __DIR__ . '/modules.config.php',

 // These are various options for the listeners attached to the ModuleManager
 'module_listener_options' => [

 // This should be an array of paths in which modules reside.
 // If a string key is provided, the listener will consider that a module
 // namespace, the value of that key the specific path to that module's
 // Module class.
 'module_paths' => [
 './module',
 './vendor',
 ],

 // An array of paths from which to glob configuration files after
 // modules are loaded. These effectively override configuration
 // provided by modules themselves. Paths may use GLOB_BRACE notation.
 'config_glob_paths' => [
 realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local}.php',
 ],

 // Whether or not to enable a configuration cache.
 // If enabled, the merged configuration will be cached and used in
 // subsequent requests.
 'config_cache_enabled' => true,

 // The key used to create the configuration cache file name.
 'config_cache_key' => 'application.config.cache',

 // Whether or not to enable a module class map cache.
 // If enabled, creates a module class map cache which will be used
 // by in future requests, to reduce the autoloading process.
 'module_map_cache_enabled' => true,

 // The key used to create the class map cache file name.
 'module_map_cache_key' => 'application.module.cache',

 // The path in which to cache merged configuration.
 'cache_dir' => 'data/cache/',

 // Whether or not to enable modules dependency checking.
 // Enabled by default, prevents usage of modules that depend on other modules
 // that weren't loaded.
 // 'check_dependencies' => true,
 ],

 // Used to create an own service manager. May contain one or more child arrays.
 // 'service_listener_options' => [
 // [
 // 'service_manager' => $stringServiceManagerName,
 // 'config_key' => $stringConfigKey,
 // 'interface' => $stringOptionalInterface,
 // 'method' => $stringRequiredMethodName,
 // ],
 // ],

 // Initial configuration with which to seed the ServiceManager.
 // Should be compatible with Zend\ServiceManager\Config.
 // 'service_manager' => [],
];

And here is my “config/modules.config.php” file:

<?php
/**
 * @link http://github.com/zendframework/ZendSkeletonApplication for the canonical source repository
 * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
 * @license http://framework.zend.com/license/new-bsd New BSD License
 */

/**
 * List of enabled modules for this application.
 *
 * This should be an array of module namespaces used in the application.
 */
return [
 'Zend\ServiceManager\Di',
 'Zend\Session',
 'Zend\Mvc\Plugin\Prg',
 'Zend\Mvc\Plugin\Identity',
 'Zend\Mvc\Plugin\FlashMessenger',
 'Zend\Mvc\Plugin\FilePrg',
 'Zend\Mvc\I18n',
 'Zend\Mvc\Console',
 'Zend\Log',
 'Zend\Form',
 'Zend\Db',
 'Zend\Cache',
 'Zend\Router',
 'Zend\Validator',
 'Application',
 'Album'
];

That is quite old tutorial, may be you will have more luck with current version: https://docs.zendframework.com/tutorials/getting-started/overview/

Is this one fit for ZF2 ?

It is for ZF3 but it is not very different from ZF2.

Join slack https://zendframework-slack.herokuapp.com/ It will be easier to help you in chat.

Album module entry will be looking for Album\Module class. There are two ways to get it working, preferred is to place it into module\Album\src\Module.php and add composer autoloader entry for it:

"autoload": { 
    "psr-4": {
        "Album\\": "module/Album/src/"
    }
},

run composer dump-autoload to update autoloader.
(in 2.3 it was expected to be in module\Album\Module.php, where it would be autoloaded automatically, but autoloading for module code would still need to be setup, so I recommend composer)

Now, your error indicates that module was actually loaded(no module load error). So you either:

Some of the reasons for the 404 can be provided if you add this config to config\autoload\global.php:

'view_manager' => [
    'display_not_found_reason' => true,
    'display_exceptions' => true,
],

Thanks.

I’ve added what you tell me in config\autoload\global.php, to no avail alas (same result, no additional information).

Here is the content of the files you’ve talked about. I don’t see the mistake however (but there is surely one !):

module/Album/Module.php

<?php

namespace Album;

use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;

class Module implements AutoloaderProviderInterface, ConfigProviderInterface
{
 public function getAutoloaderConfig()
 {
 return array(
 'Zend\Loader\ClassMapAutoloader' => array(
 __DIR__ . '/autoload_classmap.php',
 ),
 'Zend\Loader\StandardAutoloader' => array(
 'namespaces' => array(
 __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
 ),
 ),
 );
 }

 public function getConfig()
 {
 return include __DIR__ . '/config/module.config.php';
 }
}

File module/Album/config/module.config.php:

<?php

return array(
 'controllers' => array(
 'invokables' => array(
 'Album\Controller\Album' => 'Album\Controller\AlbumController',
 ),
 ),
 'view_manager' => array(
 'template_path_stack' => array(
 'album' => __DIR__ . '/../view',
 ),
 ),
 // The following section is new and should be added to your file
 'router' => array(
 'routes' => array(
 'album' => array(
 'type' => 'segment',
 'options' => array(
 'route' => '/album[/:action][/:id]',
 'constraints' => array(
 'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
 'id' => '[0-9]+',
 ),
 'defaults' => array(
 'controller' => 'Album\Controller\Album',
 'action' => 'index',
 ),
 ),
 ),
 ),
 ),
);

File module/Album/autoload_classmap.php:

<?php

return array();

Content of config/application.config.php and config/modules.config.php files is in my first comments above.

Here is content of file module/Album/src/Album/Controller/AlbumController.php

<?php

namespace Album\Controller;

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

class AlbumController extends AbstractActionController
{
 public function indexAction()
 {
 }

 public function addAction()
 {
 }

 public function editAction()
 {
 }

 public function deleteAction()
 {
 }
}

And of course views are present (but almost empty):
module/Album/view/album/album/index.phtml
module/Album/view/album/album/add.phtml
module/Album/view/album/album/edit.phtml
module/Album/view/album/album/delete.phtml

I really don’t see what was made wrong? :frowning:

thanks for your time!

Everything looks ok except that config cache is enabled:

Change the values to false and delete everything in data/cache/ except for .gitignore file

OMG, that did it, I didn’t even noticed there was a cache !!!

I would never have found without your help… Thanks again :smiley:

You are welcome.
I should note though that you should not really use ZF2, it is old and does not work with php 7.2

I agree with you, however I’ll probably have to maintain ZF2 apps.

Is ZF2 so different from ZF3 that it would be necessary to me to re-learn Zend a third time (I already know ZF1) after having finished to learn ZF2 ? :o

Another problem happening. It seems that the Skeleton application uses a function named ```
getServiceLocator that my controller can’t find.

Here is the ErrorMessage:

Zend\ServiceManager\Exception\ServiceNotFoundException

A plugin by the name "getServiceLocator" was not found in the plugin manager Zend\Mvc\Controller\PluginManager

(and the interesting part of stack trace):

#3 C:\xampp\htdocs\zf2-tutorial\module\Album\src\Album\Controller\AlbumController.php(64): Zend\Mvc\Controller\AbstractController->__call('getServiceLocat...', Array)
#4 C:\xampp\htdocs\zf2-tutorial\module\Album\src\Album\Controller\AlbumController.php(31): Album\Controller\AlbumController->getAlbumTable()

Here is my Controller code (module/Album/src/Album/Controller/AlbumController.php)


class AlbumController extends AbstractActionController
{
 /**
 * @var
 */
 protected $albumTable;

 /**
 * @return array|void
 */
 public function indexAction()
 {
 return new ViewModel(array(
 'albums' => $this->getAlbumTable()->fetchAll(),
 ));
 }

 /**
 *
 */
 public function addAction()
 {
 }

 /**
 *
 */
 public function editAction()
 {
 }

 /**
 *
 */
 public function deleteAction()
 {
 }

 /**
 * Renvoie le modèle d'Album
 * @return AlbumTable
 */
 public function getAlbumTable()
 {
 if (!$this->albumTable)
 {
 $sm = $this->getServiceLocator();
 $this->albumTable = $sm->get('Album\Model\AlbumTable');
 }
 return $this->albumTable;
 }
}

And here is my global.php file, however the problem doesn’t seem to be implied by DbTable but by the Locator itself:

config/autoload/global.php

<?php
/**
 * Global Configuration Override
 *
 * You can use this file for overriding configuration values from modules, etc.
 * You would place values in here that are agnostic to the environment and not
 * sensitive to security.
 *
 * @NOTE: In practice, this file will typically be INCLUDED in your source
 * control, so do not include passwords or other sensitive information in this
 * file.
 */

return [
 'view_manager' => [
 'display_not_found_reason' => true,
 'display_exceptions' => true,
 ],
 'db' => array(
 'driver' => 'Pdo',
 'dsn' => 'mysql:dbname=zf2test_db;host=localhost',
 'driver_options' => array(
 PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
 ),
 ),
 'service_manager' => array(
 'factories' => array(
 'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
 ),
 ),
];

I’ve performed search over Internet, which talk about ZF3 and the incompatbility of skeleton with ZF3. Except that I’m working under ZF2 (both Skeleton and framework).

Any idea?

Ok, forget it.

The Skeleton available on the Zend website is only for ZF3 apparently :-/ Should have been mentioned somewhere however…

Of course, trying to run ZF3 Skeletonwith ZF2 framework is not a good idea.

Sorry for the inconvenience.

You can install an older version:

composer create-project zendframework/skeleton-application:^2.4.11

Thanks froschdesign :wink: