Error in file upload with sonata-admin bundle - php

I'm trying to execute a file upload with sonata admin bundle. But when I press create button in create form this error comes.
Expected argument of type "AppBundle\Entity\UploadedFile",
"Symfony\Component\HttpFoundation\File\UploadedFile" given
My entity file
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* BlogPost
*
* #ORM\Table()
* #ORM\Entity
*/
class BlogPost {
const SERVER_PATH_TO_IMAGE_FOLDER = '/uploads';
/**
* #var integer
*
* #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="body", type="text")
*/
private $body;
/**
* #var string
*
* #ORM\Column(name="filename", type="text")
*/
private $filename;
/**
* #var boolean
*
* #ORM\Column(name="draft", type="boolean")
*/
private $draft;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return BlogPost
*/
public function setTitle($title) {
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle() {
return $this->title;
}
/**
* Set body
*
* #param string $body
* #return BlogPost
*/
public function setBody($body) {
$this->body = $body;
return $this;
}
/**
* Get body
*
* #return string
*/
public function getBody() {
return $this->body;
}
/**
* Set draft
*
* #param boolean $draft
* #return BlogPost
*/
public function setDraft($draft) {
$this->draft = $draft;
return $this;
}
/**
* Get draft
*
* #return boolean
*/
public function getDraft() {
return $this->draft;
}
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="blogPosts")
*/
private $category;
public function setCategory(Category $category) {
$this->category = $category;
}
public function getCategory() {
return $this->category;
}
/**
* Unmapped property to handle file uploads
*/
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;
}
/**
* Manages the copying of the file to the relevant place on the server
*/
public function upload() {
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// we use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and target filename as params
$this->getFile()->move(
self::SERVER_PATH_TO_IMAGE_FOLDER, $this->getFile()->getClientOriginalName()
);
// set the path property to the filename where you've saved the file
$this->filename = $this->getFile()->getClientOriginalName();
// clean up the file property as you won't need it anymore
$this->setFile(null);
}
/**
* Lifecycle callback to upload the file to the server
*/
public function lifecycleFileUpload() {
$this->upload();
}
/**
* Updates the hash value to force the preUpdate and postUpdate events to fire
*/
public function refreshUpdated() {
$this->setUpdated(new \DateTime());
}
// ... the rest of your class lives under here, including the generated fields
// such as filename and updated
}
My admin file
<?php
// src/AppBundle/Admin/BlogPostAdmin.php
namespace AppBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
class BlogPostAdmin extends Admin {
protected function configureFormFields(FormMapper $formMapper) {
$formMapper
->tab('Post')
->with('Content', array('class' => 'col-md-9'))
->add('title', 'text')
->add('body', 'textarea')
->add('file', 'file', array(
'required' => false
))
->end()
->end()
->tab('Publish Options')
->with('Meta data', array('class' => 'col-md-3'))
->add('category', 'sonata_type_model', array(
'class' => 'AppBundle\Entity\Category',
'property' => 'name',
))
->end()
->end()
;
}
public function prePersist($image) {
$this->manageFileUpload($image);
}
public function preUpdate($image) {
$this->manageFileUpload($image);
}
private function manageFileUpload($image) {
if ($image->getFile()) {
$image->refreshUpdated();
}
}
protected function configureListFields(ListMapper $listMapper) {
$listMapper
->addIdentifier('title')
->add('category.name')
->add('draft')
;
}
public function toString($object) {
return $object instanceof BlogPost ? $object->getTitle() : 'Blog Post'; // shown in the breadcrumb on the create view
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper) {
$datagridMapper
->add('title')
->add('body')
->add('category', null, array(), 'entity', array(
'class' => 'AppBundle\Entity\Category',
'property' => 'name',
))
;
}
}

In your entity file, you forgot to include the specific use...
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile; // <--- HERE

Related

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

CollectionType entities issue

