Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Activos, CSS, imágenes, etc

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Si descargas el código del curso desde la página en la que estás viendo este vídeo, después de descomprimirlo, encontrarás un directorio start/ que contiene la misma aplicación nueva de Symfony 6 que hemos creado antes. En realidad no necesitas ese código, pero contiene un directorio extra llamado tutorial/, como el que tengo aquí. Este contiene algunos archivos que vamos a utilizar.

Así que hablemos de nuestro siguiente objetivo: hacer que este sitio parezca un sitio real... en lugar de parecer algo que he diseñado yo mismo. Y eso significa que necesitamos un verdadero diseño HTML que incluya algo de CSS.

Añadir un diseño y archivos CSS

Sabemos que nuestro archivo de diseño es base.html.twig... y también hay un archivobase.html.twig en el nuevo directorio tutorial/. Copia eso... pégalo en las plantillas, y anula el original.

Antes de ver eso, copia también los tres archivos .png y ponlos en el directorio public/... para que nuestros usuarios puedan acceder a ellos.

Muy bien. Abre el nuevo archivo base.html.twig. Aquí no hay nada especial. Traemos algunos archivos CSS externos de algunos CDN para Bootstrap y FontAwesome. Al final de este tutorial, refactorizaremos esto para que sea una forma más elegante de manejar el CSS... pero por ahora, esto funcionará bien.

Por lo demás, todo sigue estando codificado. Tenemos una navegación codificada, el mismo bloque body... y un pie de página codificado. Vamos a ver cómo queda. ¡Refresca y woo! Bueno, no es perfecto, pero es mejor

Añadir un archivo CSS personalizado

El directorio tutorial/ también contiene un archivo app.css con CSS personalizado. Para que esté disponible públicamente, de modo que el navegador de nuestro usuario pueda descargarlo, tiene que estar en algún lugar del directorio public/. Pero no importa dónde o cómo organices las cosas dentro.

Creemos un directorio styles/... y luego copiamos app.css... y lo pegamos allí.

De vuelta en base.html.twig, dirígete a la parte superior. Después de todos los archivos CSS externos, vamos a añadir una etiqueta de enlace para nuestro app.css. Así que <link rel="stylesheet"y href="". Como el directorio public/ es la raíz de nuestro documento, para referirse a un archivo CSS o de imagen allí, la ruta debe ser con respecto a ese directorio. Así que esto será /styles/app.css.

<!DOCTYPE html>
<html>
<head>
... lines 4 - 15
<link rel="stylesheet" href="/styles/app.css">
... lines 17 - 25
</head>
... lines 27 - 85
</html>

Vamos a comprobarlo. Actualiza ahora y... ¡aún mejor!

La función asset()

Quiero que te des cuenta de algo. Hasta ahora, Symfony no interviene para nada en cómo organizamos o utilizamos las imágenes o los archivos CSS. No. Nuestra configuración es muy sencilla: ponemos las cosas en el directorio public/... y luego nos referimos a ellas con sus rutas.

Pero, ¿tiene Symfony alguna función interesante para ayudar a trabajar con CSS y JavaScript? Por supuesto. Se llaman Webpack Encore y Stimulus. Y hablaremos de ambas hacia el final del tutorial.

Pero incluso en esta sencilla configuración -en la que sólo ponemos archivos en public/ y apuntamos a ellos- Symfony tiene una característica menor: la función asset().

Funciona así: en lugar de usar /styles/app.css, decimos {{ asset() }} y luego, entre comillas, movemos nuestra ruta allí... pero sin la apertura "/".

<!DOCTYPE html>
<html>
<head>
... lines 4 - 15
<link rel="stylesheet" href="{{ asset('styles/app.css') }}">
... lines 17 - 25
</head>
... lines 27 - 85
</html>

Así, la ruta sigue siendo relativa al directorio public/... sólo que no necesitas incluir el primer "/".

Antes de hablar de lo que hace esto... vamos a ver si funciona. Actualiza y... ¡no lo hace! Error:

Función desconocida: ¿te has olvidado de ejecutar composer require symfony/asset.

Sigo diciendo que Symfony empieza con algo pequeño... y luego vas instalando cosas a medida que las necesitas. ¡Aparentemente, esta función asset() viene de una parte de Symfony que aún no tenemos! Pero conseguirla es fácil. Copia este comando composer require, pásalo a tu terminal y ejecútalo:

