(Sorry for my english)
I am a beginner with Symfony and I have a little problem in one of my functions.
I want to import data from an Excel or CSV file via a file input Symfony formType.
I haven't an error message. everything is working properly. I can send the dates but I do not know how to send the other data like First name, last name…
In this function, I use the PortPHP bundle.
/* -------- My entity -------- */
namespace App\Entity;
use Cocur\Slugify\Slugify;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* @ORM\Entity(repositoryClass="App\Repository\CollaboratorRepository")
* @UniqueEntity(
* fields={"emailAddress"},
* message="Un collaborateur avec cette adresse email existe déja."
* )
* @ORM\HasLifecycleCallbacks()
* @Vich\Uploadable()
*/
class Collaborator
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $firstName;
/**
* @ORM\Column(type="string", length=255)
*/
private $lastName;
/**
* @ORM\Column(type="string", length=255, unique=true)
* @Assert\Email(
* message = "L'adresse email {{ value }} n'est pas valide",
* )
*/
private $emailAddress;
/**
* @ORM\Column(type="string", length=255)
*/
private $phone;
/**
* @ORM\Column(type="string", length=255)
*/
private $cellphone;
/**
* @ORM\Column(type="datetime")
* @Assert\DateTime()
*/
private $birthday;
/**
* @ORM\Column(type="datetime")
* @Assert\DateTime()
*/
private $arrivalDate;
/**
* @ORM\Column(type="string", length=255)
*/
private $titleJob;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Agency", inversedBy="collaborators")
*/
private $agency;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Department", inversedBy="collaborators")
*/
private $department;
/**
* @ORM\Column(type="string", length=255)
*
*/
private $slug;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* @Vich\UploadableField(mapping="collaborator", fileNameProperty="imageName", size="imageSize")
*
* @var File
*/
private $imageFile;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*
* @var string
*/
private $imageName;
/**
* @ORM\Column(type="integer", nullable=true)
*
* @var integer
*/
private $imageSize;
/**
* @ORM\Column(type="datetime", nullable=true)
* @ORM\OrderBy({"fullName": "ASC"})
*
* @var \DateTime
*/
private $updatedAt;
/**
* @ORM\ManyToMany(targetEntity="App\Entity\Event", mappedBy="speaker")
*/
private $events;
public function __construct()
{
$this->events = new ArrayCollection();
}
/**
* Check before and after persistence in database
* @ORM\PrePersist()
* @ORM\PreUpdate()
*/
public function initializeSlug()
{
if (empty($this->slug)) {
$slugify = new Slugify();
$this->slug = $slugify->slugify($this->firstName . '-' . $this->lastName);
}
}
public function getFullName()
{
return $this->firstName . ' ' . $this->getLastName();
}
public function getLastName(): ?string
{
return $this->lastName;
}
public function setLastName(string $lastName): self
{
$this->lastName = $lastName;
return $this;
}
public function getId(): ?int
{
return $this->id;
}
public function getFirstName(): ?string
{
return $this->firstName;
}
public function setFirstName(string $firstName): self
{
$this->firstName = $firstName;
return $this;
}
public function getEmailAddress(): ?string
{
return $this->emailAddress;
}
public function setEmailAddress(string $emailAddress): self
{
$this->emailAddress = $emailAddress;
return $this;
}
public function getPhone(): ?string
{
return $this->phone;
}
public function setPhone(string $phone): self
{
$this->phone = $phone;
return $this;
}
public function getCellphone(): ?string
{
return $this->cellphone;
}
public function setCellphone(string $cellphone): self
{
$this->cellphone = $cellphone;
return $this;
}
public function getBirthday(): ?\DateTimeInterface
{
return $this->birthday;
}
public function setBirthday(\DateTimeInterface $birthday): self
{
$this->birthday = $birthday;
return $this;
}
public function getArrivalDate(): ?\DateTimeInterface
{
return $this->arrivalDate;
}
public function setArrivalDate(\DateTimeInterface $arrivalDate): self
{
$this->arrivalDate = $arrivalDate;
return $this;
}
public function getTitleJob(): ?string
{
return $this->titleJob;
}
public function setTitleJob(string $titleJob): self
{
$this->titleJob = $titleJob;
return $this;
}
public function getAgency(): ?Agency
{
return $this->agency;
}
public function setAgency(?Agency $agency): self
{
$this->agency = $agency;
return $this;
}
public function getDepartment(): ?Department
{
return $this->department;
}
public function setDepartment(?Department $department): self
{
$this->department = $department;
return $this;
}
public function getSlug(): ?string
{
return $this->slug;
}
public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
}
public function getImageFile(): ?File
{
return $this->imageFile;
}
/**
* 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 $imageFile
* @throws \Exception
*/
public function setImageFile(?File $imageFile = null): void
{
$this->imageFile = $imageFile;
if (null !== $imageFile) {
// 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 \DateTimeImmutable();
}
}
public function getImageName(): ?string
{
return $this->imageName;
}
public function setImageName(?string $imageName): void
{
$this->imageName = $imageName;
}
public function getImageSize(): ?int
{
return $this->imageSize;
}
public function setImageSize(?int $imageSize): void
{
$this->imageSize = $imageSize;
}
/**
* @return Collection|Event[]
*/
public function getEvents(): Collection
{
return $this->events;
}
public function addEvent(Event $event): self
{
if (!$this->events->contains($event)) {
$this->events[] = $event;
$event->addSpeaker($this);
}
return $this;
}
public function removeEvent(Event $event): self
{
if ($this->events->contains($event)) {
$this->events->removeElement($event);
$event->removeSpeaker($this);
}
return $this;
}
}
/* ------ My controller ------ */
* @Route("/collaborators", name="collaborator_index")
* @param CollaboratorRepository $collaboratorRepository
* @param Breadcrumbs $breadcrumbs
* @param Request $request
* @return Response
* @throws \Ddeboer\DataImport\Exception
*/
public function collaborator(
CollaboratorRepository $collaboratorRepository,
Breadcrumbs $breadcrumbs,
Request $request
): Response
{
// Add the breadcrumbs after the home
$breadcrumbs->addRouteItem("Liste des collaborateurs", "collaborator_index");
// Add the home breadcrumbs
$breadcrumbs->prependRouteItem("Dashboard", "dashboard_index");
$manager = $this->getDoctrine()->getManager();
$upload = new Import();
$form = $this->createForm(ImportType::class, $upload);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$file = new \SplFileObject('./../public/storage/import.csv');
$csvReader = new CsvReader($file);
$csvReader->setHeaderRowNumber(0);
/** @var TYPE_NAME $csvReader */
$workflow = new StepAggregator($csvReader);
$doctrineWriter = new DoctrineWriter($manager, 'App\Entity\Collaborator');
$workflow->addWriter($doctrineWriter);
// Add a converter to the workflow that will convert `birthday` and `arrivalDate`
$dateTimeConverter = new DateTimeValueConverter('d/m/Y');
$converterStep = new ValueConverterStep();
$converterStep
->add('birthday', $dateTimeConverter)
->add('arrivalDate', $dateTimeConverter);
$workflow->addStep($converterStep);
//Process the workflow
$workflow->process();
}
return $this->render('admin/collaborator/collaborator.html.twig', [
'collaborators' => $collaboratorRepository->findAll(),
'form' => $form->createView(),
]);
}```
/* ------ My formType ------ */
namespace App\Form;
use App\Entity\Import;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;
class ImportType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'file',
VichFileType::class, [
'label' => 'Fichier CSV',
'attr' => [
'placeholder' => 'Choisissez votre fichier'
]
]
)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Import::class,
]);
}
}
/* ------ My twig template ------ */
{% extends 'admin/base.html.twig' %}
{% block title %}Liste des collaborateurs{% endblock %}
{% block content %}
{{ parent() }}
<div class="padding">
{{ wo_render_breadcrumbs() }}
<h2 class="mb-4">Liste des collaborateurs</h2>
{% for label, messages in app.flashes %}
<div class="alert alert-{{ label }} fade show align-items-center" role="alert">
<i class="fa fa-check-circle alert-icon mr-3"></i>
{% for message in messages %}
{{ message | striptags }}
{% endfor %}
<button type="button" class="close" aria-label="Close" data-dismiss="alert">
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor %}
<div class="row mb-3">
<div class="col-sm-12">
<div class="d-inline-block">
<div class="my-2 dropdown">
<button type="button"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
class="btn w-sm btn-primary mr-2 dropdown-toggle"
data-placement="right"
>
Ajouter
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href="{{ path('collaborator_create') }}">Ajouter</a>
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#addModal">Importer</a>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog"
aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title" id="exampleModalCenterTitle">Importer par fichier</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{{ form_start(form) }}
<div class="modal-body">
<div class="form-group row">
<div class="col-sm-12">
<div>
<p>Utilisez le formulaire ci-dessous pour importer les données des
collaborateurs depuis le fichier CSV<br/>
<span class="small font-italic">Les données doivent être séparées par un point virgule (;)</span>
</p>
</div>
<div class="custom-file">
{{ form_row(form.file) }}
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Fermer
</button>
<button type="submit" class="btn btn-primary">Importer</button>
</div>
{{ form_end(form) }}
</div>
</div>
</div>
</div>
<div class="d-inline-block float-right">
<div class="my-2">
<a href="{{ path('export_index') }}" class="btn btn-primary">Exporter</a>
</div>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-theme v-middle display responsive nowrap" id="collaboratorTable"
style="width:100%">
<thead>
<tr>
<th>ID</th>
<th>Nom</th>
<th>Email</th>
<th>Téléphone</th>
<th>Poste</th>
<th>Agence</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for collaborator in collaborators %}
<tr class="" data-id="{{ collaborator.id }}">
<td>
<small class="text-muted">{{ collaborator.id }}</small>
</td>
<td>
<span class="item-amount text-sm">
{% if collaborator.imageName is not null %}
<img src="{{ vich_uploader_asset(collaborator, 'imageFile') }}"
class="rounded-circle mr-2" alt="" style="height: 25px"/>
{% else %}
<img src="{{ asset('images/collaborators/user-unknown.jpg') }}"
class="rounded-circle mr-2" alt="" style="height: 25px"/>
{% endif %}
{{ collaborator.fullName }}
</span>
</td>
<td><span class="item-amount text-sm">{{ collaborator.emailAddress }}</span></td>
<td><span class="item-amount text-sm">{{ collaborator.phone }}</span></td>
<td><span class="item-amount text-sm">{{ collaborator.titleJob }}</span></td>
<td><span class="item-amount text-sm">{{ collaborator.agency }}</span></td>
<td>
<div class="item-action dropdown text-right">
<a href="#" data-toggle="dropdown" class="text-muted"><i
class="i-con i-con-more"><i></i></i></a>
<div class="dropdown-menu dropdown-menu-right bg-dark" role="menu">
<a class="dropdown-item"
href="{{ path('collaborator_show', {'slug': collaborator.slug}) }}">Consulter </a>
<a class="dropdown-item"
href="{{ path('collaborator_edit', {'id': collaborator.id}) }}">Modifier</a>
<div class="dropdown-divider"></div>
{{ include('admin/partials/_delete_form.html.twig', {routeName: 'collaborator_delete', entityId: collaborator.id}, with_context = false) }} {# Import the delete confirmation modal JavaScript #}
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
{{ encore_entry_script_tags('app') }}
<script type="text/javascript">
$(document).ready(function () {
{# Import the custom JavaScript here #}
});
</script>
{% endblock %}