Gestió de la pujada de fitxers
En el nostre projecte de pel·lícules tenim definida la propistat pòster que emmagatzema el nom del fitxer del pòster d’una pel·lícula, en la següent sessió anem a implementar la gestió de fitxers sense utilitzar ningun bunble de tercer i poder emmagatzemar i mostrar els posters.
En la següent sessió aprendràs a:
- Usar i gestionar les pujades de fitxers.
- Crear missatges flash.
- Crear paràmetres globals de la aplicació.
La classe FileType
Per a generar el component de pujada de fitxers d’HTML5 Symfony disposa de la classe FileType
, que cal implementar en el formularis. Com que el camp no existirà en l’entitat, ja que en l’entitat emmagatzenem sols el nom del fiter cal indicar que serà un camp unmapped perquè Symfony no intente enllaçar-lo.
->add('poster', TextType::class,
['disabled' => true,
'required' => false])
->add('posterFile', FileType::class, [
'label' => 'Poster (image file)',
'required' => false,
'mapped' => false,
'constraints' => [
new Image(['maxSize' => '1024k',
'mimeTypes' => [
'image/jpeg',
'image/jpg',
],])
],
Com es pot observar hi ha dos camps que fan referència al pòster:
-
poster
de tipusTextType
que representa la propietat de la classeMovie
, hem indicat que estarà deshabilitat perquè s’emplenarà automàticament en el controlador. Altra opció possible era definir-lo com aHiddenType
però hem optat per un camp de text de sols lectura perquè és la forma més senzill per mostar el valor en el formulari d’edició. -
posterFile
que serà el quadre de text que ens permetrà pujar el fitxer, en el nostre cas una image menor de 1MB i en formatjpg
. Aquest camp té la propietatmapped
igual afalse
perquè no es troba en l’entitat.
Com que en el formulari de creació l’atribut poster
s’enviarà buit, hem de llevar la restricció de Assert\NotNull
de l’atribut.
Els controladors
Per a gestionar la pujada de fitxer cal afegir el següent codi:
/**
* @Route("/movies/create", name="movies_create")
*/
public function create(Request $request)
{
...
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$movie = $form->getData();
if ($posterFile = $form['posterFile']->getData()) {
$filename = bin2hex(random_bytes(6)) . '.' . $posterFile->guessExtension();
try {
$projectDir = $this->getParameter('kernel.project_dir');
$posterFile->move($projectDir . '/public/images/posters/', $filename);
$movie->setPoster($filename);
} catch (FileException $e) {
$this->addFlash(
'danger',
$e->getMessage()
);
return $this->redirectToRoute('admin');
}
}
...
return $this->redirectToRoute('admin');
}
return $this->render('movies_create.html.twig', array(
'form' => $form->createView()));
}
Obtenim en $posterFile el fitxer pujat des del formulari, recorda que tenia el nom posterFile
. Si existeix generem un nom aleatori amb la combinació de les funcions random_bytes
i bin2hex
. Amb el mètode guessExtension
obtenim l’extensió del fitxer.
Després obtenim el directori del projecte i afegim la ruta on volem guardar la imatge en el sistema de fitxers. Amb el mètode move
movem el fitxer de la seua ubicació temporal a la carpeta i el nom indicat.
Si tot ha funcionat correctament guardem el nom del fitxer a l’entitat $movie->setPoster($filename)
.
En cas d’haver una excepció escrivim un missatge flash i redigirim al panell de control.
Missatges flash
Els missatges flaix ja el coneixes, en Symfony, funcionen de la mateixa forma, s’escriuen en una variable de sessió i una vegada són llegits en la pàgina de destí desapareixen.
Per exemple:
$this->addFlash(
'success',
'Your changes were saved!'
);
El primer paràmetre pots qualsevol nom. En el nostre cas, com que usem bootstrap és convenient que la etiqueta coincidisca amb les classes preestarblertes: success
, danger
,warning
, info
, etc.
Canvis en la plantilla base
Per a integrar-los en la plantilla base pots usar aquest codi:
{# read and display all flash messages #}
{% for label, messages in app.flashes %}
{% for message in messages %}
<div class="alert alert-{{ label }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor %}
{% endfor %}
Com es pot observar la variable label
, serà la determinarà la classe que s’aplicarà.
Més informació en Flash Messages
Paràmeters globals
Com ja hem vist en les sessions introductòries podem crear paràmetres globlals per poder-los usar des de qualsevol lloc de l’aplicació.
Anem a aplicar-ho al directori on es guarden els posters. Definirem un paràmetre posters_directory
que indicarà quin és el directori.
parameters:
posters_directory: '%kernel.project_dir%/public/images/posters'
La variable d’entorn %kernel.project_dir%
representa el directori on tenim el nostre projecte Symfony.
Ara modificarem el controlador perquè agafe la ruta del paràmetre:
...
try {
$postersDir = $this->getParameter('posters_directory');
$posterFile->move($postersDir, $filename);
$movie->setPoster($filename);
} catch (FileException $e) {
...
També podem crear paràmetres globals per a usar-los en les plantilles de Twig. Per exemple, per a indicar la ruta pública dels pòsters:
# config/packages/twig.yaml
twig:
...
globals:
posters_public_directory: 'images/posters/'
I l’usariem així:
<img class="card-img-top" src="{{ asset(posters_public_directory ~ movie.poster) }}"
title="{{ movie.title }}"></a>