composer require symfony/asset

Se trata de una instalación bastante sencilla: sólo descarga este paquete... y no hay recetas.

Pero cuando probamos la página ahora... ¡funciona! Comprueba el código fuente HTML. Interesante: la etiqueta link href sigue siendo, literalmente, /styles/app.css. ¡Es exactamente lo que teníamos antes! Entonces, ¿qué diablos hace esta función asset()?

La respuesta es... no mucho. Pero sigue siendo una buena idea utilizarla. La función asset() te ofrece dos características. En primer lugar, imagina que te despliegas en un subdirectorio de un dominio. Por ejemplo, la página de inicio vive en https://example.com/mixed-vinyl.

Si ese fuera el caso, para que nuestro CSS funcione, el href tendría que ser /mixed-vinyl/styles/app.css. En esta situación, la función asset()detectaría el subdirectorio automáticamente y añadiría ese prefijo por ti.

Lo segundo -y más importante- que hace la función asset() es permitirte cambiar fácilmente a una CDN más adelante. Como esta ruta pasa ahora por la funciónasset(), podríamos, a través de un archivo de configuración, decir:

¡Hey Symfony! Cuando emitas esta ruta, por favor ponle el prefijo de la URL a mi CDN.

Esto significa que, cuando carguemos la página, en lugar de href="/styles/app.css, sería algo como https://mycdn.com/styles/app.css.

Así que la función asset() puede que no haga nada que necesites hoy, pero siempre que hagas referencia a un archivo estático, ya sea un archivo CSS, un archivo JavaScript, una imagen, lo que sea, utiliza esta función.

