Symfony2 save embedding collection form - php

I got problem to save embedded collection form field. Let say I have program, and the program have many levels. In my form type:
ProgramType.php
<?php
namespace Eifl\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ProgramType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id','text',array(
'label'=>'Program Code',
))
->add('program','text',array(
'label'=>'Program Name',
))
->add('levels','collection', array(
'type'=>new LevelType(),
'allow_add'=>true,
'allow_delete'=>true,
'by_reference' => false,
))
->add('save','submit', array(
'label'=>'Save',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Eifl\AdminBundle\Entity\Program',
));
}
public function getName()
{
return 'Program';
}
}
LevelType.php
<?php
namespace Eifl\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LevelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('level','text',array(
'label'=>'Level Name',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Eifl\AdminBundle\Entity\Level',
));
}
public function getName()
{
return 'level';
}
}
I made the form that can add a new collection level field using jquery, following the tutorial from this link, and the result of my form is same with the picture in this link.
Now my problem is I can't save more than 2 levels. The collection form only save 2 levels.
e.g. I want to save a program name as English, and after that I add 3 levels(basic,intermediate,advanced). The levels that saved successfully to database only the first level(basic) and the last level(Advanced). All I want is to save all the levels to database. Can anyone help me?
In addition, here is part of my controller:
DefaultController.php
public function programAction(Request $request)
{
$program = new Program();
$level = new Level();
$program->getLevels()->add($level);
$newProgram = $this->createForm(new ProgramType(),$program);
$newProgram->handleRequest($request);
if($newProgram->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($program);
$em->flush();
return $this->redirect($this->generateUrl('eifl_admin_program'));
}
return $this->render('EiflAdminBundle:Default:program.html.twig',array("new_program_form"=>$newProgram->createView()));
}
UPDATE
Here is my entity class.
Program.php
<?php
namespace Eifl\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Program
*
* #ORM\Entity
* #ORM\Table(name="tbl_program")
*/
Class Program
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="string")
*/
protected $id;
/**
*#ORM\Column(name="program_name",type="string")
*/
public $program;
/**
* #ORM\OneToMany(targetEntity="Eifl\AdminBundle\Entity\Level", mappedBy="program", cascade={"remove","persist"})
*/
public $levels;
public function __construct(){
$this->levels = new ArrayCollection();
}
/**
* #param string $id
* #return string
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Add Level
*
* #param \Eifl\AdminBundle\Entity\Level $levels
* #return Program
*/
public function addLevels(\Eifl\AdminBundle\Entity\Level $level) {
$this->levels[] = $level;
$levels->setProgram($this);
return $this;
}
/**
* set levels
*
* #param \Doctrine\Common\Collections\ArrayCollection $levels
* #return levels
*/
public function setLevels(\Doctrine\Common\Collections\ArrayCollection $levels) {
foreach ($levels as $level) {
$level->setProgram($this);
}
$this->levels = $levels;
}
public function removeLevels(\Eifl\AdminBundle\Entity\Level $level)
{
$this->levels->removeElement($level);
}
/**
* #param string $program
* #return string
*/
public function setProgram($program)
{
$this->program = $program;
return $this;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return string
*/
public function getProgram()
{
return $this->program;
}
/**
* #return string
*/
public function getLevels()
{
return $this->levels;
}
}
Level.php
<?php
namespace Eifl\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Level
*
* #ORM\Entity
* #ORM\Table(name="tbl_level")
*/
Class Level
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*#ORM\Column(name="level",type="string")
*/
public $level;
/**
* #ORM\ManyToOne(targetEntity="Eifl\AdminBundle\Entity\Program", inversedBy="levels")
* #ORM\JoinColumn(name="program", referencedColumnName="id")
*/
public $program;
/**
* #param string $id
* #return string
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* #param string $level
* #return string
*/
public function setLevel($level)
{
$this->level = $level;
return $this;
}
/**
* #param mixed $program
* #return string
*/
public function setProgram(\Eifl\AdminBundle\Entity\Program $program)
{
$this->program = $program;
return $this;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return string
*/
public function getLevel()
{
return $this->level;
}
/**
* #return string
*/
public function getProgram()
{
return $this->program;
}
}

