Symfony: ManyToOne Form Issue - php

I have two entities called, Ticket & TicketUpdate.
Each Ticket can have many TicketUpdates, but every TicketUpdate can only have 1 Ticket.
Next I have a form which shows the current Ticket, but also allows me to add 1 TicketUpdate & change attributes of Ticket.
This is my controller:
//TicketController.php
...
/**
* #Route("/ticket/{id}", name="app_ticket")
*/
public function ticket(Request $request, Ticket $ticket)
{
$ticketUpdate = new TicketUpdate();
$ticketUpdate->setTicket($ticket);
$form = $this->createForm(TicketUpdateType::class, $ticketUpdate); //custom form type
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($ticketUpdate);
$em->persist($ticket);
$em->flush();
}
return $this->render('ticket/view.html.twig', ['ticket' => $ticket, 'form' => $form->createView()]);
}
...
TicketUpdateType:
//TicketUpdateType.php
...
class TicketUpdateType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('text', TextareaType::class, ['label' => 'update', 'required' => false, 'attr' => ['class' => 'textarea-sm'])
->add('ticket', TicketType::class, ['label' => false, 'by_reference' => false]) //custom Type for Tickets
->add('submit', SubmitType::class, ['label' => 'save']);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => TicketUpdate::class
]);
}
}
...
However, this solution does not work for me. Symfony always wants to create a new Ticket entry, instead of changing the old one.
Is there any way to fix this?

May you know, a magic with symfony forms, with you can get an Entity (Ticket), like in your example, I dont know... but this will working:
/**
* #Route("/ticket/{ticketId}", name="app_ticket", requirements={"ticketId"="\d+"})
*/
public function ticket(Request $request, int $ticketId = 0)
{
$em = $this->getDoctrine()->getManager();
$ticket = $em->getRepository(Ticket::class)
->findOneBy([
'id' => $ticketId
]);
if ($ticket instanceof Ticket === false)
{
die('Ticket dont exist with the requested ID.'); #Just return here some error message
}
$ticketUpdate = new TicketUpdate();
//Because your setTicket() setter inside your TicketUpdate Entity
//sure have nullable typehinted argument (?Ticket $ticket)
//if this is a valid doctrine relationship
//(but if i'm wrong, please show the touched parts of your TicketUpdate entity)
$ticketUpdate->setTicket($ticket); #<-- here is an "old" Ticket $ticket
$form = $this->createForm(TicketUpdateType::class, $ticketUpdate); //custom form type
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
$em->persist($ticketUpdate);
//I'm working a lot with doctrine relationships
//In many time necessary using a setter in the both Entity
$ticket->addTicketUpdate($ticketUpdate); #You know this setter! Change, if my tip wrong
$em->persist($ticket);
$em->flush();
}
return $this->render('ticket/view.html.twig', [
//Now, this is an instance of the Ticket
//not an int ID!
//so if you need the ID, you can get in twig, like:
//{{ ticket.id }}
'ticket' => $ticket, #Or: $ticket->getId()
'form' => $form->createView()
]);
}
The requirements inside the #route means, the method will running only on pages, where the {ticketId} is numeric.

Update: I changed by_reference to true and removed every logic in my TicketType, which seemed to cause the issue.
Atleast for now I got it running. Here is my controller:
//TicketController.php
...
/**
* #Route("/ticket/{id}", name="app_ticket")
*/
public function ticket(Request $request, Ticket $ticket)
{
$ticketUpdate = new TicketUpdate();
$ticketUpdate->setTicket($ticket);
$form = $this->createForm(TicketUpdateType::class, $ticketUpdate); //custom form type
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($ticketUpdate);
//$em->persist($ticket); -> removed, will be automatically updated by symfony
$em->flush();
}
return $this->render('ticket/view.html.twig', ['ticket' => $ticket, 'form' => $form->createView()]);
}
...

Related

is it possible to assign a value to a field added with EventListener symfony in $builder symfony?

