zend 2: Trying to use Zend\Navigation in my view helper - dependency-injection

I'm trying to create a menu bar from a template in my view helper with Zend\Navigation.
I'm getting a little closer and edited this thread with code I have now.
Here is the view helper:
<?php
namespace Helpdesk\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Navbar extends AbstractHelper implements ServiceLocatorAwareInterface {
public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator() {
return $this->serviceLocator;
}
public function __invoke() {
$partial = array('helpdesk/helpdesk/subNavTest.phtml','default');
$navigation = $this->getServiceLocator()->get('navigation');
$navigation->menu()->setPartial($partial);
return $navigation->menu()->render();
}
}
I configured the navigation in module.config.php like so:
'view_helpers' => array(
'invokables' => array(
'navbar' => 'Helpdesk\View\Helper\Navbar',
),
),
'navigation' => array(
'default' => array(
array(
'label' => 'One',
'route' => 'link',
),
array(
'label' => 'Two',
'route' => 'link',
),
array(
'label' => 'Three',
'route' => 'link',
), ...
But when I display it in my view like this <?php echo $this->navbar(); ?> it just displays the partial template without the navigation config from module.config.php.
If I do the following right in my view it displays fine with the config that I set:
<?php $partial = array('helpdesk/helpdesk/subNavTest.phtml','default') ?>
<?php $this->navigation('navigation')->menu()->setPartial($partial) ?>
<?php echo $this->navigation('navigation')->menu()->render() ?>
Why isn't my view helper pulling in the navigation config?

If I do the following right in my view it displays fine with the config that I set:
Yes, that's because in your view (the code that works), you're telling the navigation helper to use a menu container called navigation at this line...
<?php $this->navigation('navigation')->menu()->setPartial($partial) ?>
^^^^^^^^^^- This is the menu container
In your navbar helper, you don't specify a menu container. If you haven't already used the navigation helper at that point it has no menu, and creates an empty one.
You have two choices, either tell the navigation helper which container to use before calling your helper
// set the menu
<$php $this->navigation('navigation'); ?>
// render helper
<?php echo $this->navbar(); ?>
or, have your helper accept a parameter in its __invoke method which it can pass to the helper
public function __invoke($container) {
$partial = array('helpdesk/helpdesk/subNavTest.phtml','default');
$navigation = $this->getServiceLocator()->get('navigation');
// tell navigation which container to use
$navigation($container)->menu()->setPartial($partial);
return $navigation->menu()->render();
}
and call it in your view as
<?php echo $this->navbar('navigation'); ?>

Related

ZF2 An Invalid Factory Was Registered

I've the following classes and my module config in ZF2 application and it is giving the below error:
While attempting to create applicationformuserform(alias: Application\Form
\UserForm) an invalid factory was registered for this instance type.
UserFormFactory.php
<?php
namespace Application\Factory\Form;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Application\Form\UserForm;
class UserFormFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $serviceLocator) {
$services = $serviceLocator->getServiceLocator();
$entityManager = $services->get('Doctrine\ORM\EntityManager');
$form = new UserForm($entityManager);
return $form;
}
}
?>
UserForm.php
<?php
namespace Application\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
use Doctrine\ORM\EntityManager;
class UserForm extends Form implements InputFilterProviderInterface {
protected $entityManager;
public function __construct(EntityManager $entityManager) {
parent::__construct();
$this->entityManager = $entityManager;
}
public function init() {
$this->add(array(
'name' => 'username',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'User Name',
),
));
$this->add(array(
'name' => 'first_name',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'First Name',
),
));
$this->add(array(
'name' => 'last_name',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'Last Name',
),
));
$this->add(array(
'name' => 'role_id',
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'options' => array(
'object_manager' => $this->entityManager,
'target_class' => 'Application\Entity\Role',
'property' => 'id',
'is_method' => true,
'find_method' => array(
'name' => 'getRoles',
),
'label' => 'User Role',
),
));
}
public function getInputFilterSpecification() {
return array(); // filter and validation here
}
}
?>
Module.config.php
'form_elements' => array(
'factories' => array(
'Application\Form\UserForm' => 'Application\Factory\Form\UserFormFactory',
),
),
And I'm using this form factory in another controller factory
UserControllerFactory.php
<?php
namespace Member\Factory\Controller;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Member\Controller\UserController;
use Application\Form\UserForm;
class UserControllerFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $serviceLocator) {
$services = $serviceLocator->getServiceLocator();
$userForm = $services->get('FormElementManager')->get('Application\Form\UserForm');
$controller = new UserController($userForm);
return $controller;
}
}
?>
Could anybody tell me that what may be the issue?
Your Factory is not being found.
Check if you using PSR-4 or PSR-0 in your controller among the other answers
Briefly
did you name the Factory correctly (no misspellings)?
Is your composer.json updated with PSR-0, or PSR-4 namespaces for your modules?
did you run composer dump-autoload?
Does your autoload_classmap.php contain an outdated entry & confuses autoloader?
Check your folder structure and names
make sure your Factory implements FactoryInterface
Ask yourself "Why is my Factory class not being found when I have placed it right there" where it obviously without a doubt must be found? That will help you guide your way into finding out what's wrong.
I got my answer at my own after looking at the code again and again. Actually my Factory and Form folders were outside of src folder that's why Zend could not found all the classes of both folders.
I moved both Factory and Form folder in src and now it's working fine.
I had a similar problem. I had made some changes to my factory classes (refactoring + minor class name changes). Turns out that because I was using the Classmap Autoloader ... and forgot to re-run php vendor/bin/classmap_generator.php in the module structure ... the newly renamed classes were not found. Too bad a "class not found" error wasn't generated.

