Zend Framework 2: Create Search Widget for Layout - php

I have a small private Project to learn ZF2. I have integrated Zend Lucene as a Search Function. This works well, but I now want to integrate a Search Field in my Layout, so that it is available on all pages. I am really not sure how to achieve this. First of all, am I correct that this is done with a View Helper? As you can see below, I have got no idea what I have to enter into the __invoke() of my Helper to display my Search Form. Is my way correct in general or is there a better way? I would like a good ZF2 solution, can someone give me some advice? Thank you very much in advance.
Okay, what have I done so far:
1. I created a Form:
namespace Advert\Form;
use Zend\Form\Form;
class SearchForm extends Form
{
public function __construct()
{
parent::__construct('search');
$this->setAttribute('method', 'post');
$this->add(array(
'name' => 'query',
'attributes' => array(
'type' => 'text',
'id' => 'queryText',
'required' => 'required'
),
'options' => array(
'label' => 'Search String',
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Search'
),
));
}
}
2. created a View Helper DisplaySearchForm.php !!! 2. UPDATE !!!
A BIG Thank you to AlexP for his help !!!
namespace Advert\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\Form\ElementInterface;
class DisplaySearchForm extends AbstractHelper
{
protected $form;
public function __construct($form)
{
$this->form = $form;
}
public function __invoke($form = null)
{
if ($form) {
$this->form = $form;
}
return $this->render($this->form);
}
public function render($form)
{
// return $this->getView()->form($form);
// To use my own Style, I have added a Partial
return $this->getView()->render('partial/search', array('form' => $form));
}
}
I read somewhere that it would not be good to use the ServiceLocator in the Helper, so I thought about doing that in a Factory, where I will then get the Form. So I created a Factory (not sure the Factory is right)
3. created Factory
namespace Advert\View\Helper;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class DisplaySearchFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator) {
$realServiceLocator = $serviceLocator->getServiceLocator();
$form = $realServiceLocator->get('FormElementManager')->get('\Advert\Form\SearchForm');
$helper = new DisplaySearchForm($form);
return $helper;
}
}
4. I registered the Factory in the module.php
public function getViewHelperConfig()
{
return array(
'factories' => array(
'displaySearchForm' => 'Advert\View\Helper\DisplaySearchForm',
)
)
}
5. In my Layout layout.phtml
<?php echo $this->displaySearchForm(); ?>

The AbstractHelper has a getView() which returns the 'renderer'. This means you can use all the view helpers you need, as if you were in a view script.
The new helper could look like this.
use Zend\Form\ElementInterface;
class DisplaySearchForm extends AbstractHelper
{
protected $element; // Form element to render
public function __construct(ElementInterface $element)
{
$this->element = $element;
}
public function __invoke($element = null)
{
if ($element) {
$this->element = $element;
}
return $this->render($this->element);
}
public function render(ElementInterface $element)
{
return $this->getView()->form($element);
}
}

Related

How to call doctrine.entitymanager.orm_default in zf2 plugin

I wanna use my entities inside my custom plugin. So, I am doing in that order:
1) Declared my plugin in Module\src\Plugin\Plugin.php
namespace Application\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Doctrine\ORM\EntityManager;
use User\Entity\UserProfile;
use Zend\ServiceManager\ServiceManager;
class AuthenticationPlugin extends AbstractPlugin {
protected $entityManager;
protected $serviceManager;
public function setServiceManager(ServiceManager $locator) {
$this->serviceManager = $locator;
}
public function getServiceManager() {
return $this->serviceManager;
}
public function getEntityManager() {
$userEntityFactory = new \Application\Factory\UserEntityFactory();
$this->entityManager = $userEntityFactory->createService($this->getServiceManager());
return $this->entityManager;
}
public function someAction($user_email) {
$user = $this->getEntityManager()->getRepository('User\Entity\User')->findBy(array('email'=>$user_email));
}
}
2) Created my factory:
namespace User\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class UserEntityFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $serviceLocator) {
return $serviceLocator->get('doctrine.entitymanager.orm_default');
}
}
3) Defines it in module.config.php:
'service_manager' => array(
'factories' => array(
'UserEntityFactory' => 'Application\Factory\UserEntityFactory',
),
),
'controller_plugins' => array(
'invokables' => array(
'AuthenticationPlugin' => 'Application\Plugin\AuthenticationPlugin',
)
),
4) Sending ServiceLocator to my plugin in Module.php:
public function getServiceConfig() {
return array(
'factories' => array(
'AuthenticationPlugin' => function($sm) {
$locator = $sm->getServiceLocator();
$instance = new \Application\Plugin\AuthenticationPlugin();
$instance->setServiceManager($locator);
return $instance;
},
),
);
}
5) ...and calling it in onBootstrap:
$em->attach('ZfcUser\Service\User', 'register', function($e) {
$user = $e->getParam('user'); // User account object
$authenticationPlugin = new AuthenticationPlugin();
$authenticationPlugin->someAction($user->getEmail());
});
But I received the error that $locator in plugin is null... I'm confused and I am sure that I'm doing something wrong... or all. I would be happy if somebody will share experiences or will show the order of actions. Thanks.
You don't need to inject the entire service manager object into your plugin class.
You only need to inject the User\Entity\User repository object, this appears to be the only dependancy required in your plugin class.
You should pass this into the constructor of your plugin class via your factory :
public function getServiceConfig() {
return array(
'factories' => array(
'AuthenticationPlugin' => function($sm) {
return new \Application\Plugin\AuthenticationPlugin($sm->get('doctrine.entitymanager.orm_default')->getRepository('User\Entity\User'));
},
),
);
}
in your plugin class:
class AuthenticationPlugin extends AbstractPlugin {
private $userRepository;
public function __construct(\User\Entity\User $userRepository){
$this->userRepository=$userRepository;
}
public function someAction($user_email) {
$user = $this->userRepository->findBy(array('email'=>$user_email));
}
}
As you are configuring the plugin via the module.php you don't need to also declare the plugin as an invokable in your config file. So remove the following line from your module.config.php
'AuthenticationPlugin' => 'Application\Plugin\AuthenticationPlugin'
As a side note, there are various pros and cons between declaring your services/plugins in either the module.php or the module.config file. This though wasn't the question so I won't go into detail here.

