I can't upload multiple files in Symfony 3 - php

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

Related

Upload image in Symfony 4

I am trying to upload file in my Symfony Project, using this tutorial:
http://symfony.com/doc/current/controller/upload_file.html
Symfony shows no error, but when I upload the file nothing happens, and in my DB instead of having the filename.jpg I have this line C:\wamp64\tmp\php9294.tmp
Here is my Entity :
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\File;
/**
* #ORM\Entity(repositoryClass="App\Repository\ItemRepository")
*/
class Item
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string")
* #Assert\NotBlank(message="Upload your image")
* #Assert\File(mimeTypes={ "image/png", "image/jpeg" })
*/
private $image;
public function getImage()
{
return $this->image;
}
public function setImage($image)
{
$this->image = $image;
return $this;
}
My form :
namespace App\Form;
use App\Entity\Item;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
class ItemType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('image',FileType::class,array('data_class'=> null, 'label' => 'Image'))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Item::class,
));
}
}
My Controller :
namespace App\Controller;
use App\Entity\Item;
use App\Form\ItemType;
use App\Repository\ItemRepository;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* #Route("/item")
*/
class ItemController extends Controller
{
/**
* #Route("/", name="item_index", methods="GET")
*/
/**
* #Route("/new", name="item_new", methods="GET|POST")
*/
public function new(Request $request): Response
{
$item = new Item();
$form = $this->createForm(ItemType::class, $item);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$file = $item->getImage();
$fileName = $this->generateUniqueFileName().'.'.$file->guessExtension();
// moves the file to the directory where brochures are stored
$file->move(
$this->getParameter('brochures_directory'),
$fileName
);
$item->setImage($fileName);
return $this->redirectToRoute('item_index');
}
return $this->render('item/new.html.twig', [
'item' => $item,
'form' => $form->createView(),
]);
}
/**
* #return string
*/
private function generateUniqueFileName()
{
// md5() reduces the similarity of the file names generated by
// uniqid(), which is based on timestamps
return md5(uniqid());
}
}
I followed exactly the steps said in the tutorial but the image doesn't seems to be uploaded in my folder
I had the same problem, and I found out that it was a matter of permissions, if someone else got stuck in the part, give the folder all permissions and try it again.
Hope it could be helpful.
I recommand you to use the StofDoctrineExtensionsBundle: https://symfony.com/doc/master/bundles/StofDoctrineExtensionsBundle/index.html
The uploadable extensions is for you, is help you to upload file with some services.
In first, require it: composer require stof/doctrine-extensions-bundle
Now, configure it in the file config/packages/stof_doctrine_extensions.yml like that (considering the folder public/uploads is created with read/write permission):
stof_doctrine_extensions:
default_locale: en_US
uploadable:
default_file_path: "%kernel.root_dir%/../public/uploads"
mime_type_guesser_class: Stof\DoctrineExtensionsBundle\Uploadable\MimeTypeGuesserAdapter
default_file_info_class: Stof\DoctrineExtensionsBundle\Uploadable\UploadedFileInfo
orm:
default:
uploadable: true
Now, create the Entity/File.php for use it:
<?php
namespace App\Entity;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Overblog\GraphQLBundle\Annotation\GraphQLColumn;
/**
* #ORM\Entity
* #Gedmo\Uploadable(filenameGenerator="SHA1", allowOverwrite=true, appendNumber=true)
*/
class File
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #ORM\Column(name="path", type="string")
* #Gedmo\UploadableFilePath
*/
protected $path;
/**
* #ORM\Column(name="name", type="string")
* #Gedmo\UploadableFileName
*/
protected $name;
/**
* #ORM\Column(name="mime_type", type="string")
* #Gedmo\UploadableFileMimeType
*/
protected $mimeType;
/**
* #ORM\Column(name="size", type="decimal")
* #Gedmo\UploadableFileSize
*/
protected $size;
/**
* #GraphQLColumn(type="String")
*/
protected $publicPath;
/**
* #return string
*/
public function getPublicPath()
{
return '/uploads/'.$this->name;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getPath()
{
return $this->path;
}
/**
* #param mixed $path
*
* #return File
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*
* #return File
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* #return mixed
*/
public function getMimeType()
{
return $this->mimeType;
}
/**
* #param mixed $mimeType
*
* #return File
*/
public function setMimeType($mimeType)
{
$this->mimeType = $mimeType;
return $this;
}
/**
* #return mixed
*/
public function getSize()
{
return $this->size;
}
/**
* #param mixed $size
*
* #return File
*/
public function setSize($size)
{
$this->size = $size;
return $this;
}
}
The last step is to create a relation the File entity. After that, you can concretize the upload using the UploadManager like that:
<?php
public function uploadFile(\Stof\DoctrineExtensionsBundle\Uploadable\UploadableManager $uploadableManager)
{
$uploadableManager->markEntityToUpload($file, $fileInfo);
}
And just persist/flush it.

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' }}) }}

Symfony3 Trying to add record in join table

