Symfony 2 How do I embed collection form using some criterias - forms

I have a problem using the embed collection forms because I want to filter the data displayed on the collection given. i.e.
<?php
Class Parent
{
... some attributes ...
/**
* #ORM\OneToMany(targetEntity="Child", mappedBy="parent", cascade={"all"})
*/
private $children;
... some setters & getters ...
}
Class Child
{
private $attribute1;
private $attribute2;
/**
* #ORM\ManyToOne(targetEntity="Parent", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
private $parent;
... some setters & getters ...
}
Then I build the form using:
class ParentChildType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('children', 'collection', array(
'type' => ChildrenType(),
'allow_add' => true,
));
}
}
...
On controller:
$parent = $this->getDoctrine()->getEntityManager()->getRepository('AcmeBundle:Parent')->find( $id );
$forms = $this->createForm( new ParentChildType(), $parent)->createView();
and then..
return array('forms' => $forms->crateView());
My problem is when I want to filter the collection by $attribute1 and/or $attribute2 of Child model class.
There's a way to filter by a criteria for this collection forms?

It's seems that I have to filter the object before using CreateQuery and then create the form using this filtered object.
Like this:
$parent = $this->getDoctrine()->getEntityManager()->createQuery("
SELECT p, c
FROM AcmeBundle:Parent p
JOIN p.children c
WHERE c.attribute1 = :attr1
AND c.attribute2 = :attr2
")
->setParameter('attr1', <some_value>)
->setParameter('attr2', <some_value>)
->getOneOrNullResult();
$forms = $this->createForm( new ParentChildType(), $parent)->createView();
....
return array('forms' => $form->createView());

I point you to right direction (I hope) :
http://www.craftitonline.com/2011/08/symfony2-ajax-form-republish/
This article deals with field dependencies. for example, when you select a country, you have the towns that belongs to the country that appears in the list.
It's seems it looks like your problem

Related

Select or create an entity

I usually find answers to solve my problems after some research, but that is not the case today.
Let's suppose I have 2 entities, "Task" and "TaskCategory", each task has one category.
I want to let my users not only attribute an existing category to their tasks but also create new ones.
Here's the code I have so far:
<?php
// TaskCategoryType.php
class TaskCategoryType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
;
}
// Some stuff...
and
<?php
// TaskType.php
// Some stuff...
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// Add some fields there
->add(
'category',
TransformableEntityType::class,
['class' => 'AppBundle:TaskCategory']
)
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$task = $event->getData();
$form = $event->getForm();
if (empty($task['category']['id']) && !empty($task['category']['name'])) {
$task['category']['id'] = null;
$event->setData($task);
$form->remove('category');
$form->add('category', TaskCategoryType::class);
}
})
;
}
// Some stuff...
It works fine for task creation, but when I edit an existing task, it edit the associated TaskCategory instead of creating a new one.
I was trying to force the creation of a new TaskCategory with : $task['category']['id'] = null; but it does not work.
Thanks for your help I'm really stuck :(
EDIT: I forgot to mention that I'm using this form only as an API that's why I have only one 'category' field, otherwise I would have used another 'category_new' field.
Well it seems I finally found something working, it's not pretty, I'm not pretty happy with how it's done but at the moment I have not found any alternative.
If you have a cleaner solution I'll be happy to learn.
<?php
class TaskType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// Some fields...
->add(
'category',
TransformableEntityType::class,
['class' => 'AppBundle:TaskCategory']
)
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$task = $event->getData();
$form = $event->getForm();
if (empty($task['category']['id']) && !empty($task['category']['name'])) {
// If there is no category id but there is a category
// name it means it is a new category. so replace
// the custom entity field with this simple field
$form->remove('category');
$form->add('category', TaskCategoryType::class, ['allow_extra_fields' => true]);
}
})
->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
$catForm = $event->getForm()->get('category');
if ($catForm->has('name') && $catForm->get('name')->getData() !== "") {
// If there is a category name it means
// it is a new category.
$category = new TaskCategory(); // Create new category
$category->setName($catForm->get('name')->getData());
$task = $event->getData()->setCategory($category);
$event->setData($task);
}
})
;
}
Considering you have cascade-persist enabled for your association from Task to TaskCategory. I guess the best work around will be, to use an autocompleter which should work as tag based.
The Transformer should check with database if given TaskCategory is available, else create a new one.

Symfony2: How call the repository of an entity in the FormType

I tried to call the repository of my entity Category in the class form of my entity BonCommande, but this error ocuured:
Notice: Undefined property: Application\VehiculeBundle\Form\BonCommandeType::$em in C:\wamp\www\Symfony_test\src\Application\VehiculeBundle\Form\BonCommandeType.php line 74
This is my code:
The class BonCommandeType.php:
namespace Application\VehiculeBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Application\VehiculeBundle\Entity\Category;
class BonCommandeType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Name of the user
$builder->add('observation', 'text');
/* Add additional fields... */
$builder->add('save', 'submit');
// Add listeners
$builder->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'onPreSetData'));
$builder->addEventListener(FormEvents::PRE_SUBMIT, array($this, 'onPreSubmit'));
}
protected function addElements(FormInterface $form, Category $categorie = null) {
// Remove the submit button, we will place this at the end of the form later
$submit = $form->get('save');
$form->remove('save');
// Add the province element
$form->add('categorie', 'entity', array(
'data' => $categorie,
'empty_value' => '-- Choose --',
'class' => 'ApplicationVehiculeBundle:Category',
'property' => 'intitule',
'mapped' => false)
);
// Cities are empty, unless we actually supplied a province
$vehicules = array();
if ($categorie) {
// Fetch the cities from specified province
$repo = $this->em->getRepository('ApplicationVehiculeBundle:Vehicule');
$cities = $repo->findByCategory($categorie);
}
// Add the city element
$form->add('vehicule', 'entity', array(
'empty_value' => '-- Select a categorie first --',
'class' => 'ApplicationVehiculeBundle:Vehicule',
'choices' => $vehicules,
));
// Add submit button again, this time, it's back at the end of the form
$form->add($submit);
}
function onPreSubmit(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
// Note that the data is not yet hydrated into the entity.
$categorie = $this->em->getRepository('ApplicationVehiculeBundle:Category')->find($data['categorie']);
$this->addElements($form, $categorie);
}
function onPreSetData(FormEvent $event) {
$account = $event->getData();
$form = $event->getForm();
// We might have an empty account (when we insert a new account, for instance)
$categorie = $account->getVehicule() ? $account->getVehicule()->getCategorie() : null;
$this->addElements($form, $categorie);
}
...
}
This is the instruction that causes the error:
$categorie = $this->em->getRepository('ApplicationVehiculeBundle:Category')->find($data['categorie']);
FormComponent is an independent component and it doesn't provide any entityManager to use. You have to inject it or pass it by $options if you want to use it..
In your case it would be correct if you directly pass it to the type's __construct or pass by $options array or declare your type as a service and inject entity manager to it:
class BonCommandeType extends AbstractType
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
...
}
or
$this->createForm(TYPE, DATA, ['em' => $em]);
From your code I assume you are missing this:
//Somewhere at the begging of your BonCommandeType
protected $em;
...
public function __construct(EntityManager $em)
{
$this->em = $em;
}
Keep in mind that when you create a new form object you should use smth like :
BonCommandeType($this->getDoctrine()->getManager()) // if inside a controller