ZF2 dependency form not getting fields from fieldset

I am trying to build a form via the ZF2 FormElementManager and have followed examples on the web, including Zend's own instructions here:
http://framework.zend.com/manual/2.1/en/modules/zend.form.advanced-use-of-forms.html
Module.php
use Zend\ModuleManager\Feature\FormElementProviderInterface;
use Application\Form\PlayerFieldset;
class Module implements FormElementProviderInterface
{
public function getFormElementConfig()
{
return array(
'factories' => array(
'PlayerFieldset' => function ($sm) {
return new Form\PlayerFieldset();
},
),
);
}
}
IndexController.php
class IndexController extends AbstractActionController
{
public function indexAction()
{
$formManager = $sm->get('FormElementManager');
$form = $formManager->get('Application\Form\PlayerForm');
$formViewObj = new ViewModel(array('form' => $form));
$formViewObj->setTemplate('application/widgets/form.phtml');
return $formViewObj;
}
}
PlayerForm.php
use Zend\Form\Form;
class PlayerForm extends Form
{
public function init()
{
$this->add(array(
'name' => 'playerdetails',
'type' => 'PlayerFieldset',
));
}
}
PlayerFieldset.php
class PlayerFieldset extends Fieldset implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
public function init()
{
parent::__construct('Player');
$this->setHydrator(new ClassMethodsHydrator(false))
->setObject(new Player());
$sm = $this->getFormFactory()->getFormElementManager()->getServiceLocator();
$msi = $sm->get('MyServiceInterface');
$elements = $msi->request('FieldManager','getFieldsByEntity','Player');
// Add fields to fieldset
foreach ($elements as $element) {
$this->add($element);
}
}
}
At the end of PlayerFieldset.php, the elements have been added to $this (I can see this by doing a call to $this->getElements(); and printing out the result.
However, back in IndexController.php the $form object does not have my elements attached to it.
Can Anyone help me explain why?
The form wont have the elements the fieldset will
So rather than the forms elements :
$form->getElements();
You would need to be first fetching the playerdetails fieldset and then returning it's elements.
$form->get('playerdetails')->getElements();

Zend Framework 2: Database connection in view helper

I found a few other posts relevant to this issue, however i wasn't able to achieve what i wanted so i decided to delete everything and start over with some help...
This is my work so far, which does the job but the data are provided hard coded in an array and i need to create a database connection to fetch those data.
In my module class i have:
public function getViewHelperConfig()
{
return array(
'factories' => array(
'liveStreaming' => function() {
return new LiveStreaming();
},
),
);
}
This is the code i have in my view helper:
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
class LiveStreaming extends AbstractHelper
{
protected $liveStreamingTable;
public function __invoke()
{
$events = array(
'1' => array('name' => 'Event name',
'sport' => 'Soccer',
'time' => '11:30'),
'2' => array('name' => 'Event name',
'sport' => 'Soccer',
'time' => '17:00'),
);
return $events;
//this is what should be used (or something like that) to get the data from the db...
//return array('events' => $this->getLiveStreamingTable()->fetchAll() );
}
public function getLiveStreamingTable()
{
if (!$this->liveStreamingTable) {
$sm = $this->getServiceLocator();
$this->liveStreamingTable = $sm->get('LiveStreaming\Model\LiveStreamingTable');
}
return $this->liveStreamingTable;
}
}
So, i want to get the $events array from the database. I've created Application\Model\LiveStreaming and Application\Model\LiveStreamingTable (following the instructions of the ZF2 official tutorial) and i need some help proceeding to the next step, which should probably have to do with the service locator.
You seem to be almost there. The only thing missing is the ability to call $this->getServiceLocator(); from within the view helper (as the AbstractHelper doesn't provide this method).
There are two options
Inject the LiveStreamingTable into the view helper directly
inject the ServiceManager itself and create the LiveStreamingTable within the helper
Option 1 Make LiveStreamingTable a dependency of the view helper (type hint in constructor)
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
use LiveStreaming\Model\LiveStreamingTable;
class LiveStreaming extends AbstractHelper
{
protected $liveStreamingTable;
public function __construct(LiveStreamingTable $liveStreamingTable)
{
$this->liveStreamingTable = $liveStreamingTable;
}
public function getLiveStreamingTable()
{
return $this->liveStreamingTable;
}
}
And the factory becomes:
public function getViewHelperConfig()
{
return array(
'factories' => array(
'liveStreaming' => function($sl) {
// Get the shared service manager instance
$sm = $sl->getServiceLocator();
$liveStreamingTable = $sm->get('LiveStreaming\Model\LiveStreamingTable');
// Now inject it into the view helper constructor
return new LiveStreaming($liveStreamingTable);
},
),
);
}
Option 2 - Implement the ServiceLocatorAwareInterface (making it again a dependency of the view helper)
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class LiveStreaming extends AbstractHelper implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
protected $liveStreamingTable;
public function __construct(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator);
public function getServiceLocator();
public function getLiveStreamingTable()
{
if (null == $this->liveStreamingTable) {
$this->liveStreamingTable = $this->getServiceLocator()->get('LiveStreaming\Model\LiveStreamingTable');
}
return $this->liveStreamingTable;
}
}
Your factory will then look like:
public function getViewHelperConfig()
{
return array(
'factories' => array(
'liveStreaming' => function($sl) {
// Get the shared service manager instance
$sm = $sl->getServiceLocator();
// Now inject it into the view helper constructor
return new LiveStreaming($sm);
},
),
);
}
Personally, I feel that Option 1 makes more sense from a Dependency Injection (DI) point of view - It's clear that the LiveStreamingTable is what is needed to create the view helper.
Edit
Make sure you have the LiveStreaming\Model\LiveStreamingTable service also registered with the service manager (as we request it in the above code when we did $sm->get('LiveStreaming\Model\LiveStreamingTable');)
// Module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'LiveStreaming\Model\LiveStreamingTable' => function($sm) {
// If you have any dependencies for the this instance
// Such as the database adapter etc either create them here
// or request it from the service manager
// for example:
$foo = $sm->get('Some/Other/Registered/Service');
$bar = new /Directly/Created/Instance/Bar();
return new \LiveStreaming\Model\LiveStreamingTable($foo, $bar);
},
),
);
}

