multiple instances of view helper plugin? - zend-framework

How do I instantiate multiple instances of a view helper plugin in Zend 2?
I want to return a new instance every time I call $this->pluginName(); from the view.
How do I return a new instance of the view plugin?

Add the service name to the getViewHelperConfig() shared configuration key in Module.php and set this value to false
Module.php
function getViewHelperConfig()
{
return array(
'shared' => array(
'MyViewHelper' => false,
),
'factories' => array(
'MyViewHelper' => 'App\View\Helper\MyViewHelperFactory',
)
);
}
By adding 'MyViewHelper' => false, the service manager (or View Helper plugin manager) will create a new instance of that service each time it is used.
The documentation states
shared An array of service name/boolean pairs, indicating whether or not a service should be shared. By default, the ServiceManager assumes all services are shared, but you may specify a boolean false value here to indicate a new instance should be returned.

Related

How can I display the name of all services with specific tag in symfony2 form

I have several services tagged with the same tag. I wonder how to proceed to retrieve the list of all thoses services and to inject it in a form ?
Here is an exemple of what I want at the end:
$builder->add('days', 'choice', array(
'choices' => $SERVICES,
'multiple' => false,
'expanded' => false,
))
The main idea is to define a service that will handle all tagged services of a certain type, and then within the compiler pass add tagged services to this service.
Taking example from the documentation on Working with Tagged Services, you may add a method that returns all services :
<?php
// ...
class TransportChain
{
// ...
/**
* #return array
*/
public function getTransports()
{
return $this->transports;
}
}
And then from anywhere you have access to the DIC, simply use :
// Get access to all services tagged with "acme_mailer.transport"
$transports = $this
->getContainer()
->get('acme_mailer.transport_chain')
->getTransports()
;
Of course you will have to tweak all this a bit in order to, perhaps, return strings instead of objects.

Why does not work short names when I create custom form elements in Zend Framework 2?

I create custom element like here: ZF2Docs: Advanced use of Forms
1.Create CustomElement class in Application/Form/Element/CustomElement.php
2.Add to my Module.php function
public function getFormElementConfig()
{
return array(
'invokables' => array(
'custom' => 'Application\Form\Element\CustomElement',
),
);
}
If I use FQCN it works fine:
$form->add(array(
'type' => 'Application\Form\Element\CustomElement',
'name' => 'myCustomElement'
));
But if I use short name:
$form->add(array(
'type' => 'Custom',
'name' => 'myCustomElement'
));
throws Exception:
Zend\ServiceManager\ServiceManager::get was unable to fetch or create
an instance for Custom
Problem
The error is probably due to how you are instantiating the $form object. If you just use the new Zend\Form\Form expression or something similar the form will not be set up with the correct service locator.
$form = new \Zend\Form\Form;
$form->add(array(
'type' => 'custom',
'name' => 'foobar',
));
Solution
The trick here is to use the FormElementManager service locator to instantiate the form.
// inside a controller action
$form = $this->getServiceLocator()->get('FormElementManager')->get('Form');
$form->add(array(
'type' => 'custom',
'name' => 'foobar',
));
Better yet, define a form() method in your controller as a shortcut to do this for you:
class MyController extends AbstractActionController
{
public function form($name, $options = array())
{
$forms = $this->getServiceLocator()->get('FormElementManager');
return $forms->get($name, $options);
}
public function createAction()
{
$form = $this->form('SomeForm');
// ...
}
}
Explanation
Each form object is attached to a form factory which is in turn attached to a service locator. This service locator is in charge of fetching all the classes used to instantiate new form/element/fieldset objects.
If you instantiate a new form object (all by itself), a blank service locator is instantiated and used to fetch later classes within that form. But each subsequent object is then attached to that same service locator.
The problem here is that getFormElementConfig configures a very specific instance of this service locator. This is the FormElementManager service locator. Once it's configured, all forms pulled from this service locator will then be attached to this service locator and will be used to fetch other elements/fieldsets etc.
Hope this solves your issue.

Full dynamic router in Zend Framework