A User has many Games.
A Game can Belong to many Users.
I have a form with a list of games, I want the list of Games to be added to the current logged User.
When I submit the form nothing happens, I want at least 1 record to be added to users_games:
Update:
Added User entity
FormType addGameToUserType:
namespace AppBundle\Form;
use AppBundle\Entity\Game;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class addGameToUserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('game', EntityType::class, [
'class' => 'AppBundle:Game',
'choice_label' => function ($game) {
return $game->getName();
},
'multiple' => true,
'expanded' => false,
]);
}
public function configureOptions(OptionsResolver $resolver)
{
}
public function getBlockPrefix()
{
return 'app_bundleadd_game_to_user';
}
}
UserController addGameAction:
/**
* Adds game(s) to current user.
*
* #Route("user/game/add", name="game_add")
* #Method({"GET", "POST"})
*/
public function addGameAction(Request $request)
{
/** #var $form */
$form = $this->createForm('AppBundle\Form\addGameToUserType');
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
echo $form->get('game')->getData();
$em = $this->getDoctrine()->getManager();
/** #var $game */
$game = new Game();
$game->getId();
/** #var User $userObject */
$userObject = $this->getUser();
$user = $em->getRepository('AppBundle:User')
->find(['id' => $userObject->getId()]);
$game->addGameUser($user);
$em->persist($user);
$em->flush();
}
return $this->render('user/addGame.html.twig', array(
'form' => $form->createView()
));
}
Game entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Game
*
* #ORM\Table(name="game")
* #ORM\Entity(repositoryClass="AppBundle\Repository\GameRepository")
*/
class Game
{
/**
* #ORM\OneToMany(targetEntity="PlayLog", mappedBy="game")
* #ORM\OrderBy({"date" = "DESC"})
*
*/
private $playlogs;
private $users;
/**
* #return ArrayCollection
*/
public function getUsers()
{
return $this->users;
}
/**
* #param ArrayCollection $users
*/
public function setUsers($users)
{
$this->users = $users;
}
// private $categories;
public function __construct()
{
$this->playlogs = new ArrayCollection();
$this->users = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #Assert\NotBlank()
* #Assert\Length(
* min = "3",
* max = "100"
* )
* #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 getPlaylogs()
{
return $this->playlogs;
}
/**
* #param mixed $playlogs
*/
public function setPlaylogs($playlogs)
{
$this->playlogs = $playlogs;
}
public function addPlayLog(PlayLog $playlog)
{
$this->playlog->add($playlog);
$playlog->setPlayLogs($this);
}
public function addGameUser(User $user){
$this->users[] = $user;
}
}
User entity:
namespace AppBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\ManyToMany(targetEntity="Game")
*
* #ORM\JoinTable(name="users_games",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="game_id", referencedColumnName="id")}
* )
*/
private $games;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\PlayLog", mappedBy="user")
*
*/
private $playlogs;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
public function __construct()
{
$this->games = new ArrayCollection();
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #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);
$this->games[] = $game;
return $this;
}
public function removeGame(Game $game)
{
$this->games->removeElement($game);
}
/**
* #return mixed
*/
public function getPlaylogs()
{
return $this->playlogs;
}
/**
* #param mixed $playlogs
*/
public function setPlaylogs($playlogs)
{
$this->playlogs = $playlogs;
}
public function addPlayLog(PlayLog $playlog)
{
$this->playlog->add($playlog);
$playlog->setPlayLogs($this);
}
}

Symfony2 multiple file upload with add / remove method