Check if you put addLevel and setLevel in your program entity
/**
* Add Level
*
* #param \Eifl\AdminBundle\Entity\Level $levels
* #return Program
*/
public function addLevel(\Eifl\AdminBundle\Entity\Level $levels) {
$this->levels[] = $levles;
$level->setProgram($this);
return $this;
}
/**
* set levels
*
* #param \Doctrine\Common\Collections\ArrayCollection $levels
* #return levels
*/
public function setLevels(\Doctrine\Common\Collections\ArrayCollection $levels) {
foreach ($levles as $level) {
$level->setPogram($this);
}
$this->levels = $levels;
}

remove this code from the controllor
$level = new Level();
$program->getLevels()->add($level);

Your getter and setter $levels in Entity Program are confused...
make :
public function addLevel(Level $level) { //without s
$this->levels[] = $level;
}
public function removeLevel(Level $level) //without s
{
$this->levels->removeElement($level);
}
public function getLevels()
{
return $this->levels;
}
Remove otherspublic function setLevels, public function removeLevels, etc
Like Adene Choubi said, $program->getLevels()->add($level); make no sense too.
Don't know if it's the only trouble but begin with that.

Related

try upload file with symfony

I am trying to upload a file with symfony. But I have an error when I send data formulaire to controller. Variable file is null and function move file don't working.
code entity (par uplaod):
<?php
namespace AppBundle\Entity;
use AppBundle\Model\CompanyInterface;
use AppBundle\Model\RecruiterInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Company.
*
* #ORM\Table(name="company")
* #ORM\Entity(repositoryClass="AppBundle\Repository\CompanyRepository")
*/
class Company implements CompanyInterface
{
/**
* #var string
*
* #ORM\Column(name="id", type="guid")
* #ORM\Id
* #ORM\GeneratedValue(strategy="UUID")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="description", type="text")
*/
private $description;
/**
* #var array
*
* #ORM\Column(name="activities", type="array")
*/
private $activities;
/**
* #var RecruiterInterface
*
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Recruiter", cascade={"persist"})
*/
private $recruiter;
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255,nullable=true)
*/
private $logo;
/**
* #Assert\File(maxSize="6000000")
*/
private $file;
/**
* {#inheritdoc}
*/
public static function create(string $name, RecruiterInterface $recruiter, string $description = '', array $activities = []): CompanyInterface
{
return new self($name, $recruiter, $description, $activities);
}
/**
* {#inheritdoc}
*/
public function update(string $name, string $description, array $activities): CompanyInterface
{
$this->name = $name;
$this->description = $description;
$this->activities = $activities;
return $this;
}
/**
* {#inheritdoc}
*/
public function updateName(string $name): CompanyInterface
{
$this->name = $name;
return $this;
}
/**
* {#inheritdoc}
*/
public function getId(): string
{
return $this->id;
}
/**
* {#inheritdoc}
*/
public function getName(): ?string
{
return $this->name;
}
/**
* function setName.
*
* #param string $name
*
* #return string
*/
public function setName(string $name)
{
$this->name = $name;
return $this;
}
/**
* function setDescription.
*
* #param string $description
*
* #return string
*/
public function setDescription(string $description)
{
$this->description = $description;
return $this;
}
/**
* function setActivities.
*
* #param string $activities
*
* #return string
*/
public function setActivities(string $activities)
{
$this->activities = $activities;
return $this;
}
/**
* {#inheritdoc}
*/
public function getDescription(): ?string
{
return $this->description;
}
/**
* {#inheritdoc}
*/
public function getActivities()
{
return $this->activities;
}
/**
* {#inheritdoc}
*/
public function getRecruiter(): ?RecruiterInterface
{
return $this->recruiter;
}
/**
* #return string
*/
public function __toString()
{
return $this->name;
}
/**
* {#inheritdoc}
*/
public function updateLogo(string $logo): CompanyInterface
{
$this->logo = $logo;
return $this;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
/**
* Sets file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
}
/**
* Set logo.
*
* #param string $logo
*
* #return Company
*/
public function setLogo($logo)
{
$this->logo = $logo;
return $this;
}
/**
* Get logo.
*
* #return string
*/
public function getLogo()
{
return $this->logo;
}
/**
* function upload.
*/
public function upload()
{
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
$this->file->move($this->getUploadRootDir(),$this->path);
// set the path property to the filename where you've saved the file
$this->logo = $this->getFile()->getClientOriginalName();
// clean up the file property as you won't need it anymore
//$this->file = null;
}
/**
* function getUploadRootDir.
*/
protected function getUploadRootDir()
{
return __DIR__.'/../../../web/uploads';
}
}
code formType(content variable file):
<?php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
/**
* Class CompanyType.
*/
class CompanyType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('description')
->add('activities', TextType::class)
->add('file', FileType::class)
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Company',
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return '';
}
}
code other FormTyp(I use form imbrique):
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Entity\Recruiter;
/**
* Class RecruiterType.
*/
class RecruiterType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder->remove('username');
$builder->remove('plainPassword');
$builder
->add('email')
->add('lastname')
->add('firstname')
->add('phone')
->add('civility', ChoiceType::class, array(
'choices' => ['Mr' => 'Mr', 'Mrs' => 'Mrs'], 'expanded' => true, 'multiple' => false,
))
->add('company', CompanyType::class)
;
}
/**
* function get Parent Form.
*
* #return string
*/
public function getParent()
{
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
}
/**
* name for this form.
*
* #return string
*/
public function getBlockPrefix()
{
return '';
}
/**
* name for this form.
*
* #return string
*/
public function getName()
{
return $this->getBlockPrefix();
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Recruiter::class,
'allow_extra_fields' => true,
'csrf_protection' => false,
));
}
}
function post (persist object):
public function post(array $parameters, bool $submitted = true, array $options = [])
{
/** #var Recruiter $recruiter */
$recruiter = $this->formHandler->handle(
$this->userManager->createRecruiter(),
$parameters,
Request::METHOD_POST,
$submitted,
$options
);
if (false === $submitted) {
return $recruiter;
}
$recruiter->submit($parameters);
if ($recruiter->isSubmitted() && $recruiter->isValid()) {
$recruiter->getData()->setCompany($recruiter->getData()->getCompany());
$recruiter->getData()->getCompany()->upload();
$this->em->persist($recruiter->getData());
$this->em->flush();
$event = new FormEvent($recruiter, $this->request);
$this->eventDispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event);
return $recruiter->getData();
}
return $recruiter;
}
code controller :
<?php
namespace AppBundle\Controller\Recruiter;
use FOS\UserBundle\Controller\RegistrationController as BaseController;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use AppBundle\Handler\RecruiterHandler;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use AppBundle\Entity\Recruiter;
use Symfony\Component\HttpFoundation\RedirectResponse;
use AppBundle\Util\UserManagerr;
/**
* Class RegistrationRecruiterController.
*/
class RegistrationRecruiterController extends BaseController
{
/**
* #Template()
* #Route("/register", name="register_recruiter")
*
* #param Request $request
*
* #return array|\Symfony\Component\HttpFoundation\RedirectResponse
*/
public function registerAction(Request $request)
{
try {
$user = new UserManagerr();
$dispatcher = $this->get('event_dispatcher');
$user = $user->createRecruiter();
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$recruiter = $this->getHandler()->post($request->request->all(), $request->isMethod(Request::METHOD_POST));
if ($recruiter instanceof Recruiter) {
$url = $this->generateUrl('fos_user_registration_check_email');
$response = new RedirectResponse($url);
return $response;
}
} catch (InvalidFormException $e) {
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_FAILURE, $event);
return [
'form' => $e->getForm()->createView(),
'edit' => false,
'event' => $event->getResponse(),
];
}
return ['form' => $recruiter->createView()];
}
/**
* #return RecruiterHandler
*/
public function getHandler()
{
return $this->get(RecruiterHandler::class);
}
}
I use form imbriqu (form in other form) but when I send form I have all variable from object but variable file has null and function move doesn't work.
How to resolve this error please?
Thanks in advance for any help.
Change this
public function post(array $parameters, bool $submitted = true, array $options = [])
For
public function post(Request $request, bool $submitted = true, array $options = [])
[...]
$recruiter->submit($request);
[...]
Your issue is probably in the for tag, which you should declare it like:
{{ form_start(form, {'method': 'post', 'action': '', 'attr': {'class': '', 'novalidate': 'novalidate' }}) }}

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

