how to use helper function in form in zf2 - php

I want to populate the select box in the zend form using helper funtion.
following are the helper and form code.
myhelper.php
namespace myspace\View\Helper;
use Zend\View\Helper\AbstractHelper,
Zend\ServiceManager\ServiceLocatorInterface as ServiceLocator;
use Zend\Db\ResultSet\ResultSet;
class MyHelper extends AbstractHelper {
protected $serviceLocator;
protected $dbAdapter;
protected $resultData;
public function __construct(ServiceLocator $serviceLocator) {
$this->serviceLocator = $serviceLocator;
}
public function getMyList() {
return $states = array(
'a' => 'a',
'b' => 'b',
'c' => 'c', );
}
public function getServiceLocator() {
return $this->serviceLocator;
}
}
My form code
Myform.php
namespace myspace\Form;
use Zend\Form\Form;
use Zend\Db\Adapter\AdapterInterface;
use Zend\Form\Element;
use masterinfo\View\Helper\MasterHelper;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class UserForm extends Form implements ServiceLocatorAwareInterface {
protected $dbAdapter;
protected $serviceLocator;
public function __construct($args) {
parent::__construct('user');
$dbAdapter = $args['dbAdapter'];
$this->setDbAdapter($dbAdapter);
$this->setAttribute('method', 'post');
$this->setAttribute('class', 'form-horizontal');
$this->setAttribute('role', 'form');
$this->setAttribute('enctype', 'multipart/form-data');
$this->add(array(
'name' => 'testselect',
'type' => 'Zend\Form\Element\Select',
'attributes' => array(
'class' => 'single-select',
'id' => 'testselect',
'required' => 'required',
),
'options' => array(
'value_options' => /***here I need to call helper function getMyList()****/,
'empty_option' => 'Select Status'
),
));
}
function setDbAdapter(AdapterInterface $dbAdapter) {
$this->dbAdapter = $dbAdapter;
}
function getDbAdapter() {
return $this->dbAdapter;
}
public function getServiceLocator() {
return $this->serviceLocator;
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
$this->serviceLocator = $serviceLocator;
}
}
I am not sure how to call the helper function here. Please help I am relatively new to ZF2.
The code I pasted is sample actually getMyList function is suppose to populate lengthy array and I don't want to put that lengthy array in form as I will be reusing the array at few more places.

got it myself.
I can pass the servicelocator from controller.
$form = new \myspace\Form\UserForm(array('dbAdapter' => $dbAdapter,'sm'=>$this->getServiceLocator()));
and then in form
....
....
$this->add(array(
'name' => 'testselect',
'type' => 'Zend\Form\Element\Select',
'attributes' => array(
'class' => 'single-select',
'id' => 'testselect',
'required' => 'required',
),
'options' => array(
'value_options' => $this->getMyArray($args['sm']),
'empty_option' => 'Select Status'
),
));
....
....
function getMyArray($serviceLocator) {
$master = new MyHelper($serviceLocator);
return $master->getMyList();
}

Related

Zend Framework 2 Form creation via Factory. How to remove elements from fieldset depending on role?

