We are working in a project where we need upload several files when we created and edit an entity with EasyAdmin 3. We use vichupload Bundle to upload the files. We have tried two ways to upload files.
Firts, we have created the next two entities Expedient and Attachment.
`/**
* @ORM\Entity
* @Vich\Uploadable
*/
class Expedient
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $number;
/**
*@ORM\OneToMany(targetEntity="Attachment", mappedBy="expedient", cascade={"persist"})
*/
private $attachments;
public function getAttachments(): ?Collection
{
return $this->attachments;
}
public function addAttachment(?UploadedFile $attachment): self
{
$attachmentObject = new Attachment();
$attachmentObject->setFile($attachment);
$attachmentObject->setExpedient($this);
$this->attachments[] = $attachementObject;
return $this;
}
public function removeAttachment(Attachment $attachment): self
{
if ($this->attachments->removeElement($attachment)) {
if ($attachment->getExpedient() === $this) {
$attachment->setExpedient(null);
}
}
return $this;
}`
`/**
* @ORM\Entity
* @Vich\Uploadable
*/
class Attachment
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", nullable=true)
*/
private $fileName;
/**
* @Vich\UploadableField(mapping="model", fileNameProperty="fileName")
*/
private $file;
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private $fileUpdatedAt;
/**
*@ORM\ManyToOne(targetEntity="Expedient", inversedBy="attachments")
*/
private $expedient;
public function __construct()
{
$this->fileUpdatedAt = new \DateTime();
}
public function getId(): ?int
{
return $this->id;
}
public function setId(int $id): self
{
$this->id = $id;
return $this;
}
public function setFileName(?string $fileName): self
{
$this->fileName = $fileName;
return $this;
}
public function getFileName(): ?string
{
return $this->fileName;
}
public function setFile(?File $file = null): self
{
$this->file = $file;
if (null !== $file) {
$this->fileUpdatedAt = new \DateTime();
}
return $this;
}
public function getFile(): ?File
{
return $this->file;
}
public function getFileUpdatedAt(): ?\DateTime
{
return $this->fileUpdatedAt;
}
public function setFileUpdatedAt(?\DateTime $fileUpdatedAt): self
{
$this->fileUpdatedAt = $fileUpdatedAt;
return $this;
}
public function getExpedient()
{
return $this->expedient;
}
public function setExpedient($expedient): void
{
$this->expedient = $expedient;
}
}`
At the first way, we include this code in ExpedientCrudController.php. For this way, we can upload several files when created a new entity Expedient. But when we want edit entity Expedient, symfony throw an exception =>NotUploaddableException
The class "Doctrine\ORM\PersistentCollection" is not uploadable. If you use annotations to configure VichUploaderBundle,
you probably just forgot to add @Vich\Uploadable on top of your entity. If you don't use annotations, check
that the configuration files are in the right place. In both cases, clearing the cache can also solve the issue.
public function configureFields(string $pageName): iterable
{
...
$fields[] = CollectionField::new('attachments', $this->translator->trans('pages.rule.common.image_file'))
->setEntryType(VichFileType::class);
...
}
The second way, We have both entities too, Expedient and Attachment. We have created a custom field (CollectionFileField) and have add in configure fields to field attachments in Expedient entity.
ExpedientCrudContoller.php
public function configureFields(string $pageName): iterable
{
...
$fields[] = CollectionFileField::new('attachments', $this->translator->trans('pages.rule.common.image_file'));
...
}
CollectionFileField have a custom Form type (CollectionFileType ) . CollectionFileType is a Collection of VichFileType.
class CollectionFileField implements FieldInterface
{
use FieldTrait;
public static function new(string $propertyName, ?string $label = null)
{
return (new self())
->setProperty($propertyName)
->setTemplatePath('')
->setLabel($label)
->addCssClass('field-collection-file')
->setFormType(CollectionFileType::class)
->setFormTypeOption('block_name' , 'custom_collection_file')
;
}
}
class CollectionFileType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Expedient::class,
]);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('attachments', CollectionType::class, [
'entry_type' => FileType::class,
'allow_add' => true,
'allow_delete' => true,
'required' => false,
'label'=>false,
'by_reference' => false,
'disabled' => false
]);
}
}
class FileType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Attachment::class,
]);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file',VichFileType::class)
;
}
}
By this way, we rewrite template to use to upload several files.
public function configureCrud(Crud $crud): Crud
{
return $crud
->setFormThemes(['admin/form.html.twig', '@EasyAdmin/crud/form_theme.html.twig'])
;
}
admin/form.html.twig (template)
---------------------
<ul id="attachment-fields-list"
data-prototype="{{ form_widget(form.attachments.vars.prototype)|e }}"
data-widget-tags="{{ '<li></li>'|e }}"
data-widget-counter="{{ form.attachments|length }}">
{% for field in form.attachments %}
<li>
{{ form_errors(field) }}
{{ form_widget(field) }}
</li>
{% endfor %}
</ul>
<button type="button"
class="add-another-collection"
data-list-selector="#attachment-fields-list">Add another
</button>
uploadFiles.js (JS to add new input file)
--------------
jQuery(document).ready(function () {
jQuery('.add-another-collection').click(function (e) {
var list = $("#attachment-fields-list");
var counter = list.data('widget-counter') | list.children().length;
var newWidget = list.attr('data-prototype');
newWidget = newWidget.replace(/__name__/g, counter);
counter++;
list.data('widget-counter', counter);
var newElem = jQuery(list.attr('data-widget-tags')).html(newWidget);
newElem.appendTo(list);
newElem.append('<a href="#" class="remove-tag" style="color: darkred">remove</a>');
$('.remove-tag').click(function(e) {
e.preventDefault();
$(this).parent().remove();
});
});
});
For this way, when we want to save a new/edit entity, the validator of form show an error : This value is not valid.
In resumen, with the first way, we can create new expedient with attachments, but we can´t edit entity. For the second way, we can´t create or edit.
We are working in a project where we need upload several files when we created and edit an entity with EasyAdmin 3. We use vichupload Bundle to upload the files. We have tried two ways to upload files.
Firts, we have created the next two entities Expedient and Attachment.
At the first way, we include this code in ExpedientCrudController.php. For this way, we can upload several files when created a new entity Expedient. But when we want edit entity Expedient, symfony throw an exception =>NotUploaddableException
The class "Doctrine\ORM\PersistentCollection" is not uploadable. If you use annotations to configure VichUploaderBundle, you probably just forgot to add
@Vich\Uploadable
on top of your entity. If you don't use annotations, check that the configuration files are in the right place. In both cases, clearing the cache can also solve the issue.The second way, We have both entities too, Expedient and Attachment. We have created a custom field (CollectionFileField) and have add in configure fields to field attachments in Expedient entity.
CollectionFileField have a custom Form type (CollectionFileType ) . CollectionFileType is a Collection of VichFileType.
By this way, we rewrite template to use to upload several files.
For this way, when we want to save a new/edit entity, the validator of form show an error : This value is not valid.
In resumen, with the first way, we can create new expedient with attachments, but we can´t edit entity. For the second way, we can´t create or edit.
Any idea on this problem?