Symfony2 Persisting form collection with a relational entities - php

Haven't found a solution to this problem through research yet but I am trying to save into the database with two forms in one (embedded/collection). I have entities that are related to each other and I want the form to submit and persist both entities into the database.
Main entity:
/**
* #var integer
* #ORM\OneToMany(targetEntity="Sub", mappedBy="mainId", cascade={"persist"})
*/
protected $sub;
public function __construct() {
$this->sub = new ArrayCollection();
}
Sub entity:
/**
* #var integer
*
* #ORM\Column(name="main_id", type="integer")
*/
protected $mainId;
.......
/**
* #ORM\ManyToOne(targetEntity="Main", inversedBy="sub")
* #ORM\JoinColumn(name="main_id", referencedColumnName="id")
*/
protected $main;
Here's my MainType form:
class MainType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('dano', 'text', array(
'label' => 'DA: ',
'disabled' => true
))
->add('partno','text', array(
'label' => 'Part: ',
'disabled' => true
))
->add('batchno', 'text', array(
'label' => 'Batch: ',
'disabled' => true
))
->add('sub', 'collection', array('type' => new SubType()))
->add('submit', 'submit');
}......
And my SubType form:
class SubType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('main_id','hidden')
->add('rackno','text', array(
'label' => 'Rack No(s) '
))
->add('diecode','text', array(
'label' => 'Die Code '
))
->add('heatcode','text', array(
'label' => 'Heat Code '
))
->add('inqty','integer', array(
'label' => 'Qty In '
))
->add('onhold','choice', array(
'label' => 'Hold',
'choices' => array(
'1' => 'On Hold',
'0' => 'Released'
),
'multiple' => false,
'expanded' => true
));
And my controller:
/**
* #param Request $request
* #Route("/{dano}", name="subpart_part")
*/
public function submitPartByDAAction(Request $request, $dano) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('Bundle:Main')
->findOneByDano($dano);
$partno = $entity->getPartno();
$batchno = $entity->getBatchno();
$mainid = $entity->getId();
$main1 = new Main();
$main1->setDano($dano);
$main1->setPartno($partno);
$main1->setBatchno($batchno);
$sub1 = new Sub();
$sub1->setMainId($mainid);
$main1->getSub()->add($sub1);
$form = $this->createForm(new MainType(), $main1, array(
'method' => 'POST'
));
$form->handleRequest($request);
if($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($main1);
$em->flush();
return $this->redirect($this->generateUrl('subpart_home'));
}
return $this->render('Bundle:Parts:addparts.html.twig', array(
'form' => $form->createView()
));
}
Let me explain what I did here, at first I did not have the Sub's "main_id" field (which is related to Main's id) on but when I tried persisting the data it gave me the error:
An exception occurred while executing 'INSERT INTO sub
(main_id, rackno, heatcode, diecode, inqty, onhold) VALUES
(?, ?, ?, ?, ?, ?)' with params [null, "46", "eterte", "seteter", 3, 0]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column
'main_id' cannot be null
So then I made a field "main_id" with it being hidden, grabbed the id from Main by getId(); and passed it into the Sub's form's setMainId(); to persist and it still gives me the same error that "main_id" cannot be null.
What am I missing? Thanks!

You are defining your entities wrong. First understand the concept of ORM and relations. Your Sub entity does not need to have the integer main_id. Simply map it to Main entity. Your Main entity should look like
/**
* #var Sub
* this value is just integer in database, but doc should point it to Sub
* #ORM\OneToMany(targetEntity="Sub", mappedBy="mainId", cascade={"persist"})
*/
protected $sub;
public function __construct() {
$this->sub = new ArrayCollection();
}
And your Sub entity
/**
* #ORM\ManyToOne(targetEntity="Main", inversedBy="sub")
* #ORM\JoinColumn(name="main_id", referencedColumnName="id")
*/
protected $main;
You dont need main_id. The ORM will handle that for you. The MainType form is good. Just get rid of the main_id in SubType form.
You should reference to entities by object rather than their IDs. In your controller also rather than using
$sub1->setMainId($mainid);
You should set the object.
$sub1->setMain($main1);

