Nette Assets

Fatigué de gérer manuellement les fichiers statiques dans vos applications web ? Oubliez le codage en dur des chemins, la gestion de l'invalidation du cache ou les soucis de versioning des fichiers. Nette Assets transforme la façon dont vous travaillez avec les images, les feuilles de style, les scripts et autres ressources statiques.

  • Le versioning intelligent garantit que les navigateurs chargent toujours les derniers fichiers
  • La détection automatique des types de fichiers et des dimensions
  • L'intégration transparente de Latte avec des balises intuitives
  • Une architecture flexible supportant les systèmes de fichiers, les CDN et Vite
  • Le chargement paresseux pour des performances optimales

Pourquoi Nette Assets ?

Travailler avec des fichiers statiques signifie souvent un code répétitif et sujet aux erreurs. Vous construisez manuellement des URL, ajoutez des paramètres de version pour le cache busting et gérez différemment les différents types de fichiers. Cela conduit à un code comme :

<img src="/images/logo.png?v=1699123456" width="200" height="100" alt="Logo">
<link rel="stylesheet" href="/css/style.css?v=2">

Avec Nette Assets, toute cette complexité disparaît :

{* Tout est automatisé - URL, versioning, dimensions *}
<img n:asset="images/logo.png">
<link n:asset="css/style.css">

{* Ou simplement *}
{asset 'css/style.css'}

C'est tout ! La bibliothèque automatiquement :

  • Ajoute des paramètres de version basés sur l'heure de modification du fichier
  • Détecte les dimensions des images et les inclut dans le HTML
  • Génère l'élément HTML correct pour chaque type de fichier
  • Gère les environnements de développement et de production

Installation

Installez Nette Assets en utilisant Composer :

composer require nette/assets

Il nécessite PHP 8.1 ou supérieur et fonctionne parfaitement avec Nette Framework, mais peut également être utilisé de manière autonome.

Premiers pas

Nette Assets fonctionne dès la première utilisation sans aucune configuration. Placez vos fichiers statiques dans le répertoire www/assets/ et commencez à les utiliser :

{* Affiche une image avec des dimensions automatiques *}
{asset 'logo.png'}

{* Inclut une feuille de style avec versioning *}
{asset 'style.css'}

{* Charge un module JavaScript *}
{asset 'app.js'}

Pour plus de contrôle sur le HTML généré, utilisez l'attribut n:asset ou la fonction asset().

Comment ça marche

Nette Assets est construit autour de trois concepts fondamentaux qui le rendent puissant et simple à utiliser :

Assets – Vos fichiers rendus intelligents

Un asset représente tout fichier statique dans votre application. Chaque fichier devient un objet avec des propriétés en lecture seule utiles :

$image = $assets->getAsset('photo.jpg');
echo $image->url;      // '/assets/photo.jpg?v=1699123456'
echo $image->width;    // 1920
echo $image->height;   // 1080
echo $image->mimeType; // 'image/jpeg'

Différents types de fichiers fournissent différentes propriétés :

  • Images : largeur, hauteur, texte alternatif, chargement paresseux
  • Scripts : type de module, hachages d'intégrité, crossorigin
  • Feuilles de style : requêtes média, intégrité
  • Audio/Vidéo : durée, dimensions
  • Polices : préchargement correct avec CORS

La bibliothèque détecte automatiquement les types de fichiers et crée la classe d'asset appropriée.

Mappers – D'où viennent les fichiers

