ZF2 - Multiple entities in a single form - doctrine-orm

I am working on a shopping checkout page which requires:
OrderPerson
PersonAddress (delivery)
Order (notes, time etc. not all items are on the form at present to keep it simple)
I have more or less worked out how to put this together however it requires some tidying up.
My form looks as follows:
To make the forms play nicely together I have setup a CheckoutForm that includes the OrderPerson, PersonAddress and Order Fieldsets:
<?php
namespace MyCart\Form;
use Zend\Form\Element;
use Zend\Form\Fieldset;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterInterface;
class CheckoutForm extends Form
{
public function __construct(
InputFilterInterface $orderFilter,
InputFilterInterface $orderPersonFilter,
InputFilterInterface $personAddressFilter,
$name = null,
$options = array()
) {
parent::__construct('checkout', $options);
$this->orderFilter = $orderFilter;
$this->orderPersonFilter = $orderPersonFilter;
$this->personAddressFilter = $personAddressFilter;
}
public function init()
{
$this->add(
[
'name' => 'csrfcheck',
'type' => 'csrf'
]
);
$this->add(
[
'name' => 'order',
'type' => OrderFieldset::class,
'options' => [
'use_as_base_fieldset' => true
]
]
);
$this->add(
[
'name' => 'order-person',
'type' => OrderPersonFieldset::class,
'options' => [
'use_as_base_fieldset' => true
]
]
);
$this->add(
[
'name' => 'person-address',
'type' => PersonAddressFieldset::class,
'options' => [
'use_as_base_fieldset' => true
]
]
);
$this->add(
[
'name' => 'submit',
'type' => 'submit',
'attributes' => [
'value' => 'Update',
'class' => 'form-element'
]
]
);
$this->getInputFilter()->add($this->orderFilter, 'order');
$this->getInputFilter()->add($this->orderPersonFilter, 'order-person');
$this->getInputFilter()->add($this->personAddressFilter, 'person-address');
}
}
The Checkout Form has a factory:
<?php
namespace MyCart\Form\Factory;
use MyCart\Form\CheckoutForm;
use MyCart\InputFilter\OrderFilter;
use MyCart\InputFilter\OrderPersonFilter;
use MyCart\InputFilter\PersonAddressFilter;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class CheckoutFormFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$inputFilter = $serviceLocator->getServiceLocator()->get('InputFilterManager');
return new CheckoutForm(
$inputFilter->get(OrderFilter::class),
$inputFilter->get(OrderPersonFilter::class),
$inputFilter->get(PersonAddressFilter::class)
);
}
}
AS you can see the Checkout Form references the three input filters that are all constructed in the same manner. I have the OrderPerson fieldset here as a reference:
<?php
namespace MyCart\Form;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject;
use MyCart\Entity\OrderPerson;
use Zend\Form\Element;
use Zend\Form\Fieldset;
class OrderPersonFieldset extends Fieldset
{
/**
* #var \Doctrine\Common\Persistence\ObjectManager
* #access protected
*/
protected $objectManager;
/**
* #param ObjectManager $objectManager
* #param OrderPerson $orderPrototype
* #param null $name
* #param array $options
*/
public function __construct(
ObjectManager $objectManager,
OrderPerson $orderPrototype,
$name = null,
$options = array()
) {
parent::__construct($name, $options);
$this->objectManager = $objectManager;
$this->setHydrator(new DoctrineObject($objectManager));
$this->setObject($orderPrototype);
}
public function init()
{
$this->add(
[
'name' => 'id',
'type' => 'hidden',
]
);
$this->add(
[
'name' => 'rbu_user_id',
'type' => 'hidden',
]
);
$this->add(
[
'type' => 'text',
'name' => 'email',
'attributes' => [
'class' => 'form-control',
'required' => 'required',
],
'options' => [
'label' => 'Email Address',
'instructions' => 'Please enter your email address',
],
]
);
//etc...
}
}
On execution of the form the CheckoutController is called and this is where I am unsure of what to do or how to save the various objects, this is what I have so far:
public function indexAction()
{
//Pre check stuff here
//Start the form processing
$prg = $this->prg();
if ($prg instanceof Response) {
return $prg;
} elseif ($prg === false) {
//If logged in
if ( $this->authService->hasIdentity()) {
//Update the OrderPerson entity
$user_id = $this->identity()->getId();
$userObject = $this->userService->find($user_id);
$orderPersonEntity = new OrderPerson();
$orderPersonEntity->setRbuUser($userObject);
$orderPersonEntity->setFirstname($userObject->getFirstname());
$orderPersonEntity->setSurname($userObject->getSurname());
$this->checkoutForm->bind($orderPersonEntity);
//Update the person address entity
$personAddress = new PersonAddress();
$personAddress->setAddress1($userObject->getAddress1());
$personAddress->setAddress2($userObject->getAddress2());
$this->checkoutForm->bind($personAddress);
//Update the order entity
}
return new ViewModel(
array(
'cart' => $cart,
'form' => $this->checkoutForm
)
);
}
$this->checkoutForm->setData($prg);
if (!$this->checkoutForm->isValid()) {
return new ViewModel(
array(
'cart' => $cart,
'form' => $this->checkoutForm
)
);
}
$checkoutObject = $this->checkoutForm->getData();
die(var_dump($checkoutObject));
Dumping the result here outputs only the person address object:
object(MyCart\Entity\PersonAddress)[1177]
private 'id' => null
private 'address1' => string 'xx' (length=2)
private 'address2' => string 'xx' (length=2)
private 'region' => null
private 'postCode' => string 'xx' (length=2)
private 'city' => string 'xx' (length=2)
private 'country' => string 'xx' (length=2)
private 'created' => null
private 'modified' => null
private 'orderPerson' => null
So my question is simply, is this the right thinking and how do I complete this so that I can save the three entities in a single go?

Related

Inserting data to database using zf3 and doctrine

i'm trying to insert data to database but submitted forms does nothing.
this is my service manager:
class AutosManager
{
/**
* Entity manager.
* #var Doctrine\ORM\EntityManager;
*/
private $entityManager;
/**
* Constructor.
*/
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
public function addNewAutos($data)
{
$autos = new Autos();
$autos->setTitle($data['title']);
$autos->setDescription($data['description']);
$currentDate = date('Y-m-d H:i:s');
$autos->setDateCreated($currentDate);
$this->entityManager->persist($autos);
$this->entityManager->flush();
}
this is my controller addAction
public function addAction()
{
// Create the form.
$form = new PostForm();
if ($this->getRequest()->isPost()) {
// Get POST data.
$data = $this->params()->fromPost();
// Fill form with data.
$form->setData($data);
if ($form->isValid()) {
// Get validated form data.
$data = $form->getData();
$this->AutosManager->addNewAutos($data);
return $this->redirect()->toRoute('retrieve');
}
}
return new ViewModel([
'form' => $form
]);
}
i can retrieve data from database to the index page but i cannot add. hope to find the solution.
this is my Autos Entity
namespace Retrieve\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="\Retrieve\Repository\AutosRepository")
* #ORM\Table(name="auto")
*/
class Autos
{
/**
* #ORM\Id
* #ORM\Column(name="id")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #ORM\Column(name="title")
*/
protected $title;
/**
* #ORM\Column(name="description")
*/
protected $description;
/**
* #ORM\Column(name="featured")
*/
protected $featured;
/**
* #ORM\Column(name="date_created")
*/
protected $dateCreated;
/**
* Returns ID of this post.
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Sets ID of this post.
* #param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* Returns title.
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Sets title.
* #param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Returns featured.
* #return integer
*/
public function getFeatured()
{
return $this->featured;
}
/**
* Sets featured.
* #param integer $featured
*/
public function setFeatured($featured)
{
$this->featured = $featured;
}
/**
* Returns post description.
*/
public function getDescription()
{
return $this->description;
}
/**
* Sets post description.
* #param type $description
*/
public function setDescription($description)
{
$this->description = $description;
}
/**
* Returns the date when this post was created.
* #return string
*/
public function getDateCreated()
{
return $this->dateCreated;
}
/**
* Sets the date when this post was created.
* #param string $dateCreated
*/
public function setDateCreated($dateCreated)
{
$this->dateCreated = $dateCreated;
}
}
hope this helps to find solution.
I found the problem: it was an inputfilter element I wasn't using that was authenticating in forms. But the solution only brings me to a different problem:
Notice: Undefined index: title in C:\xampp\htdocs\ameyaw\module\BusinessGhana\src\Service\AutosManager.php on line 38
Notice: Undefined index: description in C:\xampp\htdocs\ameyaw\module\BusinessGhana\src\Service\AutosManager.php on line 39
Notice: Undefined index: featured in C:\xampp\htdocs\ameyaw\module\BusinessGhana\src\Service\AutosManager.php on line 58
Message:
An exception occurred while executing 'INSERT INTO auto (title, description, featured, date_created) VALUES (?, ?, ?, ?)' with params [null, null, null, "2017-06-15 05:04:44"]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'title' cannot be null
this is my form and fieldset
use Zend\Form\Fieldset;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use BusinessGhana\Entity\Autos;
class AddFieldset extends Fieldset
{
protected $objectManager;
public function init()
{
$this->add([
'type' => 'text',
'name' => 'title',
'attributes' => [
'id' => 'autoTitle'
],
'options' => [
'label' => 'Title',
'display_empty_item' => true,
'empty_item_label' => 'Maximum of 60 characters',
],
]);
$this->add([
'type' => 'textarea',
'name' => 'description',
'attributes' => [
'id' => 'autoDescription'
],
'options' => [
'label' => 'Description',
'display_empty_item' => true,
'empty_item_label' => 'description',
],
]);
$this->add([
'type' => 'radio',
'name' => 'featured',
'attributes' => [
'id' => 'autoFeatured'
],
'options' => array(
'label' => 'Featured',
'value_options' => array(
array('value' => '0',
'label' => 'No',
'selected' => true,
'label_attributes' => array(
'class' => 'col-sm-2 btn btn-default',
),
),
array(
'value' => '1',
'label' => 'Yes',
'label_attributes' => array(
'class' => 'col-sm-2 btn btn-danger',
),
),
),
'column-size' => 'sm-12',
'label_attributes' => array(
'class' => 'col-sm-2',
),
),
]);
}
}
use Zend\Form\Form;
//use Zend\InputFilter\InputFilter;
class AddForm extends Form
{
public function init()
{
$this->add([
'name' => 'dependentForm',
'type' => AddFieldset::class,
]);
$this->add([
'type' => 'submit',
'name' => 'submit',
'attributes' => [
'value' => 'Submit',
],
]);
}
}
i know hydration can solve this problem but i dont know how to use it yet.

Zf2 form fieldset returns no fields

I have done bunch of projects using ZF2 and Doctrine2. I build my form with as it follows: Create Form class extending Form, then create Fieldsets and set it as a base fieldset, then in the fieldset I add my fields. Within module.php I create factories in formElementConfig for my forms. It was working allways this way until now. I created a new project and suddenly I encounter a problem which I cant find what is going on. This is my code
//module.php
public function getFormElementConfig()
{
return array(
'factories' => array(
'OfferForm' => function($sm) {
$locator = $sm->getServiceLocator();
$form = new \Application\Form\OfferForm();
$form->setServiceLocator($locator);
return $form;
},
)
);
}
//Form
class OfferForm extends Form implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
public function init()
{
$this->setAttributes(array(
'id' => 'offer',
'method' => 'post',
'class' => 'custom',
'enctype' => 'multipart/form-data'
));
$this->setAttribute('method', 'post')
->setHydrator(new ClassMethodsHydrator(false))
->setInputFilter(new InputFilter());
$this->add(array(
'name' => 'offer',
'type' => 'Application\Form\Fieldset\OfferFieldset',
'options' => array(
'use_as_base_fieldset' => true
)
));
$this->add(array(
'type' => 'Zend\Form\Element\Csrf',
'name' => 'csrf'
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'id' => 'submit',
'type' => 'submit',
'value' => $this->getServiceLocator()->getServiceLocator()->get('translator')->translate('Submit offer'),
'class' => 'btn btn-info'
)
));
}
....
//Fieldset
class OfferFieldset extends Fieldset implements InputFilterProviderInterface, ServiceLocatorAwareInterface
{
public function init()
{
$this->setHydrator(new ClassMethodsHydrator(false))
->setObject(new Offer());
$this->add(array(
'name' => 'title',
'type' => 'Zend\Form\Element\Text',
'attributes' => array(
'required' => 'required',
'class' => 'form-control',
)
));
....other fileds
}
/**
* #return array
*/
public function getInputFilterSpecification()
{
....
}
}
//Controller
$em = $this->getObjectManager();
$offer = new Offer();
$form = $this->getServiceLocator()->get('FormElementManager')->get('OfferForm');
$form->setHydrator(new DoctrineHydrator($em, 'Application\Entity\Offer'))->bind($offer);
if ($this->request->isPost()) {
$form->setData($this->request->getPost());
if ($form->isValid()) {
var_dump('ok');
}
}
$form->prepare();
return new ViewModel(array(
'form' => $form,
));
This way of doing things is allways working for me until now. If I try to get form element in the Veiw with $this->form->get('offer')->get('title') it says there is no field with name 'title'
One thing that i noticed is when form is called in the controller ($form = $this->getServiceLocator()->get('FormElementManager')->get('OfferForm');) the fieldset method init() where all my fields are set is not invoked.
I tried to dump data there and to die() the application but it did not get in to the method at all.
I can provide more code but I think this is all about building the form
You also need to add your fieldset to the formelementmanager configuration. The manager's initializer will call your fieldset init() method.