Your main form is also a little weird. I do not say it is not valid, but you should consider replacing this line:
->add('sub', 'collection', array('type' => new SubType()))
With something like this:
->add('sub', new SubType(), array())
I think it is way more appropriate if you have only "ONE" item. You use collection when you want many items.
I would suggest you look into the form component... how the form is represented as a tree...
Also never make fields like "main_id", unless it is necessary. Try not to work id's and work with associations.

Related

Zend3 Form Filter - ParamConverter could not generate valid form

I'm really confused about my Form Filter.
My Test-Project contains 2 Models.
class Category extends AbstractEntity
{
use Nameable; // just property name and getter and setter
/**
* #var boolean
* #ORM\Column(name="issue", type="boolean")
*/
private $issue;
/**
* #var Collection|ArrayCollection|Entry[]
*
* #ORM\OneToMany(targetEntity="CashJournal\Model\Entry", mappedBy="category", fetch="EAGER", orphanRemoval=true, cascade={"persist", "remove"})
*/
private $entries;
}
the entry
class Entry extends AbstractEntity
{
use Nameable;
/**
* #var null|float
*
* #ORM\Column(name="amount", type="decimal")
*/
private $amount;
/**
* #var null|Category
*
* #ORM\ManyToOne(targetEntity="CashJournal\Model\Category", inversedBy="entries", fetch="EAGER")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=false)
*/
protected $category;
/**
* #var null|DateTime
*
* #ORM\Column(name="date_of_entry", type="datetime")
*/
private $dateOfEntry;
}
And if someone needed the AbstractEntity
abstract class AbstractEntity implements EntityInterface
{
/**
* #var int
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
}
Every Category can have many Entries. I'm using Doctrine for this relation. And this works fine.
I have a Form based on this FieldSet:
$this->add([
'name' => 'id',
'type' => Hidden::class
]);
$this->add([
'name' => 'name',
'type' => Text::class,
'options' => [
'label' => 'Name'
]
]);
$this->add([
'name' => 'amount',
'type' => Number::class,
'options' => [
'label' => 'Summe'
]
]);
$this->add([
'name' => 'date_of_entry',
'type' => Date::class,
'options' => [
'label' => 'Datum'
]
]);
$this->add([
'name' => 'category',
'type' => ObjectSelect::class,
'options' => [
'target_class' => Category::class,
]
]);
So my Form displays a dropdown with my categories. Yeah fine.
To load the Category for my Entry Entity i use a filter.
$this->add([
'name' => 'category',
'required' => true,
'filters' => [
[
'name' => Callback::class,
'options' => [
'callback' => [$this, 'loadCategory']
]
]
]
]);
And the callback:
public function loadCategory(string $categoryId)
{
return $this->mapper->find($categoryId);
}
The mapper loads the category fine. great. But the form is invalid because:
Object of class CashJournal\Model\Category could not be converted to int
Ok, so i'm removing the Filter, but now it failed to set the attributes to the Entry Entity, because the setter needs a Category. The Form error says:
The input is not a valid step
In Symfony i can create a ParamConverter, which converts the category_id to an valid Category Entity.
Question
How i can use the filter as my ParamConver?
Update
Also when i cast the category_id to int, i will get the error from the form.
Update 2
I changed my FieldSet to:
class EntryFieldSet extends Fieldset implements ObjectManagerAwareInterface
{
use ObjectManagerTrait;
/**
* {#inheritDoc}
*/
public function init()
{
$this->add([
'name' => 'id',
'type' => Hidden::class
]);
$this->add([
'name' => 'name',
'type' => Text::class,
'options' => [
'label' => 'Name'
]
]);
$this->add([
'name' => 'amount',
'type' => Number::class,
'options' => [
'label' => 'Summe'
]
]);
$this->add([
'name' => 'date_of_entry',
'type' => Date::class,
'options' => [
'label' => 'Datum'
]
]);
$this->add([
'name' => 'category',
'required' => false,
'type' => ObjectSelect::class,
'options' => [
'target_class' => Category::class,
'object_manager' => $this->getObjectManager(),
'property' => 'id',
'display_empty_item' => true,
'empty_item_label' => '---',
'label_generator' => function ($targetEntity) {
return $targetEntity->getName();
},
]
]);
parent::init();
}
}
But this will be quit with the error message:
Entry::setDateOfEntry() must be an instance of DateTime, string given
Have you checked the documentation for ObjectSelect? You appear to be missing a few options, namely which hydrator (EntityManager) and identifying property (id) to use. Have a look here.
Example:
$this->add([
'type' => ObjectSelect::class,
'name' => 'category', // Name of property, 'category' in your question
'options' => [
'object_manager' => $this->getObjectManager(), // Make sure you provided the EntityManager to this Fieldset/Form
'target_class' => Category::class, // Entity to target
'property' => 'id', // Identifying property
],
]);
To validate selected Element, add in your InputFilter:
$this->add([
'name' => 'category',
'required' => true,
]);
No more is needed for the InputFilter. A Category already exist and as such has been validated before. So, you should just be able to select it.
You'd only need additional filters/validators if you have special requirements, for example: "A Category may only be used once in Entries", making it so that you need to use a NoObjectExists validator. But that does not seem to be the case here.
UPDATE BASED ON COMMENTS & PAST QUESTIONS
I think you're over complicating a lot of things in what you're trying to do. It seems you want to simply populate a Form before you load it client-side. On receiving a POST (from client) you wish to put the received data in the Form, validate it and store it. Correct?
Based on that, please find a complete controller for User that I have in one of my projects. Hope you find it helpful. Providing it because updates are veering away from your original question and this might help you out.
I've removed some additional checking and error throwing, but otherwise is in complete working fashion.
(Please note that I'm using my own abstract controller, make sure to replace it with your own and/or recreate and match requirements)
I've also placed additional comments throughout this code to help you out
<?php
namespace User\Controller\User;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\ORMException;
use Exception;
use Keet\Mvc\Controller\AbstractDoctrineActionController;
use User\Entity\User;
use User\Form\UserForm;
use Zend\Http\Request;
use Zend\Http\Response;
class EditController extends AbstractDoctrineActionController
{
/**
* #var UserForm
*/
protected $userEditForm; // Provide this
public function __construct(ObjectManager $objectManager, UserForm $userEditForm)
{
parent::__construct($objectManager); // Require this in this class or your own abstract class
$this->setUserEditForm($userEditForm);
}
/**
* #return array|Response
* #throws ORMException|Exception
*/
public function editAction()
{
$id = $this->params()->fromRoute('id', null);
// check if id set -> else error/redirect
/** #var User $entity */
$entity = $this->getObjectManager()->getRepository(User::class)->find($id);
// check if entity -> else error/redirect
/** #var UserForm $form */
$form = $this->getUserEditForm(); // GET THE FORM
$form->bind($entity); // Bind the Entity (object) on the Form
// Only go into the belof if() on POST, else return Form. Above the data is set on the Form, so good to go (pre-filled with existing data)
/** #var Request $request */
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost()); // Set received POST data on Form
if ($form->isValid()) { // Validates Form. This also updates the Entity (object) with the received POST data
/** #var User $user */
$user = $form->getObject(); // Gets updated Entity (User object)
$this->getObjectManager()->persist($user); // Persist it
try {
$this->getObjectManager()->flush(); // Store in DB
} catch (Exception $e) {
throw new Exception('Could not save. Error was thrown, details: ', $e->getMessage());
}
return $this->redirectToRoute('users/view', ['id' => $user->getId()]);
}
}
// Returns the Form with bound Entity (object).
// Print magically in view with `<?= $this->form($form) ?>` (prints whole Form!!!)
return [
'form' => $form,
];
}
/**
* #return UserForm
*/
public function getUserEditForm() : UserForm
{
return $this->userEditForm;
}
/**
* #param UserForm $userEditForm
*
* #return EditController
*/
public function setUserEditForm(UserForm $userEditForm) : EditController
{
$this->userEditForm = $userEditForm;
return $this;
}
}
Hope that helps...