ZF2 - Add pages to navigation in the controller

In my project I have a navigation, which is created from an array in a config.php file using the default factory. I want to add subpages to the current pages in the controller.
class IndexController extends AbstractActionController {
public function newpageAction() {
$navigation = $this->getServiceLocator()->get('navigation');
$currentPage = $navigation->findById('index');
$options = array(
'id' => 'newpage',
'label' => 'New Page',
'route' => 'my-route',
'controller' => 'index',
'action' => 'newpage',
'active' => true,
);
$newpage = new \Zend\Navigation\Page\Mvc($options);
$currentPage->addPage($newpage);
}
}
The page is added successfully but then I try to create the url for the page in the breadcrumbs view using the getHref() method of the page:
<?php foreach($this->pages as $page) {?>
<li>
<?php echo $page->getLabel();?>
</li>
<?php }?>
But I get the following error for the newly added pages:
Additional information:
Zend\Navigation\Exception\DomainException
File:
\vendor\zendframework\zendframework\library\Zend\Navigation\Page\Mvc.php:198
Message:
Zend\Navigation\Page\Mvc::getHref cannot execute as no Zend\Mvc\Router\RouteStackInterface instance is composed
I guess the problem is in the way I create and add the pages to the navigation. Is there another way to do that or how I fix this error?
I want to add the pages after the 3th level in the controller instead of in the config file because there are params in the urls of the pages and the labels are dynamic.
Any suggestions for accomplishing this task in any other way are welcome.
You could add the default router.
\Zend\Navigation\Page\Mvc::setDefaultRouter ($this->getServiceLocator ()->get ('router'));
The error is due to the MVC page having unmet dependencies (the router). It is the factory's job to inject these components (depending on a URI or MVC type).
To make sure each MVC page has router injected create a new factory that in turn uses another already provided factory Zend\Navigation\Service\ConstructedNavigationFactory to create your own navigation container and return it's pages. In your example this will be just one page.
EDIT
If you have to add the navigation pages in the controller, where you do not know the page config prior to the newpageAction(); You could extend the class to allow config to be set within the controller.
For example
public function MyCustomNavFactory extends ConstructedNavigationFactory
{
// make the config optional
public function __construct($config = array())
{
$this->config = $config;
}
// Allow config to be set outside the class
public function setConfig($config)
{
$this->config = $config;
}
}
Module.php
// Module
public function getServiceConfig() {
return array(
'invokables' => array(
// Create the factory as an invokable (as there are no __construct args)
'MyCustomNavFactory' => 'App\Navigation\Service\MyCustomNavFactory'
),
);
}
The controller call would then just be simply just use
// Controller
public function newpageAction()
{
$serviceManager = $this->getServiceLocator();
$navigation = $serviceManager->get('MyCustomNavFactory');
$options = array(
'id' => 'newpage',
'label' => 'New Page',
'route' => 'my-route',
'controller' => 'index',
'action' => 'newpage',
'active' => true,
);
$navigation->setConfig($options);
$pages = $navigation->getPages($serviceManager);
}
The answer of #AlexP is correct.
But there error into Controller Action As when he call custom factory using ServiceLocator will get Object of type AbstractContainer Object Because ServiceLocator will call createService method into your custom factory (MyCustomNavFactory) which extends AbstractNavigationFactory So the next line will call setConfig method into AbstractContainer Object not into your custom factory (MyCustomNavFactory).
The correct Way to Set Breadcrumb configuration from Controller Action is:
// Controller
public function newpageAction()
{
$serviceManager = $this->getServiceLocator();
$navigationFactory = new MyCustomNavFactory();
$options = array(
'id' => 'newpage',
'label' => 'New Page',
'route' => 'my-route',
'controller' => 'index',
'action' => 'newpage',
'active' => true,
);
$navigationFactory->setConfig($options);
$pages = $navigationFactory->getPages($serviceManager);
}
OR
Remove setConfig method form custom factory and set configuration using it's Constructor
// custom Factory
public function MyCustomNavFactory extends ConstructedNavigationFactory
{
// make the config optional
public function __construct($config = array())
{
parent::__construct($config);
}
}
Then Controller will be:
// Controller
public function newpageAction()
{
$serviceManager = $this->getServiceLocator();
$options = array(
array(
'id' => 'newpage',
'label' => 'New Page',
'route' => 'my-route',
'controller' => 'index',
'action' => 'newpage',
'active' => true,
)
);
$navigationFactory = new MyCustomNavFactory($options);
$pages = $navigationFactory->getPages($serviceManager);
}