Un mapper sait comment trouver des fichiers et créer des URL pour eux. Vous pouvez avoir plusieurs mappers à des fins différentes – fichiers locaux, CDN, stockage cloud, ou outils de build (chacun d'eux a un nom). Le FilesystemMapper intégré gère les fichiers locaux, tandis que ViteMapper s'intègre aux outils de build modernes.

Les mappers sont définis dans la configuration.

Registry – Votre interface principale

Le registry gère tous les mappers et fournit l'API principale :

// Injecte le registry dans votre service
public function __construct(
	private Nette\Assets\Registry $assets
) {}

// Obtient les assets de différents mappers
$logo = $this->assets->getAsset('images:logo.png'); // mapper 'image'
$app = $this->assets->getAsset('app:main.js'); // mapper 'app'
$style = $this->assets->getAsset('style.css'); // utilise le mapper par défaut

Le registry sélectionne automatiquement le bon mapper et met en cache les résultats pour des raisons de performance.

Travailler avec les Assets en PHP

Le Registry fournit deux méthodes pour récupérer les assets :

// Lance Nette\Assets\AssetNotFoundException si le fichier n'existe pas
$logo = $assets->getAsset('logo.png');

// Retourne null si le fichier n'existe pas
$banner = $assets->tryGetAsset('banner.jpg');
if ($banner) {
	echo $banner->url;
}

Spécifier les Mappers

Vous pouvez choisir explicitement quel mapper utiliser :

// Utilise le mapper par défaut
$file = $assets->getAsset('document.pdf');

// Utilise un mapper spécifique avec un préfixe
$image = $assets->getAsset('images:photo.jpg');

// Utilise un mapper spécifique avec la syntaxe de tableau
$script = $assets->getAsset(['scripts', 'app.js']);

Propriétés et Types d'Asset

Chaque type d'asset fournit des propriétés en lecture seule pertinentes :

// Propriétés de l'image
$image = $assets->getAsset('photo.jpg');
echo $image->width;     // 1920
echo $image->height;    // 1080
echo $image->mimeType;  // 'image/jpeg'

// Propriétés du script
$script = $assets->getAsset('app.js');
echo $script->type;     // 'module' ou null

// Propriétés audio
$audio = $assets->getAsset('song.mp3');
echo $audio->duration;  // durée en secondes

// Tous les assets peuvent être convertis en chaîne (retourne l'URL)
$url = (string) $assets->getAsset('document.pdf');

Les propriétés comme les dimensions ou la durée ne sont chargées paresseusement que lorsqu'elles sont accédées, ce qui maintient la bibliothèque rapide.

Utilisation des Assets dans les Templates Latte

Nette Assets offre une intégration intuitive de Latte avec des balises et des fonctions.

{asset}

La balise {asset} rend des éléments HTML complets :

{* Rend : <img src="/assets/hero.jpg?v=123" width="1920" height="1080"> *}
{asset 'hero.jpg'}

{* Rend : <script src="/assets/app.js?v=456" type="module"></script> *}
{asset 'app.js'}

{* Rend : <link rel="stylesheet" href="/assets/style.css?v=789"> *}
{asset 'style.css'}

La balise automatiquement :

  • Détecte le type d'asset et génère le HTML approprié
  • Inclut le versioning pour le cache busting
  • Ajoute les dimensions pour les images
  • Définit les attributs corrects (type, media, etc.)

Lorsqu'elle est utilisée à l'intérieur d'attributs HTML, elle ne produit que l'URL :

<div style="background-image: url({asset 'bg.jpg'})">
<img srcset="{asset 'logo@2x.png'} 2x">

n:asset

Pour un contrôle total sur les attributs HTML :

{* L'attribut n:asset remplit src, les dimensions, etc. *}
<img n:asset="product.jpg" alt="Product" class="rounded">

{* Fonctionne avec tout élément pertinent *}
<script n:asset="analytics.js" defer></script>
<link n:asset="print.css" media="print"></link>
<audio n:asset="podcast.mp3" controls></audio>

Utilisez des variables et des mappers :

{* Les variables fonctionnent naturellement *}
<img n:asset="$product->image">

{* Spécifiez le mapper avec des accolades *}
<img n:asset="images:{$product->image}">

{* Spécifiez le mapper avec la notation de tableau *}
<img n:asset="[images, $product->image]">

asset()

Pour une flexibilité maximale, utilisez la fonction asset() :

{var $logo = asset('logo.png')}
<img src={$logo} width={$logo->width} height={$logo->height}>

{* Ou directement *}
<img src={asset('logo.png')} alt="Logo">

Assets optionnels

Gérez les assets manquants avec {asset?}, n:asset? et tryAsset() :

{* Balise optionnelle - ne rend rien si l'asset est manquant *}
{asset? 'optional-banner.jpg'}

{* Attribut optionnel - saute si l'asset est manquant *}
<img n:asset?="user-avatar.jpg" alt="Avatar" class="avatar">

{* Avec un fallback *}
{var $avatar = tryAsset('user-avatar.jpg') ?? asset('default-avatar.jpg')}
<img n:asset=$avatar alt="Avatar">

{preload}

Améliorez les performances de chargement de page :

{* Dans votre section <head> *}
{preload 'critical.css'}
{preload 'important-font.woff2'}
{preload 'hero-image.jpg'}

Génère les liens de préchargement appropriés :

<link rel="preload" href="/assets/critical.css?v=123" as="style">
<link rel="preload" href="/assets/important-font.woff2" as="font" crossorigin>
<link rel="preload" href="/assets/hero-image.jpg" as="image">

Fonctionnalités avancées

Détection automatique d'extension

Gérez automatiquement plusieurs formats :

assets:
	mapping:
		images:
			path: img
			extension: [webp, jpg, png]  # Essayer dans l'ordre

Maintenant, vous pouvez demander sans extension :

{* Trouve logo.webp, logo.jpg, ou logo.png automatiquement *}
{asset 'images:logo'}

Parfait pour l'amélioration progressive avec les formats modernes.

Versioning intelligent

Les fichiers sont automatiquement versionnés en fonction de l'heure de modification :

{asset 'style.css'}
{* Sortie : <link rel="stylesheet" href="/assets/style.css?v=1699123456"> *}

Lorsque vous mettez à jour le fichier, l'horodatage change, forçant le rafraîchissement du cache du navigateur.

Contrôlez le versioning par asset :

// Désactive le versioning pour un asset spécifique
$asset = $assets->getAsset('style.css', ['version' => false]);

// Dans Latte
{asset 'style.css', version: false}

Assets de police

Les polices bénéficient d'un traitement spécial avec un CORS approprié :

{* Préchargement correct avec crossorigin *}
{preload 'fonts:OpenSans-Regular.woff2'}

{* Utilisation dans CSS *}
<style>
@font-face {
	font-family: 'Open Sans';
	src: url('{asset 'fonts:OpenSans-Regular.woff2'}') format('woff2');
	font-display: swap;
}
</style>

Mappers personnalisés

Créez des mappers personnalisés pour des besoins spéciaux comme le stockage cloud ou la génération dynamique :

use Nette\Assets\Mapper;
use Nette\Assets\Asset;
use Nette\Assets\Helpers;

class CloudStorageMapper implements Mapper
{
	public function __construct(
		private CloudClient $client,
		private string $bucket,
	) {}

	public function getAsset(string $reference, array $options = []): Asset
	{
		if (!$this->client->exists($this->bucket, $reference)) {
			throw new Nette\Assets\AssetNotFoundException("Asset '$reference' not found");
		}

		$url = $this->client->getPublicUrl($this->bucket, $reference);
		return Helpers::createAssetFromUrl($url);
	}
}

Enregistrez dans la configuration :

assets:
	mapping:
		cloud: CloudStorageMapper(@cloudClient, 'my-bucket')

Utilisez comme tout autre mapper :

{asset 'cloud:user-uploads/photo.jpg'}

La méthode Helpers::createAssetFromUrl() crée automatiquement le type d'asset correct en fonction de l'extension du fichier.

Pour en savoir plus

version: 1.0