Db persistance for dynamic collection in symfony

When symfony collection fields are dynamicly added/removed in DOM with javascript, on save of this collection either validation or persisted collection elem ids get messed up. For example if I have Entity\User.php releation like:
/**
* #var \TestBundle\Entity\UserFollowers
*
* #ORM\OneToMany(targetEntity="TestBundle\Entity\UserFollowers", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
* #Assert\Valid()
*/
protected $user_followers;
and backward Entity\UserFollower.php relation like:
/**
* #ORM\ManyToOne(targetEntity="TestBundle\Entity\User", inversedBy="user_followers")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* #var string
* #ORM\Column(name="name", type="string")
* #Assert\NotBlank()
*/
private $name;
And UserFormType like:
$builder->add('user_followers', CollectionType::class, array(
'entry_type' => UserFollowersType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'prototype_name' => '__name__',
'by_reference' => false,
'label' => 'User followers'
))
and UserFollowersFormType like:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('name', TextType::class, array(
'attr'=> [
'class' =>'input input-group-field'
],
'label' => false
));
}
when form is handled and user persisted to db either form validation attaches to wrong collection elem or persisted id to db gets wrong, different DOM manipulaiton gets different error, controller is like:
$form->handleRequest($request);
if ($form->isValid()) {
$em->persist($user);
$em->flush();
}
Why is that happening? its like form collection indexes gets messed up....
When you dynamicly add new UserFollowersType form you also have to replace prototype_name __name__ with length of the collection.
On collection init:
var $collection = $(your collection container of forms );
$collection.data('index', $collection.children().length);
var index = $collection.data('index');
//-------on add new form
// Replace '__name__' in the prototype's HTML to
// instead be a number based on how many items we have
var newForm = prototype.replace(prototypeName, index);
// increase the index with one for the next item
$collection.data('index', index + 1);