ZF2: Controller's Forward plugin doesn't work. How to make it work?

I need to forward the ajax request to the other Action method of current controller. I use the Forward plugin but it doesn't work. There is an example in the manual about how to use the Forward Plugin:
$foo = $this->forward()->dispatch('foo', array('action' => 'process'));
return array(
'somekey' => $somevalue,
'foo' => $foo,
);
My code:
// From Ajax on the page. I apply to the indexAction of FooController,
// I use RegEx route
xhr.open('get', '/fooindex', true);
// My Controller
namespace Foo\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
// I extend the AbstractActionController, the manual says it's important for the Forward Plugin to work
class FooController extends AbstractActionController {
// This is the action I send my request from Ajax
public function indexAction() {
// if the request if Ajax request I forward the run to the nextAction method
if ($this->getRequest()->isXmlHttpRequest()) {
// I do as manual says
$rs = $this->forward()->dispatch('FooController', array('action' => 'next'));
}
}
public function nextAction() {
// And I just want to stop here to see that the Forward Plugin works
// But control doesn't reach here
exit('nextAction');
}
}
The error I get in the Console is:
GET http://test.localhost/fooindex 500 (Internal Server Error)
If I do not use Forward everything works fine, the request comes to the indexAction just fine. Only Forward throws an error.
From the manual, about The Forward Plugin:
For the Forward plugin to work, the controller calling it must be
ServiceLocatorAware; otherwise, the plugin will be unable to retrieve
a configured and injected instance of the requested controller.
From the manual, about Available Controllers:
Implementing each of the above interfaces is a lesson in redundancy;
you won’t often want to do it. As such, we’ve developed two abstract,
base controllers you can extend to get started.
AbstractActionController implements each of the following interfaces:
Zend\Stdlib\DispatchableInterface
Zend\Mvc\InjectApplicationEventInterface
Zend\ServiceManager\ServiceLocatorAwareInterface
Zend\EventManager\EventManagerAwareInterface
So my FooController extends AbstractActionController, which implements ServiceLocatorAwareInterface, so the Forward has to work, but it doesn't. What did I miss? How to make it work?
You should remember that the dispatch plugin gets the controller to dispatch to from the service manager by name. You should therefore use the correct name and not just the classname.
Look in your configuration for the controllers.invokables array. That should contain which name of the service maps to what FQCN.
It might be you name IS FooController, then forget what I just said
You should use fully qualified name when calling the controller, so 'FooController' should be namespaced as well.
Also, you should add the controller in the list of the invokables in the module config files, for example:
return array(
'controllers' => array(
'invokables' => array(
'FooController' => 'Namespace/Controller/FooController'
...
),
)
)
try this:
class FooController extends AbstractActionController {
public function indexAction() {
return $this->forward()->dispatch('Bar\Controller\Bar',
array(
'action' => 'process',
'somekey' => $somevalue,
));
}
}
here invokable is: 'Bar\Controller\Bar' => 'Bar\Controller\Bar'
try this:
class FooController extends AbstractActionController {
public function indexAction() {
return $this->forward()->dispatch('Foo',
array(
'action' => 'process',
'somekey' => $somevalue,
));
}
}
Your module.config.php file is like this:
'controllers' => array(
'invokables' => array(
'Foo' => 'Foo\Controller\FooController', // <----- Module Controller
),
),
'router' => array(
'routes' => array(
'foo' => array(
'type' => 'segment',
'options' => array(
'route' => '/foo[/:action][/:id]', // <---- url format module/action/id
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Foo', // <--- Defined as the module controller
'action' => 'index', // <---- Default action
),
),
),
),
),

Where to put custom settings in Zend Framework 2?

