Symfony 3.1 Post to Web Api - php

I'm trying to make a web API with Symfony but the POST method is not working. My code does not give any error.
Entity/Status:
<?php
namespace RestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
*
*
* #ORM\Table(name="status")
* #ORM\Entity
*/
class Status
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="nome", type="string", length=100, nullable=true)
*
* #Assert\NotBlank()
*/
private $nome;
}
?>
POST Function (Controller/StatusController):
public function postStatusAction(Request $request)
{
$user = new Status();
$form = $this->createForm(StatusType::class, $user);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return $user;
}
//var_dump($form->getErrors());
throw new HttpException(400, "Invalid data");
}
Form/StatusType:
<?php
/**
* Created by PhpStorm.
* Date: 10/08/16
* Time: 10:39
*/
namespace RestBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class StatusType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('nome', TextType::class);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'RestBundle\Entity\Status',
'csrf_protection' => false,
));
}
/**
* #return string
*/
public function getBlockPrefix()
{
return '';
}
}
Thanks.

A controller MUST return a Response :
public function postStatusAction(Request $request)
{
$user = new Status();
$form = $this->createForm(StatusType::class, $user);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return new Response($user->getUsername());
}
//var_dump($form->getErrors());
throw new HttpException(400, "Invalid data");
}

Related

How set null on field using symfony form

