I have a collection used in form, i know that if i want to set a value in a normal element i use:
$form->get('submit')->setValue('Update');
How can i set a value in the field 'Address "For example"' in a collection '' "I use zend Framework 2".
$companies = $this->getCompaniesTable()->getCompanies($id);
$form = new CompaniesForm();
$form->bind($companies);
$form->get('submit')->setValue('Update');
$form->get('submit')->setValue('Update');
$form->get('address')->setValue('test address');
Last line of the prev. code doesn't work, what's wrong ?!
The form code is:
<?php
namespace Companies\Form;
//use Zend\Form\Element;
use Zend\Form\Form;
class CompaniesForm extends Form {
public function __construct($name = null) {
parent::__construct('companies');
$this->setAttribute('method', 'post');
$this->setAttribute('enctype', 'multipart/form-data');
$this->add(array(
'name' => 'id',
'type' => 'Hidden'
));
$this->add(array(
'name' => 'name',
'type' => 'Text'
));
// address field
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'address',
'options' => array(
'count' => 1,
'should_create_template' => false,
'allow_add' => true,
'template_placeholder' => '__placeholder__',
'target_element' => array(
'type' => 'Companies\Form\AddressFieldset'
)
),
));
// address field
// email field
$this->add(array(
'name' => 'email',
'type' => 'text',
'options' => array('label' => 'Email:'),
));
$this->add(array(
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Go',
'id' => 'submitbutton'
)
));
}
}
The addressFieldset file:
<?php
namespace Companies\Form;
use Companies\Entity\Address;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\Stdlib\Hydrator\ClassMethods as ClassMethodsHydrator;
class AddressField {
/**
* #var string
\ */
protected $name;
/**
* #param string $name
* #return Address
\ */
public function setName($name) {
$this->name = $name;
return $this;
}
/**
* #return string
\ */
public function getName() {
return $this->name;
}
}
class AddressFieldset extends Fieldset implements InputFilterProviderInterface {
public function __construct() {
parent::__construct('Address');
$this->setHydrator(new ClassMethodsHydrator(false))->setObject(new AddressField());
$this->add(array(
'name' => 'name',
'options' => array(
'label' => 'Address: '
)
));
}
/**
* #return array
\ */
public function getInputFilterSpecification() {
return array(
'name' => array(
//'required' => true,
)
);
}
}
You need to take collection as element from your form and you get list of field sets of your collection.
In you view:
$collection = $form->get('address');
$fieldSets = $collection->getFieldsets();
// In your example you use one element as field set count = 1
// I guess you want to change field named address in your collection of the same name
$address = $fieldSets[0]->get('address');
$address->setValue('test adress');
//If you have more field sets in your collection for example count = 3 and you want this
//value for all of them just iterate your field sets.
foreach($fieldsets as $fieldset){
$fieldset->get('address')->setValue('test adress');
}
You can use Form populateValues() instead of setValue() method to do this: http://framework.zend.com/apidoc/2.3/classes/Zend.Form.Form.html#populateValues
So in Your case you should put in your controller:
$form = new CompaniesForm();
$addresses = array(
array(
'name' => 'address field 1 name'
),
array(
'name' => 'address field 2 name'
),
);
$form->get('address')->populateValues($addresses);
You can generate the addresses array using data from your DB for example.
Okay, it appears that some things are getting mixed up here. You try to manually assign Field-Values inside an EditForm. That's ... no good.
Imagine a simple Form
UserForm
textInput ("name")
textInput ("surname")
numberInput ("age")
Now you want to edit a User. So you grab the Data from DB
//$userData = $db->get('userdata')...
$userData = array(
'name' => 'Peter',
'surname' => 'Parker',
'age' => 23
);
To put the existing values into your form, all you have to do is to set the FORM into this data.
$form->setData($userData);
And that's all. In your case, obviously the data-structure is a little more different and more difficult. You'd have to have either a main Object that you could $form->bind() or your array that you set the forms data to using $form->setData() needs to be modified. In your case this would be:
$data = array(
'id' => 1, // your objects id
'name' => 'someName',
'email' => 'foo#bar.baz',
'address' => array(
0 => array(
'streetName' => 'FooStreet',
'streetNumber' => 42
),
1 => array(
'streetName' => 'OofStreet',
'streetNumber' => 24
),
)
)
When you do $form->setData($data) using the above case, your form will be pre-filled with the data coming from the array. Naturally you'd have to get the data from the DB and not write an array manually.
If you wanted to do this in a controller using getTargetElement() will return the element or fieldset assigned in the collection.
$fieldset = $form->get('parent_fieldset');
$collection = $fieldset->get('collection');
$collectionFieldset = $collection->getTargetElement();
$collectionFieldset->get('element')->setValue($value);
Related
i am new in zend framework
i need to add dynamic values inside the form selection elements
$this->add(array(
'type' => 'Zend\Form\Element\Select',
'name' => 'SECTOR_ID',
'attributes' => array(
'class' => 'form-control select2drop',
'id' => 'Sector_ID'
),
'options' => array(
'value_options' => $this->getOptionsForSectorSelect(),
),
'disable_inarray_validator' => true
));
above code help me to get dynamic values
but i need to call a controller function for getting value , now i wrote getOptionsForSectorSelect inside the form
Please help me
You could make the method inside your Controller static
class IndexController extends AbstractActionController {
public static function getOptionsForSectorSelect() {
// Building dynamic array ...
return $dynamicArray;
}
// More code ...
}
Or you could pass the array with your form when creating it in your action like so:
public function indexAction() {
$dynamicArray = $this->getOptionsForSectorSelect();
$myForm = new YourForm($dynamicArray);
// more action code...
}
And then in your form:
class YourForm extends Form {
private $dynamicArray;
public function __construct(array $dynamicArray) {
$this->dynamicArray = $dynamicArray;
}
$this->add(array(
'type' => 'Zend\Form\Element\Select',
'name' => 'SECTOR_ID',
'attributes' => array(
'class' => 'form-control select2drop',
'id' => 'Sector_ID'
),
'options' => array(
'value_options' => $this->dynamicArray,
),
'disable_inarray_validator' => true,
));
}
Hope it helps! :)
I need use form element with out isEmpty validation. This is my code.
$this->add(array(
'name' => 'test',
'type' => 'Zend\Form\Element\Number',
'attributes' => array(
'class' => 'form-control',
)
));
But following validation message is given.
[test] => Array
(
[isEmpty] => Value is required and can't be empty
)
How can i remove it?
Look here:
https://github.com/zendframework/zf2/blob/master/library/Zend/Form/Element/Number.php#L95
You can extend this class and overload getInputSpecification function and return array without 'required' => true
Like this:
namespace Your\Form\Elements;
use Zend\Form\Element\Number;
class NumberWithoutRequired extends Number{
public function getInputSpecification()
{
return array(
'name' => $this->getName(),
'required' => false,
'filters' => array(
array('name' => 'Zend\Filter\StringTrim')
),
'validators' => $this->getValidators(),
);
}
}
And then use this class for input in Your form instead of original Zend\Form\Element\Number class
If you have a specific form class, add a getInputFilterSpecification method with your validation rules:
class MyForm extends \Zend\Form\Form
{
public function init() // or __construct() if not using element manager
{
$this->add(array(
'name' => 'test',
'type' => 'Zend\Form\Element\Number',
'attributes' => array(
'class' => 'form-control',
)
));
}
public function getInputFilterSpecification()
{
return [
'test' => [
'required' => false,
]
];
}
}
You could do that by creating new ValidatorChain, and then loop through the validators attached to your element and dettach the Zend\Validator\NotEmpty validator. Just like this :
$newValidatorChain = new \Zend\Validator\ValidatorChain;
foreach ($form->getInputFilter()->get('test')->getValidatorChain()->getValidators()
as $validator)
{
//Attach all validators except the \Zend\Validator\NotEmpty one
if (!($validator['instance'] instanceof \Zend\Validator\NotEmpty)) {
$newValidatorChain->addValidator($validator['instance'],
$validator['breakChainOnFailure']);
}
}
$form->getInputFilter()->get('test')->setValidatorChain($newValidatorChain);
I'm trying to write my first form in ZF2 and my code is
namespace Frontend\Forms;
use Zend\Form\Form;
use Zend\Validator;
class Pagecontent extends Form
{
public function __construct($name = null)
{
// we want to ignore the name passed
parent::__construct('logo');
$this->setAttribute('method', 'post');
$this->add(array(
'name' => 'content_yes_no',
'type'=>'text',
'required' => true,
'validators' => array(
'name' => 'Alnum',
'options'=>array(
'allowWhiteSpace'=>true,
),
),
));
}
}
I want to know can I set validators like this?
Please advice
You've got to surround validators by another array:
'validators' => array(
array(
'name' => 'Alnum',
'options' => array(
'allowWhiteSpace'=>true,
),
),
),
To setup filters and validators you need an inputFilter. Typically you will find the inputFilter defined in the form class or associated model class. Here is a template for a form.
<?php
/* All bracket enclosed items are to be replaced with information from your
* implementation.
*/
namespace {Module}\Form;
class {Entity}Form
{
public function __construct()
{
// Name the form
parent::__construct('{entity}_form');
// Typically there is an id field on the form
$this->add(array(
'name' => 'id',
'type' => 'Hidden',
));
// Add a csrf field to help with security
$this->add(array(
'type' => 'Zend\Form\Element\Csrf',
'name' => 'csrf'
));
// Add more form fields here
$this->add(array(
'name' => 'example',
'type' => 'Text',
'options' => array(
'label' => 'Example',
),
));
//Of course we need a submit button
$this->add(array(
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Submit',
'id' => 'submitbutton',
),
));
}
}
The form defines all of the elements that will be displayed in the form. Now, you can either create the inputFilter in the form's class or in a model that is associated with the form's class. Either way it would look like:
<?php
/* All bracket enclosed items are to be replaced with information from your
* implementation.
*/
namespace {Module}\Model;
/*
* Include these if you require input filtering.
*/
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class {Model} implements InputFilterAwareInterface
{
/*
* Add in model members as necessary
*/
public $id;
public $example;
/*
* Declare an inputFilter
*/
private $inputFilter;
/*
* You don't need a set function but the InputFilterAwareInterface makes
* you declare one
*/
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
/*
* Put all of your form's fields' filters and validators in here
*/
public function getInputFilter()
{
if (!$this->inputFilter)
{
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'id',
'required' => true,
'filters' => array(
array('name' => 'Int'),
),
)));
// This example input cannot have html tags in it, is trimmed, and
// must be 1-32 characters long
$inputFilter->add($factory->createInput(array(
'name' => 'example',
'required' => false,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 32,
),
),
),
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
Then when you are programming your controller's action you can bring it all together like this:
if($request->isPost())
{
$model = new Model;
$form->setInputFilter($model->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid())
{
// Do some database stuff
}
}
Notice that we get the inputFilter from the model and use the form's setInputFilter() method to attach it.
To summarize, You must create a form class to place all of your form elements in, then create an inputFilter to hold all of your filters and validators. Then you can grab the inputFilter in the controller and apply it to the form. Of course this is just a couple ways to skin a cat though.
You can use Input Filter component:
<?php
namespace Frontend\Forms;
use Zend\Form\Form;
use Zend\Validator;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
class Pagecontent extends Form
{
public function __construct($name = null)
{
...
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'content_yes_no',
'required' => true,
'filters' => array(),
'validators' => array(
array(
'name' => 'Alnum',
'options' => array(
'allowWhiteSpace' => true,
),
),
),
)));
$this->setInputFilter($inputFilter);
}
}
// your controller
$form = new \Frontend\Forms\Pagecontent();
$form->setData($request->getPost());
if ($form->isValid()) {
// your code
}
Front End
Ext Js 4.2
The config of Ext.data.writer.Writer, writeAllFields when set false only send the fields that were modified.
http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.data.writer.Writer-cfg-writeAllFields
Back End
Zend Framework 2.2
class SomeForm extends Zend\Form\Form {
public function __construct($name = null, $options = array()) {
parent::__construct($name, $options);
$this->add(array(
'name' => 'id',
'type' => 'Zend\Form\Element\Hidden',
));
$this->add(array(
'name' => 'field_foo',
'type' => 'Zend\Form\Element\Text',
));
$this->add(array(
'name' => 'field_bar',
'type' => 'Zend\Form\Element\Text',
));
}
}
class SomeFormFilter extends Zend\InputFilter\InputFilter {
public function __construct() {
$this->add(array(
'name' => 'id',
'required' => false
));
$this->add(array(
'name' => 'field_foo',
'required' => true
));
$this->add(array(
'name' => 'field_bar',
'required' => true
));
}
}
In the service layer we check if the data sent is valid
http://framework.zend.com/apidoc/2.2/classes/Zend.Form.Form.html#isValid
$form->isValid()
When back end receives
Modified all fields: Request Method: PUT Form Data:
{"field_foo":"value string","field_bar":"value string", "id":"22"}
That's right.field_foo and field_bar are required and return true.
Modified only, field_foo: Request Method: PUT Form Data:
{"field_foo":"value string","id":"22"} isValid() return false
because field_bar is required.
Which way to the solution?
Remove field?
http://framework.zend.com/apidoc/2.2/classes/Zend.Form.Form.html#remove
Or?
Provide a different inputFilter for update operations
or
set writeAllFields to true and send and save all values
Validating only the fields that are interested. In this case, only the fields sent in the request.
public function foo($rawData) // array('id' => 1, 'field_foo' => 'value')
{
$form = $this->getForm(); // #var Zend\Form\Form
$group = array_keys($rawData); // array(0 => 'id', 1 => 'field_foo')
$form->setValidationGroup($group);
$form->setData($rawData); // array('id' => 1, 'field_foo' => 'value')
$isValid = $form->isValid();
// some code
}
https://framework.zend.com/manual/2.0/en/modules/zend.form.quick-start.html#validation-groups
I'm in the process of creating a form that let's the user schedule an event at a specified date, time and timezone. I want to combine the input of those three form fields and store them in one datetime column in the database. Based on the input I want to convert the specified date and time to UTC.
However I'm not completely sure how to write the form code for this. I was writing a Fieldset class extending Fieldset and adding the three fields to this fieldset:
<?php
namespace Application\Form\Fieldset;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterInterface;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\Stdlib\Hydrator\ClassMethods;
class SendDateFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct('senddate');
$this->add(array(
'name' => 'date',
'type' => 'Text',
'options' => array(
'label' => 'Date to send:',
)
)
);
$this->add(array(
'name' => 'time',
'type' => 'Text',
'options' => array(
'label' => 'Time to send:',
)
)
);
$this->add(array(
'name' => 'timezone',
'type' => 'Select',
'options' => array(
'label' => "Recipient's timezone",
'value_options' => array(
-12 => '(GMT-12:00) International Date Line West',
-11 => '(GMT-11:00) Midway Island, Samoa',
-10 => '(GMT-10:00) Hawaii',
),
),
)
);
}
public function getInputFilterSpecification()
{
return array(
'date' => array(
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'Date',
'break_chain_on_failure' => true,
'options' => array(
'message' => 'Invalid date'
),
),
),
),
'time' => array(
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
),
),
'timezone' => array(
'required' => true,
),
);
}
}
I then add this fieldset to my form like so:
<?php
namespace Application\Form;
use Zend\Form\Form;
class Order extends Form
{
public function __construct()
{
parent::__construct("new-order");
$this->setAttribute('action', '/order');
$this->setAttribute('method', 'post');
$this->add(
array(
'type' => 'Application\Form\Fieldset\SendDateFieldset',
'options' => array(
'use_as_base_fieldset' => false
),
)
);
}
}
Of course I will add other fieldsets to the form, the base fieldset for the order information itself and another fieldset with recipient info.
I have two questions about this:
What would be the most elegant way to handle the three fields and
store them as 1 datetime (converted to UTC) in the database? I have
an Order service object too that will be responsible for handling a
new order, so I could take care of it in the method responsible for
handling a new order in that service class or is there a better way?
I only posted a small snippet of the list of timezones in the
SendDate fieldset. Is there a cleaner way to do render this list?
Okay, so as promised I'll share my solution to this problem. Hopefully it will help someone else in the future.
I ended up using the SendDateFieldset which I initially had already.
Application\Form\Fieldset\SendDateFieldset:
<?php
namespace Application\Form\Fieldset;
use Application\Hydrator\SendDate as SendDateHydrator;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterInterface;
use Zend\InputFilter\InputFilterProviderInterface;
class SendDateFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct()
{
parent::__construct('senddate');
$this->setHydrator(new SendDateHydrator());
$this->setObject(new \DateTime());
$this->add(array(
'name' => 'date',
'type' => 'Text',
'options' => array(
'label' => 'Date to send:',
)
)
);
$this->add(array(
'name' => 'time',
'type' => 'Text',
'options' => array(
'label' => 'Time to send:',
)
)
);
$this->add(array(
'name' => 'timezone',
'type' => 'Select',
'options' => array(
'label' => "Recipient's timezone",
'value_options' => array(
// The list of timezones is being populated by the OrderFormFactory
),
),
)
);
}
public function getInputFilterSpecification()
{
return array(
'date' => array(
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'Date',
'break_chain_on_failure' => true,
'options' => array(
'message' => 'Invalid date'
),
),
),
),
'time' => array(
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'Callback',
'options' => array(
'callback' => function($value, $context)
{
// #todo: check if date and time is in the future
return true;
}
),
),
),
),
'timezone' => array(
'required' => true,
),
);
}
}
As you can see in this fieldset I now use a plain DateTime object as entity. To populate the DateTime object I use a custom hydrator for this fieldset: SendDateHydrator, which looks like this:
<?php
namespace Application\Hydrator;
use Zend\Stdlib\Hydrator\AbstractHydrator;
use DateTime;
use DateTimeZone;
class SendDate extends AbstractHydrator
{
public function __construct($underscoreSeparatedKeys = true)
{
parent::__construct();
}
/**
* Extract values from an object
*
* #param object $object
* #return array
* #throws Exception\BadMethodCallException for a non-object $object
*/
public function extract($object)
{
throw new Exception\BadMethodCallException(sprintf(
'%s is not implemented yet)', __METHOD__
));
}
/**
* Hydrate data into DateTime object
*
* #param array $data
* #param object $object
* #return object
* #throws Exception\BadMethodCallException for a non-object $object
*/
public function hydrate(array $data, $object)
{
if (!$object instanceof DateTime)
{
throw new Exception\BadMethodCallException(sprintf(
'%s expects the provided $object to be a DateTime object)', __METHOD__
));
}
$object = null;
$object = new DateTime();
if (array_key_exists('date', $data) && array_key_exists('time', $data) && array_key_exists('timezone', $data))
{
$object = new DateTime($data['date'] . ' ' . $data['time'], new DateTimeZone($data['timezone']));
}
else
{
throw new Exception\BadMethodCallException(sprintf(
'%s expects the provided $data to contain a date, time and timezone)', __METHOD__
));
}
return $object;
}
}
The hydrate method takes care of creating the DateTime object using the timezone specified by the user using a selectbox.
To generate the select with timezones in the form I made a small service which uses DateTimeZone to generate a list of timezones and formats them nicely. The end result is an associative array that can be passed to the value options of the select. The keys of this array are official timezone identifiers that DateTimeZone can handle. I pass this list in the factory class responsible for creating the form where I use this selectbox:
Application\Factory\OrderFormFactory:
<?php
namespace Application\Factory;
use Application\Service\TimezoneService;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Application\Form\Order as OrderForm;
class OrderFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$orderForm = new OrderForm();
/* #var $timezoneSvc TimezoneService */
$timezoneSvc = $serviceLocator->get('Application\Service\TimezoneService');
// Set list of timezones in SendDate fieldset
$orderForm->get('order')->get('senddate')->get('timezone')->setValueOptions(
$timezoneSvc->getListOfTimezones()
);
return $orderForm;
}
}
The generated fieldset in the form looks like this:
When saving the order the orderservice converts the DateTime to a UTC time before storing it in the database.