Symfony 3.x - using entity getId() method on submitted form data object returns object (not integer)

I am trying to create a filter form with multiple fields, and one of them is a select/drop-down for selecting from a list of Tournaments.
The idea is to make the form reusable so I'm creating a FilterType form class. The Tournament select field is created by embedding a TournamentChoiceType form class which creates an EntityType field using the Tournament entity.
The issue I'm facing is when the form is submitted and I get the submitted data. The $tournamentChoice is a Tournament object (which is OK), but $tournamentId = $tournamentChoice->getId() is returning a Tournament object too, and it should be an integer (the ID columnn).
The mess seems to happen here:
$filterForm->handleRequest($request);
because when I view/dump the raw POST request all seems normal and the filter[tournament][id] is just an integer parameter passed in, as expected.
Here's the code.
Controller:
$em = $this->getDoctrine()->getManager();
$tournamentSelected = $em->getRepository('MyBundle:Tournament')
-> findOneById($id);
$tournamentsList = $em->getRepository('MyBundle:Tournament')
->findAll();
$formInputData = array();
$formInputData['tournament']['data'] = $tournamentSelected;
$formInputData['tournament']['choices'] = $tournamentsList;
formData = array();
$filterForm = $this->createForm(FilterType::class, $formData, array(
'data' => $formInputData
));
$filterForm->handleRequest($request);
if ($filterForm->isSubmitted() && $filterForm->isValid()) {
$formData = $filterForm->getData();
$tournamentChoice = $formData['tournament'];
$tournamentId = $tournamentChoice->getId();
$dateFromChoice = $formData['date_from'];
$dateToChoice = $formData['date_to'];
return $this->redirectToRoute(
'index',
array('tournament' => $tournamentId)
);
}
FilterType form class:
class FilterType extends AbstractType
{
protected $data;
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => null
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->data = $options['data'];
...
$builder
->add('tournament', TournamentChoiceType::class, array(
'choices' => $this->data['tournament']['choices'],
'data' => $this->data['tournament']['data']
))
;
...
}
}
TournamentChoiceType form class:
class TournamentChoiceType extends AbstractType
{
protected $data;
protected $choices;
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MyBundle\Entity\Tournament',
'choices' => null
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->data = $options['data'];
$this->choices = $options['choices'];
$builder
->add('id', EntityType::class, array(
'class' => 'MyBundle:Tournament',
'choices' => $this->choices,
'choice_label' => 'name',
'label' => false,
'data' => $this->data
))
;
}
}
Tournament entity class:
class Tournament
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
...
}
Here's how var_dump($filterForm->getData()) looks like, after a submit:
array (size=4)
'field1' => string 'text' (length=4)
'field2' => string 'text' (length=4)
...
'tournament' =>
object(MyBundle\Entity\Tournament)[631]
private 'id' =>
object(MyBundle\Entity\Tournament)[522]
private 'id' => int 11
private 'name' => string 'tournament1' (length=11)
private ...
...
and here's how var_dump($filterForm->getData()) looks like, after a second submit:
array (size=4)
'field1' => string 'text' (length=4)
'field2' => string 'text' (length=4)
...
'tournament' =>
object(MyBundle\Entity\Tournament)[513]
private 'id' =>
&object(MyBundle\Entity\Tournament)[513]
private 'name' => string 'tournament1' (length=11)
private ...
What's with the object's ID being an object (and not being just an interger), or referencing an object?
The problem is caused by double data insertion. You have to refactor your code to set the data only once.
Firstly you set it here:
$formInputData['tournament']['data'] = $tournamentSelected;
Secondly if there is any request, then you set it again here:
$filterForm->handleRequest($request);
I'm not certain, but I believe in your controller you need to change this:
from:
$tournamentChoice = $formData['tournament'];
to:
$tournamentChoice = $formData['tournament']['choices'];
Try it. Otherwise you need to post more of your code in the controller. In particular how you create the $formInputData array.
Edit #2.
On now seeing the rest of your controller code, I now sees what's going on. You need to get the form data like so:
$formData = $filterForm->get("tournament")->getData();
This gets an array of results, so then you need to do a foreach (or similar). This is because when you created $tournamentList, you did it like this:
$tournamentsList = $em->getRepository('MyBundle:Tournament')->findAll();