Doctrine: referenced object always null OneToMany / ManyToOne cascade

Please tell me why the value of field 'pool_id' is always NULL in 'Question' Table?
(To add fiels 'Question' in the form used JS)
Pool.php
use Acme\ExamBundle\Entity\Question;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Pool
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $name;
/**
* #ORM\OneToMany(targetEntity="Question", mappedBy="pools",cascade={"persist"})
*/
protected $questions;
public function __construct()
{
$this->questions = new ArrayCollection();
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getQuestions()
{
return $this->questions;
}
/**
* Add question
*
* #param \Acme\ExamBundle\Entity\Question $question
* #return Pool
*/
public function addQuestion(Question $question)
{
/*$question->addPool($this);
$this->questions->add($question);*/
if (!$this->questions->contains($question)) {
$this->questions->add($question);
}
}
/**
* Remove question
*
* #param \Acme\ExamBundle\Entity\Question $question
*/
public function removeQuestion(Question $question)
{
$this->questions->removeElement($question);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set questions
*
* #param \Acme\ExamBundle\Entity\Question $questions
* #return Pool
*/
public function setQuestions(Question $questions = null)
{
$this->questions = $questions;
return $this;
}
}
Question.php
/**
* #ORM\Entity
*/
class Question
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="text")
*/
protected $description;
/**
* #ORM\Column(type="string")
*/
protected $variants;
/**
* #ORM\Column(type="string")
*/
protected $ansver;
/**
* #ORM\ManyToOne(targetEntity="Pool", inversedBy="questions")
* #ORM\JoinColumn(name="pool_Id", referencedColumnName="id")
*/
protected $pools;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set description
*
* #param string $description
* #return Question
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set variants
*
* #param string $variants
* #return Question
*/
public function setVariants($variants='ok')
{
$this->variants = $variants;
return $this;
}
/**
* Get variants
*
* #return string
*/
public function getVariants()
{
return $this->variants;
}
/**
* Set ansver
*
* #param string $ansver
* #return Question
*/
public function setAnsver($ansver)
{
$this->ansver = $ansver;
return $this;
}
/**
* Get ansver
*
* #return string
*/
public function getAnsver()
{
return $this->ansver;
}
/**
* Set pools
*
* #param string $pools
* #return Question
*/
public function setPools($pools)
{
$this->pools = $pools;
return $this;
}
/**
* Get pools
*
* #return string
*/
public function getPools()
{
return $this->pools;
}
}
QuestionType
class QuestionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('description');
$builder->add('variants');
$builder->add('ansver');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\ExamBundle\Entity\Question',
));
}
public function getName()
{
return 'question';
}
}
PoolType
class PoolType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder->add('questions', 'collection', array(
'type' => new QuestionType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\ExamBundle\Entity\Pool',
));
}
public function getName()
{
return 'pool';
}
}
Thanks for your thoughts. Hope you can help me.
class Pool
/**
* #ORM\OneToMany(targetEntity="Question", mappedBy="pools",cascade={"persist"})
*/
protected $questions;
class Question
/**
* #ORM\ManyToOne(targetEntity="Pool", inversedBy="questions")
* #ORM\JoinColumn(name="pool_Id", referencedColumnName="id")
*/
protected $pools;
Using $pools to represent one pool is a bit confusing. You do understand that a OneToMany relation implies that a question can belong to at most one pool?
The reason that Question::pool_id is null is that you are not calling Question::setPools. Fix this by tweaking Pool:addQuestion
class Pool
public function addQuestion(Question $question)
{
/*$question->addPool($this);
$this->questions->add($question);*/
if (!$this->questions->contains($question)) {
$this->questions->add($question);
$question->setPools($this); // ADD THIS LINE
}
}
Looks like you almost had it in there. Maybe you changed the relation from a ManyToMany?
Also, change the column name from pool_Id to pool_id. SQL is not always consistent when dealing with mixed case names.
Finally, might want to change Ansver to Answer