I have some custom application specific settings, I want to put in a configuration file. Where would I put these? I considered /config/autoload/global.php and/or local.php. But I'm not sure which key(s) I should use in the config array to be sure not to override any system settings.
I was thinking of something like this (e.g. in global.php):
return array(
'settings' => array(
'settingA' => 'foo',
'settingB' => 'bar',
),
);
Is that an agreeable way? If so, how can I access the settings e.g. from within a controller?
Tips are highly appreciated.
In case you need to create custom config file for specific module, you can create additional config file in module/CustomModule/config folder, something like this:
module.config.php
module.customconfig.php
This is content of your module.customconfig.php file:
return array(
'settings' => array(
'settingA' => 'foo',
'settingB' => 'bar',
),
);
Then you need to change getConfig() method in CustomModule/module.php file:
public function getConfig() {
$config = array();
$configFiles = array(
include __DIR__ . '/config/module.config.php',
include __DIR__ . '/config/module.customconfig.php',
);
foreach ($configFiles as $file) {
$config = \Zend\Stdlib\ArrayUtils::merge($config, $file);
}
return $config;
}
Then you can use custom settings in controller:
$config = $this->getServiceLocator()->get('config');
$settings = $config["settings"];
it is work for me and hope it help you.
You use your module.config.php
return array(
'foo' => array(
'bar' => 'baz'
)
//all default ZF Stuff
);
Inside your *Controller.php you'd call your settings via
$config = $this->getServiceLocator()->get('config');
$config['foo'];
It's as simple as that :)
You can use any option from the following.
Option 1
Create one file called config/autoload/custom.global.php. In custom.global.php
return array(
'settings' => array(
'settingA' => 'foo',
'settingB' => 'bar'
)
)
And in controller,
$config = $this->getServiceLocator()->get('Config');
echo $config['settings']['settingA'];
Option 2
In config\autoload\global.php or config\autoload\local.php
return array(
// Predefined settings if any
'customsetting' => array(
'settings' => array(
'settingA' => 'foo',
'settingB' => 'bar'
)
)
)
And in controller,
$config = $this->getServiceLocator()->get('Config');
echo $config['customsetting']['settings']['settingA'];
Option 3
In module.config.php
return array(
'settings' => array(
'settingA' => 'foo',
'settingB' => 'bar'
)
)
And in controller,
$config = $this->getServiceLocator()->get('Config');
echo $config['settings']['settingA'];
If you look in config/application.config.php it says:
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
So ZF2 by default will autoload configuration files from config/autoload/ - so for example you could have myapplication.global.php it would get picked up and added into the configuration.
Evan.pro wrote a blog post that touches on this: https://web.archive.org/web/20140531023328/http://blog.evan.pro/environment-specific-configuration-in-zend-framework-2

how to customize the display of a sfWidgetFormInputText?

i have this in RcProfileTableForm.class.php
abstract class BaseRcProfileTableForm extends BaseFormPropel
{
public function setup()
{
$this->setWidgets(array(
'id' => new sfWidgetFormInputHidden(),
'created_at' => new sfWidgetFormDateTime(),
'name' => new sfWidgetFormInputText(),
'password' => new sfWidgetFormInputText(),
'unique_code' => new sfWidgetFormInputText(),
'msisdn' => new sfWidgetFormInputText(),
'profile_pic' => new sfWidgetFormInputText(),
'email' => new sfWidgetFormInputText(),
'age' => new sfWidgetFormInputText(),
)
}
and the form gets called like this:
<?php
include_partial('form', array('form' => $form));
echo $form->renderGlobalErrors(); ?>
so on the actual page it displays the label and the inputbox
what i need to do now is add a "*" to the end of the inputboxes of msisdn and email
the * will denote to a message appropriate to these fields
how can i achieve this? there is nowhere html that i can add a to this because the whole form gets called from the widgets
any advice please?
thanks
In your form (RcProfileTableForm) you can add custom label
class RcProfileTableForm extends BaseRcProfileTableForm
{
public function configure()
{
$this->widgetSchema->setLabels(array(
'email' => 'email*',
}
// If you want to customize widget you can make:
$this->widgetSchema['email'] = new sfWidgetFormInputText(array(), array("style"=>'width: 290px;'));
));
// UPADATE
Read this
So you must customize your form partial in apps/frontend/yourmodules/template/_form.php
In _form.php I think you have now something like this:
You can make :
<?php echo $form['email']->renderError(); ?>
<?php echo $form['email']->render() ?> <?php echo __("Email*"); ?>
//or
<?php echo $form['email']->render() ?> $form['email']->renderLabel() ?>

Resources