Symfony2 multiple embedded forms one to many update foreign key

Hello i have a problem with my multiple embedded form (one to many).
One game have many prizes and one prize have many options. When i attemp to save this form get an error message
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'fk_prize' cannot be null
I have already set in my entity on cascade and in my form type set by_refference false but didnt work. All the other assigned foreign keys works perfectly.
UPDATED :
In controller when i do this form saved successfull. But i want to do this with doctrine. Is it bug in doctrine or something wrong in my code ?
Thanks for your time!
//Hacked code in controller to save the form
$prizes = $data->getPrizes();
foreach ($prizes as $prize) {
$prizeOptions = $prize->getPrizesOptions();
foreach ($prizeOptions as $prizeOption) {
$prizeOption->setPrize($prize);
}
}
$em->persist($data);
$em->flush();
<?php
class Game
{
/**
* #ORM\OneToMany(targetEntity="Prize", mappedBy="game", cascade={"persist"})
*/
protected $prizes;
public function __construct()
{
$this->gameUsers = new ArrayCollection();
$this->prizes = new ArrayCollection();
}
}
?>
<?php
class GameType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('alias', 'text' , [
'label'=>'Name'
])
->add('prizes' ,'collection', array(
'type' => new PrizeType($this->intention),
'allow_add' => true,
'allow_delete' => false,
'prototype' => true,
'by_reference' => false,
'label' => false,
))
->add('save', 'submit', [
'attr' => [
'class' => 'btn btn-primary'
]
]);
}
}
<?php
class Prize
{
/**
* The Game
* #ORM\ManyToOne(targetEntity="Game")
* #ORM\JoinColumn(name="game_id", referencedColumnName="id")
*/
protected $game;
/**
* #ORM\OneToMany(targetEntity="PrizeOptions", mappedBy="prize", cascade={"persist"})
*/
protected $prizes_options;
/**
* Constructor
*/
public function __construct()
{
$this->prizes_options = new \Doctrine\Common\Collections\ArrayCollection();
}
}
class PrizeType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('prizes_options' ,'collection', array(
'type' => new PrizeOptionsType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'label' => false,
))
;
}
}
<?php
class PrizeOptionsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text' , [
'label'=>'Value'
])
;
}
}
Doctrine can handle changes in entities only on "owning" side. That means, you can modify relations only in entity where is defined join column/join table.
http://doctrine-orm.readthedocs.org/en/latest/reference/unitofwork-associations.html

