Symfony2 Sonata admin datatransformer - symfony

I'm trying to set a form type "sonata_type_immutable_array" as follows:
->add('metadatos', 'sonata_type_immutable_array', array(
'keys' => array(
array('Test', 'text', array('required' => false)),
array('URL', 'url', array('required' => false)),
)
))
And saving like this:
public function setMetadatos(\Portal\EntradasBundle\Entity\EntradaMeta $metadatos = null)
{
$this->metadatos = $metadatos;
return $this;
}
But always get the error:
Catchable Fatal Error: Argument 1 passed to Portal\EntradasBundle\Entity\Entrada::setMetadatos() must be an instance of Portal\EntradasBundle\Entity\EntradaMeta, array given
I dont know how to set a datatransformer (ArrayToModelTransformer) to reach this.
Anyone can help me plz. Thanks in advance!

A data transformer is quite simple,
Look at this:
http://symfony.com/doc/current/cookbook/form/data_transformers.html
A data transformer is used like this:
/**
* #var ObjectManager
*/
private $om;
/**
* #param ObjectManager $om
*/
public function __construct($om)
{
$this->om = $om;
}
[..]
$yourTransformer = new YourDataTransformer($this->om);
And then ->addModelTransformer($yourTransformer))
It's used to get the id of an object , and/or get the object from an id.

Related

Symfony form submission with handleRequest issue

I'm experiencing an odd error with my form validation in Symfony 4. It is a simple contact form represented by this entity:
class ContactRequest
{
/** #var int */
private $id;
/** #var string */
private $fullName;
//...
/**
* #return string
*/
public function getFullName() : string
{
return $this->fullName;
}
In my controller I'm handling the submission as per Symfony website but there is something I'm missing for I'm getting the following error:
Type error: Return value of App\Entity\ContactRequest::getFullName() must be of the type string, null returned
Now, I know what is the meaning of that: it expects a string to be returned by the method getFullName whereas null is actually returned but I don't understand why.
This is my controller
public function contactSubmit(Request $request, ValidatorInterface $validator)
{
$form = $this->createForm(ContactType::class);
$form->handleRequest($request);
if($form->isValid()){
//...
}
$errors = $validator->validate($form);
Shouldn't the handleRequest() method set the values in the entity for me?
To my surprise, when I have initialised the entity before, it worked well notwithstanding the entity is already set in the configureOptions() method in the form:
$contact = new ContactRequest;
$contact
->setFullName($request->request->get('contact')['fullName'])
//...
$form = $this->createForm(
ContactType::class
$contact
);
$form->setData($contact);
$form->handleRequest($request);
However, shouldn't the scope of using the handleRequest() be to avoid setting the entity's values manually? Shouldn't the handleRequest() method take care of setting those values?
I know I could validate the submitted data against the entity too (thing that I've successfully tried), without using the handleRequest() at all but it would piss me off a little. Why would I need to set a form in such a case?
This is the ContactType form:
//...
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fullName', TextType::class, [
'required' => true,
'empty_data' => 'a',
'attr' => [
'placeholder' => 'Full Name',
'class' => 'form-control'
]
])
//...
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ContactRequest::class
]);
}
In order to find out where your getFullName gets called you could (atleast in dev environment) print the backtrace on the call:
/**
* #return string
*/
public function getFullName() : string
{
if ($this->fullName === null)
{
echo "<pre>getFullName on uninitialized entity:\n";
debug_print_backtrace();
die();
}
return $this->fullName;
}
But as said in the comments: Initializing the entity with a null value in that field and not allowing the getter to return a null value seems kinda odd to me, so : ?string to allow for nullable return values (as of PHP 7.1) seems to be the next best option.

Symfony 2.7: Automatically Populate Form Field from Within Form Builder