De hecho, aquí arriba, estoy haciendo referencia a tres imágenes. Usemos asset: {{ asset()... ¡y entonces se autocompleta la ruta! ¡Gracias plugin Symfony! Repite esto para la segunda imagen... y la tercera.

<!DOCTYPE html>
<html>
<head>
... lines 4 - 6
<link rel="apple-touch-icon" sizes="180x180" href="{{ asset('apple-touch-icon.png') }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ asset('favicon-32x32.png') }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ asset('favicon-16x16.png') }}">
... lines 10 - 15
<link rel="stylesheet" href="{{ asset('styles/app.css') }}">
... lines 17 - 25
</head>
... lines 27 - 85
</html>

Sabemos que esto no supondrá ninguna diferencia hoy... podemos refrescar el código fuente HTML para ver las mismas rutas... pero estamos preparados para una CDN en el futuro.

HTML de la página de inicio y de navegación

¡Así que el diseño ahora se ve muy bien! Pero el contenido de nuestra página de inicio está... como colgando... con un aspecto raro... como yo en la escuela secundaria. De vuelta al directorio tutorial/, copia la plantilla de la página de inicio... y sobrescribe nuestro archivo original.

Ábrelo. Esto sigue extendiendo base.html.twig... y sigue anulando el bloquebody. Y además, tiene un montón de HTML completamente codificado. Vamos a ver qué aspecto tiene. Actualiza y... ¡se ve genial!

Excepto que... está 100% codificado. Vamos a arreglarlo. En la parte superior, aquí está el nombre de nuestro disco, imprime la variable title.

Y luego, abajo para las canciones... tenemos una larga lista de HTML codificado. Convirtamos esto en un bucle. Añade {% for track in tracks %} como teníamos antes. Y... al final, endfor.

Para los detalles de la canción, utiliza track.song... y track.artist. Y ahora podemos eliminar todas las canciones codificadas.

{% extends 'base.html.twig' %}
... lines 2 - 4
{% block body %}
<div class="container">
<h1 class="d-inline me-3">{{ title }}</h1> <i class="fas fa-edit"></i>
<div class="row mt-5">
... lines 9 - 34
<div class="col-12 col-md-8 ps-5">
<h2 class="mb-4">10 songs (30 minutes of 60 still available)</h2>
{% for track in tracks %}
<div class="song-list">
<div class="d-flex mb-3">
<a href="#">
<i class="fas fa-play me-3"></i>
</a>
<span class="song-details">{{ track.song }} - {{ track.artist }}</span>
<a href="#">
<i class="fas fa-bars mx-3"></i>
</a>
<a href="#">
<i class="fas fa-times"></i>
</a>
</div>
</div>
{% endfor %}
<button type="button" class="btn btn-success"><i class="fas fa-plus"></i> Add a song</button>
</div>
</div>
</div>
{% endblock %}

¡Genial! Vamos a probarlo. ¡Hey! ¡Está cobrando vida gente!

¡Falta una página más! La página /browse. Ya sabes lo que hay que hacer: copiar browse.html.twig, y pegar en nuestro directorio. Esto se parece mucho a la página de inicio: extiendebase.html.twig y anula el bloque body.

En VinylController, no hemos renderizado antes una plantilla... así que hagámoslo ahora: return $this->render('vinyl/browse.html.twig') y pasemos el género. Añade una variable para ello: $genre = y si tenemos un slug... utiliza nuestro elegante código de mayúsculas y minúsculas, si no, ponlo en null. Luego borra lo de $title... y pasagenre a Twig.

<?php
... lines 3 - 9
class VinylController extends AbstractController
{
... lines 12 - 29
#[Route('/browse/{slug}')]
public function browse(string $slug = null): Response
{
$genre = $slug ? u(str_replace('-', ' ', $slug))->title(true) : null;
return $this->render('vinyl/browse.html.twig', [
'genre' => $genre
]);
}
}

De vuelta a la plantilla, utiliza esto en el h1. En Twig, también podemos utilizar una sintaxis de fantasía. Así que si tenemos un genre, imprime genre, si no imprime All Genres.

{% extends 'base.html.twig' %}
{% block body %}
<div class="container">
<h1>Browse {{ genre ? genre : 'All Genres' }}</h1>
... lines 6 - 45
</div>
{% endblock %}

Es hora de probar. Dirígete a /browse: "Navega por todos los géneros" Y luego/browse/death-metal: Navega por el Death Metal. Amigos, ¡esto empieza a parecerse a un sitio real!

Excepto que estos enlaces en el navegador... ¡no van a ninguna parte! Vamos a arreglar eso aprendiendo a generar URLs. También vamos a conocer la mega-poderosa herramienta de línea de comandosbin/console.

Leave a comment!

15
Login or Register to join the conversation
Benanamen Avatar
Benanamen Avatar Benanamen | posted hace 5 meses | edited

After following the instructions exactly and copying the files from the tutorial folder I refreshed the page and nothing changed. After some digging I discovered the page was cached by Symphony and kept reading the old page.

FIX: Clear the cache by running command: php bin/console cache:clear

RESPONSE:

C:\laragon\www\mixed_vinyl>php bin/console cache:clear

 // Clearing the cache for the dev environment with debug true


 [OK] Cache for the "dev" environment (debug=true) was successfully cleared.
7 Reply

Hey Benanamen,

Yeah, clearing the cache should be the first option to try, in most cases it helps :) Thanks for this tip!

Cheers!

Reply

So the layout now looks great! But the content for our homepage is... just kind of hanging out... looking weird... like me in middle school

Golden 😆

1 Reply

For a quick revision on how to use filters you can add

<h2 class="mb-4">{{ tracks|length }} songs ({{ tracks|length * 10 / 2 }} minutes of {{ tracks|length * 10 }} still available)</h2>

in the homepage.html.twig file, which reads out the length of the tracklist

Reply

Hey @Brentspine ,

Thanks for sharing this tip :) Ideally, it would be great to know the exact length of each song so you could compute it with seconds approximations.

Cheers!

Reply
Lotfi Avatar

i just cloned the repo switched to the styling branch
i'm not seeing any tutorial folder

Reply

Hey Lotfi,

The repo is not the right way to get the correct course code as the course code is shared across many tutorials and also coded and separated with our internal tools. To get the course code you need to open any video page and in the right-up corner find the "Download" -> "Course Code" button. That's the only correct way to get the related course code for the specific tutorial with all the related materials.

Cheers!

1 Reply
Otacon Avatar

Hello can't get the asset path autocompletion , any idea please?

Reply

Hey Otacon,

do you have enabled the Symfony support plugin in your PhpStorm installation?

Reply
Otacon Avatar

Hi MolloKhan, yes its enabled...

Reply