I am creating a form using factories and specified form structure by configuring Fieldsets.
However, user with the role "admin" may edit form with all fields of an Entity and regular user "client" edit just few fields. That is why I have to delete elements from fieldsets in controller.
$this->form->getBaseFieldset()->remove('name');
$this->form->getBaseFieldset()->remove('title');
$this->form->getBaseFieldset()->remove('message');`
Is it possible to specify in Fieldset or Form configuration for what role element must be added or deleted?
class ZoneDefaultElement extends Fieldset implements InputFilterProviderInterface
{
public function __construct($name, $entity)
{
parent::__construct($name);
$this->add([
'name' => 'title',
'type' => Element\Text::class,
'attributes' => [
'class' => 'form-control',
],
'options' => [
'label' => 'Title',
'label_attributes' => [
'class' => 'col-sm-2 control-label required',
],
],
], ['priority' => 1])
};
}
The second parameter of the constructor can be anything (in fact in Fieldset it is an empty array if not given), so you should be able to just pass in an array of items to use:
class ZoneDefaultElement extends Fieldset implements InputFilterProviderInterface
{
public function __construct($name, $options)
{
parent::__construct($name);
$entity = $options['entity'];
$user = $options['user'];
// Standard element
$this->add([
'name' => 'title',
'type' => Element\Text::class,
'attributes' => [
'class' => 'form-control',
],
'options' => [
'label' => 'Title',
'label_attributes' => [
'class' => 'col-sm-2 control-label required',
],
],
], ['priority' => 1]);
if ($user->isAdmin()) {
// Add "admin-only" elements
}
};
}
Solution provided above is pretty ok. If system is not very large and contains few elements in forms it is possible to maintain it. As fare as system is a little bit complex I decided to provide more OOP solution.
class ZoneDefaultElement extends Fieldset implements InputFilterProviderInterface
{
public function __construct($name, $entity)
{
parent::__construct($name);
$this->add([
'name' => 'title',
'options' => [
'label' => 'Title',
],
], ['priority' => 1, 'access' => ['allow' => ['admin'])
}
};
And deny configuration will be like this :
$this->add([
'name' => 'message',
'options' => [
'label' => 'Message',
],
], ['priority' => 1, 'access' => ['deny' => ['guest'])
I have added one more layer between Fieldset and my custom fieldsets:
class ExtendedFieldset extends Fieldset
{
public $formMiddleware;
public function __construct($name = null, $options = array())
{
parent::__construct($name);
}
public function add($elementOrFieldset, array $flags = [])
{
if (array_key_exists('access', $flags)) {
if(!$this->getFormMiddleware()->filter($flags['access'])){
return false;
};
}
parent::add($elementOrFieldset, $flags);
}
public function setFormMiddleware(FormMiddleware $formMiddleware)
{
$this->formMiddleware = $formMiddleware;
}
public function getFormMiddleware()
{
if (!$this->formMiddleware) {
throw new \InvalidArgumentException("FormMiddleware not specified");
}
return $this->formMiddleware;
}
}
Now we have to extend from this ExtendedFiedset witch overwrites parent add() method and has setter and getter for middleware where filter logic realized.
class UserFieldset extends ExtendedFieldset implements InputFilterProviderInterface
{
private $entityManager;
public function __construct(EntityManager $entity, FormMiddleware $formMiddleware)
{
$this->setFormMiddleware($formMiddleware);
parent::__construct('fieldset');
$this->add([
'name' => 'email',
'type' => Element\Email::class,
'attributes' => [
'class' => 'form-control',
'required' => 'required',
],
'options' => [
'label' => 'Email:',
'label_attributes' => [
'class' => 'col-sm-4 control-label required',
],
],
], ['priority' => 1, ['access' => ['deny' => ['guest']]]]);
}
}
And finally FormMiddleware:
class FormMiddleware
{
private $authenticationService;
public function __construct(AuthenticationServiceInterface $service)
{
$this->authenticationService = $service;
}
private function getUserRole() : string
{
$this->getIdentity()->getRole();
}
public function getIdentity()
{
$identity = $this->authenticationService->getIdentity();
return $identity;
}
public function filter(array $resource = [])
{
$marker = true;
if(!empty($resource)){
if(array_key_exists('deny', $resource)){
if(in_array($this->getUserRole(), $resource['deny'])){
$marker = false;
}else{
$marker = true;
}
}
if(array_key_exists('allow', $resource)) {
if(in_array($this->getUserRole(), $resource['allow'])){
$marker = true;
}else{
$marker = false;
}
}
}
return $marker;
}
}
It depends on project structure, I hope an idea is clear...

zf2 Date element not required

I have a problem with Zend Framework 2 and Date element. The attribute I'm trying to store is a DateOfBirth, but this attribute maybe empty. For example the date is unknown. The column in the database allows NULL. The Doctrine class attached to it has a attribute that let's it know it allows null. But Zend Framework 2 still gives me this error:
"Value is required and can't be empty".
Even though I set the required attribute=false, also the allow_empty=true, but nothing works.
The attirbute it a member of a nested fieldset within a form. The nesting looks as follows:
UserManagementForm
User (fieldset)
Person (fieldset)
DateOfBirth (element)
Couple examples i tried:
Form not validating correctly zend framework 2
https://github.com/zendframework/zf2/issues/4302
Here is the code I am using at the moment. Hopefully you see something that I'm missing. I don't know if it due to the fact that it is nested, but the rest works perfect, only the date element is causing me trouble.
UserManagementForm
<?php
namespace Application\Form;
use Zend\Form\Form;
class UserManagementForm extends Form
{
public function __construct()
{
parent::__construct('usermanagementform');
$this->setAttribute('method', 'post');
$fieldset = new \Application\Form\Fieldset\User();
$fieldset
->setHydrator(new \Zend\Stdlib\Hydrator\ObjectProperty(false))
->setObject(new \Application\Entity\User())
->setOptions(array('use_as_base_fieldset' => true))
;
$this->add($fieldset);
$this->add(array(
'name' => 'btnSubmit',
'type' => 'submit',
'attributes' => array(
'class' => 'btn-primary',
),
'options' => array(
'column-size' => 'sm-9 col-sm-offset-3',
'label' => 'Save changes',
),
));
}
}
?>
User (Fieldset)
<?php
namespace Application\Form\Fieldset;
use Zend\Form\Fieldset;
class User extends Fieldset
{
public function __construct()
{
parent::__construct('User');
$fieldset = new \Application\Form\Fieldset\EmailAddress();
$fieldset
->setHydrator(new \Zend\Stdlib\Hydrator\ObjectProperty(false))
->setObject(new \Application\Entity\EmailAddress());
$this->add($fieldset);
$fieldset = new \Application\Form\Fieldset\Person();
$fieldset
->setHydrator(new \Zend\Stdlib\Hydrator\ObjectProperty(false))
->setObject(new \Application\Entity\Person());
$this->add($fieldset);
}
}
?>
Person (fieldset)
<?php
namespace Application\Form\Fieldset;
use Zend\Form\Fieldset;
class Person extends Fieldset
{
public function __construct()
{
parent::__construct('Person');
$this->add(array(
'type' => 'date',
'name' => 'DateOfBirth',
'required' => false,
'allowEmpty' => true,
'options' => array(
'label' => 'Date of birth',
'column-size' => 'sm-9',
'label_attributes' => array(
'class' => 'col-sm-3',
),
'format' => 'd-m-Y',
),
));
}
}
?>
'required' isn't an attribute of element but an validator attribute.
The solution consist to implement Zend\InputFilter\InputFilterProviderInterface
use Zend\InputFilter\InputFilterProviderInterface;
class UserManagementForm extends AbstractSbmForm implements InputFilterProviderInterface {
public function __construct()
{
... without change
}
public function getInputFilterSpecification()
{
return array(
'DateOfBirth' => array(
'name' => 'DateOfBirth',
'required' => false,
);
);
}
}

Using a database populated select in a form collection

I have made a form collection based on the one in the manual. Then I have made a select that is populated from a database and also based on the example from the manual. Both works by them self but now I want to use the select in the form collection and then I get the following error:
Catchable fatal error: Argument 1 passed to
Application\Form\ClientSelectFieldset::__construct() must be an instance of
Application\Model\ClientTable, none given, called in
/home/path/vendor/zendframework/zendframework/library/Zend/Form/FormElementManager.php
on line 174 and defined in
/home/path/module/Application/src/Application/Form/ClientSelectFieldset.php on line 9
It feels like the init() isn't run correctly or something, is there something special I need to do get my select to work in form collections?
Module.php
public function getFormElementConfig() {
return array(
'factories' => array(
'Application\Form\ClientSelectFieldset' => function($sm) {
$serviceLocator = $sm->getServiceLocator();
$clientTable = $serviceLocator->get('Application\Model\ClientTable');
$fieldset = new ClientSelectFieldset($clientTable);
return $fieldset;
}
)
);
}
src/Application/Form/ClientFieldset.php
<?php
namespace Application\Form;
use Application\Model\ClientTable;
use Zend\Form\Fieldset;
class ClientSelectFieldset extends Fieldset {
public function __construct(ClientTable $clientTable) {
parent::__construct('clientselectfieldset');
$options = array();
$options[] = array('value' => 0, 'label' => "Select client");
$options[] = array('value' => -1, 'label' => "---------------", 'disabled' => 'disabled');
foreach($clientTable->fetchAll() as $clientRow) {
$options[] = array('value' => $clientRow->id, 'label' => $clientRow->name);
}
$this->add(array(
'name' => 'id',
'type' => 'Zend\Form\Element\Select',
'options' => array(
'label' => 'Client',
'options' => $options,
),
));
}
public function getInputFilterSpecification() {
return array(
'id' => array(
'required' => true,
'filters' => array(
array('name' => 'Int'),
),
)
);
}
}
src/Application/Form/InvoiceFieldset.php
<?php
namespace Application\Form;
use Application\Model\Invoice;
use Zend\Form\Fieldset;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class InvoiceFieldset extends Fieldset {
... more code ...
public function init() {
$this->add(array(
'name' => 'client',
'type' => 'Application\Form\ClientSelectFieldset'
));
}
}
src/Application/Form/InvoiceForm.php
<?php
namespace Application\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilter;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class InvoiceForm extends Form {
public function __construct() {
parent::__construct('invoice-form');
... more code ...
$this->add(array(
'type' => 'Application\Form\InvoiceFieldset',
'options' => array(
'use_as_base_fieldset' => true
)
));
... more code ...
}
}
The solution was simple, i had to move the declaration of Application\Form\InvoiceFieldset in the class InvoiceForm from the __construct() to the init() function.
src/Application/Form/InvoiceForm.php
<?php
namespace Application\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilter;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class InvoiceForm extends Form {
public function __construct() {
parent::__construct('invoice-form');
... more code ...
}
public function init() {
$this->add(array(
'type' => 'Application\Form\InvoiceFieldset',
'options' => array(
'use_as_base_fieldset' => true
)
));
}
}

Zend framework 2.3 translate form

I have a problem with translating form (labels).
After searching hours on the internet, I can't find a decent explanation how it should be done.
Anybody who can give me a help here?
I'm using the formCollection($form) as written in the ZF2.3 manual
add.phtml
$form->setAttribute('action', $this->url('album', array('action' => 'add')));
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formCollection($form);
echo $this->form()->closeTag();
AlbumForm.php
namespace Album\Form;
use Zend\Form\Form;
use Zend\I18n\Translator\Translator;
class AlbumForm extends Form
{
public function __construct($name = null)
{
// we want to ignore the name passed
parent::__construct('album');
$this->add(array(
'name' => 'id',
'type' => 'Hidden',
));
$this->add(array(
'name' => 'title',
'type' => 'Text',
'options' => array(
'label' => $this->getTranslator()->translate('Name'), //'Naam',
),
));
$this->add(array(
'name' => 'artist',
'type' => 'Text',
'options' => array(
'label' => 'Code: ',
),
));
$this->add(array(
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Go',
'id' => 'submitbutton',
),
));
}
}
Error:
Fatal error: Call to undefined method Album\Form\AlbumForm::getTranslator() in /Applications/MAMP/htdocs/demo/module/Album/src/Album/Form/AlbumForm.php on line 24
The form has no knowledge of a translator by default. What you can do, is make it explicit and inject a translator. Therefore, define a factory for your form:
'service_manager' => [
'factories' => [
'Album\Form\AlbumForm' => 'Album\Factory\AlbumFormFactory',
],
],
Now you can create a factory for this form:
namespace Album\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Album\Form\AlbumForm;
class AlbumFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $sl)
{
$translator = $this->get('MvcTranslator');
$form = new AlbumForm($translator);
return $form;
}
}
Now, finalize your form class:
namespace Album\Form;
use Zend\Form\Form;
use Zend\I18n\Translator\TranslatorInterface;
class AlbumForm extends Form
{
protected $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
parent::__construct('album');
// here your methods
}
protected function getTranslator()
{
return $this->translator;
}
}

Issue with Zend Form hydrator Classmethod not binding properly to object entity

I was trying to bind entity Contact with default values (using getters) to the form ContactForm using Classmethod() hydrator.
The problem is when I then call setData with a set of values, the Hydrator was not able to merge the default values and the set of values but instead it returned only the set of values. Kindly find below an excerpt of my codes.
<?php
// My contact form
namespace Application\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterInterface;
class Contact extends Form
{
public function __construct($name = 'contact')
{
parent::__construct($name);
$this->add(array(
'name' => 'name',
'options' => array(
'label' => 'Your name',
),
'type' => 'Text',
));
$this->add(array(
'name' => 'subject',
'options' => array(
'label' => 'Subject',
),
'type' => 'Text',
));
$this->add(new \Zend\Form\Element\Csrf('security'));
$this->add(array(
'name' => 'send',
'type' => 'Submit',
'attributes' => array(
'value' => 'Submit',
),
));
// We could also define the input filter here, or
// lazy-create it in the getInputFilter() method.
}
public function getInputFilter()
{
if (!$this->filter) {
$inputFilter = new InputFilter();
$inputFilter->add(array('name' => 'name', 'required' => false));
$inputFilter->add(array('name' => 'subject', 'required' => false));
$this->filter = $inputFilter;
}
return $this->filter;
}
}
Here's my entity
class Contact
{
protected $name;
protected $subject;
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $subject
*/
public function setSubject($subject)
{
$this->subject = $subject;
}
/**
* #return mixed
*/
public function getSubject()
{
// Trying to set a default value
if (null == $this->subject) {
return 'default subject';
}
return $this->subject;
}
}
Here I test it in a controller action
class TestController extends AbstractActionController
{
public function indexAction()
{
$data = array(
'name' => 'myName'
);
$class = '\Application\Entity\Contact';
$contact = new $class;
$form = new \Application\Form\Contact();
$form->setHydrator(new ClassMethods(false));
$form->bind($contact);
$form->setData($data);
if ($form->isValid()) {
var_dump($form->getData());
}
die("end");
}
}
I wanted to get
object(Application\Entity\Contact)[---]
protected 'name' => string 'myName' (length=6)
protected 'subject' => string 'default subject' ...
But instead I got this result
object(Application\Entity\Contact)[---]
protected 'name' => string 'myName' (length=6)
protected 'subject' => null
Any idea how to make Classmethod() extract getter values from bind and merge the remaining data on setData()?
That is actually quiet easy to set the default value.
Define the default value in the entity:
class Contact
{
protected $subject = 'Default Subject';
// other properties and methods
}
In addition, you can also define the default value in the form:
$this->add(array(
'name' => 'subject',
'options' => array(
'label' => 'Subject',
),
'attributes' => array(
'value' => 'Default Subject'
),
'type' => 'Text',
));

Categories