Zend Framework 2 Doctrine 2 one-to-many checkbox hydration

I have a ManyToMany that I broke into OneToMany and ManyToOne relationship. I want to build a form that has checkboxes instead of collection, and I am using the 'DoctrineObject' hydrator, but it does not work and I don't know what is going wrong.
I removed from my code below all of the other not related fields.
Role Entity:
/**
* #orm\Entity
* #orm\Table(name="roles")
*/
class RolesEntity extends HemisEntity {
/**
* #orm\Id
* #orm\Column(type="integer");
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #orm\Column(name="role_code",type="string")
*/
protected $roleCode;
/**
* #orm\OneToMany(targetEntity="RolesPermissionsEntity", mappedBy="role", cascade={"persist"})
*/
protected $rolePermissions;
public function __construct()
{
$this->rolePermissions = new ArrayCollection();
}
public function setRolePermissions($rolePermissions)
{
$this->rolePermissions = $rolePermissions;
return $this;
}
public function addRolePermissions(Collection $rolePermissions)
{
foreach ($rolePermissions as $rolePermission) {
$rolePermission->setRole($this);
$this->rolePermissions->add($rolePermission);
}
}
public function removeRolePermissions(Collection $rolePermissions)
{
foreach ($rolePermissions as $rolePermission) {
$rolePermission->setRole(null);
$this->rolePermissions->removeElement($rolePermission);
}
}
public function getRolePermissions()
{
return $this->rolePermissions;
}
}
The ManyToMany table entity (it has more fields and so I broke it):
/**
* #orm\Entity
* #orm\Table(name="roles_permissions")
*/
class RolesPermissionsEntity extends HemisEntity {
/**
* #orm\Id
* #orm\Column(type="integer");
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #orm\ManyToOne(targetEntity="RolesEntity", inversedBy="rolePermissions")
* #orm\JoinColumn(name="role_id", referencedColumnName="id")
**/
protected $role;
/**
* #orm\ManyToOne(targetEntity="PermissionsEntity", inversedBy="permissionRoles")
* #orm\JoinColumn(name="permission_id", referencedColumnName="id")
**/
protected $permission;
public function setRole($role)
{
$this->role = $role;
return $this;
}
public function getRole()
{
return $this->role;
}
public function setPermission($permission)
{
$this->permission = $permission;
return $this;
}
public function getPermission()
{
return $this->permission;
}
}
My form look like this:
class RoleForm extends Form implements InputFilterProviderInterface
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('role');
$this->setHydrator(new DoctrineHydrator($objectManager))
->setObject(new RolesEntity());
$this->add(array(
'type' => 'Zend\Form\Element\Hidden',
'name' => 'id'
));
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'roleCode',
'options' => array(
'label' => 'Role Code'
),
));
$this->add(array(
'name' => 'rolePermissions',
'type' => 'DoctrineModule\Form\Element\ObjectMultiCheckbox',
'options' => array(
'label' => 'Permissions',
'object_manager' => $objectManager,
'target_class' => 'Hemis\Fnd\PermissionsEntity',
'property' => 'permissionDesc'
),
));
$this->add(array(
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Submit',
),
));
}
public function getInputFilterSpecification()
{
return array(
'roleCode' => array(
'required' => false
),
'rolePermissions' => array(
'required' => true
)
);
}
}
The problem is that when I dump the $role it does not contains any rolePermissions and even that they are passed from the form they are just not hydrated into the object. I hope that my question is clear.
Any idea about what is wrong with my code or there is a better way to do that using checkboxes?
class RoleForm extends Form implements InputFilterProviderInterface
{
public function __construct(ObjectManager $objectManager)
{
// ...
$this->add(array(
'name' => 'rolePermissions',
'type' => 'Zend\Form\Element\Collection',
'options' => array(
'label' => 'Role Permissions',
'count' => 0,
'should_create_template' => true,
'allow_add' => true,
'target_element' => array(
'type' => 'Zend\Form\Fieldset',
'options' => array(
'use_as_base_fieldset' => true
),
'elements' => array(
// add form fields for the properties of the RolesPermissionsEntity class here
array(
'name' => 'id',
'type' => 'Zend\Form\Element\Hidden',
),
array(
'name' => 'role',
'type' => 'Zend\Form\Element\Checkbox',
// other options
),
// ...
),
),
),
));
// ...
}
// ...
}