Form post-processing in Symfony2

I am new of Symfony, and I am trying to create a form bound to an Entity User.
One field of this entity is of type ArrayCollection. It is actually a OneToMany relationship with objects of another class.
So, a little bit of code just to be clearer.
class User
{
\\...
/**
* #ORM\OneToMany(targetEntity="UserGoods", mappedBy="users")
* #ORM\JoinColumn(name="goods", referencedColumnName="id")
*/
private $goods;
public function __construct()
{
$this->goods = new ArrayCollection();
}
\\...
}
And the associated class
class UserGoods
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime
*
* #ORM\Column(name="inserted_at", type="datetime")
*/
private $insertedAt;
/**
* #var float
*
* #ORM\Column(name="value", type="float")
*/
private $value;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="goods")
*/
protected $users;
}
Now, I want to create a FormBuilder that does something extremely simple, yet I couldn't figure it out how to do it by myself.
I want just a field of type number, and if an object of type Goods with the current date exists, modify it, otherwise add a new object to the collection.
This could be easily done inside the controller, but I have a lot of instances of this form, and this would make my program impossible to maintain.
Is there a way to add some post-processing of submitted data inside the form builder?
I already tried with DataTransformers but these won't suffice, as at most they would transform a number to a UserGoods object, and the original ArrayCollection would not be preserved (and what about doctrine associations?).
In addition, if I declare the field type as collection of number types, all the items inside the ArrayCollection would be displayed when rendering the form, not just the last one.
Any idea on how to get out of this?
Thank you in advance for your help.
As suggested, use Form Events. Inside the event you will check if the Goods with the submitted date already exist (load them from database) and your will modify them with the post data. If they dont exist, you will be creating new ones. You can also make another method in your entity, getLastItemsInCollection(), where you can use Criteria, to only load the last one from the database (recommended), or get the last item from original ArrayCollection. You can make a field unmapped, and map the Goods manually in the FormEvent, as described above. I hope that helps and I hope I understood correctly.
I followed Cerad and tomazahlin suggestions and I came up with a solution.
I am sure that every year at least 2 people over the world share my same problem, so I'll take some time to post my outcome.
Feel free to correct, criticize or add me, in the end I am a newbie of Symfony!
First, how I defined my two classes in the end.
class User
{
//...
/**
* #ORM\ManyToMany(targetEntity="UserGoods", inversedBy="users", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="goods", referencedColumnName="id")
*/
// Should have been a OneToMany relationship, but Doctrine requires the
// owner side to be on the Many side, and I need it on the One side.
// A ManyToMany relationship compensate this.
private $goods;
public function __construct()
{
$this->goods = new ArrayCollection();
}
//...
}
And the connected class
/**
* #ORM\HasLifecycleCallbacks()
**/
class UserGoods
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime
*
* #ORM\Column(name="inserted_at", type="datetime")
*/
private $insertedAt;
/**
* #var float
*
* #ORM\Column(name="value", type="float", nullable=true)
*/
// I do not want this field to be null, but in this way when
// persisting I can look for null elements and remove them
private $value;
/**
* #ORM\ManyToMany(targetEntity="User", inversedBy="goods")
*/
protected $users;
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
// This automatically sets InsertedAt value when inserting or
// updating an element.
public function setInsertedAtValue()
{
$date = new \DateTime();
$this->setInsertedAt( $date );
}
}
As I said, I wanted a FormBuilder to handle my array collection. The best form type for this purpose is... collection type.
This require a subform to be defined as its type.
<?php
namespace MyBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use MyBundle\Entity\UserGoods;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('goods', 'collection', array(
'type' => new GoodsdataWithDateType(),
'required' => false,
)
);
\\ ...
And the subform.
Since I need only the today's value to be displayed, and not all of them, I also need to add a FormEvent clause to check which items to insert.
namespace MyBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class GoodsdataWithDateType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Here I add the event listener:
// Since I want only today's value to be displayed, I implement
// a check on this field of each element
$builder->addEventListener(
FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$goods = $event->getData();
$form = $event->getForm();
$datetime1 = $goods->getInsertedAt();
$datetime2 = new \DateTime();
$datetime2->setTime(0, 0, 0);
if ($datetime1 > $datetime2)
{
$form->add('value', 'number', array(
'required' => false,
));
// I am setting this value with LifecycleCallbacks, and I do not
// want the user to change it, I am adding it commented just for
// completeness
// $form->add('insertedAt', 'date', array(
// 'widget' => 'single_text',
// 'format' => 'yyyy,MM,dd',
// ));
}
});
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MyBundle\Entity\UserGoods',
));
}
public function getName()
{
return 'goodsdatawithdate';
}
}
This works fine, but is displayed very badly when rendered with something like {{ form(form) }} in twig files.
To make it more user-friendly, I customized how the form was presented, in order to remove some garbage and include only the labels that were necessary.
So in my twig:
{{ form_start(form) }}
{{ form_errors(form) }}
<div>
{{ form_label(form.goods) }}
{{ form_errors(form.goods) }}
<br>
{% for field in form.goods %}
{{ form_widget(field) }}
{% endfor %}
</div>
{{ form_end(form) }}
This is nice so far, but I also want to include new elements in my collection, in particular if today's good has not been inserted yet.
I can do this inside my FormBuilder, by manually add a new item in the array before calling the $builder.
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$thisuser = $builder->getData();
// I added the following function inside the User class.
// I use a for loop to scroll all the associated Goods to get the
// latest one.
$mygoods = $thisuser->getLatestGoods();
if ( $mygoods && null !== $mygoods->getId() ) {
// The Array contains already some elements
$datetime1 = $mygoods->getInsertedAt();
$datetime2 = new \DateTime();
$datetime2->setTime(0, 0, 0);
// Check when was the last one inserted
if ($datetime1 < $datetime2) // Nice way to compare dates
{
// If it is older than today, add a new element to the array
$newgoods = new UserGoods();
$thisuser->addGoods($newgoods);
}
} else {
// The array is empty and I need to create the firs element
$newgoods = new UserGoods();
$thisuser->addGoods($newgoods);
}
$builder->add('goods', 'collection', array(
'type' => new GoodsdataWithDateType(),
'required' => false,
'allow_add' => true, // this enables the array to be
// populated with new elements
)
);
But I also want that if a user removes an inserted value (i.e., inserts nothing in the form), the associated array element should be removed.
Allowing the user to remove elements is a little bit trickyer. I cannot rely on 'allow_delete' property, since by working only with the last item in the collection, all the previous ones would be removed when the form is submitted.
I cannot rely on LifecycleCallbacks neither, because the changes made to relationships are not persisted in the database.
Thankfully to open source, I found a post here that helped me.
What I needed was an EventListener on Doctrine Flush operations.
namespace MyBundle\EventListener;
use Doctrine\ORM\Event\OnFlushEventArgs;
use MyBundle\Entity\UserGoods;
class EmptyValueListener
{
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$entities = array_merge(
$uow->getScheduledEntityInsertions(),
$uow->getScheduledEntityUpdates()
);
foreach ($entities as $entity) {
if ($entity instanceof UserGoods) {
if ($entity && null !== $entity )
{
if ( empty($entity->getValue()) )
{
$users = $entity->getUsers();
foreach ($users as $curruser)
{
$curruser->removeGoods($entity);
$em->remove($entity);
$md = $em->getClassMetadata('MyBundle\Entity\UserGoods');
$uow->computeChangeSet($md, $entity);
$em->persist($curruser);
$md = $em->getClassMetadata('MyBundle\Entity\User');
$uow->computeChangeSet($md, $curruser);
}
}
}
}
}
}
}
and registered it in my config.yml as
mybundle.emptyvalues_listener:
class: MyBundle\EventListener\EmptyValueListener
tags:
- { name: doctrine.event_listener, event: onFlush }

