Vues

Vues

Des fichiers .php ordinaires dans resources/views/, sans moteur de template séparé — cette page de documentation elle-même en est un exemple.

Vues PHP natives

Niang\Core\View ne fait rien d'exotique : renderView() extrait le tableau de données dans la portée locale (extract($data, EXTR_SKIP)), inclut le fichier avec ob_start()/ob_get_clean(), et retourne le HTML capturé. Aucune compilation, aucun cache de bytecode de template : c'est du PHP, exécuté comme du PHP.

app/Controllers/HomeController.php
public function index(): Response
{
    return $this->view('home', ['title' => 'Bienvenue']);
}

\$this->view() (dans un contrôleur, via Niang\Core\Controller) et le helper global view() font tous deux la même chose : View::make($view, $data), qui enveloppe renderView() dans une Response::html(). Le chemin est résolu par convention : 'home' → resources/views/home.php, et 'admin.posts.index' (points comme séparateurs) → resources/views/admin/posts/index.php. Une vue introuvable ne lève pas d'exception : elle rend simplement le texte "Vue introuvable : nom", à la place du contenu.

layout() : composer une page dans un gabarit

Appelé en haut d'une vue, layout($vue, $donnees) ne rend rien immédiatement : il mémorise le gabarit visé. Une fois la vue entièrement rendue, son HTML est injecté dans $content du gabarit, qui est alors rendu à son tour — un aller-retour géré par View::renderView(), invisible depuis la vue elle-même.

resources/views/home.php
layout('layouts.app', ['title' => $title]); ?>

<h1><?= e($framework) ?></h1>
<p>Votre framework tourne. Bienvenue !</p>

Le gabarit reçoit ses propres données plus $content :

resources/views/layouts/app.php (extrait)
<title><?= e($title ?? 'NiangPro') ?></title>
...
<div class="card">
    <?= $content ?>
</div>