Well I'm trying to create a form which can handle multiple file uploads. So far so good. I have two tables image and product which has ManyToOne relationship. I'm using CollecitonType field for the images so I can dynamically add/remove fields on the fly. The problem is that I get this exception on form rendering
Neither the property "imageFile" nor one of the methods "getImageFile()", "imageFile()", "isImageFile()", "hasImageFile()", "__get()" exist and have public access in class "AppBundle\Entity\Product".
What I'm doing wrong?
AppBundle\Entity\Image
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* Product
*
* #ORM\Table()
* #ORM\Entity
*/
class Image
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Product")
*/
protected $product;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="product_image", fileNameProperty="imageName")
*
* #var File
*/
private $imageFile;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
private $imageName;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
private $updatedAt;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($image) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTime('now');
}
}
/**
* #return File
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* #param string $imageName
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
}
/**
* #return string
*/
public function getImageName()
{
return $this->imageName;
}
/**
* Set updatedAt
*
* #param \DateTime $updatedAt
*
* #return Image
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* #return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* Set product
*
* #param \AppBundle\Entity\Product $product
*
* #return Image
*/
public function setProduct(\AppBundle\Entity\Product $product = null)
{
$this->product = $product;
return $this;
}
/**
* Get product
*
* #return \AppBundle\Entity\Product
*/
public function getProduct()
{
return $this->product;
}
}
AppBundle\Entity\Product
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection ;
use Doctrine\ORM\Mapping as ORM;
/**
* Product
*
* #ORM\Table()
* #ORM\Entity
*/
class Product
{
/**
* #var integer
*
* #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 integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Product
*/
private $image;
/**
* Constructor
*/
public function __construct()
{
$this->image = new ArrayCollection;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
public function getImages()
{
return $this->images;
}
public function addImage(ImageInterface $image)
{
if (!$this->images->contains($image)) {
$this->images->add($image);
}
return $this;
}
public function removeImage(ImageInterface $image)
{
$this->images->remove($image);
return $this;
}
public function setImages(Collection $images)
{
$this->images = $images;
}
}
AppBundle\Form\ImageType
..............
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('imageFile', 'file')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Image',
'attr'=> array('novalidate'=>'novalidate')
));
}
.............
AppBundle\Form\ProductType
...........
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text')
->add('imageFile', 'collection', array(
'allow_add' => true,
'allow_delete' => true,
'required' => false,
'type' => new ImageType(),
'prototype' => true,
'attr' => array(
'class' => 'selection',
),
))
->add('upload', 'submit')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Product',
'attr'=> array('novalidate'=>'novalidate')
));
}
......
First :
In your Product class:
Your variable is named private $image, it should be private $images; with the 'S' for plurial.
Second :
In your product form builder, you ask for a field named ImageFile, but it doesn't exist in Product class (but exists on Image class), it should be images.

Symfony2: One To Many Relationship

