I am working through the zend framework 2 tutorial application. I have set up my Album Module directory as follows:
I am running into an error when I start my MAMP server, Fatal error: Uncaught exception 'Zend\ModuleManager\Exception\RuntimeException' with message 'Module (Album) could not be initialized.'
If I comment out the Album module from the following code (in /config/application.config.php):
'modules' => array(
'Application',
'Album',
),
I get to the skeleton application homepage.
Here is my /module/Album/Module.php code:
namespace Album;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
use Album\Model\Album;
use Album\Model\AlbumTable;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;
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’;
}
public function getServiceConfig() {
return array(
’factories’ => array(
’Album\Model\AlbumTable’ => function($sm) {
$tableGateway = $sm->get(’AlbumTableGateway’);
$table = new AlbumTable($tableGateway);
return $table;
},
’AlbumTableGateway’ => function ($sm) {
$dbAdapter = $sm->get(’Zend\Db\Adapter\Adapter’);
$resultSetPrototype = new ResultSet(); $resultSetPrototype->setArrayObjectPrototype(new Album());
return new TableGateway(’album’, $dbAdapter, null, $resultSetPrototype);
},
),
);
}
}
And here is my module.config.php code in /module/Album/config/:
return array(
’controllers’ => array(
’invokables’ => array(
’Album\Controller\Album’ => ’Album\Controller\AlbumController’,
),
),
’view_manager’ => array(
’template_path_stack’ => array(
’album’ => __DIR__ . ’/../view’,
),
),
'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',
),
),
),
),
),'
);
I have read through a few people who have ran in to similar situations, but their issues were due to misspelled/incorrectly named classes. I don't see anything wrong with my code (even went as far as copying/pasting directly from tutorial).
Can someone give me some suggestions?
Thanks
I had the same problem, and the solution was to start every .php file with <?php
This is not clear in the tutorial (if you just copy the code from there), and it was the reason I was getting the same exception.
The only reason i could see is that you should replace all ’ with normal single quotes '.
using ’ can results in unexpected behaviors.
There is an error in the tutorials module.config.php file.
Change 'id' => '[0-9]+',
to 'id' => '[0-9]*',
+ means one or more digits. If you just call http://zf2-tutorial.localhost/album/ there is no digit in the url so the rewriterule doesn't match. Change from + to * (0 or more)
I had the exact same issue, my issue and solution were
the very first command in the tutorial is:
php composer.phar create-project --stability="dev" zendframework/skeleton-application path/to/install
It was cut off my screen so I copy/pasted the command to the command line without reading the end. So my app directory structure included a directory ./path/to/install where all of the installation files were, including application.config.php.
Moving everything in /path/to/install/ to the root directory of the app allowed Zend to find the Album module.
Note: /path/to/install/module contains the album module but will fail if you do
mv ./path/to/install/* .
so be sure to move the Application module at /path/to/install/module into app_root_dir/module/.
You can check composer.json weather include the following:
"autoload": {
"psr-4": {
"Application\\": "module/Application/src/",
"Album\\": "module/Album/src"
}
},
Please check your Module::getConfig() ,
and play with :
return include __DIR__ . '/../config/module.config.php';
I have added '../' prefix and it works well .
Related
I'm having trouble setting up a route for a very simple controller. I'm getting the "The requested URL could not be matched by routing." error. I've viewed similar solved questions on SO and can't pinpoint what I'm doing wrong (Ex: ZF2 - Zend Framework 2, understanding routing)
I've followed the skeleton tutorial with the albums subject and everything functioned perfectly fine. I tried duplicating the album module and then changing the name of the controller, folder, module config, etc. I figured this would be a good way to confirm I can at least replicate working code. I'm just trying to echo "123" to the page, so I tried eliminating the directories for forms, models and some of the views from the new module.
Is there some way to see what route I'm really looking for and what routes I defined? I know CI actually created a log file I was able to check. It was kind of like Apache logs but specific to framework functionality.
I'd like to post some of my code so someone could point out the mistake I am making and possibly explain why it is wrong. I tried paying close attention to case since different variations of the word album are used throughout the tutorial and I'm not 100% sure which ones are supposed to match up with what just yet. I'm trying to make it work for http://www.example.com/productbriefs.
Folder Structure
module.config.php:
return array(
'controllers' => array(
'invokables' => array(
'Productbriefs\Controller\Productbriefs' => 'Productbriefs\Controller\ProductbriefsController',
),
),
// The following section is new and should be added to your file
'router' => array(
'routes' => array(
'productbriefs' => array(
'type' => 'Literal',
'options' => array(
'route' => '/productbriefs',
'defaults' => array(
'controller' => 'Productbriefs\Controller\Productbriefs',
'action' => 'index',
),
),
),
),
),
'view_manager' => array(
'template_path_stack' => array(
'productbriefs' => __DIR__ . '/../view',
),
),
);
ProductbriefsController.php
namespace Productbriefs\Controller;
use Zend\Mvc\Controller\AbstractActionController;
class ProductbriefsController extends AbstractActionController
{
public function indexAction()
{
echo "123";
}
}
Module.php
namespace Productbriefs;
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';
}
// Add this method:
public function getServiceConfig()
{
return array(
'factories' => array(),
);
}
}
As per my comment, you need to add Productbriefs to the module array in application.config.php or the module (including its configuration) will not be loaded.
To answer your second question, the controller manager needs to know how to load the controller classes your application uses. An 'invokable' is a class that can be instantiated without needing any arguments passed to it, so by adding controllers to that array you're telling the controller manager that it can instantiate that class simply by doing $controller = new Productbriefs\Controller\ProductbriefsController(). The key for the array is an alias, yes. This can be anything, although the ZF convention is to use the fully qualified name of the class but omit the 'Controller' suffix from the end. When you refer to controllers in your routing config you use these aliases.
In order to use the Doctrine Module ORM tools for a Zend 2 project, I need to run the command via Zend so that the bootstrapping options defined in index.php and application.config.php are correctly established.
The sum off these definitions enable a config file to be loaded which contains DB settings I wish to inject into Doctrine. This is achieved via a custom DBALConnectionFactory.
The doctrine configuration in my application.config.php is like this:
'doctrine' => array(
'entity_path' => array (
__DIR__ . '../src/Application/Entity'
),
'driver' => array(
'ApplicationDriver' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/Application/Entity')
),
'orm_default' => array(
'drivers' => array(
'Application\Entity' => 'ApplicationDriver'
)
)
),
'connection' => array(
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'DoctrineTypeMappings' => array(
'enum' => "string"
)
)
)
),
'doctrine_factories' => array(
'connection' => 'Application\ORM\DBALConnectionFactory',
),
According to Sam in how to configure doctrine command line tools on zenframework 2, I should be able to use
php public/index.php orm:schema-tool:create
but all that does for me is to lists the commands available (that suggests that the parameters are not being mapped correctly).
I have also tried (from the project root) the following commands to no avail:
php public/index.php doctrine orm:schema-tool:create
php public/index.php doctrine orm orm:schema-tool:create
Has anyone had any luck using the Doctrine Tools via Zend? All responses gratefully received!
The issue was caused by a custom console route interfering with the parameters for the standard route. I have amended it to pass all requests made via public/index.php straight to the standard router.
public function match(Request $request)
{
// Get command line arguments and present working directory from
// server superglobal:
$filename = $request->getScriptName();
if ("public/index.php" === $filename) {
return parent::match($request);
}
// WARNING: cwd is $APPLICATION_HOME, so that throws off realpath!
//
// Convert base filename (minus .php extension and underscores) and
// containing directory name into action and controller, respectively:
$base = basename($filename, ".php");
$actionName = str_replace('_', '', $base);
$dir = dirname($filename);
//invoked in directory e.g. $base=util/ping.php
$level1 = basename(dirname($filename));
// re-orient relative to APPLICATION_HOME
$path = $level1 . '/' . basename($filename);
$controller = basename($dir);
$routeMatch = new RouteMatch(
array('controller' => $controller, 'action' => $actionName), 1
);
// Override standard routing:
$routeMatch->setMatchedRouteName('default');
return $routeMatch;
}
I am trying to build some Cronjobs handling.
I run my command (temp) from the CLI for testing:
php public/index.php send-private-messages-reminders direct
Therefor i created a Module with this routing in module.config.php
// cron route
'console' => array(
'router' => array(
'routes' => array(
'cronjobs' => array(
'type' => 'simple',
'options' => array(
'route' => 'send-private-messages-reminders <freq>',
'defaults' => array(
'controller' => 'Cron\Controller\SendPrivateMessagesReminders',
'action' => 'task'
),
)
),
),
),
),
Then i get my users and the messages for these users in my controller. I create my viewModel and prepare my mail:
$mailer = $this->getServiceLocator()->get('\SxMail\Service\SxMail');
$sxMail = $mailer->prepare();
$viewModel = new ViewModel();
For the layout i want to use a layout that i use in my Application also. So i define my layout in module.config.php
'view_manager' => array(
'template_map' => array(
'cron/mail/layout' => __DIR__ . '/../view/layout/mail.twig',
),
'template_path_stack' => array(
__DIR__ . '/../view',
),
),
and set my layout:
$sxMail->setLayout('cron/mail/layout');
Also there is more code for instance for the template:
$viewModel->setTemplate('mailtemplates/private-messages-reminders.twig');
and setting variables for that template:
$viewModel->setVariables(array( .... ));
So far so good. It works almost as expected. Only...
in my mail.twig template i use a ViewHelper for the urls, like this ie:
<img src="{{ url('mysite', {}, {force_canonical: true}) }}"images/merk-logo.png" />
In my Application everything is fine with the ViewHelper, but in my cron it is not. I get this error:
An exception has been thrown during the rendering of a template
("Request URI has not been set") in "application/mail/layout" at line ...
From here i'am at a loss. I can ask the routeMatch:
$routeMatch = $this->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch();
this gives me:
object(Zend\Mvc\Router\Console\RouteMatch)#517 (3) {
["length":protected]=>
int(0)
["params":protected]=>
array(3) {
["controller"]=>
string(44) "Cron\Controller\SendPrivateMessagesReminders"
["action"]=>
string(4) "task"
["freq"]=>
string(6) "direct"
}
["matchedRouteName":protected]=>
string(8) "cronjobs"
}
but that is about it what i can find out. I have no clue what to do or how to solve the problem. I have been searching a lot, but the "solutions" so far are not detailed enough for me.
There is a missing route_match, but what to do about it?
First of all, I know there are a few quite similar questions here on stackoverflow about that form problem but none of them could actually help me so I'm giving it a try myself. I've been stuck with this for the past 10 hours and I'm really desesperate right now.
So, let's start with my directory structure:
gpos/
---- application/
--- forms/
------- CustomerForm.php
--- modules/
--- default/
--- controllers/
--- CustomerController.php
--- views/
--- Bootstrap.php
---- public/
---- library/
--- Doctrine/
--- GPos/
--- Doctrine/
--- ActiveEntity.php
--- Models/
--- Customer.php
As you can see, I'm using Zend's standard forms/ folder to store my forms and I'm also using Doctrine as DB manager.
So I've been trying to reach my CustomerForm.php from CustomerController.php but simply getting a not found error.
Here is the error I'm getting:
Fatal error: Class 'Form_CustomerForm' not found in C:\wamp\www\GPos\gpos\application\modules\default\controllers\CustomerController.php on line 9
CustomerController.php:
class CustomerController extends Zend_Controller_Action {
public function init() {
/* Initialize action controller here */
}
public function indexAction() {
$form = new Form_CustomerForm(); //line 9
$form->setAction('/customer/index');
$form->setMethod('post');
$this->view->form = $form;
}
and CustomerForm.php:
class Form_CustomerForm extends Zend_Form {
public function init() {
and finally views/scripts/customer/submit.phtml:
<h2>Customer</h2>
<p>To create blabla</p>
<?php echo $this->form; ?>
I've been desesperately trying to add Form namespace to the autoloader but I learned today that it was already in thanks to Zend doing it for us. So... What am I doing wrong? I found tons of posts saying how to add resources and how to manage custom forms and I feel I'm doing exactly what's asked but it just won't work not matter what.
I'm afraid that it comes from somewhere else cause I've been trying to add different namespaces unsuccessfully too such as GPos_Doctrine for my ActiveEntity.php file in library/GPos/Doctrine/ with:
protected function _initAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'Application',
'basePath' => APPLICATION_PATH,
));
$autoloader->addResourceType('gpos', '/../../library/GPos', 'GPos');
$autoloader->addResourceType('doctrine', '/../../library/GPos/Doctrine', 'GPos_Doctrine');
return $autoloader;
}
This didn't work either. I'm quite unsure of the 'path' parameter (2nd param) but I saw in a tutorial that the path of eath resource type you add must be relative to autoloader's basePath, so I came up with these paths.
The only way I could make my GPos_Doctrine_ActiveEntity() work was by adding autoloadernamespaces[] = "GPos_" to application.ini. Adding autoloadernamespaces[] = "Form_"didn't work though... I really don't understand what's wrong with my resource types I'm adding.
Please note that I didn't use zf tools to build that project. I'm considering doing it if I don't find a way to make it all work correctly.
I've also tried to rename my form class to "Application_Form_CustomerForm" but didn't do any good either. I feel I've tried everything I could now. I'm just deseperate :(
Oh and by the way, the only work around I found at first is to put forms/ folder in my library, that worked. Don't understand why, but I don't want to use that hack. See, I'm doing this project as my IT Bachelor work graduation and I really shouldn't get into hacking things to make them work :P.
Thank you for anybody paying attention!
either add an bootstrap to the default module (which in turn sets the resourcetypes automaticly)
class Default_Bootstrap extends Zend_Application_Module_Bootstrap
{
protected function _initAutoload() {
$moduleLoader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'Default_',
'basePath' => APPLICATION_PATH . '/modules/default'));
return $moduleLoader;
}
}
or add this to your global bootstrap (extend it with your two custom resourcetypes):
public function _initDefaultResourceTypes() {
$al = new Zend_Application_Module_Autoloader(array(
'namespace' => '',
'basePath' => APPLICATION_PATH.'/modules/default/',
'resourceTypes' => array(
'dbtable' => array(
'namespace' => 'Model_DbTable',
'path' => 'models/DbTable',
),
'mappers' => array(
'namespace' => 'Model_Mapper',
'path' => 'models/mappers',
),
'form' => array(
'namespace' => 'Form',
'path' => 'forms',
),
'model' => array(
'namespace' => 'Model',
'path' => 'models',
),
'plugin' => array(
'namespace' => 'Plugin',
'path' => 'plugins',
),
'service' => array(
'namespace' => 'Service',
'path' => 'services',
),
'viewhelper' => array(
'namespace' => 'View_Helper',
'path' => 'views/helpers',
),
'viewfilter' => array(
'namespace' => 'View_Filter',
'path' => 'views/filters',
)
)
));
$al->setDefaultResourceType('model');
}
take a look at this ZF 1 Skeleton for further info:
https://github.com/eddiejaoude/Zend-Framework--Doctrine-ORM--PHPUnit--Ant--Jenkins-CI--TDD-
I don't use modules but here what I do, which is very basic :
In /public/index.php, I add "/forms" to the include path :
(important part is the line with "forms" of course
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
realpath(APPLICATION_PATH . '/forms'),
get_include_path(),
)));
My form are then named :
class Default_Form_Login extends Zend_Form
Then I would do :
$form = new Default_Form_Login();
I need to use autoloading for my custom classes in Zend Framework 2.0. My custom library located in /vendor/Garvey/library/Garvey. I have a simple extended AbstractTable class in /vendor/Garvey/library/Garvey/Db/Table/AbstractTable.php:
<?php
namespace Garvey\Db\Table;
use Zend\Db\Table\AbstractTable;
abstract class AbstractTable extends AbstractTable
{
public function getItemById($id)
{
}
}
In the index.php I have the following code:
require_once 'vendor/ZendFramework/library/Zend/Loader/AutoloaderFactory.php';
Zend\Loader\AutoloaderFactory::factory(array('Zend\Loader\StandardAutoloader' => array(
'prefixes' => array(
'Garvey' => 'vendor/Garvey/library/Garvey',
)
)));
But I have the following error. What I have missed?
Fatal error: Class 'Garvey\Db\Table\AbstractTable' not found
Thank you in advance.
Your original index.php would also worked if you changed the 'prefixes' key to 'namespaces' and specify path like below:
Zend\Loader\AutoloaderFactory::factory(array('Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
'Garvey' => dirname(__DIR__) . '/vendor/Garvey',
)
)));
Or you can defime method in Module.php
public function getAutoloaderConfig()
{
$return = array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php'
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
'Garvey' => __DIR__ . '/../../vendor/Garvey/library/Garvey',
)
)
);
}
But I would not recommend it. Since ZF2 purpose all centered about speed in autoloading the best way is to use class_map style to load your classes. It will work much quicker at the end but require additional work. You can to register every class in you class_map file.
You can create class_map.php in the root of your library and place there
<?php
return array(
'Garvey\Db\Table\AbstractTable' => __DIR__ . '/Garvey/Db/Table/AbstractTable.php',
);
And add there as many classes as you use. And in getAutoloaderConfig() you can add you classmap
public function getAutoloaderConfig()
{
$return = array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
__DIR__ . '/../../vendor/Garvey/library/Garvey/class_map.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
)
)
);
}
Matthew Weier O'Phinney explains in this video that there are now 3 methods for autoloading :
ZF1-style include_path autoloader ( old zf1 method, not recommended )
Per-namespace/prefix autoloading ( new zf2 method, better )
Class-map autoloading ( recommended and the fastest )
A class-map generator utility is mentioned in the docs that will take care of writing the /vendor/vendor_name/library/autoload_classmap.php for you.
The solution you found is similar to the one Matthew mentions in the video for the Per-namespace/prefix autoloading. Following the code structure in ZendSkeletonApplication, that code would go in the /init_autoloader.php file, rather than in the /public/index.php file.
I have found the answer. Put this in your index.php:
require_once 'vendor/ZendFramework/library/Zend/Loader/StandardAutoloader.php';
$loader = new Zend\Loader\StandardAutoloader();
$loader->registerNamespace('Garvey', realpath('vendor/Garvey/library/Garvey'));
$loader->register();
Have a quick look at this post.
Now next step is add some code into our custom library.
First of all open a file ./vendor/Garvey/autoload_classmap.php
return array(
'Garvey\Module' => __DIR__ . '/Module.php',
'Garvey\Db\Table' => __DIR__ . '/library/Garvey/Db/Table/AbstractTable.php',
)
Next is ./vendor/Garvey/Module.php
namespace Garvey;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
class Module implements AutoloaderProviderInterface
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/library/' . __NAMESPACE__,
),
),
);
}
}
Now inside your library create a file inside a folder:
./vendor/Kdecom/library/Kdecom/Db/Table/AbstractTable.php
One final thing that we need to do which is add this library into your application.config.php file.
So your application.config.php file will looks something like this way...
return array(
'modules' => array(
'Application',
'Garvey'
),
'module_listener_options' => array(
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'./module',
'./vendor',
),
),
);