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
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 found a similar topic How to create modular MVC components in Zend Framework but it's about ZF1, and I wanted to know, maybe ZF2 has more convinient ways to implement widgets?
For example, I need to display Featured Products block on several pages but without duplicating the code.
You can do this with child layouts and partials.
You will need to add a line like to your layout (called my-layout.phtml in my example):
my-layout.phtml
<?php echo $this->featuredProducts; ?>
Then in your controller you can do:
$view = new ViewModel();
$featuredProductsView = new ViewModel(array('products' => $products));
$featuredProductsView->setTemplate('partial/featured-products');
$view->addChild($featuredProductsView, 'featuredProducts');
return $view;
This assume you have some config entries like:
'view_manager' => array(
'template_map' => array(
'layout/layout' => __DIR__ . '/../view/layout/my-layout.phtml',
'partial/featured-products' => __DIR__ . '/../view/products/partial/featured-products.phtml',
'template_path_stack' => array(
'expert' => __DIR__ . '/../view',
),
),
Then you would probably want to wrap this up into a controller plugin or something maybe
I have been trying to configure our Module.php to use the Module Manager Listeners for configuration (i.e interfaces that are available under Zend\ModuleManager\Feature\*). Specifically, I want to be able to configure the routes of my module outside of the main module.config.php. I have not been able to find any actual examples of this.
What I have found, if I have read the documentation correctly, is that the method getRouteConfig() should merge in my routes into the array provided by getConfig()?
Module.php
class Module implements Feature\RouteProviderInterface
{
//...
public function getRouteConfig()
{
return include __DIR__ . '/config/route.config.php';
}
//...
}
/config/route.config.php
return array(
'route_manager' => array(
'router' => array (
'routes' => array(
//.. routes that were working correctly when added to module.config.php
),
),
),
);
I can see the array returned via getRouteConfig() so I know the method is being called correctly.
Perhaps I am misunderstanding the purpose of the above interface, or I have not provided the correct "key" (route_manager) for this to be merged correctly, as I'm getting 404 for my routes.
Any help would be appreciated!
I haven't done this in the way you mentioned yet, but the key route_manager is not required within the getRouteConfig() Method.
This is due to the fact that all of the get{$specificManager}Config()-Methods are called directly from their respective Manager-Classes. Therefore the initial key is not required. Using another terminology, when using getRouteConfig() you are already in the scope of route_manager. Same as when you use getServiceConfig() you're already in the scope of service_manager. However getConfig() is within the application-scope and therefore accessing configuration of application-parts, you need to address tose specificaly.
One thing to note is: the configuration of getConfig() can be cached to increase performance, whereas all the other get{$specificManager}Config() methods are not. Especially in the case of the RouteConfiguration I'd highly suggest to use the getConfig()-Method for your RouteConfig.
If you really need to separate the configuration, then I'd suggest the way that #Hendriq displayed for you.
Well I have it working but I only use the getConfig(). What is do is I use an array_merge in the getConfig().
public function getConfig()
{
return array_merge(
require_once 'path_to_config/module.config.php',
require_once 'path_to_config/routes.config.php'
);
}
My router.config.php looks then like:
return [
'router' => [
'routes' => [
// routes
]
]
];
This way I also got some other config files seperated (ACL).
Edit
Thanks to the article Understanding ZF2-Configuration, I got an idea. I think your array should not be:
return array(
'route_manager' => array(
'router' => array (
'routes' => array(
//.. routes that were working correctly when added to module.config.php
)
)
)
);
but rather be
return array(
'router' => array (
'routes' => array(
//.. routes that were working correctly when added to module.config.php
),
),
);
The getRouteConfig is similar to the other providers it is there so you're able to create some custom routes. I guess what you're trying to do is most appropiate through hendriq's method.
An example of getRouteConfigcan be found at http://zf2cheatsheet.com/
public function getRouteConfig()
{
return array(
'factories' => array(
'pageRoute' => function ($routePluginManager) {
$locator = $routePluginManager->getServiceLocator();
$params = array('defaults' => array('controller' => 'routeTest','action' => 'page','id' => 'pages'));
$route = Route\PageRoute::factory($params);
$route->setServiceManager($locator);
return $route;
},
),
);
}
In our Module\Route namespace we create the class PageRoute which implements Zend\Mvc\Http\RouteInterface and, in our specific case for the example, Zend\ServiceManager\ServiceManagerAwareInterface. Now just implement the functions of the interface... In the sample he uses Doctrine to load the pages from the database.
Finally we can add our new custom route to our module.config.php so it can be used:
'page' => array(
'type' => 'pageRoute',
),
As you can see in this last step we go back to Hendriq's solution as the intended use is not to load the routes into the router, but creating custom routes.
Hope this helps
I'm working on an app that needs to send an email after a process is complete. Since the email needs to be HTML I had the bright idea of rendering a view as the email message body so that I can implement a "Click here to see this on your browser" functionality. This is all taking part inside a controller that implements AbstractRestfulController so the view itself resides in my front end module so that it can be accessed from a URL through a browser. However I am getting an
No RouteStackInterface instance provided
error when I try to render the view.
This is my code:
use Zend\View\HelperPluginManager;
use Zend\View\Resolver;
use Zend\View\Renderer\PhpRenderer;
//Instantiate the renderer
$renderer = new PhpRenderer();
$resolver = new Resolver\TemplateMapResolver(array(
'layout' => realpath(__DIR__ . '/../../../../Application/view/layout/email.layout.phtml'),
'email/view' => realpath(__DIR__ . '/../../../../Application/view/application/email/view.phtml')
)
);
$renderer->setResolver($resolver);
$renderer->url()->setRouter($this->getEvent()->getRouter());
I saw on the API documentation that you can set the router to the URL plugin by giving it a RouteStackInterface, hence the last line. However, that didn't seem to work either.
I would like to use the same view to send an HTML email message that has links in the message body & to display on the browser through a URL.
Any ideas/suggestions as to how to accomplish this?
EDIT/SOLUTION:
As per dotwired's answer below, getting the instance of the renderer from the service manager causes the plugins to be instantiated correctly. So this is the code that worked:
module.config.php:
array('view_manager' => array(
'template_map' => array(
'layout/email' => __DIR__ . '/../../Application/view/layout/email.layout.phtml',
'email/share' => __DIR__ . '/../../Application/view/application/email/share.phtml',
'email/view' => __DIR__ . '/../../Application/view/application/email/view.phtml',
),
),
);
REST controller:
use Zend\View\Model\ViewModel;
//get the renderer
$renderer = $this->getServiceLocator()->get('Zend\View\Renderer\RendererInterface');
//Create the views
$shareView = new ViewModel(array('data' => $data));
$shareView->setTemplate('email/view');
$emailLayout = new ViewModel(array('subject' => $this->_subject, 'content' => $renderer->render($shareView)));
$emailLayout->setTemplate('layout/email');
//Render the message
$markup = $renderer->render($emailLayout);
Using the renderer from the service manager the $this->url() view helper work without issue.
Just use the module.config.php of your module to specify your email template, like:
'view_manager' => array(
'template_path_stack' => array(
'user' => __DIR__ . '/../view'
),
'template_map' => array(
'email/view' => __DIR__ . '/../view/application/email/view.phtml'
)
),
After which you can go on with this part of the documentation. You can then pass your view template from the renderer to the MimePart which will be used by the MimeMessage like
$viewModel = new \Zend\View\Model\ViewModel();
$viewModel->setTemplate('email/view');
$renderer = $this->serviceLocator->get('Zend\View\Renderer\RendererInterface');
$htmlPart = new \Zend\Mime\Part($renderer->render($viewModel));
I have ZF 1 where i got working Bootstrap.php with lots of routing and other preDispatch stuff.
But in ZF2 there is no Bootstrap.php concept anymore? Or i mean how can i do this same in Zf2?
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {
protected function _initPdispatch() {
$this->bootstrap('frontController');
require_once APPLICATION_PATH . '/controllers/plugin/LanguageSelector.php';
$plugin = new LanguageSelector();
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin($plugin);
return $plugin;
}
protected function _initRoutes() {
$front = Zend_Controller_Front::getInstance();
$router = $front->getRouter();
$dynamic1 = new Zend_Controller_Router_Route(
'/:variable1',
array(
'controller' => 'router',
),
// array('variable1' => '^[a-zA-Z0-9_-]*$')
array('variable1' => '^[\w.-]*$')
);
$router->addRoute('dynamic1', $dynamic1);
}
One of the best features of ZF2 is something that I actually hated at first, which are the routes. It's both great and annoying because now you're required to set the routes for all of your modules.
Part of understanding ZF2 (more quickly) is understanding modules. If you can get past this, you will begin to adapt much more quickly. (At least that's how it was for me). So, what in ZF2 is a module? Anything!
Anyway, all of the config files for every module and for the application eventually get merged within the Zend Framework, so that means you can define routes anywhere really.
That said, you don't need to "bootstrap" your routes anymore as that is part of your ModuleName/config/module.config.php file. now.
Now, I'm not an expert on regex routes within ZF2, but it would be something like:
// MyModule/config/module.config.php
return array(
'router' => array(
'routes' => array(
'dynamic1' => array(
'type' => 'regex',
'options' => array(
'route' => '/[:variable1]'
)
)
)
)
);
Somewhere in there you define the regex. Additionally, I saw in their docs that you can also define a regex route manually:
use Zend\Mvc\Router\Http\Regex;
// ...
$route = Regex::factory(array(
'regex' => '/blog/(?<id>[a-zA-Z0-9_-]+)(\.(?<format>(json|html|xml|rss)))?',
'defaults' => array(
'controller' => 'Application\Controller\BlogController',
'action' => 'view',
'format' => 'html',
),
'spec' => '/blog/%id%.%format%',
));
$router->addRoute($route);
You should be able to add this as a service or put it in onBootstrap() within the Application module if you're using the Skeleton Application.
Keep in mind, that was their example and again, I'm not an expert on this. Here is some more information.
Hope this helps!