Experiencing some serious problems in getting a OneToMany relationship to work. The error happens when trying to add "Children" to Beauties in Sonata Admin.
Using:
Symfony2
Doctrine
Sonata Admin
Been trying to follow the guide at: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-bidirectional
and
http://sonata-project.org/bundles/doctrine-orm-admin/2-2/doc/reference/form_field_definition.html
Error:
"CRITICAL - Uncaught PHP Exception
Doctrine\ORM\ORMInvalidArgumentException: "A new entity was found
through the relationship
'Beautify\BeautiesBundle\Entity\Beauty#children' that was not
configured to cascade persist operations for entity:
Beautify\BeautiesBundle\Entity\Children#000000000eff91d5000000017f8ca53b.
To solve this issue: Either explicitly call EntityManager#persist() on
this unknown entity or configure cascade persist this association in
the mapping for example #ManyToOne(..,cascade={"persist"}). If you
cannot find out which entity causes the problem implement
'Beautify\BeautiesBundle\Entity\Children#__toString()' to get a clue."
at
/Users/gmf/symfony/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php
line 91 "
.
.
BeautyAdmin.php
<?php
// src/Beautify/BeautiesBundle/Admin/BeautyAdmin.php
namespace Beautify\BeautiesBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Knp\Menu\ItemInterface as MenuItemInterface;
use Beautify\CategoryBundle\Entity\Category;
use Beautify\CategoryBundle\Entity\Children;
class BeautyAdmin extends Admin
{
// Fields to be shown on create/edit forms
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('General')
->add('name', 'text', array('label' => 'Name'))
->add('content', 'textarea', array('label' => 'Content'))
->add('imageFile', 'file', array('label' => 'Image file', 'required' => false))
->end()
->with('Category')
->add('category', 'sonata_type_model', array('expanded' => true))
->end()
->with('Children')
->add('children', 'sonata_type_collection', array(), array(
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position'
))
->end()
;
}
// Fields to be shown on filter forms
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('name')
->add('content')
->add('category')
;
}
// Fields to be shown on lists
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('name')
->add('content')
->add('category')
->add('imageName')
->add('_action', 'actions', array(
'actions' => array(
'show' => array(),
'edit' => array(),
'delete' => array(),
)
))
;
}
}
Beauty.php Entity
<?php
// src/Beautify/BeautiesBundle/Entity/Beauty.php
namespace Beautify\BeautiesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="beauties")
* #ORM\HasLifecycleCallbacks
* #Vich\Uploadable
*/
class Beauty
{
/**
* #ORM\OneToMany(targetEntity="Children", mappedBy="beauties")
**/
private $children;
public function __construct()
{
$this->children = new ArrayCollection();
}
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=100)
*/
protected $name;
/**
* #ORM\Column(type="text")
*/
protected $content;
/**
* #Vich\UploadableField(mapping="beauty_image", fileNameProperty="imageName")
* #var File $imageFile
*/
protected $imageFile;
/**
* #ORM\Column(type="string", length=255, nullable=true, name="image_name")
* #var string $imageName
*/
protected $imageName;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="beauties")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($image) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTime('now');
}
}
/**
* #return File
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* #param string $imageName
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
}
/**
* #return string
*/
public function getImageName()
{
return $this->imageName;
}
public function __toString()
{
return ($this->getName()) ? : '';
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Beauty
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set content
*
* #param string $content
* #return Beauty
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set category
*
* #param \Beautify\BeautiesBundle\Entity\Category $category
* #return Beauty
*/
public function setCategory(\Beautify\BeautiesBundle\Entity\Category $category = null)
{
$this->category = $category;
return $this;
}
/**
* Get category
*
* #return \Beautify\BeautiesBundle\Entity\Category
*/
public function getCategory()
{
return $this->category;
}
/**
* Set children
*
* #param \Beautify\BeautiesBundle\Entity\Children $children
* #return Beauty
*/
public function setChildren(\Beautify\BeautiesBundle\Entity\Children $children = null)
{
$this->children = $children;
return $this;
}
/**
* Get children
*
* #return \Beautify\BeautiesBundle\Entity\Children
*/
public function getChildren()
{
return $this->children;
}
/**
* Add children
*
* #param \Beautify\BeautiesBundle\Entity\Children $children
* #return Beauty
*/
public function addChild(\Beautify\BeautiesBundle\Entity\Children $children)
{
$this->children[] = $children;
return $this;
}
/**
* Remove children
*
* #param \Beautify\BeautiesBundle\Entity\Children $children
*/
public function removeChild(\Beautify\BeautiesBundle\Entity\Children $children)
{
$this->children->removeElement($children);
}
}
Children.php Entity
<?php
namespace Beautify\BeautiesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Children
*
* #ORM\Table(name="children")
* #ORM\Entity
*/
class Children
{
/**
* #ORM\ManyToOne(targetEntity="Beauty", inversedBy="children")
* #ORM\JoinColumn(name="beauty_id", referencedColumnName="id")
**/
private $beauties;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="image", type="string", length=255)
*/
private $image;
/**
* #var string
*
* #ORM\Column(name="content", type="text")
*/
private $content;
/**
* #var integer
*
* #ORM\Column(name="priority", type="integer")
*/
private $priority;
/**
* #var integer
*
* #ORM\Column(name="parent_id", type="integer")
*/
private $parentId;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set image
*
* #param string $image
* #return Children
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return string
*/
public function getImage()
{
return $this->image;
}
/**
* Set content
*
* #param string $content
* #return Children
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set priority
*
* #param integer $priority
* #return Children
*/
public function setPriority($priority)
{
$this->priority = $priority;
return $this;
}
/**
* Get priority
*
* #return integer
*/
public function getPriority()
{
return $this->priority;
}
/**
* Set parentId
*
* #param integer $parentId
* #return Children
*/
public function setParentId($parentId)
{
$this->parentId = $parentId;
return $this;
}
/**
* Get parentId
*
* #return integer
*/
public function getParentId()
{
return $this->parentId;
}
/**
* Set beauties
*
* #param \Beautify\BeautiesBundle\Entity\Beauty $beauties
* #return Children
*/
public function setBeauties(\Beautify\BeautiesBundle\Entity\Beauty $beauties = null)
{
$this->beauties = $beauties;
return $this;
}
/**
* Get beauties
*
* #return \Beautify\BeautiesBundle\Entity\Beauty
*/
public function getBeauties()
{
return $this->beauties;
}
}
Very thankful for any help...
The error says it all. You need to add cascade="persist" to the children property.
/**
* #ORM\OneToMany(targetEntity="Children", mappedBy="beauties", cascade={"persist"})
**/
private $children;
See http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-associations.html#transitive-persistence-cascade-operations