By default you have the following URL-syntax in ZF: /module/controller/action. What i want, is to build an menu-system where i can use any URL I want.
Lets say I make an menu-item called 'news'. When i call http://www.site.com/news i want to have the folowing loaded:
module: news
controller: frontpage
action: display
These config-values must be configured in the database-record for the menu-item.
How can I do this in zend? I spend a lot of time searching for it, but I still can't figure out how to. Does anybody?
I'd suggest using a front controller plugin to scan your database for all the entries, create routing rules based on those entries and add them to the router (see this).
Of course caching strategy is recommended so that you don't do a lot of processing on every request.
You can create a plugin and in routeStartup define something that intercept your request and route /module/controller/action to /action, but for this all your action names must be unique :
class My_CustomRouterPlugin extends Zend_Controller_Plugin_Abstract
{
public function routeStartup(Zend_Controller_Request_Abstract $request)
{
$fc = Zend_Controller_Front::getInstance();
$action =$fc->getRequest()->getActionName();
$router = $fc->getRouter();
$model= new myModel();
$myPage = $model->getPageByAction($action);
$route = new Zend_Controller_Router_Route('/action', array(
'module' => $myPage->getModule();
'controller' => $myPage->getController();
'action' => $action;
));
$router->addRoute($action, $route);
return $router;
}
}
In myModel define a method can get you an object(or an array) that contains module, controller names (from you DB ).
and register this plugin in your bootstrap:
$front->registerPlugin(new My_CustomRouterPlugin());

Zend router behaviour

I have some trouble with the router.
I have a custom route :
$router->addRoute('showTopic',
new Zend_Controller_Router_Route('/forum/topic/:topic',
array('module' => 'forum',
'controller' => 'topic',
'action' => 'show'),
array('topic' => '\d+')));
But when I try to access this url : localhost/forum/topic/16
I get this error :
Fatal error: Uncaught exception 'Zend_Controller_Router_Exception' with message 'topic is not specified'
But I don't want to put a default value for topic, because I also want the route /forum/topic to list all topics...
Secondly, I know that if I add a custom route, the default router is overridden, but I need to have some default routes too. The only way I have found is to set 'default' in the second parameter of the url view helper, like this
$this->url(array(
'module' => 'forum',
'controller' => 'topic',
'action' => 'add'
), 'default', true)
Is there a more elegant way instead of doing this for all url where I want to use the default behavior ?
You should have a default value for a topic and add the more general route (the one for forum/topic) after the more specific one. Route_Rewrite checks the routes beginning with the last one (it actually does an array_inverse).
The url helper delegates assembly urls to a route, its second paremeter being the name of the route to pull from the router. Since the default route is registered under the name of 'default', there is nothing really inelegant in using the name (it is not a magic string or a special case). If this really bugs you, you could write a custom helper (to be placed under "views/helpers"):
class Zend_View_Helper_DefaultUrl extends Zend_View_Helper_Abstract {
public function defaultUrl($params) {
return $this->view->url($params, 'default');
}
}
And use it in your view like defaultUrl(array('action'=>'test')) ?>.

Zend_Navigation url generation issue

I use Navigation component for site menus. I also use let zend figure-out the selected menu item from request parameters - I guess this is done automatically. The only problem is, that for this to work, action and controller have to be specified in navigation configuration for every node. This also means that when zend generates links from route, action and controller information to appended to the generated link automatically.
Anyone had the same problem?
Zend manual section, explaining the Mvc navigation page features.
Example:
some route defined in bootstrap:
$router->addRoute('user_profile_tab', new Zend_Controller_Router_Route(
'profil/:user/:location/:tab/*',
array(
'action' => 'profile',
'controller' => 'user',
'user' => ($user ? $user->id : 0), //change later
'location' => 0 //inject appropriate value later
)
));
navigation container object:
$container = .....
......,
array(
'label' => tr('Privileges'),
'id' => 'user-profile-perms',
'type' => 'Zulu_Navigation_Page',
'controller' => 'user',
'action' => 'profile',
'route'=> 'user_profile_tab',
'params' => array('tab'=>Main_Lib_Common::NAVI_USER_TAB_PERMS)
)
);
the result when using
$page = $container->getById('user-profile-perms');
$page->href;
http://www.example.com/profil/1/0/3/controller/user/action/profile
WHY action and controler params in the navigation container object you ask. The $page->isActive() check needs this data to make a perfect match.
THE FIX:
extend mvc navigation page and provide an alternative getHref() method ... one that removes action, controller and module params when a route does not define them.
I have done this to fix this weird behaviour:
extend mvc navigation page
provide an alternative getHref() method
check for routes, not having action , controller and module parameters and remove them from params array before href generation.
This way the isActive matching will still work, as we didnt modify the route or navigation nodes in any way.