Interesting... Try upgrading PhpStorm and the plugin. Then open the settings configuration and check all the options below the "code folding" label, and then click on the "Clear index" button. It should do the trick.

Cheers!

Reply
Peter Avatar

Hey Ryan,

Thanks for this excellent tutorial about assets.

One thing that I'm wondering how to use the asset helper (like you use in twig) in a controller, so that I could use it to generate a url to lets say $this->asset('assets/imgs/profilepic.jpg');

Any help would be much appreciated I've googled and googled but nothing really came up for Symfony 6.

Best wishes,
Peter

Reply

Hey Peter,

Excellent question! Well, if you use a Symfony plugin in PhpStorm - you can hold Cmd and click on the asset() Twig function - it should open the method responsible where you can see what service it uses behind the scene :) With this little trick you would figure out that asset() function uses Symfony\Component\Asset\Packages::getVersion() behind the scene, try to inject that service into your controller and call the getVersion() method on it for your path.

I hope this helps!

Cheers!

2 Reply
Benoit-L Avatar
Benoit-L Avatar Benoit-L | posted hace 1 año

Hi there, when I try to run the final solution just to see how it runs I have the following error : [Application] Apr 18 19:12:57 |CRITICA| REQUES Uncaught PHP Exception Twig\Error\RuntimeError: "An exception has been thrown during the rendering of a template ("Asset manifest file "C:\wamp64\www\code-symfony\
finish/public/build/manifest.json" does not exist.")." at C:\wamp64\www\code-symfony\finish\templates\base.html.twig line 7.
Any idea of what it could be ?

in the start version of the application, when I use the asset function it works (=it displays correctly) but I have the following error in the terminal : [Application] Apr 24 12:18:12 |ERROR | REQUES Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\NotFoundHttpException: "No route found for "GET https://127.0.0.1:8000/favicon.ico" (from "https://12
7.0.0.1:8000/")" at C:\wamp64\www\code-symfony\start\vendor\symfony\http-kernel\EventListener\RouterListener.php line 130

Reply

Hey Benoit L.!

I can answer both of these :).

Uncaught PHP Exception Twig\Error\RuntimeError: "An exception has been thrown during the rendering of a template ("Asset manifest file
"C:\wamp64\www\code-symfony\finish/public/build/manifest.json" does not exist.")."

This is my fault! It looks like I need to update the README instructions for the "finish" code. What you need to do is install and run Encore. The commands are:


yarn install
# or you can run
npm install

yarn watch
# of you can run
npm run watch

That will compile the Encore assets, which includes building this missing build/manifest.json file.

in the start version of the application, when I use the asset function it works (=it displays correctly) but I have the following error in the terminal :
[Application] Apr 24 12:18:12 |ERROR | REQUES Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\NotFoundHttpException:
"No route found for "GET https://127.0.0.1:8000/favicon.ico&quot;

This is ok, you can ignore this :). Our site, at the start, doesn't have a "favicon". But, your browser still looks for it: the first time you come to the site, it makes a request in the background to "/favicon.ico" to see if it's there. It's not, so the request is handed by Symfony, which returns a 404. That's all you're seeing, and it's nothing to be worried about. In a real app, you would create a favicon, because, of course, you want a nice looking icon in your browser when someone visits your site.

Cheers!

1 Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.0.2",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "symfony/asset": "6.0.*", // v6.0.3
        "symfony/console": "6.0.*", // v6.0.3
        "symfony/dotenv": "6.0.*", // v6.0.3
        "symfony/flex": "^2", // v2.1.5
        "symfony/framework-bundle": "6.0.*", // v6.0.4
        "symfony/monolog-bundle": "^3.0", // v3.7.1
        "symfony/runtime": "6.0.*", // v6.0.3
        "symfony/twig-bundle": "6.0.*", // v6.0.3
        "symfony/ux-turbo": "^2.0", // v2.0.1
        "symfony/webpack-encore-bundle": "^1.13", // v1.13.2
        "symfony/yaml": "6.0.*", // v6.0.3
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.8
        "twig/twig": "^2.12|^3.0" // v3.3.8
    },
    "require-dev": {
        "symfony/debug-bundle": "6.0.*", // v6.0.3
        "symfony/stopwatch": "6.0.*", // v6.0.3
        "symfony/web-profiler-bundle": "6.0.*" // v6.0.3
    }
}
userVoice