I've been getting errors when I press update.
I'm using the same code as adding.
I think it may be the picture upload that is causing the problem.
Here's the error:
The form's view data is expected to be an instance of class Symfony\Component\HttpFoundation\File\File, but is a(n) string.
You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) string to an instance of Symfony\Component\HttpFoundation\File\File.
My Controller:
public function updateAction(Request $request ,$id)
{
$em = $this->getDoctrine()->getManager();
$forum = $em->getRepository("ForumBundle:Forum")->find($id);
$forum->setModifiee(new \DateTime('now'));
$form = $this->createForm(ForumType::class, $forum);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$file = $form['image']->getData();
$file->move("images/", $file->getClientOriginalName());
$forum->setImage("images/" . $file->getClientOriginalName());
$em->persist($forum);
$em->flush();
return $this->redirectToRoute('forum_show');
}
return $this->render("#Forum/Sujet/Update_topic.html.twig", array("form" => $form->createView()));
}
My Form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('titre')
->add('tags')
->add('image', FileType::class,array('attr' => array(
'class'=>'form-control'
//'class'=>'btn btn-default btn-file'
)))
->add('blog',TextareaType::class)
->add('Ajouter',SubmitType::class,array('attr' => array(
'class'=>'theme_button color3 wide_button'
)));
}
These are my includes:
namespace ForumBundle\Controller;
use blackknight467\StarRatingBundle\Form\RatingType;
use ForumBundle\Form\RateType;
use ForumBundle\Entity\Commentaire;
use ForumBundle\Entity\Forum;
use ForumBundle\Entity\Rating;
use ForumBundle\Form\CommentaireType;
use ForumBundle\Form\ForumType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\File\File;
According to the Symfony documentation : How to upload file, you have to set a File Object into Image setter, not a string. That'a why you have this error :
The form's view data is expected to be an instance of class Symfony\Component\HttpFoundation\File\File, but is a(n) string.
You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) string to an instance of Symfony\Component\HttpFoundation\File\File.
The rigth way to accomplish this is to set a new File instance to your image on your controller object, like this :
use Symfony\Component\HttpFoundation\File\File;
// ...
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$forum = $em->getRepository("ForumBundle:Forum")->find($id);
$forum->setModifiee(new \DateTime('now'));
$form = $this->createForm(ForumType::class, $forum);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
$file = $form['image']->getData();
$file->move("images/", $file->getClientOriginalName());
$forum->setImage("images/" . $file->getClientOriginalName());
$forum->setImage(new File($this->getParameter('images_directory').'/'.$forum->getImage()));
$em->persist($forum);
$em->flush();
return $this->redirectToRoute('forum_show');
}
}
// ...
The parameter images_directory have to be set in the services.yml file, according to the symfony's documentation :
# config/services.yaml
# ...
parameters:
# depending of your symfony version
images_directory: '%kernel.project_dir%/web|public/uploads/images'
Another way is to use an uploader service or a dedicated bundle like : VichUploaderBundle or OneupUploaderBundle
Hope this help and have a nice day !!!
In symfony 2.8 in updateAction just correct define fileType for $id
I think it should look like this:
public function updateAction(Request $request ,$id) {
$id->setImage(
new File($this->getParameter('images_directory').'/'.$id->getImage()
));
...
Related
Referencing this question: JMS Serializer DateTime not deserializing?, I have narrowed this down to the folowing:
I have got a controller with a put action using a Request Body Listener:
/**
* #ParamConverter("client", converter="fos_rest.request_body")
*/
public function putClientAction($id, Client $client)
{
$logger = $this->get('logger');
$logger->info(serialize((array) $client));
$logger->info("ID: " . $id);
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('SomeBundle:Client')->find($id);
$logger->info(serialize($entity));
$form = $this->get('form.factory')->createNamed('', new \SomeBundle\Form\ClientType(), $entity);
$form->submit((array) $client, false);
$logger->info(serialize($entity));
$em->persist($entity);
$em->flush();
return $this->get('fos_rest.view_handler')->handle($this->view(null, Codes::HTTP_OK));
}
My question concerns the $form->submit() call, or rather, how would I go about and actually submit the incoming $client entity? I have tried submitting the object, or an (array) representation of it, with the $clearmissing flag set to false to avoid null values being passed in, but an UPDATE never happens.
To be sure, the logged entity representations look fine, it's just that $entity never gets filled with $clients values.
Got any hints on what I'm doing wrong?
Edit
This is what a PUT request looks like:
{
"shortname":"...",
"officialname":"...",
"shortinfo":"..."
...
}
Edit 2
I've narrowed it down (again) to a problem with the DateTime data type:
...
$logger->info("REQUEST" . print_r($request->request->all(), true));
$form = $this->get('form.factory')->createNamed('', new Type(), $entity);
$form->submit($request, false);
$logger->info($form->getData()->getUpdatedAt()->format('Y-m-d\TH:i:sP'));
The $request object contains an updated DateTime, but it is never passed to the form (the second log shows the original date). The form Type:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('updatedAt', 'datetime', array('format' => 'Y-m-d\TH:i:sP'))
}
I guess this is a problem with Symfony Forms and DateTime objects, then?
The problem is how you submit your data. You should use less complex way.
Here is example:
public function putPostAction(Post $post, Request $request)
{
$em = $this->getDoctrine()->getManager();
$form = $this->createForm('content_post',$post);
$form->submit($request);
if($form->isValid()){
$em->persist($form->getData());
$em->flush();
return $post;
}
return $form;
}
You don't need to query client in your code because controller will automatically convert it for you.
Also you should use Request object, it will contain all data that you've submitted.
$form->submit($request) has a key part here.
$form will return all your validations errors.
My FOSRestConfig
fos_rest:
param_fetcher_listener: true
routing_loader:
default_format: json
view:
view_response_listener: force
I'm trying to do as easy thing as make the date field not to be set under "now" value. I'd emphasise that this date field is not linked to any Entity. Symfony keeps ignoring constraint even though I set it in two places:
1. Constraint set in the class definition
This is the first thing I've done. I decided to put this constraint directly where I define this date field
namespace Asmox\BubblechecklistBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints\LessThanOrEqual;
class DeadlineType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('date', 'datetime',array(
'mapped' => false,
'date_format' => 'dd MMMM yyyy',
'constraints' => array(
new LessThanOrEqual("now")
)
));
}
2. Constraint set in the validation.yml file
I also added constraint into separate file, because the first way didn't give the results:
Asmox\BubblechecklistBundle\Form\Type\DeadlineType:
properties:
date:
- LessThanOrEqual: now
I ensured that I have enabled validation in both my configuration files (for prod and dev):
validation: { enabled: true, enable_annotations: true }
But every time I try to submit form with date earlier than now, Symfony pass it. I don't know what I can do more. Please take a look on my paste from Controller:
public function newTaskAction(Request $request)
{
// ...
$form = $this->createForm(new TaskType());
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
// Add new task...
// Redirect to the task list
$this->addFlash('notice', 'I've added new task!');
return $this->redirectToRoute('tasksList');
}
else {
// Create view with form if form is not validate
$request = $this->getRequest();
$request->setLocale('pl');
if ($mobileDetector->isMobile())
$template = 'AsmoxBubblechecklistBundle:Task:taskFormPhone.html.twig';
else
$template = 'AsmoxBubblechecklistBundle:Task:taskForm.html.twig';
return $this->render($template, array('form'=>$form->createView()));
}
}
else {
// Create view with form if request isn't POST
$request = $this->getRequest();
$request->setLocale('pl');
// Utwórz widok
if ($mobileDetector->isMobile())
$template = 'AsmoxBubblechecklistBundle:Task:taskFormPhone.html.twig';
else
$template = 'AsmoxBubblechecklistBundle:Task:taskForm.html.twig';
return $this->render($template, array('form'=>$form->createView()));
}
}
Whats wrong?
I wanted to have a contact-form-block that i can reuse on different pages and templates. So i decided to write a Twig extension. The problem is that i cant access the createFormBuilder() function. The second problem will be then that i cant access the request object for validation. My current code looks like this:
<?php
namespace Name\NameBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class ContactExtension extends \Twig_Extension
{
function getName() {
return 'contact_extension';
}
function getFunctions() {
return array(
'contactform' => new \Twig_Function_Method($this, 'contactform'),
);
}
function contactform() {
$form = $this->createFormBuilder()
->add('Name', 'text')
->add('Message', 'textarea')
->add('Send', 'submit')
->getForm();
return $this->render('NameBundle:forms:contactform.html.twig', array(
'form' => $form->createView(),
}
}
But i get error "Call to undefined method createFormBuilder()"...
Also i will get error if i change the function to function contactform(Request $request) { ... }
What do i need to add to use this function an object? Or maybe the twig extension is the completely wrong approach?
createFormBuilder() is a Controller helper that allows you to access the form.factory service within your controllers through the container (code below)
namespace Symfony\Bundle\FrameworkBundle\Controller;
// ...
class Controller extends ContainerAware
{
// ...
public function createFormBuilder($data = null, array $options = array())
{
return $this->container->get('form.factory')->createBuilder('form', $data, $options);
}
You're not in a "Controller context" here, so if you want to use the form.factory service within your extension you've to inject it.
BUT,
I'll not advice your to manage your contactForm this way (using a Twig Extension function). Why don't you just create a contactAction within the appropriate controller. You can then render your form in your templates using the twig render helper,
{{ render(controller('YourBundle:YourController:contactAction')) }}
If you use Symfony to make your code clear you should make Forms (src/forms) in diffrent file and just call it in your view and controller.
I have been trying to create an extremely basic symfony form (used for search functionality) with only one input. It uses GET method on submit. It seems to work as expected, however it generates an extremely ugly and unnecessarily long URL. I have been trying to 'clean' the URL up for a quite a while now, I was wondering if someone ran into the same problem and knows how to fix it?
Form
$form = $this->createFormBuilder($search)
->setMethod('GET')
->add('q', 'text')
->add('search', 'submit')
->getForm();
On submit the form generates the following URL:
search?form[q]=red+apple&form[search]=&form[_token]=bb342d7ef928e984713d8cf3eda9a63440f973f2
Desired URL:
search?q=red+apple
Thanks in advance!
To create your desired URL, you will have to set the form name by using createNamedBuilder which you'll just leave blank ''.
To remove _token you need to set csrf_protection to false. Please look into csrf protection to make sure you know what could happen if it is turned off.
Changing your code to the following should give you the results you want.
$form = $this->get('form.factory')->createNamedBuilder('', 'form', $search, array(
'csrf_protection' => false,
))->setMethod('GET')
->add('q', 'text')
->add('search', 'submit')
->getForm();
This should produce a URL like:
search?q=red+apple&search=
Edit:
If you want to get rid of &search=, one way would be to change search from submit to button.
->add('search', 'button')
This will require javascript to submit your form.
Here is simple example in jquery:
//This assumes one form and one button
$(document).ready(function(){
$('button').click(function(){
$('form').submit();
});
});
This will produce a URL like:
search?q=red+apple
To access GET vars you put something like this in your controller:
public function yourSearchAction(Request $request)
{
// your code ...
$form->handleRequest($request);
if ($form->isValid()) {
$getVars = $form->getData();
$q = $getVars['q'];
$page = $getVars['page'];
$billing = $em
//Do something
}
return //your code
}
Just to clarify if you are adding page to your URL you will need to add it to your form:
->add('page', 'text')
Old question but, for people who want to know, this does the job too (Symfony 2.8) :
<?php
// src/AppBundle/Form/SearchType.php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class SearchType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setMethod('GET')
->add('q', TextType::class)
->add('submit', SubmitType::class))
;
}
public function getBlockPrefix(){
return '';
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_protection' => false,
]);
}
}
In your controller :
<?php
//...
use AppBundle\Form\SearchType;
//...
public function yourSearchAction(Request $request)
{
$form = $this->createForm(SearchType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$q = $form->get('q')->getData();
// ...
}
// ...
}
Can anyone please show me a specific example of a Symfony2 form entity update? The book only shows how to create a new entity. I need an example of how to update an existing entity where I initially pass the id of the entity on the query string.
I'm having trouble understanding how to access the form again in the code that checks for a post without re-creating the form.
And if I do recreate the form, it means I have to also query for the entity again, which doesn't seem to make much sense.
Here is what I currently have but it doesn't work because it overwrites the entity when the form gets posted.
public function updateAction($id)
{
$em = $this->getDoctrine()->getEntityManager();
$testimonial = $em->getRepository('MyBundle:Testimonial')->find($id);
$form = $this->createForm(new TestimonialType(), $testimonial);
$request = $this->get('request');
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
echo $testimonial->getName();
if ($form->isValid()) {
// perform some action, such as save the object to the database
//$testimonial = $form->getData();
echo 'testimonial: ';
echo var_dump($testimonial);
$em->persist($testimonial);
$em->flush();
return $this->redirect($this->generateUrl('MyBundle_list_testimonials'));
}
}
return $this->render('MyBundle:Testimonial:update.html.twig', array(
'form' => $form->createView()
));
}
Working now. Had to tweak a few things:
public function updateAction($id)
{
$request = $this->get('request');
if (is_null($id)) {
$postData = $request->get('testimonial');
$id = $postData['id'];
}
$em = $this->getDoctrine()->getEntityManager();
$testimonial = $em->getRepository('MyBundle:Testimonial')->find($id);
$form = $this->createForm(new TestimonialType(), $testimonial);
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
// perform some action, such as save the object to the database
$em->flush();
return $this->redirect($this->generateUrl('MyBundle_list_testimonials'));
}
}
return $this->render('MyBundle:Testimonial:update.html.twig', array(
'form' => $form->createView()
));
}
This is actually a native function of Symfony 2 :
You can generate automatically a CRUD controller from the command line (via doctrine:generate:crud) and the reuse the generated code.
Documentation here :
http://symfony.com/doc/current/bundles/SensioGeneratorBundle/commands/generate_doctrine_crud.html
A quick look at the auto-generated CRUD code by the Symfony's command generate:doctrine:crudshows the following source code for the edit action
/**
* Displays a form to edit an existing product entity.
*
* #Route("/{id}/edit", name="product_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Product $product)
{
$editForm = $this->createForm('AppBundle\Form\ProductType', $product);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('product_edit', array('id' => $product->getId()));
}
return $this->render('product/edit.html.twig', array(
'product' => $product,
'edit_form' => $editForm->createView(),
));
}
Note that a Doctrine entity is passed to the action instead of an id (string or integer). This will make an implicit parameter conversion and saves you from manually fetching the corresponding entity with the given id.
It is mentioned as best practice in the Symfony's documentation