I have an entity named HoursSpecial with a foreign key relationship to an entity called HoursArea. Each HoursSpecial belongs to an HoursArea. When I create a new HoursSpecial via my HoursSpecialType, I want the form field to automatically populate the HoursArea field.
I know what you're thinking, just do something like this in my controller's method:
$form->add('area', 'hidden', array('data'=>$area));
That would be fine except I need to make a DataTransformer to switch between the area's ID and the actual area entity. So I have to declare my HoursArea field within my HoursSpecialType with the transformer:
$builder
...
->add('area', 'hidden')
;
$builder->get('area')->addModelTransformer(new HoursAreaToIntTransformer($this->manager));
Now, I can't simply feed my HoursArea entity into the form. Is there an effective way to make this happen?
I've thumbed through Symfony's documentation on How to Dynamically Modify Forms Using Form Events, but I can't make heads or tails of how I would pass in that HoursArea entity dynamically from outside of the form builder. Maybe I'm just missing something?
UPDATE
Following the recommendation of the answer (Recommendation #1) below from #Ryan, I have created the custom type HiddenHoursAreaType:
// AppBundle\Form\Type\HideenHoursAreaType.php
class HiddenHoursAreaType extends AbstractType
{
//need to instantiate HoursAreaToIntTransformer
private $manager;
public function __construct(ObjectManager $manager)
{
$this->manager = $manager;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new HoursAreaToIntTransformer($this->manager);
$builder->addModelTransformer($transformer);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => null,
'compound' => true //this should be FALSE as there are no children
));
}
/**
*
* the return value of the getParent function indicates that you're extending the choice field type.
* This means that, by default, you inherit all of the logic and rendering of that field type.
*/
public function getParent()
{
return 'hidden';
}
public function getName()
{
return 'app_hoursArea';
}
I have added my transformer into the custom type class. Here is the transformer class:
// AppBundle\Form\DataTransformer;
class HoursAreaToIntTransformer implements DataTransformerInterface
{
private $manager;
public function __construct(ObjectManager $manager)
{
$this->manager = $manager;
}
/**
* Transforms an object (HoursArea) to a string (number).
*
* #param Issue|null $issue
* #return string
*/
public function transform($area)
{
if (null === $area) {
return '';
}
return $area->getId();
}
/**
* Transforms a string (number) to an object (HoursArea).
*
* #param string $areaId
* #return HoursArea|null
* #throws TransformationFailedException if object (HoursArea) is not found.
*/
public function reverseTransform($areaId)
{
// no area number? It's optional, so that's ok
if (!$areaId) {
return;
}
$area = $this->manager
->getRepository('AppBundle:HoursArea')
// query for the issue with this id
->find($areaId)
;
if (null === $area) {
// causes a validation error
// this message is not shown to the user
// see the invalid_message option
throw new TransformationFailedException(sprintf(
'An area with number "%s" does not exist!',
$areaId
));
}
return $area;
}
}
Within my controller I create the form with the custom type field:
$form = $this->createForm(new HoursSpecialType($this->getDoctrine()->getManager()), $entity, array(
'action' => $this->generateUrl('hoursspecial_postcreate'),
'method' => 'POST',
));
$form->add('eventDate', 'hidden', array('data'=>$dateString));
$form->add('area', new \AppBundle\Form\Type\HiddenHoursAreaType($this->getDoctrine()->getManager()), array(
'data'=>$area,
'invalid_message'=>'Area field not converted proerly'
));
$form->add('submit', 'submit', array('label' => 'Create'));
Thanks to the transformer and the custom type, the form now correctly converts the HoursArea entity to an integer for population in the hidden field.
The problem now is that upon form submission, the integer is not converted back into an HoursArea object. I know this because I get the 'invalid_message' upon submission.
Final Update
The reason the HoursArea id wasn't being inserted properly had something to do with the
'compound' => true
setting I had in my custom type. I assume it was looking for child fields and wasn't finding any...which it shouldn't have because there were none!
You could create a custom type for it and add the addModelTransformer() call in the buildForm() of your custom type, but still pass the data in explicitly. So your $form->add('area', 'hidden', array('data'=>$area)) would become $form->add('area', new HiddenHoursAreaType(), array('data'=>$area)) where HiddenHoursAreaType::getParent() would be the hidden type.
You could set the data in a POST_SET_DATA listener.
You could get the $options['data'] value in buildForm() and explicitly pass in the HoursArea ID.
/** #var HoursSpecial $hoursSpecial Prepopulated in controller */
$hoursSpecial = $options['data']
$builder->add('area', 'hidden', ['data' => $hoursSpecial->getHoursArea()->getId()])

One to many relation throw exception on save

