Handling Form errors in the controller ( Ajax call ) - symfony

I'd like to have the field name in addition to the error message.
I performed the following set of instructions to put all errors into an array :
$errors= array();
foreach ($newRdvForm->getErrors(true) as $key => $error) {
$errors[$key] = $error->getMessage();
}
So what can i to have the field name of each input ?
If there are other method, feel free to publish it

The FormInterface::getErrors() method returns a FormErrorIterator instance. The underlying FormError objects provide a getOrigin() method which returns the FormInterface the error relates to.

Related

I get error on Adding custom action in symfony Warning: array_merge(): Expected parameter 2 to be an array, null given

I have a dating website programmed with symfony. I want to add a function in easyadmin to be able to delete a user and her/his messages and posts. In UserCrudController I followed the instruction in symfony website and write a custom action. But I get an error
Warning: array_merge(): Expected parameter 2 to be an array, null given
public function configureActions(Actions $actions): Actions
{
// this action executes the 'renderInvoice()' method of the current CRUD controller
$delUserAction = Action::new('deleteUser', 'Delete user completely')
->linkToRoute('delete_user', function (User $user){
$id = $user->getId();
$res = $this->getDoctrine()->getRepository(User::class)->find($id);
if ($res) {
$em = $this->getDoctrine()->getManager();
$em->remove($res);
$em->flush();
}
$res = $this->getDoctrine()->getRepository(Message::class)->remove_all_message($id);
$res = $this->getDoctrine()->getRepository(Beziehungen::class)->remove_all_relations($id);
$res = $this->getDoctrine()->getRepository(Album::class)->remove_album_of_user($id);
$res = $this->getDoctrine()->getRepository(Blog::class)->remove_blog_of_user($id);
$res = $this->getDoctrine()->getRepository(Comment::class)->remove_comments_of_user($id);
$res = $this->getDoctrine()->getRepository(Subcomment::class)->remove_subcomments_of_user($id);
$res = $this->getDoctrine()->getRepository(Like::class)->remove_likes_of_user($id);
});
return $actions
// ...
->add(Crud::PAGE_INDEX, $delUserAction)
;
}
Can you please help me?
According to the tutorial, linkToRoute will connect an action to a route (as the name suggests). Similar to other route functions, it expects as first parameter the route name and as second parameter an array or a function that returns an array - the parameters to fill the placeholders of the route.
Your function doesn't return an array but instead IS the action.
So you really should put the deletion code you have and put it into an extra function like ... deleteUserAction, and then define your action as
$delUserAction = Action::new('deleteUser', 'Delete user completely')
->linkToCrudAction('deleteUserAction');

How to properply update an entity which contains an Image path?

I have a form that I use both for registration and edition of the user informations. This form contains a profile picture property on which I put #Assert\Image.
I succeed in creating a new user through my registration form but when I try to edit the user informations (with a PATCH method, just to update what need to be updated) I encounter an error with a 'File could not be found' message.
I suppose it's because the path stored in the database is a string and my #Assert\Image want an image.
I'm not sure about how I should manage this kind of update.
When I dd() the $user right after the submission, I see that the profilePicture property still contains the path saved in the database.
Here is my function regarding the form handling:
public function myProfile(Request $request)
{
$user = $this->getUser();
$form = $this->createForm(UserFormType::class, $user, ['method' => 'PATCH']);
if ($request->isMethod('PATCH')){
$form->submit($request->request->get($form->getName()), false);
if ($form->isSubmitted() && $form->isValid()) {
//...
}
}
//if no request just display the page
return $this->render('connected/myProfile.html.twig', [
'user' => $user,
'userProfileForm' => $form->createView()
]);
}
The Validator will check if your object contains a image and that seems not the case when you’re updating your object.
A workaround is to use group validation you define a specific group to the property that have the assert Image and in the method getGroupSequence you return the group if you’re in creation (id == null) or if the property is setted.

Nested form: passing values