Cette page de documentation applique exactement ce schéma : chaque vue resources/views/docs/fr/*.php (et docs/en/*.php pour l'anglais) commence par layout('layouts.docs', [...]), et le gabarit ajoute la barre latérale, le sommaire et la recherche autour du contenu.

component() : inclure une vue partielle

component($vue, $donnees) rend une vue et retourne directement le HTML sous forme de chaîne (au lieu d'une Response) — pour l'insérer dans une autre vue :

php
<?= component('components/icon', ['name' => 'check']) ?>

Chaque icône de cette documentation (le bouton copier, la recherche, le sommaire mobile...) est un component('components/icon', ['name' => '...']) — un SVG inline choisi dans resources/views/components/icon-paths.php, jamais une police d'icônes chargée à distance.

field() : un champ de formulaire complet

field($name, $label, $options = []) est un raccourci pour component('components/field', [...]) : name et label reviennent, obligatoires, dans presque toutes les vues de formulaire — les répéter en tableau associatif à chaque champ est le principal bruit visuel d'un formulaire.

php
<?= field('email', 'Adresse email', ['type' => 'email', 'autocomplete' => 'email']) ?>

plutôt que :

php
<?= component('components/field', ['name' => 'email', 'label' => 'Adresse email', 'type' => 'email', 'autocomplete' => 'email']) ?>

$options accepte les mêmes clés que le composant (type — texte par défaut, rows pour un <textarea>, value, autocomplete, required). Ce n'est qu'un raccourci d'appel, pas un nouveau mécanisme : resources/views/components/field.php reste le seul endroit qui décide du HTML produit — label, ancienne saisie via old() après une erreur de validation (jamais pour un password, toujours vidé), et message d'erreur via components/field-errors, déjà câblés.

e() : échapper l'affichage

e($valeur) est htmlspecialchars((string) $valeur, ENT_QUOTES, 'UTF-8') — à utiliser explicitement à chaque affichage d'une donnée qui n'est pas déjà du HTML de confiance (pas d'échappement automatique implicite, puisqu'il n'y a pas de moteur de template) :

php
<h1><?= e($titre) ?></h1>

e() échappe pour un contexte HTML. Pour injecter des données dans un attribut consommé par du JavaScript (ex. data-props pour Alpine.js), utilisez plutôt json_for_html() — voir le tableau ci-dessous — qui échappe spécifiquement contre une évasion de balise </script> ou d'attribut.

Langues : __() et lang/

Les textes que voit un visiteur — messages de validation, erreurs d'upload, pages d'erreur, messages 401/403/419/429, pagination — viennent de lang/<langue>/*.php à la racine du projet, livrés en français (défaut) et en anglais. La langue se choisit dans .env (APP_LOCALE=en) ou pour une requête avec Lang::setLocale('en') :

php
__('validation.required', ['attribute' => 'email']);  // « Le champ email est requis. »
__('http.404');                                        // « Page introuvable. »
__('pagination.next');

Lang::setLocale('en');
__('http.404');                                        // « Page not found. »
FichierContient
validation.phpUn message par règle, et la section attributes : libellés lisibles des champs ('items.*.name' => "nom de l'article").
upload.phpLes raisons d'un upload invalide (taille dépassée, envoi partiel...).
http.phpMessages des erreurs HTTP, des middlewares d'authentification et des pages d'erreur.
pagination.phpLiens « Précédent » / « Suivant » de Paginator::links().

Ces fichiers appartiennent à votre projet : modifiez un message directement, ou ajoutez une langue en copiant lang/en/ vers lang/es/. :attribute, :min... sont remplacés, :Attribute met la première lettre en majuscule. Une clé absente de la langue courante est cherchée dans APP_FALLBACK_LOCALE (fr), puis affichée telle quelle. La CLI et les exceptions destinées au développeur restent en français.

Pluriels

php
// lang/fr/panier.php
'articles' => 'un article|:count articles',
'etat' => '{0} Votre panier est vide|{1} Un article|[2,9] :count articles|[10,*] Plus de :count articles',

trans_choice('panier.articles', 3);   // « 3 articles »
Lang::choice('panier.etat', 0);        // « Votre panier est vide »

Les formes {n} et [min,max] sont testées d'abord, sinon la règle de la langue : en français, 0 et 1 sont au singulier ; en anglais, seul 1 l'est.

Tous les helpers globaux

Chargés automatiquement sur chaque requête (composer.json les déclare en autoload.files) : aucun use à écrire, ils sont disponibles partout. Liste complète (packages/core/src/helpers.php et packages/http/src/helpers.php) :

HelperRôle
base_path(string $path = '')Chemin absolu depuis la racine du projet.
env(string $key, mixed $default = null)Lit une variable de .env.
config(string $key, mixed $default = null)Lit config/*.php, ex. config('app.providers').
route(string $name, array $params = [])URL d'une route nommée. Voir Routing.
signedRoute(string $name, array $params = [], ?int $expiresInSeconds = null)route() + signature expirable (UrlSignature::sign()) — lien cliquable sans authentification préalable.
view(string $view, array $data = [])Équivalent global de $this->view() dans un contrôleur.
layout(string $view, array $data = [])Voir ci-dessus.
component(string $view, array $data = [])Voir ci-dessus.
field(string $name, string $label, array $options = [])Voir ci-dessus.
e(mixed $value)Voir ci-dessus.
__(string $key, array $replace = [])Traduction dans la langue courante, ex. __('http.404'). Voir Langues.
json_response(mixed $data, int $status = 200)Équivalent global de Response::json().
csrf_token()Jeton CSRF courant (Niang\Core\Csrf::token()).
csrf_field()Le <input type="hidden"> CSRF prêt à insérer dans un <form>.
old(string $key, mixed $default = null)Ancienne saisie après une redirection de validation échouée. Voir Validation.
flashed(string $key, mixed $default = null)Lit une valeur flash de session (ex. message de succès après redirection).
errors(?string $key = null)Erreurs de validation flashées ; avec $key, celles d'un seul champ.
abort(int $status, string $message = '')Lève une HttpException, interceptée par Handler (ex. abort(404)).
json_for_html(mixed $data)JSON échappé pour un attribut HTML (JSON_HEX_*) — hydrater un composant Alpine.js/Vue/React côté client sans risque XSS.
uuid()UUID version 4 aléatoire, ex. pour une colonne $table->uuid().
url(string $path = '')URL absolue construite avec APP_URL (jamais l'en-tête Host) : pour les liens envoyés par email.
trans_choice(string $key, int|float $count, array $replace = [])Traduction au pluriel (voir Pluriels).
trigger_deprecation(string $package, string $version, string $message)Signale une API dépréciée ; consigné dans les logs en production (voir stabilité de l'API).
vite_asset(string $entry)URL réelle d'un asset Vite (Niang\Core\ViteAssets) : bascule sur public/hot en dev, lit public/build/manifest.json en prod.
dd(mixed ...$vars)Affiche chaque valeur (var_dump) et arrête l'exécution — debug uniquement.
exemple : json_for_html() pour hydrater un composant client
<div data-props="<?= json_for_html($props) ?>" x-data="JSON.parse($el.dataset.props)"></div>
⏱ 3.67 ms 🗄 0 requête(s) SQL 🧠 4.00 MB ↩ 200