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',
),
),
);
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.
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 .
I'm writing a Zend Framework 2.3.1 Application using PHP 5.5.12
My application contains several modules, and in each module's Module.php i have
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoLoader' => array(
__DIR__ . '/autoload_classmap.php'
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
that's the only indication that I saw that is related to ClassMapAutoLoader.
when I try to run the application i get:
Fatal error: Uncaught exception 'Zend\Loader\Exception\InvalidArgumentException' with message 'Autoloader class "Zend\Loader\ClassMapAutoLoader" not loaded' in /mnt/storage/home/ufk/work-projects/global-logic/project/xpogames/vendor/zendframework/zendframework/library/Zend/Loader/AutoloaderFactory.php on line 85
any ideas why ?
I had the same problem as you, and I figured out what was causing the issue:
'Zend\Loader\ClassMapAutoLoader' => array(
__DIR__ . '/autoload_classmap.php'
),
Needs to be:
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php'
),
Small L in Autoloader. We both camel cased when we shouldn't have. This cleared it up for me. Let me know if it works for you.
The Module.php autoloading has nothing to do with the ZF2 autoloading.
How do u installed ZF2? I think u installed it on your own and not used composer?
In ZendSkeletonApp are some examples, how to autoload ZF2 itself:
https://github.com/zendframework/ZendSkeletonApplication/blob/master/init_autoloader.php
Include this part in your public/index.php
https://github.com/zendframework/ZendSkeletonApplication/blob/master/public/index.php#L14
My Enviroment (Zend Server):
PHP Version 5.4.11
Zend Server Version: 6.0.1
Zend Framework: 1.12.3, 2.1.4
Zend Server Gateway: 0.9.0.201301302347
Build: 69400
So I tried to write my first application in ZF2 form official tutorial.
ZF version in skeleton application: 2.2
This is link to my project, if someone wants to look at it closer.
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',
),
),
);
module\Album\Module.php
<?php
namespace Album;
class Module
{
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';
}
}
And this is my error:
Zend\ModuleManager\Listener\Exception\InvalidArgumentException: Config being merged must be an array, implement the Traversable interface, or be an instance of Zend\Config\Config. integer given. in F:\Zend\Apache2\htdocs\learnzf2\vendor\zendframework\zendframework\library\Zend\ModuleManager\Listener\ConfigListener.php on line 317
Call Stack
# Time Memory Function Location
1 1.0510 137456 {main}( ) ..\index.php:0
2 1.0986 163240 Zend\Mvc\Application::init( ) ..\index.php:18
3 1.2171 345520 Zend\ModuleManager\ModuleManager->loadModules( ) ..\Application.php:253
4 1.2171 345680 Zend\EventManager\EventManager->trigger( ) ..\ModuleManager.php:109
5 1.2171 345936 Zend\EventManager\EventManager->triggerListeners( ) ..\EventManager.php:207
6 1.2195 354296 call_user_func ( ) ..\EventManager.php:468
7 1.2195 354312 Zend\ModuleManager\ModuleManager->onLoadModules( ) ..\EventManager.php:468
8 1.2371 383920 Zend\ModuleManager\ModuleManager->loadModule( ) ..\ModuleManager.php:90
9 1.2390 384984 Zend\EventManager\EventManager->trigger( ) ..\ModuleManager.php:154
10 1.2391 385016 Zend\EventManager\EventManager->triggerListeners( ) ..\EventManager.php:207
11 1.2431 391096 call_user_func ( ) ..\EventManager.php:468
12 1.2431 391112 Zend\ModuleManager\Listener\ConfigListener->onLoadModule( ) ..\EventManager.php:468
13 1.2439 391464 Zend\ModuleManager\Listener\ConfigListener->addConfig( ) ..\ConfigListener.php:127
What is problem with it? I tried to debug it but I don't know what is wrong with my code, except that is int value even I return array in config/module.config.php.
Ps. Eariler, I didn't use Xdebug (+PHPStorm), and when eg I put breakpoint somewhere into
config/module.config.php, it skips this breakpoint and go into (it don't even shows) and goes into ZF library files (where for example is another breakpoint). Is it normal or I'm doing sth wrong?
Your module/Album/config/module.config.php file is empty, while it should return a config array (like in the example code you've posted).
I had this same problem when I was about creating a new module after some time. I had other modules that were already working but this other one was giving the error you have now. In my search I realized that I wrote:
public function getConfig()
{
return __DIR__ . '/config/module.config.php';
}
instead of:
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
in my Module.php file. After correcting it my app works great! Please look through your code carefully especially in the Module.php and module.config.php files for omissions etc. Good luck!
My directory structure is like this:
c:\Workspaces\Zend
c:\Workspaces\Custom library
Custom library is a shared library, which is in use in other applications. It doesn't use namespaces, just old style underscores.
I downloaded the ZF2-Restful-Module-Skeleton which i intend to use as a restful server. In the InfoController I have this code:
namespace Main\Controller;
use Zend\Mvc\Controller\AbstractRestfulController;
class InfoController extends AbstractRestfulController
{
public function getList()
{
$data = array(
'phone' => '+30123456789',
'email' => 'email#domain',
);
$Res = new CL_Res_Chain_Mutable();
return $data;
}
}
Error message is:
Fatal error: Class 'Main\Controller\CL_Res_Chain_Mutable' not found in C:\Workspaces\Zend\module\Main\src\Main\Controller\InfoController.php
Obviously, I need to add this custom library to my Zend application, but Im "little" lost here, I really don't know how to do this. I have googled couple solutions, but none of them seem to be like this.
Also, I have another library in folder c:\Workspaces\Custom library 2, which has (among other files) file(class) D.php, which I have used like D::dump($data);
How can I get it to work in my Zend application like that?
You need to configure the StandardAutoloader to load your library classes. The easiest way is to modify the Application module's Module::getAutoloaderConfig() method so that it looks something like this:
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
'prefixes' => array(
'CL' => 'c:\\Workspaces\\Custom library/CL',
'D' => 'c:\\Workspaces\\Custom library 2/D',
),
),
);
}
I've added a prefixes key and then listed the prefix name and where to find it on disk. The Standard Autoloader documentation has more details.
If you are working with a Zend Skeleton Application you may also simply add these namespaces to your init_autoloader.php file.
The namespace of your class is Main\Controller. If you instanciate a new class here new CL_Res_Chain_Mutable() php will load it relative to the current namespace Main\Controller\CL_Res_Chain_Mutable. Your class is not a namespaced class so you need to load it from the root. Just put a \ in front new \CL_Res_Chain_Mutable().
By default your application will be using the Standard Autloader (PSR-0). This will find your files based on a namespaces, and a naming convension used by ZF2.
ZF2 will allow you to register multiple Autoloaders, so you can use different strategies, which is what you will need to do, here's an example:
Module.php
/**
* Get autoloader config
*
* #return array
*/
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
// File containing class map key/value pairs
__DIR__ . '/library/autoloader_classmap.php',
// Or provide an array with the class map instead...
array(
'Application\Bootstrap' => __DIR__ . '/application/Bootstrap.php',
'CL_Res_Chain_Mutable' => __DIR__ . '/library/pathhere/Mutable.php',
),
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
This setup will use tell ZF2 to check the class map first, if it can't find what it's looking for it will revert to the standard autoloader.
http://framework.zend.com/manual/2.0/en/modules/zend.loader.class-map-autoloader.html