I would like to know if it is possible to automatically assign values ​​to added fields of type:
datetime
entity
Thanks for your help
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user = $options['user']; // entity User
$player = $options['player']; // entity Player
$today = new DateTime('now');
$builder
->add('fieldA')
->add('fieldB')
->add('fieldC');
$builder
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($user, $player, $today) {
$form = $event->getForm();
$datas = $event->getData();
$form->add('today');
$form->add('user');
$form->add('player');
//dd($form); ok = 3 fields added
$datas['dateDuJour'] = $today;
$datas['user'] = $user;
$datas['player'] = $player;
//dd($datas); ok = 3 assigned values
$form->setData($datas);
question 1 : how to insert the data in the form
question 2 : pb from entity (object) to string
//dd($form, $datas);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Evaluation::class,
'user' => null,
'player' => null
]);
}
}
I thought about inserting the 3 fields with the type = hidden and using Data Transformer
I do not know what is the best practice?
if you have a concrete example
From what i can see, you have some form and you want to plug 3 data to the form on submit.
Depending on your database configuration, you can do 3 different way:
The best one is to use the mapping
Your evaluation have those 3 fields:
date
user
player
Then just add them to the original builder as hidden field whith default value what you have:
$builder->add('token', HiddenType::class, [
'data' => $today,
])->add('user', HiddenType::class, [
'data' => $user,
])->add('player', HiddenType::class, [
'data' => $player,
]);
As they are hidden, the security check will not autorise users to change those value plus thoise fields will be hiddent
It require those three fields exist in your entity
Second one is to use unmapped hidden field. Same a previous, but add 'mapped'` => false . Then you in your controller, you will have the value and use them as needed.
The third one is to not use them in your form (my favorite) but in your controller
public function addEvaluation(Request $request, EvaluationManager $evaluationManager): Response
{
$evaluation = new Evaluation();
$form = $this->createForm(EvaluationType::class, $evaluation);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$evaluation->setTime(new DateTime('now'))
->setUser($user)
->setPlayer($player);
$evaluationManager->save($evaluation);
return $this->redirectToRoute('evaluation_add');
}
return $this->render('/evaluation_add.twig', [
'form' => $form,
]);
}

TransformationFailedException when entity is passed to Form

I'm using Symfony 3.3 and im getting this TransformationFailedException Error, when i load my profile page:
Unable to transform value for property path "postalcode": Expected a numeric.
The postalcode value for this user in the database is:
'34125abc'
The postalcode attribute defined in UserProfile Entity:
/**
* #ORM\Column(type="string")
*/
private $postalcode;
My ProfileController:
class ProfileController extends Controller{
/**
* #Route("/edit_profile", name="edit_profile")
*/
public function profileAction(Request $request){
$profile = $this->getDoctrine()->getManager()->getRepository('AppBundle:UserProfile')->findOneBy(['user_id' => $this->getUser()->getUserId()]);
// If no UserProfile exists, create a UserProfile Object to insert it into database after POST
if(null === $profile){
$profile = new UserProfile();
$profile->setUserId($this->getUser()->getUserId());
}
$form = $this->createForm(EditProfileFormType::class);
$form->setData($profile);
// only handles data on POST
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$result = $this->forward('AppBundle:API\User\Profile:update_profile', array(
'profile' => $profile
));
if(200 === $result->getStatusCode()){
$this->addFlash('success', "Profile successfully created!");
}
}
return $this->render(':User/Profile:edit_profile.html.twig', [
'EditProfileForm' => $form->createView(),
]);
}
}
My EditProfileFormType:
class EditProfileFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', ChoiceType::class, array(
'choices' => array(
'Mr' => 'Mr',
'Mrs' => 'Mrs'
)
))
->add('firstName')
->add('lastName')
->add('street')
->add('postalcode', NumberType::class)
->add('city')
->add('telephone')
->add('mobile')
->add('company')
->add('birthday' , BirthdayType::class)
->add('callback', CheckboxType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\UserProfile',
'validation_groups' => array('edit_profile')
]);
}
public function getBlockPrefix()
{
return 'app_bundle_edit_profile_form_type';
}
}
So the problem here seems to be the not numeric string value in the databse
'34125abc'
which is stored in the $profile entity object and is passed to the form by $form->setData($profile); So when the data is being set, the error is thrown because of the Numbertype in this line ->add('postalcode', NumberType::class). Is there a way to pass the postalcode value to the form, even if it's not numeric and only check the Numbertype, when the form is submitted? Because i don't need validation, when I pass data to the form. Just when, it's submitted.
The solution was quite simple, but hard to find out.. I changed
->add('postalcode', NumberType::class)
to
->add('postalcode', TextType::class)
in my EditProfileFormType.php. Why? Because the form builder just need to know the type of the field in the database. It's shouldn't care in this case if it is numeric or not, because thats the task of the model restriction. In this case it is a string, so it is Texttype in a form. All the form type's are applied, when the form is set, but the validation groups are just validated, when the form is submitted! That's exactly they way it should be!

Symfony2 - Get entity in presubmit/submit form event

As the title says, I need to get the entity which is going to be deleted in a form event. I'm using Symfony 2.7. I'm able to get entity on POST_SUBMIT event if it gets created/edited, but I'm not able to get it on PRE_SUBMIT, SUBMIT and also POST_SUBMIT before it gets deleted.
What I tried so far (I wrote results of variables in comments)
public static function getSubscribedEvents()
{
return array(
FormEvents::POST_SUBMIT => 'onPostSubmit',
FormEvents::PRE_SUBMIT => 'onPreSubmit',
FormEvents::SUBMIT => 'onSubmit'
);
}
public function onPreSubmit(FormEvent $event)
{
dump($event->getForm()->getData()); // <-- null
dump($event->getData()); // <-- array:1 ["submit" => ""]
}
public function onSubmit(FormEvent $event)
{
dump($event->getForm()->getData()); // <-- null
dump($event->getData()); // <-- array:0 []
}
public function onPostSubmit(FormEvent $event)
{
dump($event->getForm()->getData()); // <-- array:0 []
dump($event->getData()); // <-- array:0 []
}
This is basically how deletion starts. I'll not put the whole functions used by it because I don't think is needed:
public function deleteConfirmAction(Request $request, $id)
{
$form = $this->createDeleteForm($id);
$form->handleRequest($request);
$entity = $coreService->getArea($id);
if ($form->isValid()) {
$coreService->deleteEntity($entity); // will remove also relationships
$coreService->persistChanges(); // basically a Doctrine flush()
$this->addFlash('success', GlobalHelper::get()->getCore()->translate('flash.entity.delete.success'));
return $this->redirect($this->generateUrl('index')));
}
}
private function createDeleteForm($id)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('area_delete_confirm', array('id' => $id)))
->setMethod('POST')
->add('submit', 'submit', array('label' => 'entity.delete', 'attr' => ['class' => 'btn button-primary-right']))
->getForm();
}
Any idea?
You don't pass your entity to the form, you can do this by specifying first argument for createFormBuilder call:
$this->createFormBuilder(['entity' => $entity]);
and then you should be able to retrieve entity in pre submit event listener.

How to get user ID (FOSUserBundle) in form builder in Symfony2?

I'm quite new here, be patient, please.
I'm trying to make notice board project in Symfony2 using FOSUserBundle.
I try to get logged user id to put it into form created with form builder (and then to MySQL database).
One of attempts is:
public function createNoticeAction(Request $request)
{
$notice = new Notice();
$form = $this->createFormBuilder($notice)
->add("content", "text")
->add("user_id","entity",
array("class"=>"FOS/UserBundle/FOSUserBundle:", "choice_label"=>"id"))
->add("isActive", "true")
->add("category", "entity",
array("class" => "AppBundle:Category", "choice_label" => "name"))
->add("save", "submit", array("label" => "Save"))
->getForm();
$form->handleRequest($request);
$em = $this->getDoctrine()->getManager();
$em->persist($notice);
$em->flush();
return $this->redirectToRoute('app_user_showuserpage');
}
I tried many solutions again and again and I get some error.
You already have the user object Symfony > 2.1.x
In you Controller like this:
$userId = $this->getUser()->getId();
...
$notice->setUserId($userId);
$em->persist($notice);
Don't ->add field in you FormBuilder, its not safely. Set this value in you Controller and don't ->add this field in FormBuilder
for symfony 3.2.13
have excelent solution (just because is working, but is dangerous if someone discover it in pure HTML)
1) first build YourFormType class.
add normal field in Forms/YourFormType.php (if not, formbuilder tell you that you passing smth not quite right (too many fields) ; -) )
$builder
->add(
'MyModelAddedById',
HiddenType::class,
[
'label' => 'echhh', //somehow it has to be here
'attr' => ['style' => 'display:none'], //somehow it has to be here
]
);
2) in your controller
public function addSomethingAction(Request $request){
$form = $this->createForm(MyModelFormType::class);
//set field value
$request->request->set("prodModelAddedById", $this->getUser()->getId());
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$product = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
$this->addFlash('success', 'record was added');
return $this->redirectToRoute('products');
}
return $this->render(
'default.add.form.html.twig',
[
'newprod' => $form->createView(),
]
);
}
explenation:
you are passing a field and variable to formbuilder (settig it already to default value!)
and important thing, becose of BUG in my opinion - you can't in your form type set method:
public function getBlockPrefix()
{
//return 'app_bundle_my_form_type';
}
because
$request->request->set
can't work properly if your POST data from form are in bag (parameterbag)
no entity managers, no services, no listeners...
hope it helps.

Symfony 2 - Layout embed "no entity/class form" validation isn't working

I'm developing a blog in symfony and i'm stuck with forms that are embed inside the layout. In my case a simple search form.
<div class="b-header-block m-search">
{{ render(controller('YagoQuinoySimpleBlogBundle:Blog:searchArticles')) }}
</div>
To render the form i'm using an embed controller inside the layout twig file.
public function searchArticlesAction(Request $request)
{
$form = $this->createForm(new SearchArticlesType());
$form->handleRequest($request);
if ($form->isValid()) {
// Do stuff here
}
return $this->render('YagoQuinoySimpleBlogBundle:Blog:searchArticles.html.twig', array(
'form' => $form->createView()
));
}
The indexAction is the one that retrieves the form data and filters a list of articles.
public function indexAction(Request $request)
{
$form = $this->createForm(new SearchArticlesType());
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
$criteria = array(
'title' => $data['search']
);
} else {
$criteria = array();
}
$articles = $this->getDoctrine()->getRepository('YagoQuinoySimpleBlogBundle:Article')->findBy($criteria, array(
'createDateTime' => 'DESC'
), 5);
return $this->render('YagoQuinoySimpleBlogBundle:Blog:index.html.twig', array('articles' => $articles));
}
SearchArticlesType is a form class
class SearchArticlesType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('search', 'text', array(
'constraints' => new NotBlank()
))
->add('submit', 'submit', array(
'label' => 'Buscar'
));
}
public function getName()
{
return 'searchArticles';
}
}
The problem comes when i submit this form. The indexAction do his part, validating the form and filtering the articles but when the embed controller tries to validate data (just for displaying info or whatever)
$form->handleRequest($request);
if ($form->isValid()) {
// Do stuff here
}
I feel like i'm missing something.
Thank you for your help!
When you call render(controller('your_route')) you are actually making a sub request which means the parameters bags are emptied so your request isn't "handled" by the form.
If you are using 2.4+ you could get the master request from the request stack using ..
/** #var \Symfony\Component\HttpFoundation\RequestStack $requestStack */
$requestStack = $this->get('request_stack');
$masterRequest = $requestStack->getMasterRequest();
And then you could handle that request in your rendered controller as opposed to the current (sub) request like..
$form->handleRequest($masterRequest);
In your: public function searchArticlesAction(Request $request) you're missing second argument on create form
$searchArticle = new SearchArticle(); // I assume this is how you named the Entity, if not just change the entity name
$form = $this->createForm(new SearchArticlesType(), $article);

Categories