I tried to upgrade my existing and working single file upload form to a multi file upload with add and remove function. Therefor I used this docu: http://symfony.com/doc/current/cookbook/form/form_collections.html#allowing-new-tags-with-the-prototype
My Entity is not called tags but attachments.
I can add via Javascript unlimited name + file fields. After adding, I get this exception:
FatalErrorException: Error: Call to a member function contains() on a non-object in /Applications/MAMP/htdocs/Seotool/src/Seotool/MainBundle/Entity/Attachments.php line 232
I also think, that not all in my code is correct while I tried to upgrade to multiple upload function. Maybe someone can help me get all fixed?
That's my current code, starting with my controller, who is generating the form.
/**
#Route(
* path = "/taskmanager/user/{user_id}",
* name = "taskmanager"
* )
* #Template()
*/
public function taskManagerAction($user_id, Request $request)
{
/* #### NEW TASK #### */
$task = new Task();
$attachment = new Attachments();
$task->getAttachments()->add($attachment);
$addTaskForm = $this->createForm(new TaskType(), $task);
$addTaskForm->handleRequest($request);
if($addTaskForm->isValid()):
/* User Object of current Users task list */
$userid = $this->getDoctrine()
->getRepository('SeotoolMainBundle:User')
->find($user_id);
$task->setDone(FALSE);
$task->setUser($userid);
$task->setDateCreated(new \DateTime());
$task->setDateDone(NULL);
$task->setTaskDeleted(FALSE);
$attachment->setTask($task);
$attachment->setUser($userid);
$attachment->upload();
$em = $this->getDoctrine()->getManager();
$em->persist($task);
$em->flush();
$this->log($user_id, $task->getId(), 'addTask');
return $this->redirect($this->generateUrl('taskmanager', array('user_id' => $user_id)));
endif;
.....
This are particular code snippets of my Task.php Entity
<?php
namespace Seotool\MainBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="tasks")
*/
class Task {
....
/**
* #ORM\OneToMany(targetEntity="Attachments", mappedBy="task", cascade={"persist"})
*/
protected $attachments;
....
/**
* Constructor
*/
public function __construct()
{
$this->log = new \Doctrine\Common\Collections\ArrayCollection();
$this->attachments = new \Doctrine\Common\Collections\ArrayCollection();
}
....
/**
* Add attachments
*
* #param \Seotool\MainBundle\Entity\Attachments $attachments
* #return Task
*/
public function addAttachment(\Seotool\MainBundle\Entity\Attachments $attachments)
{
$attachments->addTask($this);
$this->attachments->add($attachments);
}
/**
* Remove attachments
*
* #param \Seotool\MainBundle\Entity\Attachments $attachments
*/
public function removeAttachment(\Seotool\MainBundle\Entity\Attachments $attachments)
{
$this->attachments->removeElement($attachments);
}
/**
* Get attachments
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttachments()
{
return $this->attachments;
}
This is my Attachments.php Entity
<?php
namespace Seotool\MainBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="attachments")
*/
class Attachments {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
*/
public $name;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
public $path;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="attachments")
* #ORM\JoinColumn(name="user", referencedColumnName="id")
*/
protected $User;
/**
* #ORM\ManyToOne(targetEntity="Task", inversedBy="attachments")
* #ORM\JoinColumn(name="task", referencedColumnName="id")
*/
protected $task;
/**
* #Assert\File(maxSize="6000000")
*/
private $file;
/**
* Sets file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
public function getAbsolutePath()
{
return null === $this->path
? null
: $this->getUploadRootDir().'/'.$this->path;
}
public function getWebPath()
{
return null === $this->path
? null
: $this->getUploadDir().'/'.$this->path;
}
protected function getUploadRootDir()
{
// the absolute directory path where uploaded
// documents should be saved
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
// get rid of the __DIR__ so it doesn't screw up
// when displaying uploaded doc/image in the view.
return 'uploads';
}
public function upload()
{
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the
// target filename to move to
$this->getFile()->move(
$this->getUploadRootDir(),
$this->getFile()->getClientOriginalName()
);
// set the path property to the filename where you've saved the file
$this->path = $this->getFile()->getClientOriginalName();
// clean up the file property as you won't need it anymore
$this->file = null;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Attachments
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set path
*
* #param string $path
* #return Attachments
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
/**
* Set User
*
* #param \Seotool\MainBundle\Entity\User $user
* #return Attachments
*/
public function setUser(\Seotool\MainBundle\Entity\User $user = null)
{
$this->User = $user;
return $this;
}
/**
* Get User
*
* #return \Seotool\MainBundle\Entity\User
*/
public function getUser()
{
return $this->User;
}
/**
* Set Task
*
* #param \Seotool\MainBundle\Entity\Task $task
* #return Attachments
*/
public function setTask(\Seotool\MainBundle\Entity\Task $task = null)
{
$this->task = $task;
return $this;
}
/**
* Get Task
*
* #return \Seotool\MainBundle\Entity\Task
*/
public function getTask()
{
return $this->task;
}
public function addTask(Task $task)
{
if (!$this->task->contains($task)) {
$this->task->add($task);
}
}
}
This one is my TaskType.php Form Type Class:
<?php
namespace Seotool\MainBundle\Form\Type;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
....
$builder->add('attachments', 'collection', array(
'type' => new AttachmentsType(),
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'by_reference' => false,
));
This is my AttachmentsType.php Form Type Class:
<?php
namespace Seotool\MainBundle\Form\Type;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class AttachmentsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', 'text', array(
'label' => 'Dateiname',
'required' => false,
));
$builder->add('file', 'file', array(
'label' => false,
'required' => false,
"attr" => array(
"multiple" => "multiple",
)
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver
->setDefaults(array(
'data_class' => 'Seotool\MainBundle\Entity\Attachments'
));
}
public function getName()
{
return 'attachments';
}
}
Your problem is that you are using the wrong call when adding your attachment.
Your association between your attachment and task is a manyToOne meaning that attachments needed to be added to a task but a task needs to be set on an attachment.
To sort out your problem your should just change your add attachment call from
public function addAttachment(\Seotool\MainBundle\Entity\Attachments $attachments)
{
$attachments->addTask($this);
// this should be set not add
$this->attachments->add($attachments);
}
to
public function addAttachment(\Seotool\MainBundle\Entity\Attachments $attachment)
{
$attachment->setTask($this);
$this->attachments->add($attachment);
}

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