Une prévisualisation avec l'API fetch et PHP
Ce tutoriel vous permettra de réaliser une prévisualisation de vos contenus en utilisant CKEditor, l’API fetch de JavaScript et du PHP natif.
Vous pouvez retrouver le code du tutoriel dans le repo GitHub.
Nous réutilisons ici le code du Routeur Simple en PHP (le tuto d’explication est disponible ici).
Le but du tuto
Nous voulons créer un moyen de visualiser en avance le rendu d’un contenu sans avoir besoin de recharger la page ou de rechanger de page.
La résultat final ressemble au Gif au dessus : une editeur de texte un peu avancé avec des styles et niveaux de titre, et lorsque nous cliquons sur prévisualiser, nous voyons le contenu mis en forme à côté.
Les routes
Le fichier routes.txt
Le contenu de notre fichier config/routes.txt
:
/ HomeController:index
/page-preview PageController:preview
Nous avons donc deux routes :
- une route
/
qui ne fait rien. - une route
/page-preview
avec notre outil de prévisualisation
Le Routeur
Notre routeur a un peu changé par rapport à celui du tutoriel.
En effet nous allons avoir besoin de gérer les requêtes $_POST
. Nous ajoutons donc la gestion des $_POST à nos appels de controllers.
Le contenu du fichier services/Router.php
:
<?php
class Router {
private function parseRequest(string $request)
{
$route = [];
$routeData = explode("/", $request);
$route["path"] = "/".$routeData[1];
if(count($routeData) > 2)
{
$route["parameter"] = $routeData[2];
}
else
{
$route["parameter"] = null;
}
return $route;
}
public function route(array $routes, string $request)
{
$requestData = $this->parseRequest($request);
$routeFound = false;
foreach($routes as $route)
{
$controller = $route["controller"];
$method = $route["method"];
$parameter = $route["parameter"];
if($route["path"] === $requestData["path"])
{
if($route["parameter"] && $requestData["parameter"] !== null)
{
$routeFound = true;
$ctrl = new $controller();
$ctrl->$method($_POST, $requestData["parameter"]);
}
else if(!$route["parameter"] && $requestData["parameter"] === null)
{
$routeFound = true;
$ctrl = new $controller();
$ctrl->$method($_POST);
}
}
}
if(!$routeFound)
{
throw new Exception("Route not found", 404);
}
}
}
?>
Les controllers
Comme nous pouvons le déduire de notre fichier config/routes.txt
nous avons deux controllers:
-
HomeController
qui ne fait rien d’autre qu’afficher la page d’accueil -
PageController
dont la méthodepreview
va nous afficher notre outil
HomeController
Le code de HomeController
n’a pas changé depuis le tuto :
<?php
class HomeController
{
public function index(array $post)
{
require "templates/home.phtml";
}
}
PageController
Le code du PageController controllers/PageController.php
:
<?php
class PageController extends AbstractController
{
public function preview(array $post)
{
if(isset($_POST["data"])) // the form has been submitted
{
$title = $_POST["title"];
$content = $_POST["content"];
$view = $this->renderPartial("_preview", [
"title" => $title,
"content" => $content
]);
}
else
{
$this->render("page_preview", [
]);
}
}
}
Comme vous pouvez le constater, PageController
hérite d’une autre classe (mot clé extends
) : AbstractController
.
AbstractController
Le code de l’AbstractController controllers/AbstractController.php
:
<?php
abstract class AbstractController
{
protected function renderPartial(string $template, array $values)
{
$data = $values;
require "templates/".$template.".phtml";
}
protected function render(string $template, array $values)
{
$data = $values;
$page = $template;
require "templates/layout.phtml";
}
}
L’AbstractController
est une sorte de modèle de controller. Il contient deux méthodes :
-
renderPartial
qui affiche un bout précis d’un template mais sans remettre tout le layout. -
render
qui afiche une page complète avec<!-- DOCTYPE>
,<head>
et autres.
Retour à notre PageController
. Il a deux rôles :
Afficher la page
else
{
$this->render("page_preview", [
]);
}
Recevoir le formulaire et générer le HTML pour la visualisation
if(isset($_POST["data"])) // the form has been submitted
{
$title = $_POST["title"];
$content = $_POST["content"];
$view = $this->renderPartial("_preview", [
"title" => $title,
"content" => $content
]);
}
Les templates
Les templates de la page 404 et de la home sont les mêmes que ceux du tuto du Routeur. Par contre nous en avons de nouveaux :
layout.phtml
Le contenu du fichier templates/layout.phtml
:
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<title>Page Previewer</title>
<link rel="stylesheet" href="assets/style.css" />
</head>
<body>
<h1>Page Previewer</h1>
<?php require "templates/".$page.".phtml"; ?>
<script src="https://cdn.ckeditor.com/ckeditor5/35.0.1/classic/ckeditor.js"></script>
<script type="application/javascript" src="assets/index.js"></script>
</body>
</html>
Il charge notre style, le javascript de CKEditor et notre propre JavaScript.
page_preview.phtml
Le contenu du fichier templates/page_preview.phtml
:
<main>
<section>
<h2>Le formulaire</h2>
<form id="editPageForm" method="POST" action="/page-previewer/page-preview">
<fieldset>
<label for="title">Titre</label>
<input type="text" name="title" id="title" />
</fieldset>
<fieldset>
<label for="content">Contenu</label>
<textarea name="content" id="content">
</textarea>
</fieldset>
<fieldset>
<input id="previewSubmit" type="submit" value="Prévisualiser" />
</fieldset>
</form>
</section>
<section>
<h2>L'aperçu</h2>
<section id="previewContent">
</section>
</section>
</main>
Nous avons d’un côté notre formulaire de saisie :
<section>
<h2>Le formulaire</h2>
<form id="editPageForm" method="POST" action="/page-previewer/page-preview">
<fieldset>
<label for="title">Titre</label>
<input type="text" name="title" id="title" />
</fieldset>
<fieldset>
<label for="content">Contenu</label>
<textarea name="content" id="content">
</textarea>
</fieldset>
<fieldset>
<input id="previewSubmit" type="submit" value="Prévisualiser" />
</fieldset>
</form>
</section>
et de l’autre la zone dans laquelle nous allons prévisualiser :
<section>
<h2>L'aperçu</h2>
<section id="previewContent">
</section>
</section>
_preview.phtml
Le contenu de templates/_preview.phtml
qui génèrera le HTML de notre prévisualisation :
<article>
<header>
<h1>
<?= $data["title"]; ?>
</h1>
</header>
<main>
<?= $data["content"]; ?>
</main>
</article>
Le CSS
Ici du CSS assez simpliste, il nous sert juste à créer deux espaces côte à côte pour le formulaire et la prévisualisation. Il nous sert également à mettre du style différent dans l’espace de prévisualisation pour bien voir la différence.
Le contenu de assets/style.css
:
body {
width:100vw;
height:100vh;
}
body > main {
display:grid;
height:100%;
grid-template-rows:1fr;
grid-template-columns:1fr 1fr;
}
.ck-editor__editable[role="textbox"] {
/* editing area */
min-height: 50vh;
}
#previewContent {
font-family: Arial, sans-serif;
display:flex;
}
#previewContent h1 {
font-size: 1.8rem;
}
#previewContent h2 {
font-size: 1.6rem;
}
#previewContent h3 {
font-size:1.2rem;
}
#previewContent a {
color: red;
}
#previewContent a:hover {
color:blue;
}
.ck-editor__editable[role="textbox"] {
/* editing area */
min-height: 50vh;
}
Nous permet de nous assurer que nous aurons assez de place pour écrire dans l’editeur.
Le JavaScript
Le contenu du fichier assets/index.js
:
window.addEventListener("DOMContentLoaded", (event) => {
ClassicEditor
.create( document.querySelector( '#content' ) )
.then( editor => {
contentEditor = editor;
} )
.catch( error => {
} );
$submit = document.getElementById("previewSubmit");
$submit.addEventListener('click', function(event){
event.preventDefault();
title = document.getElementById("title").value;
formData = new FormData();
formData.append('data', true);
formData.append('title', title);
formData.append('content', contentEditor.getData());
const options = {
method: 'POST',
body: formData
};
fetch('/page-previewer/page-preview', options)
.then(response => response.text())
.then(data => {
preview = document.getElementById("previewContent");
preview.innerHTML = data;
});
});
});
Commençons par le début : nous mettons notre code dans
window.addEventListener("DOMContentLoaded", (event) => {
}
pour nous assurer que tout le DOM est bien chargé avant de commencer à le manipuler.
ClassicEditor
.create( document.querySelector( '#content' ) )
.then( editor => {
contentEditor = editor;
} )
.catch( error => {
} );
Nous sert à initialiser l’éditeur de texte en le chargeant depuis nos textarea qui à l’id content
.
$submit = document.getElementById("previewSubmit");
Nous récupérons notre bouton de soumission de formulaire.
$submit.addEventListener('click', function(event){
event.preventDefault();
});
et nous écoutons chaque clic sur le bouton pour intercepter la soumission du formulaire.
title = document.getElementById("title").value;
formData = new FormData();
formData.append('data', true);
formData.append('title', title);
formData.append('content', contentEditor.getData());
Nous récupérons les valeurs des champs du formulaire et nous nous en servons pour créer un objet FormData qui nous permet d’envoyer les champs du formulaire à PHP.
const options = {
method: 'POST',
body: formData
};
fetch('/page-previewer/page-preview', options)
.then(response => response.text())
.then(data => {
preview = document.getElementById("previewContent");
preview.innerHTML = data;
});
Nous utilisons l’API fetch pour envoyer notre requête “POST” :
const options = {
method: 'POST',
body: formData
};
fetch('/page-previewer/page-preview', options)
Puis nous récupérons la réponse de PHP (il nous renvoie du HTML c’est donc du texte) :
.then(response => response.text())
Nous injectons enfin le HTML récupéré dans notre section de prévisualisation previewContent
:
.then(data => {
preview = document.getElementById("previewContent");
preview.innerHTML = data;
}
N’oubliez pas de modifier la route qu’appelle fetch en fonction de la configuration de votre serveur !
Happy Coding !