ZF2 dependency injection in parent

I want to call the method SteeringWheelMapper->fetchCarBrandList() from a controller.
This works good now, but there's a problem.
The SteeringWheelMapper extends the AbstractWebServiceMapper which has a construct method which requires an instance of \Zend\Http\Client.
As you can see in my module.config.php file, I use "factories" for the instantiation of my SteeringWheelMapper.
The supplier has multiple products, so I will have to build multiple mappers. In the current situation that means I have to add a key to the factories config for every mapper which extends AbstractWebServiceMapper.
For example, when I want to add an ExhaustMapper, I have to add
SupplierName\Mapper\Exhaust => function ($serviceMapper) {
$httpClient => new \Zend\Http\Client;
return new SupplierName\Mapper\ExhaustMapper($httpClient);
}
Now I am repeating myself, because I also have to do this for SupplierName\Mapper\SteeringWheelMapper.
I think there should be a way to make a factory for all the mappers, instead of a new key added to the factories config.
Is my thought right?
Does anyone has a suggestion how I should do this?
Please see code below.
I'm using ZF2 and I use this setup:
/vendor
SupplierName
config
module.config.php
log
log.log
src
SupplierName
Entity
AbstractEntity.php
SteeringWheelEntity.php
Mapper
AbstractWebServiceMapper.php
SteeringWheelMapper.php
$steeringWheelMapper = $this->getServiceLocator()->get('SupplierName\Mapper\SteeringWheel');
$carBrandList = $steeringWheelMapper->fetchCarBrandsList();
SteeringWheelMapper.php
<?php
namespace SupplierName\Mapper;
class SteeringWheelMapper extends AbstractWebServiceMapper
{
public function fetchCarBrandList()
{
// Code for request
// Dispatch HTTP request
$this->dispatch();
}
}
My SupplierName/config/module.config.php looks like this:
<?php
return array(
'service_manager' => array(
'factories' => array(
'SupplierName\Mapper\SteeringWheel' => function ($serviceManager) {
$httpClient = new \Zend\Http\Client;
return new SupplierName\Mapper\SteeringWheelMapper($httpClient);
},
),
),
'supplier_name' => array(
'api' => array(
'url' => 'http://api.example.com',
),
'log' => array(
'file_location' => __DIR__ . '/../log/log.log',
),
),
);
What you're actually talking about is an abstract factory, the service manager supports the concept, but you'll need to write your own, here's an example that assumes all your mappers begin with SupplierName\Mapper
<?php
namespace SupplierName\Services;
use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MapperAbstractFactory implements AbstractFactoryInterface
{
public function canCreateServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
{
if (0 === strpos($requestedName, 'SupplierName\\Mapper') && class_exists($requestedName)){
return true;
}
return false;
}
public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
{
$httpClient = new \Zend\Http\Client;
return new $requestedName($httpClient);
}
}
In your service config, add an abstract factories key, along with the fqcn of the abstract factory, and hopefully any time you call $sm->get('SupplierName\Mapper\SomeClass'); providing the class exists, you'll get a composed instance returned
public function getServiceConfig()
{
return array(
'abstract_factories' => array(
'SupplierName\Services\MapperAbstractFactory'
),
);
}
Final working solution:
<?php
// module/Application/src/Application/Controller/IndexController.php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\I18n\Translator\Translator;
class IndexController extends AbstractActionController
{
protected $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
public function indexAction()
{
$steeringWheelMapper = $this->getServiceLocator()->get('SupplierName\Mapper\SteeringWheel');
$carBrandList = $steeringWheelMapper->fetchCarBrandList();
return new ViewModel();
}
}
<?php
// vendor/SupplierName/src/SupplierName/Module.php
namespace SupplierName;
class Module
{
public function getConfig()
{
return include __DIR__ . '/../../config/module.config.php';
}
public function getServiceConfig()
{
return array(
'abstract_factories' => array(
'SupplierName\Mapper\MapperAbstractFactory'
),
);
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__,
),
),
);
}
}
<?php
// vendor/SupplierName/src/SupplierName/Mapper/SteeringWheelMapper.php
namespace SupplierName\Mapper;
class SteeringWheelMapper extends AbstractWebServiceMapper
{
public function fetchCarBrandList()
{
$this->dispatch();
}
}
<?php
// vendor/SupplierName/src/SupplierName/Mapper/AbstractWebServiceMapper.php
namespace SupplierName\Mapper;
use \Zend\Http\Client;
class AbstractWebServiceMapper
{
public function __construct(Client $client)
{
}
public function dispatch()
{
}
}
<?php
// vendor/SupplierName/src/SupplierName/Mapper/MapperAbstractFactory.php
namespace SupplierName\Mapper;
use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use \Zend\Http\Client;
class MapperAbstractFactory implements AbstractFactoryInterface
{
public function canCreateServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
{
if (0 === strpos($requestedName, 'SupplierName\Mapper')) {
$requestedName .= 'Mapper';
if (class_exists($requestedName)) {
return true;
}
}
return false;
}
public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
{
$requestedName .= 'Mapper';
$httpClient = new Client();
return new $requestedName($httpClient);
}
}

Yii disable Model Behavior for one Controller

Im using a behavior(DateTimeI18NBehavior) in Users.php model, but specifically in a controller (ApiController.php) I would like to disable it.
Model - Users.php:
public function behaviors()
{
return array(
'datetimeI18NBehavior'=>array(
'class' => 'ext.DateTimeI18NBehavior',
),
);
}
I know that I can it disable with:
$model->disableBehavior('datetimeI18NBehavior');
But how to disable to entire Controller?
Not sure, but maybe this would work:
class ApiController extends CController
{
function init()
{
Users::model()->disableBehavior('datetimeI18NBehavior');
}
}
Or you can try to add some logic in your model:
function behaviors()
{
if (Yii::app()->controller->uniqueId != 'api') {
return parent::behaviors();
}
return array(
'datetimeI18NBehavior'=>array(
'class' => 'ext.DateTimeI18NBehavior',
),
);
}
Both ways aren't perfect though in my opinion.

Categories