See EDIT above
I think the issue is pretty simple to solve but I can't find any clear answer right now. I hope you might have an idea.
I'm trying to upload an image with sonata admin.
In my entity I have this field
/**
* @ORM\Column(type="string", length=2000)
* @Assert\File(mimeTypes={ "image/png", "image/jpeg" })
*/
private $image;
When I go to the sonata admin form view. The button Upload file is there and defined as below
$formMapper->add('image', FileType::class);
But when I try to send the form, I'm getting this error
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.
I hint this is due to the doctrine string type. But I don't think doctrine has a "File" type.
Thanks for your help.
EDIT:
Considering the link provided in comment, here is the new error
The current field
image
is not linked to an admin. Please create one for the target entity : ``
<?php
namespace App\Entity;
// src/Entity/Image.php
class Image{
const SERVER_PATH_TO_IMAGE_FOLDER = '/public/images';
/**
* Unmapped property to handle file uploads
*/
private $file;
/**
* @param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $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
}
In my ForumAdmin, now I have
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('name', TextType::class);
$formMapper->add('description', TextAreaType::class);
$formMapper->add('weight', IntegerType::class);
$formMapper->add('category', EntityType::class, [
'class' => Category::class,
'choice_label' => 'name',
]);
$formMapper
->add('image', AdminType::class)
;
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper->add('name');
$datagridMapper->add('category');
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper->addIdentifier('name');
$listMapper->addIdentifier('description');
$listMapper->addIdentifier('category');
$listMapper->addIdentifier('weight');
$listMapper->addIdentifier('createdAt');
$listMapper->addIdentifier('updatedAt');
$listMapper->addIdentifier('image');
}
public function prePersist($object)
{
parent::prePersist($object);
$this->manageEmbeddedImageAdmins($object);
if($object instanceof Forum){
$object->setCreatedAt(new \DateTime('now'));
$object->setUpdatedAt(new \DateTime('now'));
$object->setStatus("NO_NEW");
}
}
public function preUpdate($page)
{
$this->manageEmbeddedImageAdmins($page);
}
private function manageEmbeddedImageAdmins($page)
{
// Cycle through each field
foreach ($this->getFormFieldDescriptions() as $fieldName => $fieldDescription) {
// detect embedded Admins that manage Images
if ($fieldDescription->getType() === 'sonata_type_admin' &&
($associationMapping = $fieldDescription->getAssociationMapping()) &&
$associationMapping['targetEntity'] === 'App\Entity\Image'
) {
$getter = 'get'.$fieldName;
$setter = 'set'.$fieldName;
/** @var Image $image */
$image = $page->$getter();
if ($image) {
if ($image->getFile()) {
// update the Image to trigger file management
$image->refreshUpdated();
} elseif (!$image->getFile() && !$image->getFilename()) {
// prevent Sf/Sonata trying to create and persist an empty Image
$page->$setter(null);
}
}
}
}
}
I also have this ImageAdmin even if I don't see why it would be usefull
final class ImageAdmin extends AbstractAdmin{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('image', FileType::class);
}
public function prePersist($image)
{
$this->manageFileUpload($image);
}
public function preUpdate($image)
{
$this->manageFileUpload($image);
}
private function manageFileUpload($image)
{
if ($image->getFile()) {
$image->refreshUpdated();
}
}
// ...
}
The current field image is not linked to an admin. Please create one for the target entity : ``
To fix this error you need add the service in file sonata_admin.yaml like this:
services:
...
admin.image:
class: App\Admin\ImageAdmin
arguments: [~, App\Entity\File, ~]
tags:
- { name: sonata.admin, manager_type: orm, label: 'admin.image' }
public: true