Symfony2 form field constraints validation before Data Transformer

I have created form which requires data transformer, but got myself into single problem: I transform data by exploding string (string should be be exploded to 3 parts), everything works, if I supply correct format string, but otherwise it throws error inside data transformer, because transformation cannot occur if wrong string format is supplied (this is expected behavior).
So the question is is there a way to validate form field for correct string before data transformation? I know that data transformation by default occurs before validation, but maybe there's a way to do it other way around?
I found one solution that might work on this thread: Combine constraints and data transformers ,
but it's looks like rough solution, besides I need to translate validation message, and I would really like to do it using default translation methods for symfony forms (without using translation service)
I thought, and also someone from symfony IRC (Iltar) suggested do it by using events, but I'm not sure how to go about this - how to attach data transformer dynamically to form field? Or maybe there's other way?
It's maybe too late but I eventually manage to do it.
Maybe it will help you.
Here is my FormType:
class PersonType extends AbstractType{
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('mother', 'personSelector', array('personEntity' => $options['personEntity']));
}
}
Here is my customField where are validations:
class PersonSelectorType extends AbstractType{
public function buildForm(FormBuilderInterface $builder, array $options){
$transformer = new PersonByFirstnameAndLastnameTransformer($this->entityManager,$options);
$builder->addModelTransformer($transformer);
$builder->addEventListener(FormEvents::PRE_SUBMIT, array($this, 'onPreSubmitForm'));
}
public function onPreSubmitForm(FormEvent $event){
$mother = $event->getData();
$form = $event->getForm();
$options = $form->getConfig()->getOptions();
if (!empty($mother)){
preg_match('#(.*) (.*)#', $mother, $personInformations);
if (count($personInformations) != 3){
$form->addError(new FormError('[Format incorrect] Le format attendu est "Prénom Nom".'));
}else{
$person = $this->entityManager->getRepository($options['personEntity'])->findOneBy(array('firstname' => $personInformations[1],'lastname' =>$personInformations[2]));
if ($person === null) {
$form->addError(new FormError('Il n\'existe pas de person '.$personInformations[1].' '.$personInformations[2].'.'));
}
}
}
}
}
Here is my transformer:
class PersonByFirstnameAndLastnameTransformer implements DataTransformerInterface{
public function reverseTransform($firstnameAndLastname) {
if (empty($firstnameAndLastname)) { return null; }
preg_match('#(.*) (.*)#', $firstnameAndLastname, $personInformations);
$person = $this->entityManager->getRepository($this->options['personEntity'])->findOneBy(array('firstname' =>$personInformations[1],'lastname' =>$personInformations[2]));
if (count($personInformations) == 3){
$person = $this->entityManager->getRepository($this->options['personEntity'])->findOneBy(array('firstname' =>$personInformations[1],'lastname' =>$personInformations[2]));
}
return $person;
}
public function transform($person) {
if ($person === null) { return ''; }
return $person->getFirstname().' '.$person->getLastname();
}
}
Perhaps you could pass the instance of your form to your transformer. If the string doesn't parse correctly, simply add a validation error to the form, like so:
<?php
// src/Acme/MyBundle/Form/DataTransformer/StringTransformer.php
namespace Acme\MyBundle\Form\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Doctrine\Common\Persistence\ObjectManager;
use Acme\MyBundle\Entity\MyEntity;
use Acme\MyBundle\Entity\AnotherEntity;
use Acme\MyBundle\Type\MyEntityType;
class StringTransformer implements DataTransformerInterface
{
/**
* #var MyEntityType
*/
private $form;
/**
* #param ObjectManager $om
*/
public function __construct(MyEntityType $form)
{
$this->form = $form;
}
/**
* Transforms an object (entity) to a string (number).
*
* #param MyEntity|null $entity
* #return string
*/
public function transform($value)
{
// ...
}
/**
* Transforms a string (number) to an object (entity).
*
* #param string $number
*
* #return MyEntity|null
*
* #throws TransformationFailedException if object (entity) is not found.
*/
public function reverseTransform($value)
{
$collection = new ArrayCollection();
try{
$vals = explode(',', $value);
foreach($vals as $v){
$entity = new AnotherEntity();
$entity->setValue($v);
$collection->add($v);
}
} catch(\Exception $e){
$this->form
->get('my_location')
->addError(new FormError('error message'));
}
return $collection;
}
}
but it's looks like rough solution, besides I need to translate validation message, and I would really like to do it using default translation methods for symfony forms (without using translation service)
I know this question is old, but as any answer has been marked yet as the right solution, I share with you another approach.
emottet solution, using a presubmit listener to validate the data before the model transformer has been applied, is a good approach, based on this discussion.
If you want to keep using Symfony validation system for these errors too, you could use Symfony validator service (ValidatorInterface) in your pre-submit listener and pass it the required constraints, for example:
$builder
->add('whatever1', TextType::class)
->add('whatever2', TextType::class)
;
$builder->get('whatever1')
->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
/** #var ConstraintViolationListInterface $errors */
if ($errors = $this->validator->validate($data, new Choice([
'choices' => $allowedChoices,
'message' => 'message.in.validators.locale.xlf'
]))) {
/** #var ConstraintViolationInterface $error */
foreach ($errors as $error) {
$form->addError(new FormError($error->getMessage()));
}
}
})
->addModelTransformer($myTransformer)
;
Kind of redundant, but it works. More info here.

