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
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 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;
}
}
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
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
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.