Modifying label for symfony generated form - php

I currently have a working form in Symfony where I have a list of companies with checkboxes next to each company name. This is so you can check off which company is assigned to each user. The checkbox currently shows the accountID but it would also be helpful to have the entity field 'name' as well. Can you build a property with two entity fields? Here is my form in my controller:
->add('companies', 'entity', array(
'label' => 'Company',
'class' => 'Default\Bundle\Entity\Customer',
'property' => 'accountId', //This puts the company id next to the check box
'multiple' => true,
'expanded' => true,
'query_builder' => function ($repository)
{
return $repository->createQueryBuilder('c')->orderBy('c.accountId', 'ASC');
},))
->add('Save', 'submit')
->getForm();
This is what I am trying to do:
->add('companies', 'entity', array(
'label' => 'Company',
'class' => 'Default\Bundle\Entity\Customer',
'property' => 'accountId' + 'name', // I want to combine my entity fields here
'multiple' => true,
'expanded' => true,
'query_builder' => function ($repository)
here is the entity just for reference
class Customer
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #Assert\NotBlank(message="Please enter a Customer ID.")
* #Assert\Length(max="32")
* #ORM\Column(type="string", length=32)
* #var string
*/
protected $accountId;
/**
* #Assert\NotBlank(message="Please enter a company name.")
* #Assert\Length(max="60")
* #ORM\Column(type="string", length=60)
* #var string
*/
protected $name;
And one last time... I want to go from this:
To this:

Create a simple getter and use that as the property, eg:
public function getNamePlusAccountId()
{
return $this->name." (".$this->accountId.")";
}
and use 'property' => 'namePlusAccountId' in your form.

If you only need to change the label but would like to keep the form field value then http://symfony.com/doc/current/reference/forms/types/entity.html#choice-label probably what are you looking for

Related

EntityType: Charge all the values available even thought if the option is not set

I have three entities running in Symfony:
Professional
class Professional extends User
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="TurnsProfessional", mappedBy="professional")
*/
private $turns;
}
Turn
class Turn
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="TurnsProfessional", mappedBy="turn")
*/
private $professionals;
}
TurnsProfessionals
class TurnsProfessional
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Turn", inversedBy="professionals")
* #ORM\JoinColumn(name="turn_id", referencedColumnName="id")
*/
private $turn;
/**
* #ORM\ManyToOne(targetEntity="Professional", inversedBy="turns")
* #ORM\JoinColumn(name="professional_id", referencedColumnName="id")
*/
private $professional;
/**
* #ORM\Column(type="boolean")
*/
private $status;
}
In the FormType I have this:
->add('turns', 'entity',
array('class' => 'AppBundle:TurnsProfessional',
'property' => 'label',
'multiple' => true,
'expanded' => true,
));
What I would like to do is load all the "turns" available in "Turn" entity (Monday morning, Monday evening, Tuesday morning, etc.) and show them like checkboxes in a form. If the turn is checked, the turn will be registered in TurnsProfessional with status = 1 and if not with status = 0.
When I have all the turns saved in TurnsProfessional with status = 0 or status = 1, Symfony print all the options right and everything works. But, the first time no turn are created for the professional so the add('turns') method returns an empty value with no checkboxes.
How could I show all the options available in Turn entity in this case?
Thanks!
UPDATE FormType
I've tried to add a query_builder option in the FormType:
->add('turns', EntityType::class,
array('class' => 'AppBundle:Turn',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('turn')
->orderBy('turn.id', 'ASC');
},
'choice_label' => 'label',
'multiple'=>true,
'expanded'=>true,
))
Now, the form shows all the options but when I try to save the form I get the following error:
Found entity of type AppBundle\Entity\Turn on association
AppBundle\Entity\Professional#turns, but expecting
AppBundle\Entity\TurnsProfessional
You have an error because your entity is waiting for a TurnsProfessional object and you are trying to give it a Turn object. To solve this you should not map directly the field to your entity:
->add('turns', EntityType::class,
array('class' => 'AppBundle:Turn',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('turn')
->orderBy('turn.id', 'ASC');
},
'choice_label' => 'label',
'multiple' => true,
'expanded' => true,
'mapped' => false
))
Then in your controller you can access to the result like this:
$turns = $form->get('turns);
But you have to keep in mind that $turns will only contains the Turn entities you selected in your form.

Symfony3 Forms: PropertyAccessor requires a graph of objects or arrays to operate on

I have 2 entities: Accounts and Patients:
class Patients
{
//..
/**
* #var Accounts
*
* #ORM\OneToOne(targetEntity="Accounts", mappedBy="patients")
* #Assert\Valid()
*/
private $accounts;
// ..
}
class Accounts {
// ...
/**
* #var Patients
*
* #ORM\OneToOne(targetEntity="Patients", inversedBy="accounts")
* #ORM\JoinColumn(name="patients_id", referencedColumnName="id")
*
*/
private $patients;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=100, nullable=true, unique=true)
* #Assert\NotBlank(groups={"emailValidation"})
* #Assert\Email(groups={"emailValidation"})
*
*/
private $email;
/// ...
}
I need to build a form with patients info (firstname, lastname, email) and validate it.
I did it like this:
class PatientsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// .....
->add('email', EmailType::class, array(
'mapped' => $options['formState'] == 'display' ? false : true,
'property_path' => 'accounts.email',
'data' => $options['email']
))
}
//....
$resolver->setDefaults(
array(
'formState' => 'display',
//...
}
The
'mapped' => $options['formState'] == 'display' ? false : true,
Is an ugly workaround that I had to make because, without that I had two situations:
1.
->add('email', EmailType::class, array(
'property_path' => 'accounts.email',
'data' => $options['email']
))
Will give the following error:
PropertyAccessor requires a graph of objects or arrays to operate on,
but it found type "NULL" while trying to traverse path
"accounts.email" at property "email".
->add('email', EmailType::class, array(
'mapped' => false,
'property_path' => 'accounts.email',
'data' => $options['email']
))
Is not taking into account the validation groups inside entities..
Is there an elegant way to validate the email inside the accounts entity?

How to implement a group of elements in the drop-down list ObjectSelect

How to implement a group of elements in the drop-down list ObjectSelect 'optgroup_identifier'
Form\CategoryForm.php
$this->add([
'type' => ObjectSelect::class,
'name' => 'category',
'options' => [
'label' => 'Категория',
'object_manager' => $this->getObjectManager(),
'target_class' => Category::class,
'property' => 'name',
'optgroup_identifier' => '???',
'optgroup_default' => 'Главная',
'empty_option' => '== Категория ==',
'is_method' => true,
'find_method' => [
'name' => 'findAllChildCategories',
'params' => [
],
],
],
]);
Category Table is relevant Self-referencing
Entity\Category.php
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\OneToMany(targetEntity="Application\Entity\Category", mappedBy="parent", cascade={"remove"})
*/
private $children;
/**
* #var \Application\Entity\Category
*
* #ORM\ManyToOne(targetEntity="Application\Entity\Category", inversedBy="children")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="parent", referencedColumnName="id", nullable=true)
* })
*/
private $parent;
Group name must be the parent category
$category->getParent()->getName()
Thankfully for this case, Doctrine does not do any queries to get groupings; it does it internally. optgroup_identifier is just name of a getter it uses to get group names, therefore that getter can return anything you want.
In Entity\Category, add a method dedicated to returning parent name of a category. Ensure it does not coincide with any fields so Doctrine does not return a whole object proxy into a form. For example:
public function getParentName() {
if(!$this->parent) return '';
return $this->parent->getName();
}
Since root categories will not have a parent, $this->parent will be null. Look out for that case to avoid script crashing and return empty string as a designation for it.
Then, put this getter name in optgroup_identifier of the form. Final result will be as in screenshot with sample data.

Select box related on another select box

I have an entity Travel has an attribute country and I have an entity City related to Travel.
I'd like that when I choose a country showing all cities related. In fact I don't know Ajax and I need a help
class TravelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('country', 'country', array(
'required' => true,
'label' => 'Country',
))
->add('destination', 'entity',array(
'required' => true,
'class' => 'ProjectAdminBundle:City',
'property' => 'name',
'multiple' => false,
'expanded' => false,
'empty_value' => 'Choose a city',
'label' => 'Destination',
))
//.....
}
}
and this is entity Travel:
class Travel
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="country", type="string", length=255, nullable=false)
*
* #Assert\Country
*/
protected $country;
/**
* #ORM\ManyToOne(targetEntity="Project\AdminBundle\Entity\City", inversedBy="travels")
* #ORM\JoinColumn(nullable=false)
*/
protected $destination;
//........
}
Each city has a country code, for exemple:
London -> UK
Paris -> FR
.....
A similar case like yours is discussed in details in the Symphony Cookbook:
http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms

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