zend 2 + doctrine 2 hydrator - the hydrator is not hydratating POST objects

I am having problems getting my doctrine hydrator to hydrate my return post forms.
Post forms.
I keep getting the following message:
An exception occurred while executing 'INSERT INTO worker_essay
(title) VALUES (?)' with params [null]: SQLSTATE[23000]: Integrity
constraint violation: 1048 Column 'title' cannot be null
but this cannot be correct because I have a validator on my form requiring this value to be inserted, yet my form is validating.
I would really appreciate any help or advice on resolving the problem or advice on how to go about discovering what is causing the problem.
public function getInputFilterSpecification()
{
return array(
'title' => array(
'required' => true
),
);
}
these are the var_dumped values from the returned form:
object(Zend\Stdlib\Parameters)[146] public 'WorkerStatement' =>
array (size=2)
'id' => string '' (length=0)
'title' => string 'the values from title' (length=21) public 'submit' => string 'Submit' (length=6)
As you can see, the values are clearly there, which means that the problem might be in the hydrators.
I now enclosed the rest of the documents.
The Controller
public function workerStatementAction()
{
$form = new CreateWorkerStatementForm($this->getEntityManager());
$workerStatement = new WorkerStatement();
// $form->setInputFilter($workerEssay->getInputFilter());
$form->bind($workerStatement);
// var_dump($workerStatement); die();
if ($this->request->isPost()) {
$post = $this->request->getPost();
$form = $form->setData($this->request->getPost());
if ($form->isValid()) {
$post =$this->request->getPost();
$this->getEntityManager()->persist($workerStatement);
$this->getEntityManager()->flush();
// Redirect to list of aboutyou
return $this->redirect()->toRoute('worker');
}
}
return array('form' => $form);
}
The fieldset
class WorkerStatementFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('WorkerStatement');
$this->setHydrator(new DoctrineHydrator($objectManager, 'Workers\Entity\WorkerStatement'))
->setObject(new WorkerStatement());
$this->add(array(
'name' => 'title',
'type' => 'Zend\Form\Element\Text',
'options' => array(
'label' => 'title',
),
));
}
** The Form**
class CreateWorkerStatementForm extends Form
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('WorkerStatement');
// The form will hydrate an object of type "AboutYou"
$this->setHydrator(new DoctrineHydrator($objectManager, 'Workers\Entity\WorkerStatement'));
// Add the user fieldset, and set it as the base fieldset
$workerStatementFieldset = new WorkerStatementFieldset($objectManager);
$workerStatementFieldset->setUseAsBaseFieldset(true);
$this->add($workerStatementFieldset);
}
}
Here is the var_daump of the persist in the controller:
$this->getEntityManager()->persist($workerStatement);
object(Workers\Entity\WorkerStatement)[351]
protected 'id' => null
protected 'title' => null
You will note that they are empty, yet the var dump of the values from the returned post clearly contain the values.
I enclose my workstatement class. you will note that I have used the magic getter/setter.
<?php
namespace Workers\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
/**
*
* #ORM\Entity
* #ORM\Table(name="worker_essay")
* #property string $title
*/
class WorkerStatement
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $title;
/**
* Magic getter to expose protected properties.
*
* #param string $property
* #return mixed
*/
public function __get($property)
{
return $this->$property;
}
/**
* Magic setter to save protected properties.
*
* #param string $property
* #param mixed $value
*/
public function __set($property, $value)
{
$this->$property = $value;
}
public function getInputFilterSpecification()
{
return array(
'title' => array(
'required' => true
)
);
}
}
DoctrineHydrator by default is hydrating and extracting values using getters and setters. If your entity doesn't have these methods then it cannot work properly. If you dont' want to use getters/setters, use new DoctrineHydrator($objectManager, 'Workers\Entity\WorkerStatement', false) instead of new DoctrineHydrator($objectManager, 'Workers\Entity\WorkerStatement').
Maybe it's not the reason why hydrator doesn't work. Please edit your first post and paste Workers\Entity\WorkerStatement class.
EDIT
Hydrator is calling getTitle() and your magic method is trying to access getTitle property which doesn't exist. You have three options:
Change DoctrineHydrator to new DoctrineHydrator($objectManager, 'Workers\Entity\WorkerStatement', false).
Add getters and setters. For example getTitle(), setTitle($title).
Refactor magic methods to accept getProperty, setProperty.
Actually You dont need to add the hydrator in the form , use it in the controller (or service) if its necessary .
plz add a var dump before :
$this->getEntityManager()->persist($workerStatement);
and post the result