I have two entites. "Institution" and "User". The Insitution have a onetomany relation to user. I create a form "InstitutionType" and if i want to save a new insitution the "handleReqeust($request)" throw this exception.
Neither the property "users" nor one of the methods "addUser()"/"removeUser()", "setUsers()", "users()", "__set()" or "__call()" exist and have public access in class "App\UserBundle\Entity\Institution".
Entity User
/**
* #ORM\ManyToOne(targetEntity="Institution", inversedBy="users")
* #ORM\JoinColumn(name="institution_id", referencedColumnName="id")
*/
protected $institution;
public function setInstitution(\App\UserBundle\Entity\Institution $institution = null)
{
$this->institution = $institution;
return $this;
}
public function getInstitution()
{
return $this->institution;
}
Entity Institution
/**
* #ORM\OneToMany(targetEntity="App\UserBundle\Entity\User", mappedBy="institution")
*/
protected $users;
public function addUser(\App\UserBundle\Entity\User $users)
{
$this->users[] = $users;
return $this;
}
public function removeUser(\Ebm\UserBundle\Entity\User $users)
{
$this->users->removeElement($users);
}
public function getUsers()
{
return $this->users;
}
InstitutionType
$users = $this->entityManager->getRepository('AppUserBundle:User')->findByIsActive(true);
->add('users', 'entity', array(
'label' => 'responsibleperson',
'attr' => array(),
'class' => 'AppUserBundle:User',
'choices' => $users,
'multiple' => false,
'expanded' => false,
'empty_value' => '----------------')
)
Can someone help my to solve this issue?
Rename addUser to addUsers and removeUser to removeUsers.
Symfony2/Doctrine obviously has no knowledge of singular/plural words and can't guess that to add a single entry to the users collection it would be semantically more correct to call addUser() instead of addUsers.
Please note that you can always use the console command doctrine:generate:entities AcmeDemoBundle:Institution to generate missing fields in the entity classes.
If this doesn't help you need to make sure you use the same notation in your form type like in your entity configuration (annotation, yml or xml).

Symfony 2 Entity Field

I am getting error when I am using following entity type field,
It is showing me select field fine but when I am trying to save it it is giving me following error
Code 1:
->add('agentFirstname', 'entity', array(
'class' => 'AcmeClientBundle:Client',
'property' => 'firstName',
))
Error 1: Catchable Fatal Error: Object of class
Acme\ClientBundle\Entity\Client could not be converted to string
When I am using second code then everything is working fine
Code 2:
->add('agentFirstname', 'text', array(
)
)
Error 2: No Error
Please find my entity bellow
/**
* #var string
*
* #ORM\Column(name="agent_firstname", type="string", length=255)
*/
private $agentFirstname;
I want to make select field here for client first name entity which is here
/**
* #var string
*/
private $firstName;
Symfony cannot access the $firstName property as its visibility is private.
You need to add a getFirstName() method to your Acme\ClientBundle\Entity\Client class.
public function getFirstName()
{
return $this->firstName;
}
Now change your form code to:
->add('agentFirstname', 'entity', array(
'class' => 'AcmeClientBundle:Client', 'property' => 'getFirstName'
))

Symfony2 Form euro to cents

I have a form where I can fill in my euros, my entity only knows cents and is a integer.
So I want to create (not sure if i'm using the right method) form transformer.
What I do:
class EuroTransformer implements DataTransformerInterface
{
public function transform($euro)
{
return $euro * 100;
}
public function reverseTransform($euro)
{
return $euro / 100;
}
}
form:
->add('price', 'money', array(
'attr' => array(
'style' => 'width: 70px;'
)
))
->addModelTransformer($euroTransformer)
But i'm getting the next message:
The form's view data is expected to be an instance of class Entity\InvoiceRule, but is a(n) integer. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) integer to an instance of Entity\InvoiceRule.
And yes I have already a data_class in my default options.
How to solve my problem?
using symfony2 2.2
Sf2 MoneyType handles this case !
->add('price', 'money', array(
'divisor' => 100,
'attr' => array(
'style' => 'width: 70px;'
)
))
You need to return an object in your reverseTransform method:
/**
* #param int $cents
*
* #return InvoiceRule
*/
public function reverseTransform($cents)
{
$euro = new InvoiceRule();
$euro->setValue($cents / 100);
return $euro;
}
And your transform method must transform an object into a number:
/**
* #param InvoiceRule $euro
*
* #return int
*/
public function transform($euro)
{
return $euro->getValue() * 100;
}
See the documentation for more examples.

Resources