Symfony2 + SonataAdmin + VichUploaderBundle - Image Upload Field

Spent last days trying, as a newcomer to Symfony find a good solution on integrating a image upload field in an existing Entity in Sonata Admin. I've been Googling and following guides available but unfortunately I cannot figure it out.
Setup as specified in headline
Symfony2
SonataAdmin
VichUploaderBundle
Im currently stuck with the following error:
"Catchable Fatal Error: Argument 1 passed to Beautify\BeautiesBundle\Entity\Beauty::setImageFile() must be an instance of Symfony\Component\HttpFoundation\File\UploadedFile, instance of Symfony\Component\HttpFoundation\File\File given, called in /Users/gmf/symfony/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php on line 438 and defined in /Users/gmf/symfony/src/Beautify/BeautiesBundle/Entity/Beauty.php line 75 "
The image upload field renders correctly and I receive the error after submitting.
This is line 75:
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile
*/
public function setImageFile(UploadedFile $image = null)
{
$this->imageFile = $image;
if ($image) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTime('now');
}
}
Hers's my other closely related files:
ENTITY FILE:
<?php
// src/Beautify/BeautiesBundle/Entity/Beauty.php
namespace Beautify\BeautiesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ORM\Entity
* #ORM\Table(name="beauties")
* #ORM\HasLifecycleCallbacks
* #Vich\Uploadable
*/
class Beauty
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=100)
*/
protected $name;
/**
* #ORM\Column(type="text")
*/
protected $content;
/**
* #Vich\UploadableField(mapping="beauty_image", fileNameProperty="imageName")
* #var File $imageFile
*/
protected $imageFile;
/**
* #ORM\Column(type="string", length=255, nullable=true, name="image_name")
* #var string $imageName
*/
protected $imageName;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="beauties")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($image) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTime('now');
}
}
/**
* #return File
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* #param string $imageName
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
}
/**
* #return string
*/
public function getImageName()
{
return $this->imageName;
}
public function __toString()
{
return ($this->getName()) ? : '';
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Beauty
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set content
*
* #param string $content
* #return Beauty
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set category
*
* #param \Beautify\BeautiesBundle\Entity\Category $category
* #return Beauty
*/
public function setCategory(\Beautify\BeautiesBundle\Entity\Category $category = null)
{
$this->category = $category;
return $this;
}
/**
* Get category
*
* #return \Beautify\BeautiesBundle\Entity\Category
*/
public function getCategory()
{
return $this->category;
}
}
#ADMIN FILE
<?php
// src/Beautify/BeautiesBundle/Admin/BeautyAdmin.php
namespace Beautify\BeautiesBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Knp\Menu\ItemInterface as MenuItemInterface;
use Beautify\CategoryBundle\Entity\Category;
class BeautyAdmin extends Admin
{
// Fields to be shown on create/edit forms
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('General')
->add('name', 'text', array('label' => 'Name'))
->add('content', 'textarea', array('label' => 'Content'))
->add('imageFile', 'file', array('label' => 'Image file', 'required' => false))
->end()
->with('Category')
->add('category', 'sonata_type_model', array('expanded' => true))
->end()
;
}
// Fields to be shown on filter forms
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('name')
->add('content')
->add('category')
;
}
// Fields to be shown on lists
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('name')
->add('content')
->add('category')
->add('_action', 'actions', array(
'actions' => array(
'show' => array(),
'edit' => array(),
'delete' => array(),
)
))
;
}
}
**#SERVICES FILE**
report.listener:
class: Beautify\BeautiesBundle\Entity\Beauty
tags:
- { name: doctrine.event_listener, event: prePersist }
- { name: doctrine.event_listener, event: preUpdate }
**#CONFIG.YML**
# Your other blocks
vich_uploader:
db_driver: orm
mappings:
beauty_image:
uri_prefix: /images/beauties
upload_destination: %kernel.root_dir%/../images
Any help is very appreciated and will make me very happy
You missed the use statement for the File class: use Symfony\Component\HttpFoundation\File\File;
Add the use statement above and then change the method signature to this:
public function setImageFile(File $image = null)

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

Categories