Symfony2 Embedded Form and MongoDB issue

I try to embed a form type into another form type :
$builder->add('geolocation', new GeolocationType());
However when I try and bind the request to the form
if($request->getMethod() == 'POST') {
$form->bindRequest($request);
}
I get the error
Catchable Fatal Error: Argument 1 passed to Company\StoreBundle\Document\Place::setGeolocation()
must be an instance of Company\StoreBundle\Document\Geolocation, array given, called in
/var/www/Company/vendor/symfony/src/Symfony/Component/Form/Util/PropertyPath.php on line 392 and
defined in /var/www/Company/src/Company/StoreBundle/Document/Place.php line 43
The data_class is set either for the PlaceType and the GeolocationType
Here is my code :
Place.php
namespace Company\StoreBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document(collection="places")
*/
class Place
{
/**
* #MongoDB\Id
*/
private $id;
/**
* #MongoDB\String
*/
private $title;
/**
* #MongoDB\EmbedOne(targetDocument="Geolocation")
*/
private $geolocation;
/**
* Get id
*
* #return id $id
*/
public function getId()
{
return $this->id;
}
/**
* Set geolocation
*
* #param Company\StoreBundle\Document\Geolocation $geolocation
*/
public function setGeolocation(\Company\StoreBundle\Document\Geolocation $geolocation)
{
$this->geolocation = $geolocation;
}
/**
* Get geolocation
*
* #return Company\StoreBundle\Document\Geolocation $geolocation
*/
public function getGeolocation()
{
return $this->geolocation;
}
/**
* Set title
*
* #param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Get title
*
* #return string $title
*/
public function getTitle()
{
return $this->title;
}
}
Geolocation.php
namespace Company\StoreBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\EmbeddedDocument
*/
class Geolocation
{
/**
* #MongoDB\Id
*/
private $id;
/**
* #MongoDB\Float
*/
private $latitude;
/**
* #MongoDB\Float
*/
private $longitude;
/**
* Get id
*
* #return id $id
*/
public function getId()
{
return $this->id;
}
/**
* Set latitude
*
* #param float $latitude
*/
public function setLatitude($latitude)
{
$this->latitude = $latitude;
}
/**
* Get latitude
*
* #return float $latitude
*/
public function getLatitude()
{
return $this->latitude;
}
/**
* Set longitude
*
* #param float $longitude
*/
public function setLongitude($longitude)
{
$this->longitude = $longitude;
}
/**
* Get longitude
*
* #return float $longitude
*/
public function getLongitude()
{
return $this->longitude;
}
}
PlaceType.php
namespace Company\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Company\AdminBundle\Form\Type\GeolocationType;
class PlaceType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('title');
$builder->add('geolocation', new GeolocationType());
}
public function getName()
{
return 'place';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Company\StoreBundle\Document\Place',
);
}
}
GeolocationType.php
namespace Company\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class GeolocationType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('latitude')
->add('longitude');
}
public function getDefaultOptions(array $options)
{
return array('data_class' => 'Company\StoreBundle\Document\Geolocation');
}
public function getName()
{
return 'geolocation';
}
}
Thank you all
I think you should not use dependency in your setter method in this case...
So instead of:
setGeolocation(\Company\StoreBundle\Document\Geolocation $geolocation)
Try this and see if it works correctly:
setGeolocation($geolocation)

