I followed zend Advanced use of forms to solve my problem.
Scenario:
I have two fieldset. JudgesFieldset and JudgesCareerFieldset (one judge has multiple career so i need to use collection in judge fieldset). JudgesCareerFieldset has doctrine 2 object manager dependency for creating select element and create service of JudgesCareerFieldset in module.php as described in the Advanced use of forms. Everything is fine and working and create form successfully. The code and example shown below.
class JudgesCareerFieldset extends Fieldset implements InputFilterProviderInterface {
private $entityManager;
public function __construct(ObjectManager $entityManager) {
parent::__construct('judges-career');
$this->entityManager = $entityManager;
$this->setHydrator(new DoctrineHydrator($entityManager))
->setObject(new Judges());
//fields of the entity
}
and
class JudgesFieldset extends Fieldset implements InputFilterProviderInterface {
private $entityManager;
public function __construct(ObjectManager $entityManager) {
parent::__construct('judges');
$this->entityManager = $entityManager;
$this->setHydrator(new DoctrineHydrator($entityManager))
->setObject(new Judges());
//Remaining Fields of the Judge entities
}
public function init() {
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'judgeCareer',
'options' => array(
'label' => 'Please Judge Career',
'count' => 2,
'should_create_template' => true,
'allow_add' => true,
'target_element' => array(
'type' => 'CaseLaw\Judges\Form\JudgesCareerFieldset'
),
),
));
}}
and form code
class JudgesFieldsetForm extends Form {
public function __construct(ObjectManager $entityManager) {
parent::__construct('Judges');
$this->setAttribute('method', 'post')
->setHydrator(new DoctrineHydrator($entityManager));
$judgesFieldset = new \Caselaw\Judges\Form\JudgesFieldset($entityManager);
$judgesFieldset->setUseAsBaseFieldset(true);
$this->add($judgesFieldset);
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Add Judge',
'id' => 'submit',
'class' => 'btn btn-primary'
),
));
}}
Problem:
In the view script when I tried to display collection it will display this error "No element by the name of [judgeCareer] found in form". How can i get judgeCareer collection ?
Error:
I solved my problem by changing in collection code in JudgesFieldset class.
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'judgeCareer',
'options' => array(
'label' => 'Please Judge Career',
'count' => 2,
'should_create_template' => true,
'allow_add' => true,
'target_element' => array(
'type' => 'CaseLaw\Judges\Form\JudgesCareerFieldset'
),
),
));
Change to
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'judgeCareer',
'options' => array(
'label' => 'Please choose categories for this product',
'count' => 2,
'should_create_template' => true,
'allow_add' => true,
'target_element' => new \Caselaw\Judges\Form\JudgesCareerFieldset($this->entityManager)
),
));
You are trying to add a collection named judgeCareer, but your JudgesCareerFieldset object has the name judges-career. Change the name and the Zend\Form\Fieldset should find your fieldset.
Related
I have an registration form User\UserForm which contains a fieldset User\UserFieldset. In User\UserFieldset I put an field called "passwordVerify" which should be identical with another field in the fieldset called "password".
This works fine.
However, if an admin wants to modify an user account within the Admin\UserForm, which also contains the User\UserFieldset, the field "passwordVerify" in the fieldset User\UserFieldset should be removed. Therefore I call the following within the Admin\UserForm:
$this->get('user')->remove('passwordVerify');
$this->getInputFilter()->get('user')->remove('passwordVerify')
As expected, the form lacks the field "passwordVerify" now.
If I save the form after editing some stuff, my custom filter "PasswordFilter" cannot retrieve the bound object of the fieldset anymore ($this->getOption('object'); returns an User-object) - but all properties of the bound object are nulled. If I use the Admin\UserForm without removing "passwordVerify"-field and "passwordVerify"-inputfilter everything works fine and the bound object is passed to "PasswordFilter" with populated properties (in respect of values, inserted by the user in the Admin\UserForm). The line which breaks everything is $this->getInputFilter()->get('user')->remove('passwordVerify'). So this leads to my assumption, that by removing an inputfilter, the hydrated object gets somehow nulled / emptied. Below are my some excerpts of my code, if needed I can provide more information about factories, etc.
Admin\UserForm:
class UserForm extends Form
{
/**
* #var EntityManager
*/
protected $entityManager = null;
/**
* #var Translator
*/
protected $translator = null;
public function __construct(EntityManager $entityManager, Translator $translator)
{
$this->entityManager = $entityManager;
$this->translator = $translator;
parent::__construct("userForm");
$this->setHydrator(new DoctrineHydrator($entityManager));
}
public function init()
{
// Adding UserFieldset
$this->add(array(
'name' => 'user',
'type' => \User\Form\UserFieldset::class,
'options' => array(
'use_as_base_fieldset' => true,
),
));
$this->get('user')->remove('passwordVerify');
$this->getInputFilter()->get('user')->remove('passwordVerify');
$this->add(array(
'type' => 'Zend\Form\Element\Csrf',
'name' => 'csrf',
));
$this->add(array(
'type' => 'submit',
'name' => 'submit',
'options' => array(
'label' => $this->translator->translate('Btn.submit.user', 'Form')
),
));
}
}
User\UserFieldset:
class UserFieldset extends Fieldset implements InputFilterProviderInterface
{
/**
* #var EntityManager
*/
protected $entityManager = null;
/**
* #var Translator
*/
protected $translator = null;
public function __construct(EntityManager $entityManager, Translator $translator)
{
$this->entityManager = $entityManager;
$this->translator = $translator;
parent::__construct("userFieldset");
$this->setHydrator(new DoctrineHydrator($entityManager))->setObject(new User());
}
public function init()
{
$this->add(array(
'type' => 'text',
'name' => 'firstName',
'options' => array(
'label' => $this->translator->translate('label.firstName', 'Form'),
'label_attributes' => array(
'class' => 'col-sm-3',
),
'column-size' => 'sm-5',
),
'attributes' => array(
'id' => 'firstName',
),
));
/* ... */
$this->add(array(
'type' => 'text',
'name' => 'password',
'options' => array(
'label' => $this->translator->translate('label.password', 'Form'),
'label_attributes' => array(
'class' => 'col-sm-3',
),
'column-size' => 'sm-5',
),
'attributes' => array(
'id' => 'password',
),
));
$this->add(array(
'type' => 'password',
'name' => 'passwordVerify',
'options' => array(
'label_attributes' => array(
'class' => 'col-sm-3 control-label'
),
'label' => $this->translator->translate('label.verifyPassword', 'Form'),
'column-size' => 'sm-8',
),
'attributes' => array(
'class' => 'form-control',
'id' => 'password'),
));
/* ... */
// Adding AddressFieldset
$this->add(array(
'name' => 'address',
'type' => \User\Form\AddressFieldset::class,
));
/* ... */
$this->add(array(
'type' => 'datetime',
'name' => 'created',
'options' => array(
'label' => $this->translator->translate('label.created', 'Form'),
'format' => 'd.m.Y H:i',
'label_attributes' => array(
'class' => 'col-sm-3',
),
'column-size' => 'sm-5',
),
'attributes' => array(
'id' => 'created',
),
));
}
public function getInputFilterSpecification()
{
return array(
'firstName' => array(
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
array('name' => 'StripTags'),
),
'validators' => array(),
),
/* ... */
'password' => array(
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
array('name' => 'StripTags'),
[
'name' => PasswordFilter::class,
'options' => [
'object' => $this->getObject(),
'field' => 'password'
]
]
),
'validators' => array(),
),
'passwordVerify' => array(
'required' => true,
'filters' => [
[
'name' => PasswordFilter::class,
'options' => [
'object' => $this->getObject(),
'field' => 'password'
]
]
],
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'min' => 6
)
),
array(
'name' => 'Identical',
'options' => array(
'token' => 'password'
)
)
)
),
/* ... */
'created' => array(
'required' => false,
'filters' => array(
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'Date',
'options' => array('format' => 'd.m.Y H:i')
),
),
)
);
}
}
PasswordFilter:
class PasswordFilter extends AbstractFilter
{
/** #var EntityManager */
protected $entityManager;
/** #var PasswordInterface */
protected $passwordManager;
/**
* PasswordFilter constructor.
* #param EntityManager $entityManager
* #param PasswordInterface $passwordManager
* #param array $options
*/
public function __construct(EntityManager $entityManager, PasswordInterface $passwordManager, $options = [])
{
$this->entityManager = $entityManager;
$this->passwordManager = $passwordManager;
$this->options = $options;
}
public function filter($value)
{
$object = $this->getOption('object');
$field = $this->getOption('field');
$getter = 'get'.ucfirst($field);
if (!$object || !$field) {
throw new \Exception('Options "object" and "field" are required.');
}
if ($object->getId()) {
$dbObject = $this->entityManager->getRepository(get_class($object))->find($object->getId());
if ($value === $dbObject->{$getter}()) {
return $value;
}
}
// hash password here...
return $this->passwordManager->create($value);
}
private function getOption($option)
{
if (array_key_exists($option, $this->options)) {
return $this->options[$option];
}
return false;
}
}
Any clues? Do I call remove inputfilter of "passwordVerify" to early in the process of instantiation?
I also tested to remove the inputFilter and field after "$this->form->bind($user)" in my controller, which also works. Why it does not work then if I remove it in Admin\UserForm, which is in my opinion the cleaner way of managing the "passwordVerify"-stuff?
If you call $this->getInputFilter(), the InputProviderInterface::getInputSpecification method in your UserForm is being called.
If you did not attached the object already, it cannot retrieve it.
But I dont get why you would even need that. You are hashing the password if the value does not fit to the database value, but obviously the database value seems to be plain text as the input or why would you compare it?
IMHO you just should hash the password, no matter what the current value in your database is.
If you are using doctrine and the password wont change, it wont execute an UPDATE query anyway.
I have a simple form currently consisting of a single fieldset. Now I want the fields to be filtered and validated. So I implemented the method getInputFilterSpecification() in my Fieldset class:
...
class FooFieldset extends \Zend\Form\Fieldset
{
public function __construct($name = null, $options = array())
{
parent::__construct($name, $options);
$this->setHydrator(new ClassMethods(false));
$this->setObject(new Buz());
$this->setLabel('Baz');
$this->add(array(
'type' => 'text',
'name' => 'bar',
'options' => array(
'label' => _('bar')
)
));
}
public function getInputFilterSpecification()
{
return [
'bar' => [
'required' => true,
'filters' => [
0 => [
'name' => 'Zend\Filter\StringTrim',
'options' => []
]
],
'validators' => [],
'description' => _('bar lorem ipsum'),
'allow_empty' => false,
'continue_if_empty' => false
]
];
}
}
and added the Fieldset to the Form:
...
class BuzForm extends \Zend\Form\Form
{
public function __construct($name = null, $options = array())
{
parent::__construct($name, $options);
$this->setAttribute('role', 'form');
$this->add(array(
'name' => 'baz-fieldset',
'type' => 'Buz\Form\BazFieldset'
));
$this->add(array(
'type' => 'submit',
'name' => 'submit',
'attributes' => array(
'value' => 'preview'
)
));
}
}
The problem is, that the InputFilter specifications are completely ignored. I've set a breakpoint into FooFieldset#getInputFilterSpecification() and to be sure even checked it witha die() -- the method is not called.
What is wrong here and how to get it working correctly?
You will need to implement Zend\InputFilter\InputFilterProviderInterface interface in order for the getInputFilterSpecification() method to be called.
I am developing a project with ZF2 and Doctrine. I am attempting to use Doctrine Hydrator in the form creation as shown in this tutorial. In this method, an ObjectManager object is created in the controller and passed to the new form when it is instantiated. Passing the ObjectManager object from the controller to the form creates a problem when I want to use ZF2's FormElementManager because ZF2 requires that I get an instance of the form class through the Zend\Form\FormElementManager instead of directly instantiating it. To work around this requirement, I have created form and fieldset factories based upon the answer to the question How to pass a Doctrine ObjectManager to a form through ZF2 FormElementManager. The method presented in the answer to the question works for typical fieldset elements, but I need to determine how to include a collection element. The tutorial uses the ObjectManager object in the collection element in the parent fieldset, and I need to figure out how to add the collection using a factory.
TagFieldset from the tutorial that I am trying to emulate:
namespace Application\Form;
use Application\Entity\Tag;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
class TagFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('tag');
$this->setHydrator(new DoctrineHydrator($objectManager))
->setObject(new Tag());
$this->add(array(
'type' => 'Zend\Form\Element\Hidden',
'name' => 'id'
));
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'name',
'options' => array(
'label' => 'Tag'
)
));
}
public function getInputFilterSpecification()
{
return array(
'id' => array(
'required' => false
),
'name' => array(
'required' => true
)
);
}
}
new TagFieldsetFactory:
namespace Application\Form;
use Zend\Form\Fieldset;
use Application\Entity\Tag;
class TagFieldsetFactory
{
public function __invoke($formElementManager, $name, $requestedName)
{
$serviceManager = $formElementManager->getServiceLocator();
$hydrator = $serviceManager->get('HydratorManager')->get('DoctrineEntityHydrator');
$fieldset = new Fieldset('tags');
$fieldset->setHydrator($hydrator);
$fieldset->setObject(new Tag);
//... add fieldset elements.
$fieldset->add(['...']);
//...
return $fieldset;
}
}
BlogPostFieldset from the tutorial that I am trying to emulate:
namespace Application\Form;
use Application\Entity\BlogPost;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
class BlogPostFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('blog-post');
$this->setHydrator(new DoctrineHydrator($objectManager))
->setObject(new BlogPost());
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'title'
));
$tagFieldset = new TagFieldset($objectManager);
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'tags',
'options' => array(
'count' => 2,
'target_element' => $tagFieldset
)
));
}
public function getInputFilterSpecification()
{
return array(
'title' => array(
'required' => true
),
);
}
}
new BlogPostFieldsetFactory:
namespace Application\Form;
use Zend\Form\Fieldset;
use Application\Entity\BlogPost;
class BlogPostFieldsetFactory
{
public function __invoke($formElementManager, $name, $requestedName)
{
$serviceManager = $formElementManager->getServiceLocator();
$hydrator = $serviceManager->get('HydratorManager')->get('DoctrineEntityHydrator');
$fieldset = new Fieldset('blog_post');
$fieldset->setHydrator($hydrator);
$fieldset->setObject(new BlogPost);
//... add fieldset elements.
$fieldset->add(['...']);
//...
return $fieldset;
}
}
in module.config.php:
'form_elements' => [
'factories' => [
'UpdateBlogPostForm' => 'Application\Form\UpdateBlogPostFormFactory',
'BlogPostFieldset' => 'Application\Form\BlogPostFieldsetFactory',
'TagFieldset' => 'Application\Form\TagFieldsetFactory',
],
],
When I add the fieldset elements In my new BlogPostFieldsetFactory I replace this code from the original fieldset:
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'title'
));
with this:
$fieldset->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'title'
));
How do I replace the collection element from the original fieldset:
$tagFieldset = new TagFieldset($objectManager);
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'tags',
'options' => array(
'count' => 2,
'target_element' => $tagFieldset
)
));
maybe i'm getting your question wrong.... but if you replaced this
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'title'
));
whith this:
$fieldset->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'title'
));
then you probably can replace this:
$tagFieldset = new TagFieldset($objectManager);
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'tags',
'options' => array(
'count' => 2,
'target_element' => $tagFieldset
)
));
with this:
$tagFieldset = new TagFieldset($objectManager);
$fieldset->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'tags',
'options' => array(
'count' => 2,
'target_element' => $tagFieldset
)
));
now, if you cant pass the $objectManger to the form... well if you look at the code you have this thing available $serviceManager, that thing looks like a DI container, im sure you can get the $objectManager instance from there, and if is not available, you can probably put an instance of it inside.
So de final code probably ending looks like this:
$objectManager = $serviceManager->get('DoctrineObjectManager') //or something like this
$tagFieldset = new TagFieldset($objectManager);
$fieldset->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'tags',
'options' => array(
'count' => 2,
'target_element' => $tagFieldset
)
));
I am using PHP Zendframework to build forms. I have service object that i need want to use to populate my ServiceEditForm.php. But in this service form i have Object of "Billing", "Subscription" and array of object "Commands". Below is my implementation of Service class.
class Service{
public $service_id;
public $ServiceName;
public $TelecomOperator;
public $SubMethod;
public $Provider;
public $active;
public $billingType;
public $subscriptionPlan;
public $commands;
function exchangeArray(array $data);}
I want to bind the object of Service class to my Edit form which used subscription, billing and commands related data as fieldsets. I am able to populate service values in form using bind but not other objects. here is my form implementation
class ServiceEditForm extends Form{
public function __construct($name = null)
{
parent::__construct('Edit Service');
$this->setAttribute('method', 'post');
$this->setAttribute('enctype','multipart/form-data');
//here i have other fields that belongs to service object
$this->add( array(
'name' => 'billingType',
'type' => 'Services\Form\BillingTypeFieldset',
'options' => array(
'label' => 'Billing Type',
),
));
$this->add( array(
'name' => 'subscriptionPlan',
'type' => 'Services\Form\SubscriptionPlanFieldset',
'options' => array(
'label' => 'Subscription Plan',
),
));
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'commands',
'options' => array(
'label' => 'commands',
'count' => 2,
'should_create_template' => true,
'allow_add' => true,
'target_element' => array(
'type' => 'Services\Form\CommandFieldset',
),
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Save'
),
));
}
}
As I said I am not able to populate fieldsets with bind within form with this implementation. Any suggestion will be appreciated.
Although I have already answer the question after looking up for possible solutions but Hydrator Implementations given in this link is also useful in explaining the very basics that one should read before using it. My bad i didn't went for full-throttled study before implementing fieldsets.
http://framework.zend.com/manual/current/en/modules/zend.stdlib.hydrator.html
This url explain possible implementation of hydrators that can deliver you very easy binding solution for your class.
I'm trying to work out how to filter empty records from a form collection. With my application I have 2 entities, Competition and League. A competition may have zero or more Leagues.
So I create a Competition form (CompetitionForm), a Competition fieldset (CompetitionFieldset) and a League fieldset (LeagueFieldset).
The CompetitionFieldset is added to the CompetitionForm, and the CompetitionFieldset uses Zend\Form\Element\Collection to allow the user to add 1 or more Leagues. I've added the current code for each class below.
By default, I want to display input fields for 3 leagues within a competition, so within the CompetitionFieldset, when I add the Zend\Form\Element\Collection item, I set the count option to 3.
But if a user doesn't supply any data for the leagues, I want to ignore them. At present, three empty associated leagues are created within my database.
If I set an InputFilter on the LeagueFieldset to make the name field required for example, then the form won't validate.
I should maybe also mention that I'm using Doctrine2 to model my entities and hydrate my forms etc.
I'm sure I could make it work with some additional code on my models, or even in my Controller where I process the form, but I'm sure there is a neater solution maybe using Filters.
If anyone could point me in the right direction it would be much appreciated.
Many thanks in advance for any help you can provide.
:wq
familymangreg
My CompetitionForm
use Kickoff\Form\AbstractAdminForm;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Stdlib\Hydrator\ClassMethods;
class CompetitionForm extends AbstractAdminForm
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct($objectManager, 'competition-form');
$fieldset = new CompetitionFieldset($objectManager);
$fieldset->setUseAsBaseFieldset(true);
$this->add($fieldset);
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Submit',
'id' => 'submitbutton',
),
));
}
}
My CompetitionFieldset
use Kickoff\Form\AbstractFieldset;
use Kickoff\Model\Entities\Competition;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
class CompetitionFieldset extends AbstractFieldset
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct($objectManager,'Competition');
$this->setHydrator(new DoctrineHydrator($this->objectManager,'Kickoff\Model\Entities\Competition'))
->setObject(new Competition());
$this->setLabel('Competition');
$this->add(array(
'name' => 'name',
'options' => array(
'label' => 'Competition name (e.g. Premier League)',
),
'attirbutes' => array(
'type' => 'text',
),
));
$this->add(array(
'name' => 'long_name',
'options' => array(
'label' => 'Competition long name (e.g. Barclays Premier League)',
),
'attirbutes' => array(
'type' => 'text',
),
));
$leagueFieldset = new LeagueFieldset($objectManager);
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'leagues',
'options' => array(
'label' => 'Leagues',
'count' => 3,
'should_create_template' => true,
'allow_add' => true,
'target_element' => $leagueFieldset,
),
));
}
}
My LeagueFieldset
use Kickoff\Form\AbstractFieldset;
use Kickoff\Model\Entities\League;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\InputFilter\InputFilterProviderInterface;
class LeagueFieldset extends AbstractFieldset implements InputFilterProviderInterface
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct($objectManager,'League');
$this->setHydrator(new DoctrineHydrator($this->objectManager,'Kickoff\Model\Entities\League'))
->setObject(new League());
$this->setLabel('League');
$this->add(array(
'name' => 'name',
'options' => array(
'label' => 'League name (e.g. First Qualifying Round)',
),
'attirbutes' => array(
'type' => 'text',
),
));
$this->add(array(
'name' => 'long_name',
'options' => array(
'label' => 'League long name (e.g. UEFA Champions League First Qualifying Round)',
),
'attirbutes' => array(
'type' => 'text',
),
));
}
public function getInputFilterSpecification()
{
return array(
'name' => array(
'required' => true,
)
);
}
}
You need to get data from request before you pass it to EntityManager, that's for sure. Just add your own validation to filter empty records. You can also provide javascript filters that will catch submit form event and delete empty fields before submitting it.