I'm using Symfony 2.7.39 and FOSRestBundle to build a simple REST api.
In this case I have this entity:
class DocumentacionMaquina extends Documentacion
{
/**
* #ORM\ManyToOne(targetEntity="PDCA\RestBundle\Entity\Archivo", cascade={"all"})
* #ORM\JoinColumn(name="declaracion_conformidad_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
*
* #Serializer\Groups({"details"})
*/
private $declaracion_conformidad;
// ...
}
ArchivoType:
class ArchivoType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('nombre');
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => 'PDCA\RestBundle\Entity\Archivo',
'csrf_protection' => false,
'cascade_validation' => true
));
}
}
DocumentacionMaquinaType:
class DocumentacionMaquinaType extends DocumentacionType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->add('maquina', 'entity', array(
'class' => 'PDCA\RestBundle\Entity\Maquina'
))
->add('declaracion_conformidad', new ArchivoType(),
array('required'=>false)
)
;
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => 'PDCA\RestBundle\Entity\DocumentacionMaquina',
'csrf_protection' => false,
'cascade_validation' => true
));
}
}
When I create a new DocumentacionMaquina entity launching a POST it works as expected:
{
"pdca_restbundle_documentacion":
{
"maquina": 752,
"declaracion_conformidad": {
"nombre": "fichero10"
}
}
But I would like to send a null (edit entity and create new entities with optional Archivo):
{
"pdca_restbundle_documentacion":
{
"maquina": 752,
"declaracion_conformidad": null
}
This is the validation error when I send a null value:
This value is not valid.
In my controller:
$entity = new DocumentacionMaquina();
$form = $this->createForm(new DocumentacionMaquinaType(), $entity, array('method' => $request->getMethod()));
$form->submit($request->get($form->getName()), false);
if !($form->isValid()) {
return $this->view(array('form' => $form), Codes::HTTP_BAD_REQUEST);
}
Any help will be appreciated.
EDIT #1
I created a simple entities with no inheritance for test but with equal result.
Entity Prueba:
namespace PDCA\RestBundle\Entity;
use Doctrine\ORM\Mapping AS ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
*/
class Prueba
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Serializer\Groups({"list", "details"})
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="PDCA\RestBundle\Entity\Hijo", cascade={"all"})
* #ORM\JoinColumn(name="marcado_ce_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
*
* #Serializer\Groups({"list", "details"})
*/
private $marcado_ce;
/**
* #ORM\Column(type="string", unique=false, length=255, nullable=false)
*
* #Serializer\Groups({"list", "details"})
*
* #Assert\NotBlank()
* #Assert\Length(max=255)
*/
private $nombre;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set marcado_ce
*
* #param \DateTime $marcado_ce
* #return Prueba
*/
public function setMarcadoCe(Hijo $marcado_ce = null)
{
$this->marcado_ce = $marcado_ce;
return $this;
}
/**
* Get marcado_ce
*
* #return \DateTime
*/
public function getMarcadoCe()
{
return $this->marcado_ce;
}
/**
* Set nombre
*
* #param string $nombre
* #return Prueba
*/
public function setNombre($nombre)
{
$this->nombre = $nombre;
return $this;
}
/**
* Get nombre
*
* #return string
*/
public function getNombre()
{
return $this->nombre;
}
}
Entity Hijo
namespace PDCA\RestBundle\Entity;
use Doctrine\ORM\Mapping AS ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
*
*/
class Hijo
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Serializer\Groups({"list", "details", "backend_creacion"})
*/
private $id;
/**
* #ORM\Column(type="string", unique=false, length=255, nullable=false)
*
* #Serializer\Groups({"list", "details"})
*
* #Assert\NotBlank()
* #Assert\Length(max=255)
*/
private $nombre;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set nombre
*
* #param string $nombre
* #return Artchivo
*/
public function setNombre($nombre)
{
$this->nombre = $nombre;
return $this;
}
/**
* Get nombre
*
* #return string
*/
public function getNombre()
{
return $this->nombre;
}
}
PruebaType and HijoType:
namespace PDCA\RestBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use PDCA\RestBundle\Entity\Prueba;
class PruebaType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nombre')
->add('marcado_ce', new HijoType(), array('required'=>false))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver) {
/** #var OptionResolver $resolver */
$this->configureOptions($resolver);
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => 'PDCA\RestBundle\Entity\Prueba',
'csrf_protection' => false
));
}
/**
* #return string
*/
public function getName()
{
return 'pdca_restbundle_prueba';
}
}
Type Hijo
namespace PDCA\RestBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class HijoType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nombre')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver) {
/** #var OptionResolver $resolver */
$this->configureOptions($resolver);
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => 'PDCA\RestBundle\Entity\Hijo',
'csrf_protection' => false
));
}
/**
* #return string
*/
public function getName()
{
return null;
}
}
Controller:
$entity = new Prueba();
$form = $this->createForm(new PruebaType(), $entity, array('method' => $request->getMethod()));
$form->submit($request->get($form->getName()), false);
if ($form->isValid()) {
//Persist $entity
}
return $this->view(array('form' => $form), Codes::HTTP_BAD_REQUEST);
When I do a POST with subentity, entities Prueba and Hija are persisted sucesfully:
{
"pdca_restbundle_prueba": {
"nombre": "nombre prueba",
"marcado_ce":
{
"nombre": "fichero4"
}
}
}
But when I use "marcado_ce": null:
{
"pdca_restbundle_prueba": {
"nombre": "nombre prueba2",
"marcado_ce": null
}
}
This fail with this message:
{
"code": 400,
"message": "Validation Failed",
"errors": {
"errors": [
"Este valor no es valido."
],
"children": {
"nombre": {},
"marcado_ce": {
"children": {
"nombre": {}
}
}
}
}
}
Debugging this problem, here is where code launch an exception resulting in validation failed:
File Form.php, line 570:
if ($this->config->getCompound()) {
if (null === $submittedData) {
$submittedData = array();
}
if (!is_array($submittedData)) {
**throw new TransformationFailedException('Compound forms expect an array or NULL on submission.');**
}
I uploaded a image with stack trace here:
https://i.stack.imgur.com/QzPDc.png
I edited Form.php code replacing Exception code with this (line 750). But this isn't a solution. I will try to understand what is the root cause, maybe I miss something with symfony-forms.
if (!is_array($submittedData)) {
$submittedData = array();
}

I can't upload multiple files in Symfony 3

I don't find solutions for several days to resolve my issue. I want to upload multiple images when I create a Post. My Post entity has a OneToMany relation with Image Entity. I use an embedded form in my PostType. It is a CollectionType of ImageType::class. To manage my upload functionality, I wrote an event listener called ImageUploadListener which injects my custom service called FileUploader.
This listener call a uploadFile function when a preUpdate and a prePersist event is handled. This function call my upload function from my FileUploader to move the image/file... to a target directory and to return a filename.
After that, I try to instantiate an Image Entity and to set the appropriate datas. But it doesn't work, only a file seems to be stored properly but with an additional entry unwanted in my db. (In my Image Table, for one image uploaded, I've got an entry with id 1 for example, a post_id set to NULL and file field set to /tmp/random number).
Please, could you help me ?
Post Entity
namespace UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Post
*
* #ORM\Table(name="post")
* #ORM\Entity(repositoryClass="UserBundle\Repository\PostRepository")
*/
class Post
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="Content", type="text")
*/
private $content;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="posts")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="Image", mappedBy="post", cascade={"persist", "remove"})
*/
private $images;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Post
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set content
*
* #param string $content
*
* #return Post
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set post
*
* #param \
*
* #return Post
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* Get post
*
* #return \?
*/
public function getUser()
{
return $this->user;
}
/**
* Constructor
*/
public function __construct()
{
$this->images = new ArrayCollection();
}
/**
* Add image
*
* #param \UserBundle\Entity\Image $image
*
* #return Post
*/
public function addImage(\UserBundle\Entity\Image $image)
{
$this->images[] = $image;
return $this;
}
/**
* Remove image
*
* #param \UserBundle\Entity\Image $image
*/
public function removeImage(\UserBundle\Entity\Image $image)
{
$this->images->removeElement($image);
}
/**
* Get images
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getImages()
{
return $this->images;
}
}
Image Entity
namespace UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="UserBundle\Entity\ImageRepository")
*/
class Image
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", nullable=true)
*/
private $file;
public function setFile($file)
{
$this->file = $file;
return $this;
}
public function getFile()
{
return $this->file;
}
/**
* #ORM\ManyToOne(targetEntity="Post", inversedBy="images")
* #ORM\JoinColumn(name="post_id", referencedColumnName="id")
*/
private $post;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set post
*
* #param \UserBundle\Entity\Post $post
*
* #return Image
*/
public function setPost(\UserBundle\Entity\Post $post = null)
{
$this->post = $post;
return $this;
}
/**
* Get post
*
* #return \UserBundle\Entity\Post
*/
public function getPost()
{
return $this->post;
}
}
PostType
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
//use Ivory\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use UserBundle\Entity\Post;
class PostType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('content')
->add('images', CollectionType::class, array (
'entry_type' => ImageType::class,
'entry_options' => array('label' => false),
'allow_add' => true,
'allow_delete' => true
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
//'data_class' => 'UserBundle\Entity\Post',
'data_class' => Post::class,
//'csrf_protection' => false
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'userbundle_post';
}
}
ImageType
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use UserBundle\Entity\Image;
class ImageType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class, [
'required' => false,
'data_class' => null,
])
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Image::class,
//'csrf_protection' => false
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'userbundle_image';
}
}
EDIT: ImageUploadListener
//src/UserBundle/EventListener/ImageUploadListener.php
namespace UserBundle\EventListener;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\File\File;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use UserBundle\Entity\User;
use UserBundle\Entity\Post;
use UserBundle\Entity\Image;
use UserBundle\Service\FileUploader;
class ImageUploadListener
{
private $uploader;
public function __construct(FileUploader $uploader)
{
$this->uploader = $uploader;
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
public function preUpdate(PreUpdateEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
private function uploadFile($entity)
{
if ($entity instanceof Post) {
$post = $entity;
$images = $post->getImages();
foreach ($images as $image) {
if ($image->getFile() instanceof UploadedFile) {
$imageName = $this->uploader->upload($image->getFile());
// to avoid persisting FileObject in DB
$post->removeImage($image);
$postImage = new Image();
$postImage->setFile($imageName);
$postImage->setPost($post);
$post->addImage($postImage);
}
}
}
return;
}
}
FileUploader
namespace UserBundle\Service;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class FileUploader
{
private $targetDir;
public function __construct($targetDir)
{
$this->targetDir = $targetDir;
}
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($this->getTargetDir(), $fileName);
return $fileName;
}
public function getTargetDir()
{
return $this->targetDir;
}
}

Symfony error when inserting new game: Entities passed to the choice field must be managed. Maybe persist them in the entity manager?

I'm trying to save a Game object with a Type object.
When I try to save with the form I get the following error:
Entities passed to the choice field must be managed. Maybe persist them in the entity manager?
One Game can have 1 Type while Type can have many Games.
I've managed to isolate the problem, it works fine when Game doesn't have a relation with Type so I think it has to do something with the Type object.
GameController:
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Game;
use AppBundle\Entity\Type;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
/**
* Game controller.
*
* #Route("game")
*/
class GameController extends Controller
{
/**
* Lists all game entities.
*
* #Route("/", name="game_index")
* #Method("GET")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$games = $em->getRepository('AppBundle:Game')->findAll();
return $this->render('game/index.html.twig', array(
'games' => $games,
));
}
/**
* Creates a new game entity.
*
* #Route("/new", name="game_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$type = new Type();
$game = new Game();
$game->setType($type);
$form = $this->createForm('AppBundle\Form\GameType', $game);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($type);
$em->persist($game);
$em->flush($game);
return $this->redirectToRoute('game_show', array('id' => $game->getId()));
}
return $this->render('game/new.html.twig', array(
'game' => $game,
'form' => $form->createView(),
));
}
/**
* Finds and displays a game entity.
*
* #Route("/{id}", name="game_show")
* #Method("GET")
*/
public function showAction(Game $game)
{
$deleteForm = $this->createDeleteForm($game);
return $this->render('game/show.html.twig', array(
'game' => $game,
'delete_form' => $deleteForm->createView(),
));
}
/**
* Displays a form to edit an existing game entity.
*
* #Route("/{id}/edit", name="game_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Game $game)
{
$deleteForm = $this->createDeleteForm($game);
$editForm = $this->createForm('AppBundle\Form\GameType', $game);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('game_edit', array('id' => $game->getId()));
}
return $this->render('game/edit.html.twig', array(
'game' => $game,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Deletes a game entity.
*
* #Route("/{id}", name="game_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, Game $game)
{
$form = $this->createDeleteForm($game);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($game);
$em->flush($game);
}
return $this->redirectToRoute('game_index');
}
/**
* Creates a form to delete a game entity.
*
* #param Game $game The game entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(Game $game)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('game_delete', array('id' => $game->getId())))
->setMethod('DELETE')
->getForm()
;
}
}
GameType.php:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class GameType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add( 'name', TextType::class);
$builder
->add( 'type', EntityType::class, [
'class' => 'AppBundle:Type',
'choice_label' => 'name',
'multiple' => false,
'expanded' => false
] );
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Game'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_game';
}
}
TypeType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TypeType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name') ;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Type'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_type';
}
}
Game.php:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Game
*
* #ORM\Table(name="game")
* #ORM\Entity(repositoryClass="AppBundle\Repository\GameRepository")
*/
class Game
{
/**
* #ORM\ManyToOne(targetEntity="Type", inversedBy="games")
* #ORM\JoinColumn(name="type_id", referencedColumnName="id")
*/
private $type;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Game
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getType()
{
return $this->type;
}
/**
* #param mixed $type
*/
public function setType($type)
{
$this->type = $type;
}
}
Type.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Type
*
* #ORM\Table(name="type")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TypeRepository")
*/
class Type
{
/**
* #ORM\OneToMany(targetEntity="Game", mappedBy="type")
*/
private $games;
public function __construct()
{
$this->games = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Type
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getGames()
{
return $this->games;
}
/**
* #param mixed $games
*/
public function setGames($games)
{
$this->games = $games;
}
public function addGame(Game $game)
{
$this->games->add($game);
$game->setType($this);
}
public function removeGame(Game $game)
{
$this->games->removeElement($game);
}
}
game/new.html.twig:
{% extends 'base.html.twig' %}
{% block content%}
<h1>Game creation</h1>
{{ form_start(form) }}
{{ form_widget(form.name) }}
{{ form_widget(form.type) }}
<input type="submit" value="Create" />
{{ form_end(form) }}
<ul>
<li>
Back to the list
</li>
</ul>
{% endblock %}
In the new action you should comment 3 lines
//$type = new Type();
$game = new Game();
//$game->setType($type);
$form = $this->createForm('AppBundle\Form\GameType', $game);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
//$em->persist($type);
$em->persist($game);
$em->flush($game);
The type will be set from framework because you are going to choose it from the dropdown

Edit an entity contains an file field

I have created an entity Article, with a field image. Eveything is working fine, but when I try to update and edit an article, I got the oldest fields like titre but not the image field. I got an empty image and I have to upload a new image. I want to get the oldest picture, btw, I have the path of the picture stored on the db.
This is my form ArticleType.php
<?php
namespace RoubBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
class ArticleType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('titre')
->add('image', 'file')
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'RoubBundle\Entity\Article'
));
}
}
This is my entity article.php:
<?php
namespace RoubBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="RoubBundle\Repository\ArticleRepository")
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string")
*
* #Assert\NotBlank(message="Veuillez mettre vos images.")
* #Assert\File(
* maxSize="5242880",
* mimeTypes = {
* "image/png",
* "image/jpeg",
* "image/jpg",
* "image/gif"
* }
* )
*/
private $image;
/**
* #ORM\Column(type="string")
* #var string
*/
private $titre;
public function getTitre()
{
return $this->titre;
}
public function setTitre($titre)
{
$this->titre = $titre;
return $this;
}
public function getImage()
{
return $this->image;
}
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
And this is in my ArticleController.php:
public function editAction(Request $request, Article $article)
{
$article->setImage(
new File($this->getParameter('images_directory').'/'.$article->getImage()));
$deleteForm = $this->createDeleteForm($article);
$editForm = $this->createForm('RoubBundle\Form\ArticleType', $article);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($article);
$em->flush();
return $this->redirectToRoute('article_edit', array('id' => $article->getId()));
}
return $this->render('RoubBundle:article:edit.html.twig', array(
'article' => $article,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
If someone can help me, and thank you a lot for being here, and read my problem until here <3
Keep a clone of your image somewhere at the start of your function, before handling the request :
public function editAction(Request $request, Article $article)
{
$originalImage = clone $article->getImage();
// .. rest of your function here
And when your form is valid, check if you have a new image, if not, set the old one back:
if ($editForm->isSubmitted() && $editForm->isValid()) {
if ($article->getImage()) {
$article->setImage($originalImage);
}
$em = $this->getDoctrine()->getManager();
$em->persist($article);
$em->flush();
return $this->redirectToRoute('article_edit', array('id' => $article->getId()));
}
It's pseudo code, but you get the idea. I'm using clone here in case you have a reference to an object, but if it's just a path you can simply assign the value.
EDIT : Clarify where stuff should go

Registering formular that doesn't flush. Symfony 2.5.6

I have e little problem. I followed this tutorial to create a register formular but it doesn't persist the entity. I don't understand why. It doesn't create any mistake, it just... doesn't flush.
Here is the tutorial:
http://symfony.com/fr/doc/2.5/cookbook/doctrine/registration_form.html
Here is the entity:
namespace theia\mainBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* UserMain
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="theia\mainBundle\Entity\UserMainRepository")
*/
class UserMain
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255, unique=true)
* #Assert\NotBlank()
* #Assert\Email()
*/
private $email;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255)
* #Assert\NotBlank()
*/
private $password;
/**
* #var array
*
* #ORM\Column(name="roles", type="array")
*/
private $roles;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set email
*
* #param string $email
* #return UserMain
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set password
*
* #param string $password
* #return UserMain
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set roles
*
* #param array $roles
* #return UserMain
*/
public function setRoles($roles)
{
$this->roles = $roles;
return $this;
}
/**
* Get roles
*
* #return array
*/
public function getRoles()
{
return $this->roles;
}
public function __construct()
{
$this->roles = [ 'ROLE_USER' ];
}
}
here is my "Security Controller":
namespace theia\mainBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\HttpFoundation\Request;
use theia\mainBundle\Form\Type\RegistrationType;
use theia\mainBundle\Form\Model\Registration;
class SecurityController extends Controller
{
public function loginAction(Request $request)
{
$session = $request->getSession();
if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(
SecurityContextInterface::AUTHENTIFICATION_ERROR
);
} elseif (null !== $session && $session->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
$error = $session->get(SecurityContextInterface::AUTHENTICATION_ERROR);
$session->remove(SecurityContextInterface::AUTHENTICATION_ERROR);
} else {
$error = null;
}
// last username entered by the user
$lastEmail = (null === $session) ? '' : $session->get(SecurityContextInterface::LAST_USERNAME);
return $this->render(
'theiamainBundle::security/login.html.twig',
array(
// last username entered by the user
'last_email' => $lastEmail,
'error' => $error,
)
);
}
public function loginCheckAction()
{
}
public function logoutAction()
{
}
public function registerAction()
{
$form = $this->createForm(new RegistrationType(), new Registration());
return $this->render('theiamainBundle:security:register.html.twig', array('form' => $form->createView()));
}
public function createAction()
{
$em = $this->getDoctrine()->getEntityManager();
$form = $this->createForm(new RegistrationType(), new Registration());
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
$registration = $form->getData();
$em->persist($registration->getUser());
$em->flush();
return $this->redirect('theiamainBundle::security/login.html.twig');
}
return $this->render('theiamainBundle:security:register.html.twig', array('form' => $form->createView()));
}
}
Here is what is in the directory Form:
My UserMainType:
namespace theia\mainBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class UsermainType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('email', 'email');
$builder->add('plainPassword', 'repeated', array(
'first_name' => 'password',
'second_name' => 'confirm',
'type' => 'password',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'theia\mainBundle\Entity\UserMain'
));
}
public function getName()
{
return 'user';
}
}
Here is the RegistrationType
namespace theia\mainBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('user', new UsermainType());
//$builder->add('terms', 'checkbox', array('property_path' => 'termsAccepted'));
}
public function getName()
{
return 'registration';
}
}
Here is Registration
namespace theia\mainBundle\Form\Model;
use Symfony\Component\Validator\Constraints as Assert;
use theia\mainBundle\Entity\UserMain;
class Registration
{
/**
* #Assert\Type(type="theia\mainBundle\Entity\User")
*/
protected $user;
/*
* #Assert\NotBlank()
* #Assert\True()
*
protected $termsAccepted;
*/
public function setUser(User $user)
{
$this->user = $user;
}
public function getUser()
{
return $this->user;
}
/*
public function getTermsAccepted()
{
return $this->termsAccepted;
}
public function setTermsAccepted($termsAccepted)
{
$this->termsAccepted = (Boolean) $termsAccepted;
}
*/
}
Thanks you for your helps
I am not quite sure but I think the problem is that you haven't set
'data_class' => 'theia\mainBundle\Entity\Registration'
In the setDefaultOptions of your RegistrationType, therefore the RegistrationType will not be mapped to an entity, therefore the UserMain cannot be persisted since it is used in the subform of RegistrationType.

Categories