The file "" does not exist after editing the form with file upload

I'm trying to do file upload for my Task entity. I working with two sources:
http://symfony.com/doc/3.4/controller/upload_file.html and Symfony2 file upload step by step but I can't figure out how to keep uploaded file during editing.
I'm not sure if I implemented right the part:
My form was complaining when I tried to edit any Task:
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.
So I modified getter getBrochure() in my Task entity which I want to expand of PDF file. Please, see below my getBrochure method This is code of my entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\File;
/**
* Task
*
* #ORM\Table(name="task")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TaskRepository")
*/
class Task
{
/**
* #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;
/**
* #var \DateTime
*
* #ORM\Column(name="datetime", type="datetime")
*/
private $datetime;
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="tasks")
* #ORM\JoinTable(name="categories_tasks")
*/
private $categories;
/**
* #ORM\Column(type="text")
*/
private $description;
/**
* #ORM\Column(type="string")
*
* #Assert\File(mimeTypes={ "application/pdf" })
*/
private $brochure;
public function __construct() {
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Task
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set datetime
*
* #param \DateTime $datetime
*
* #return Task
*/
public function setDatetime($datetime)
{
$this->datetime = $datetime;
return $this;
}
/**
* Get datetime
*
* #return \DateTime
*/
public function getDatetime()
{
return $this->datetime;
}
public function getCategories()
{
return $this->categories;
}
public function setCategories(Category $categories)
{
$this->categories = $categories;
}
public function getDescription()
{
return $this->description;
}
public function setDescription($description)
{
$this->description = $description;
}
public function getBrochure()
{
//return $this->brochure;
return new File($this->brochure);
}
public function setBrochure($brochure)
{
$this->brochure = $brochure;
return $this;
}
public function __toString() {
return $this->name;
}
}
?>
The result is that I can load edit page, but the field of file upload is empty, there is no information that I uploaded any file. I'm not sure if there should be any information but I see in database that the filename is there and also in web folder there is uploaded file. When I change anything in Task and save of file is cleared and when I try to launch edit page I see:
The file "" does not exis
What is clear for me, because file column for this Task was cleared. So, how to keep file during editing when I don't want to upload new file?
This is my TaskType
<?php
namespace AppBundle\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\FileType;
class TaskType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name')->add('datetime')->add('categories')
->add('description', 'Ivory\CKEditorBundle\Form\Type\CKEditorType', array())
->add('brochure', FileType::class, array('label' => 'Broszurka (PDF)', 'required' => false));
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Task'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_task';
}
}
and this is my TaskController (only edit action)
/**
* Displays a form to edit an existing task entity.
*
* #Route("/{id}/edit", name="task_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Task $task)
{
$deleteForm = $this->createDeleteForm($task);
$editForm = $this->createForm('AppBundle\Form\TaskType', $task);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('task_edit', array('id' => $task->getId()));
}
return $this->render('task/edit.html.twig', array(
'task' => $task,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
Task Entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\File;
/**
* Task
*
* #ORM\Table(name="task")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TaskRepository")
*/
class Task
{
/**
* #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;
/**
* #var \DateTime
*
* #ORM\Column(name="datetime", type="datetime")
*/
private $datetime;
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="tasks")
* #ORM\JoinTable(name="categories_tasks")
*/
private $categories;
/**
* #ORM\Column(type="text")
*/
private $description;
/**
* #ORM\Column(type="string")
*
* #Assert\File(mimeTypes={ "application/pdf" })
*/
private $brochure;
public function __construct() {
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Task
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set datetime
*
* #param \DateTime $datetime
*
* #return Task
*/
public function setDatetime($datetime)
{
$this->datetime = $datetime;
return $this;
}
/**
* Get datetime
*
* #return \DateTime
*/
public function getDatetime()
{
return $this->datetime;
}
public function getCategories()
{
return $this->categories;
}
public function setCategories(Category $categories)
{
$this->categories = $categories;
}
public function getDescription()
{
return $this->description;
}
public function setDescription($description)
{
$this->description = $description;
}
public function getBrochure()
{
//return $this->brochure;
return new File($this->brochure);
}
public function setBrochure($brochure)
{
$this->brochure = $brochure;
return $this;
}
public function __toString() {
return $this->name;
}
}
?>
You should check the file and then,If the user did not select the file,Select the file name from the database
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$form->handleRequest($request);
$TaskRepo=$em->getRepository('AppBundle:Task');
$Taskdata = $TaskRepo->find($id);///id task
$Taskdata->setName($form->get('name')->getData());
$Taskdata->setDescription($form->get('description(')->getData());
$Taskdata->setDatetime(new \DateTime('now'));
if($form->get('brochure')->getData() != ""){////Check the file selection status
$file2 = $form->get('brochure')->getData();
$fileName2 = md5(uniqid()).'.'.$file2->guessExtension();
$file2->move(
$this->getParameter('brochures_directory'), $fileName2);
$Taskdata->setBrochure($fileName2);
}
$em->flush();
}
OK I found the problem few seconds after I wrote comment and added Task entity code. In getBrochure method I tried to create File object for every Task instance even it hasn't any brochure so the solution is to use:
public function getBrochure()
{
return $this->brochure;
//return new File($this->brochure);
}

Categories