ZF2 + Doctrine2 + DoctrineObject Hydrator: Null value for ManyToOne relation

For the category structure on my proejct I'm building an entity. For the add form I use the DoctrineObject hydrator. When there is a value for the $parent this works fine, but whe there is no parent it gives me an error because there is no id to select the parent with. The value of the parent property should be null in this case.
I'd create an filter to do so. This filter is executed but the hydrator doesn't seem to get what I want it to do.
Does anybody know how to solve this?
My entity:
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
use Flex\Entity\Entity;
/**
* #Gedmo\Tree(type="materializedPath")
* #ORM\Table(name="categories")
* #ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\MaterializedPathRepository")
*/
class Category extends Entity
{
/**
* #ORM\OneToMany(mappedBy="parent", targetEntity="FlexCategories\Entity\Category")
*/
protected $children;
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue
*/
protected $id;
/**
* #Gedmo\TreeLevel
* #ORM\Column(nullable=true, type="integer")
*/
protected $level;
/**
* #ORM\Column(length=64, type="string")
*/
protected $name;
/**
* #Gedmo\TreeParent
* #ORM\ManyToOne(inversedBy="children", targetEntity="FlexCategories\Entity\Category")
* #ORM\JoinColumns({
* #ORM\JoinColumn(onDelete="SET NULL", referencedColumnName="id")
* })
*/
protected $parent;
/**
* #Gedmo\TreePath(appendId=false, endsWithSeparator=false, separator="/", startsWithSeparator=true)
* #ORM\Column(length=255, nullable=true, type="string", unique=true)
*/
protected $path;
/**
* #Gedmo\Slug(fields={"name"}, unique=false)
* #Gedmo\TreePathSource
* #ORM\Column(length=64)
*/
protected $slug;
public function setId($value)
{
$this->id = $value;
}
public function setName($value)
{
$this->name = $value;
}
public function setParent($value)
{
$this->parent = $value;
}
}
My form:
use DoctrineModule\Stdlib\Hydrator\DoctrineObject;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
class CategoryForm extends Form implements InputFilterProviderInterface, ServiceManagerAwareInterface
{
private $_serviceManager;
public function init()
{
// Init hydrator
$hydrator = new DoctrineObject($this->_serviceManager->get('doctrine.entitymanager.orm_default'),
'FlexCategories\Entity\Category');
// Set form basic configurations
$this->setAttribute('method', 'post')
->setHydrator($hydrator);
// Add parent field
$this->add(array(
'name' => 'parent',
'type' => 'Zend\Form\Element\Hidden',
));
// Add name field
$this->add(array(
'attributes' => array(
'required' => 'required',
),
'name' => 'name',
'options' => array(
'label' => 'Name',
),
'type' => 'Zend\Form\Element\Text',
));
// Add description field
$this->add(array(
'name' => 'description',
'options' => array(
'label' => 'Description',
),
'type' => 'Zend\Form\Element\Textarea',
));
// Add CSRF element
$this->add(array(
'name' => 'csrf',
'type' => 'Zend\Form\Element\Csrf',
));
// Add submit button
$this->add(array(
'attributes' => array(
'type' => 'submit',
'value' => 'Save',
),
'name' => 'submit',
));
}
public function getInputFilterSpecification()
{
return array(
'description' => array(
'filters' => array(
array(
'name' => 'Zend\Filter\StringTrim'
),
),
'required' => false,
),
'name' => array(
'filters' => array(
array(
'name' => 'Zend\Filter\StringTrim'
),
),
'required' => true,
'validators' => array(
array(
'name' => 'Flex\Validator\EntityUnique',
'options' => array(
'entity' => 'FlexCategories\Entity\Category',
'filter' => array(
array('property' => 'parent',
'value' => array('_context', 'parent')),
),
'property' => 'name',
'serviceLocator' => $this->_serviceManager,
),
),
),
),
'parent' => array(
'filters' => array(
array(
'name' => 'Flex\Filter\NullIfEmpty'
),
),
'required' => false,
),
);
}
public function setServiceManager(ServiceManager $serviceManager)
{
$this->_serviceManager = $serviceManager;
$this->init();
return $this;
}
}
My controller:
use Flex\Controller\AbstractController;
use FlexCategories\Entity\Category;
use FlexCategories\Form\CategoryForm;
class AdminController extends AbstractController
{
public function addAction()
{
// Load form
$form = $this->getServiceLocator()->get('FlexCategories\Form\CategoryForm');
// Create and bind new entity
$category = new Category();
$form->bind($category);
// Load parent category if present
$parentId = $this->params()->fromRoute('id', null);
if ($parentId !== null)
{
if (!is_numeric($parentId))
throw new \InvalidArgumentException('Invalid parent id specified');
$entityManager = $this->getEntityManager();
$repository = $entityManager->getRepository('FlexCategories\Entity\Category');
$parent = $repository->find($parentId);
if (!$parent)
throw new \InvalidArgumentException('Invalid parent id specified');
$form->get('parent')->setValue($parent->getId());
}
// Process request
$request = $this->getRequest();
if ($request->isPost())
{
$form->setData($request->getPost());
if ($form->isValid())
{
$entityManager = $this->getEntityManager();
$entityManager->persist($category);
$entityManager->flush();
$this->flashMessenger()->addSuccessMessage(sprintf('The category "%1$s" has succesfully been added.', $category->getName()));
return $this->redirect()->toRoute($this->getEvent()->getRouteMatch()->getMatchedRouteName());
}
}
// Return form
return array(
'form' => $form,
);
}
public function indexAction()
{
// Load all categories
$entityManager = $this->getEntityManager();
$repository = $entityManager->getRepository('FlexCategories\Entity\Category');
$categories = $repository->findBy(array(), array('path' => 'asc'));
return array(
'categories' => $categories,
);
}
}
My database:
CREATE TABLE `categories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`level` int(11) DEFAULT NULL,
`name` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
`path` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`slug` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_3AF34668B548B0F` (`path`),
KEY `IDX_3AF34668727ACA70` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
ALTER TABLE `categories`
ADD CONSTRAINT `FK_3AF34668727ACA70` FOREIGN KEY (`parent_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL;
I'd solve this problem by create an "HiddenElement" element based on "DoctrineModule\Form\ElementObjectSelect" and use this as the input type.
You need a strategy which handles '' values. ('' != null) as ocramius points out its possible to haven an primary key with ''.
you ran into this problem as soon as you use the "empty_option" currently u cant just set null to it, the form post will always transfer ''.
see https://github.com/doctrine/DoctrineModule/pull/119
see https://github.com/doctrine/DoctrineModule/pull/106
so add a strategy for this field to the hydrator to convert '' to null.
this could look like:
use Zend\Stdlib\Hydrator\Strategy\DefaultStrategy;
class ForeignKey extends DefaultStrategy
{
public function hydrate($value)
{
if($value == '') {
return NULL;
}
return $value;
}
}

Form collections in SF2

Offers.php
/**
* #var \ZGoffers\MainBundle\Entity\OfferParameter
*
* #ORM\OneToMany(targetEntity="OfferParameter", mappedBy="offer", cascade={"all"})
*/
private $parameters;
OfferParameter.php
/**
* #var \ZGoffers\MainBundle\Entity\Offer
*
* #ORM\ManyToOne(targetEntity="Offer", inversedBy="offer", cascade={"all"})
*/
private $offer;
OfferType.php
class OfferType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('advertiser')
->add('name')
->add('url', 'text', array('label' => 'URL'))
->add('externalUrl', 'text', array('label' => 'External URL'))
->add('dailyCap', 'text', array('label' => 'Daily Cap'))
->add('parameters', 'collection', array(
'type' => new OfferParameterType(),
'allow_add' => true,
'allow_delete' => true
))
->add('active', 'choice', array(
'choices' => array(0 => 'Disabled', 1 => 'Enabled')
));
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'ZGOffers\MainBundle\Entity\Offer'
);
}
}
OfferParameterType.php
class OfferParameterType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('field')
->add('type', 'choice', array(
'choices' => array(
'=' => 'EQUALS',
'>' => 'IS GREATER THAN',
'>=' => 'IS GREATER THAN OR EQUALS',
'<' => 'IS LESS THAN',
'<=' => 'IS GREATER THAN OR EQUALS'
)
))
->add('value');
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'ZGOffers\MainBundle\Entity\OfferParameter'
);
}
}
Form Handling
public function process()
{
if ('POST' == $this->request->getMethod()) {
// bind form data
$this->form->bindRequest($this->request);
// If form is valid
if ($this->form->isValid() && ($offer = $this->form->getData()) instanceof Offer) {
foreach ($offer->getParameters() as $parameter) {
$parameter->setOffer($offer); // THIS SHOULDNT BE NEEDED
}
// save offer to the database
$this->entityManager->persist($offer);
$this->entityManager->flush();
return true;
}
}
return false;
}
My question is.... How the hell do you delete elements in form collections in SF2????
I have multiple forms that are just like this one in my project and it's really putting a halt to development :(
Thanks for all help!
You may need to do something like below :
->add('parameters', 'collection', array(
'type' => new OfferParameterType(),
'allow_add' => true,
'allow_delete' => true,
'attr'=>array('style'=>'display:none;')
))
That makes your whole OfferParameterType collection hidden.
Or in OfferParameterType apply same thing for each field like below :
->add('field',null, array('attr'=>array('style'=>'display:none;')))