I'm a bit at a loss here with Symfony nested forms..
I have Events and RoleEntries i.e. the role that people or organizations can have for an event...
So I have an EventType form, with a nested RoleEntryType:
->addEventListener(FormEvents::PRE_SET_DATA,
function (FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
$form->add('roleEntries', 'collection', array(
'type' => new RoleEntryType($data),
'allow_add' => true,
'allow_delete' => true
));
}
)
the $data variable is caught by the RoleEntryType constructor:
$this->data=$data;
And I try to add a hidden field (since the user should not modify it, in the field):
->add($this->targetScope, 'hidden', array('data'=>$this->data))
At this point, Symfony is not happy because it cannot convert the $data to string
An exception has been thrown during the rendering of a template ("Catchable Fatal Error: Object of class IH\EventManagerBundle\Entity\Event could not be converted to string") in form_div_layout.html.twig at line 13.
so I try just to give it an Id
->add($this->targetScope, 'hidden', array('data'=>$this->data->getId()))
But it doesn't work either because a string is not enough, it wants a fully fledged event:
Catchable Fatal Error: Argument 1 passed to IH\EventManagerBundle\Entity\RoleEntry::setEvent() must be an instance of IH\EventManagerBundle\Entity\Event, string given, called in /Users/MTP/Documents/dev/MTP/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php on line 410 and defined
So I guess I'm doing it all wrong....
help!
$data contains the form data, so you need to extract your field value and pass it as value for your hidden field:
$form->add('targetScope', 'hidden', array('data' => $data['field_name']));
Without change your initial implantation, just add a __to string() magic method to Event entity and all should work as expected
Something like
Class Event
{
[...]
public function __toString()
{
return $this->name;
}
}
Of course $this->name; should be changed accordingly to your object properties.
If you need, when posting the form, to recreate (or take from db) an entity object, take a look to DataTransformers
Thanks for all your replies,
So using DataTransformer or creating a new field type (EntityHidden) both work... however, my problem stemed from something I forgot in my code which forced me to handle this in the FormType rather than in the entity...
Simply, there is no need to pass the primary key of the entity corresponding to the nesting form (Event) over to the nested form (RoleEntryType)... if I tell my Event entity setter that it should write a reference of the event in the new RoleEntry:
public function addRoleEntry(
{
$this->roleEntries[] = $roleEntry;
$roleEntry->setEvent($this);
return $this;
}
Sorry if my question wasn't clear and thanks for taking the time to answer!

Class not found when trying to index documents using Solr with Symfony2

I am very new to Solr and I am probably missing something simple however, having followed Xavier Briand's presentation I have set up Symfony2, Solarium and Nelmio\SolariumBundle.
"nelmio/solarium-bundle": "2.0.*#dev",
"solarium/solarium": "3.0.*"
Having implemented a toSolrDocument method for my doctrine php object.
public function toSolrDocument(\Solarium_Document_ReadWrite $doc)
{
$doc->id = $this->getId();
$doc->description = $this->getTitle();
$doc->path = "path";
return $doc;
}
I am faced with the following error.
Catchable Fatal Error: Argument 1 passed to ::toSolrDocument() must be an instance of Solarium_Document_ReadWrite, instance of Solarium\QueryType\Update\Query\Document given, called in Controller.php
The controller calling this toSolrDocument method has the following function
public function indexItems(){
$client = $this->get('solarium.client');
// get an update query instance
$update = $client->createUpdate();
// create documents
$documents = array();
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository('<Bundle>:<Object>');
$items = $repo->findAll();
foreach ($items as $item) {
$documents[] = $item->toSolrDocument($update->createDocument());
}
// add the documents and a commit command to the update query
$update->addDocuments($documents);
$update->addCommit();
// this executes the query and returns the result
$result = $client->update($update);
}
Most of this method again comes directly from Xavier's presentation and it is clear to see that the method $update->createDocument() does not return the correct type. In fact it returns an instance of the my php object. Does anyone know where I am going wrong here? Another thing that might be of help is that even when I try to pass a Solarium Document directly I get an exception.
foreach ($items as $item) {
$rw_item = new \Solarium_Document_ReadWrite();
$documents[] = $item->toSolrDocument($rw_item);
}
The exception is
FatalErrorException: Error: Class 'Solarium_Document_ReadWrite' not found in
I can't seem to find this class in any of the bundles and I am wondering if my setup might be causing the issues. Any help would be very much appreciated.
One additional point to note is that when I am running the solr jar I see the query requests come in from my symfony2 page it is only this indexing action that I can not work out so the config may be alright and I am miss understanding the use of the bundle.
You just need to use the correct class for the argument.
use Solarium\QueryType\Select\Result\AbstractDocument;
...
public function toSolrDocument(AbstractDocument $doc)
{
You could also not type hint it:
public function toSolrDocument($doc)
{

Symfony2 Functional test: Passing form data directly

I am using phpunit to run functional tests but I am having a problem with a few of the forms. The problem is that phpunit is not aware of JS, and I have a form with a dynamically populated select box that needs jQuery.
So I need to pass the form data directly. The 'book' gives the following example:
// Directly submit a form (but using the Crawler is easier!)
$client->request('POST', '/submit', array('name' => 'Fabien'));
When I used this example the controller didn't receive any of the form data. Intially I saw that passing the array key 'name' wasn't correct in my situation as I needed the form name which was 'timesheet' in my code. So I tried something like:
$client->request('POST', '/timesheet/create', array('timesheet[project]' => '100'));
But this still didn't work. In the controller I tried to understand what was happening and what if anything was being received:
$postData = $request->request->get('timesheet');
$project = $postData['project'];
This didn't work and $project remained empty. However if I used the following code I got the value:
$project = $request->request->get('timesheet[project]');
But clearly that's not what I want. Atleast though I can see that there is some POST data. My last attempt was to try the following in the test method:
$this->crawler = $this->client->request('POST', '/timesheet/create/', array('timesheet' => array(project => '100'));
So I am trying to pass a 'timesheet' array as the first element of the request parameter array. But with this I get the error:
Symfony\Component\Form\Exception\UnexpectedTypeException: Expected argument of type "array", "string" given (uncaught exception) at /mnt/hgfs/pmt/src/vendor/symfony/src/Symfony/Component/Form/Form.php line 489
I would be very happy if someone can expand on what's in the 'book' about how I am supposed to get this working.
Form bind in controller:
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
$postData = $request->request->get('timesheet');
$project = $postData['project'];
$timesheetmanager = $this->get('wlp_pmt.timesheet_db_access');
$timesheetmanager->editTimesheet($timesheet);
return $this->redirect($this->generateUrl('timesheet_list'));
}
}
If you are wanting to know how to inject arrays of POST data using the test client...
In your test method, do something like
$crawler = $client->request('POST', '/foo', array(
'animal_sounds' => array(
'cow' => 'moo',
'duck' => 'quack'
)
); // This would encode to '/foo?animal_sounds%5Bcow%5D=moo&animal_sounds%5Bduck%5D=quack'
$this->assertTrue( ... );
In the controller, you would access your params like this:
$data = $request->request->get('animal_sounds');
$cowNoise = $data['cow'];
$duckNoise = $data['duck'];
Or you could just use the forms API if the test method was injecting valid form data...
do you have a $request parameter in your action?
that was the reason why my request->get() was empty:
//WRONG
public function projectAction()
{
$request = Request::createFromGlobals();
$project = $request->request->get('timesheet[project]');
//$project will be empty
}
//CORRECT
public function projectAction(Request $request)
{
$project = $request->request->get('timesheet[project]');
//$project is not empty
}
see
How do I create a functional test which includes a POST to a page with parameters?
Try to use $form->bind($clientData) instead of $form->bindRequest($request).

Resources