form select parent hydration

I have a zf2 application that works with doctrine.
I have the following entity:
class Role
{
/**
* #var int
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #ORM\Column(type="string", length=255, unique=true, nullable=true)
*/
protected $name;
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="YrmUser\Entity\Role", mappedBy="parent")
*/
protected $children;
/**
* #var Role
* #ORM\ManyToOne(targetEntity="YrmUser\Entity\Role", inversedBy="children", cascade={"persist"})
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
protected $parent;
}
for this entity i have a form:
class RoleForm extends Form
{
/**
* [init description]
*
* #return void
*/
public function init()
{
$this->setHydrator(
new DoctrineHydrator($this->objectManager, 'YrmUser\Entity\Role')
)->setObject(new Role());
$this->setAttribute('method', 'post');
$this->add(
array(
'name' => 'name',
'attributes' => array(
'type' => 'text',
'placeholder' =>'Name',
),
'options' => array(
'label' => 'Name',
),
)
);
$this->add(
array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'parent',
'attributes' => array(
'id' => 'parent_id',
),
'options' => array(
'label' => 'Parent',
'object_manager' => $this->objectManager,
'property' => 'name',
'is_method' => true,
'empty_option' => '-- none --',
'target_class' => 'YrmUser\Entity\Role',
'is_method' => true,
'find_method' => array(
'name' => 'findBy',
'params' => array(
'criteria' => array('parent' => null),
),
),
),
)
);
}
}
The hydration for the select in the form works as it only shows other roles that don't have a parent.
But when editing a existing entity it shows itself in the select so i can select itself as its parent.
I figured if i would have the id of current entity inside the form i can create a custom repo with a method that retrieves all roles without a parent and does not have the current entity id.
But i cant figure out how to get the id of the currently edited entity from inside the form.
Any help is appreciated.
Cheers,
Yrm
You can fetch the bound entity within the form using $this->getObject().
You have actually already set this with setObject(new Role());. Unfortunately this means that it was not loaded via Doctine and you will have the same issue, no $id to work with.
Therefore you will need to add the 'parent role' options (value_options) after you have bound the role loaded via doctrine.
From within the controller, I normally request the 'edit' form from a service class and pass in the entity instance or id that is being edited. Once set you can then modify existing form elements before passing it back to the controller.
// Controller
class RoleController
{
public function editAction()
{
$id = $this->params('id'); // assumed id passed as param
$service = $this->getRoleService();
$form = $service->getRoleEditForm($id); // Pass the id into the getter
// rest of the controller...
}
}
By passing in the $id when you fetch the form you can then, within a service, modify the form elements for that specific role.
class RoleService implements ObjectManagerAwareInterface, ServiceLocatorAwareInterface
{
protected function loadParentRolesWithoutThisRole(Role $role);
public function getRoleEditForm($id)
{
$form = $this->getServiceLocator()->get('Role\Form\RoleEditForm');
if ($id) {
$role = $this->getObjectManager()->find('Role', $id);
$form->bind($role); // calls $form->setObject() internally
// Now the correct entity is attached to the form
// Load the roles excluding the current
$roles = $this->loadParentRolesWithoutThisRole($role);
// Find the parent select element and set the options
$form->get('parent')->setValueOptions($roles);
}
// Pass form back to the controller
return $form;
}
}
By loading the options after the form has initialized you do not need the current DoctrineModule\Form\Element\ObjectSelect. A normal Select element that has no default value_options defined should be fine.

Categories