Jusqu'ici, nous avons programmé en PHP, et découvert le fonctionnement de la programmation côté serveur, et quelques problèmes:
C'est pour cette raison qu'il existe des frameworks, dont l'utilisation devient indispensable pour des gros projets. Par exemple:
Mais avant, présentons les fonctionnalités de ces outils logiciels!
Imaginons la page PHP suivante:
<ul>
<?php if ($_GET['page'] == 'show') {
$sql = 'SELECT title FROM series ORDER by TITLE ASC';
$query = $pdo->query($sql);
foreach ($query as $row) { ?>
<li><?= $row['title']; ?></li>
<? }
}
?>
</ul>
Ici, de nombreuses choses sont mélangées ensemble, rendant le code compact, difficile à lire et peu réutilisable.
On peut d'ailleurs faire une liste des problèmes:
JSON
en plus du site web (on voudrait réutiliser
la requête)<?php
rend le code difficile à lire lorsqu'il
y a de la logique autourLe principe MVC, pour Modèle, Vue, Contrôleur, propose une séparation des responsabilités:
Par exemple, pour réécrire ce code en respectant cette séparation, on pourrait tout
d'abord écrire dans une classe Model
:
// Model.php, classe Model
protected getSeries(): array
{
$sql = 'SELECT title FROM series ORDER by TITLE ASC';
$query = $this->pdo->query($sql);
return $query->fetchAll();
}
Et ensuite, récupérer ces valeurs dans le contrôleur, si le paramètre GET est passé à la bonne valeur:
if ($_GET['page'] == 'show') {
$series = $model->getSeries();
}
Et enfin l'afficher dans la vue:
<ul>
<?php foreach ($series as $serie) { ?>
<li><?= $serie['title']; ?></li>
<?php } ?>
</ul>
Cependant, un framework n'est pas qu'une philosophie d'organisation, mais aussi un ensemble d'outils logiciels qui vous assistent dans chacune de ces tâches. Nous allons présenter certains de ces composants.
Jusqu'ici, voici comment nous avons procédé:
Ici, le travail implicite de routage est effectué par le serveur, car les scripts PHP sont des ressources différentes.
Le problème avec cette approche:
.php
, il faudra ré-inclure les différentes parties du code (boilerplate), ce qui rendra l'application peu maintenableUne solution pour avoir de belles URLs est d'utiliser le mécanisme de réécriture d'URL du serveur web:
Cette solution nous permet d'obtenir des URLs arbitraires, mais implique de maintenir un fichier de réécriture d'URLs, qui est de plus spécifique au serveur web utilisé.
De plus, elle ne résout pas le problème des multiples fichiers PHP.
Lorsqu'on utilise un framework, la réécriture d'URL est utilisée mais vers une page frontale unique:
De cette façon, le routage est réalisé en PHP au sein de l'application.
Le problème du routage correspond à l'association entre les adresses (URL) et le contrôleur (le morceau de code PHP) qui va être invoqué.
Le routeur est donc un ensemble de règles de correspondance.
Par exemple:
/home
→
DefaultController::home()
/login
→
SecurityController::login()
/product/{id}
→
ProductController::show($id)
<?php
class DefaultController
{
/**
* @Route("/hello/{name}", name="hello")
*/
public function hello($name)
{
return new Response('Hello '.$name);
}
}
Ici:
@Route("/hello/{name}")
est une manière de faire correspondre
la route /hello/{name}
avec la fonction hello($name)
hello
)Les routes permettent de faire correspondre une adresse à un contrôleur, mais également de produire des URLs.
On pourra par exemple à partir du nom de la route demander au routeur de générer l'adresse correspondante:
<?php
$homeUrl = $this->generateUrl('home');
$helloBobUrl = $this->generateUrl('hello', ['name' => 'Bob']);
De cette façon, les noms externes visibles de tous (/hello/Bob
) sont dissociés du nom interne de la route (hello
)
/product/{id}
)/admin/*
)Il est tout à fait possible d'écrire des pages PHP comme système de template.
Cependant, on peut lui faire quelques reproches:
<?php
...)Un exemple de moteur de template est Twig.
Ce système permet de simplifier l'écriture des vues, c'est à dire du contenu des pages HTML qui seront rendues.
Twig supporte l'héritage, l'échappement par défaut et de nombreuses astuces syntaxiques pour simplifier l'écriture des templates.
Voici un exemple de template:
<html>
<head>
<title>
{% block title %}Mon titre{% endblock %}
</title>
</head>
<body>
<h1>{{ block('title') }}</h1>
{% block content %}
Bonjour {{ name }} !
{% endblock %}
</body>
</html>
Comme vous le voyez, Twig permet d'écrire des documents directements en HTML, à l'exception de certains tags qui permettent d'y ajouter de la structure, à l'instar du PHP.
Dans cet exemple:
{% block contents %}
est un bloc qui pourra être surchargé dans les templates filles{% block('title') %}
sert à ré-afficher le contenu du block title précédement utilisé{{ name }}
correspond à l'affichage d'une variableLa template précédente peut être héritée comme cela:
{% extends 'index.html.twig' %}
{% block title %}
{{ parent() }} - Ma page
{% endblock %}
{% block contents %}
Bienvenue sur cette page!
{% endblock %}
Le mot clé extends
permet de décrire que cette page hérite de index.html.twig
, de la même
manière que l'héritage des classes votre template se basera alors sur cette template mère et pourra redéfinir son
comportement.
Les blocs peuvent alors être surchargés, c'est à dire modifié en les redéfinissant. Il est aussi possible d'utiliser
le mot clé parent()
pour faire appel à la template mère et utiliser son contenu, comme dans le cas du titre
qui deviendra ici "Mon titre - Ma page"
Il est également possible d'effectuer des tests et des boucles avec Twig:
{% if not users|length %}
<i>Aucun utilisateur</i>
{% else %}
<ul>
{% for user in users %}
<li>{{ user.name }}</li>
{% endfor %}
</ul>
{% endif %}
Pour une documentation plus exhaustive, vous pouvez consulter la documentation officielle de Twig.
user.name
peut être $user['name']
ou $user->getName()
)Un ORM, pour Object Relational Mapping, désigne le fait de réaliser un mapping, ou une association entre le monde relationnel (tables, lignes, champs ...) et le monde objet (classes, instances, attributs ...).
Ce mapping est généralement fait à l'aide de fichiers de configuration ou d'annotations.
Relationnel | Objet |
Table | Classe (ou entité) |
Ligne | Instance |
Colonne | Attribut |
Clé étrangère | Référence |
Cette correspondance ressort si l'on compare un schéma entité association (MCD) avec un schéma UML.
Il existe deux paradigmes en matière d'ORM:
Un exemple est Doctrine, l'ORM présent dans le framework Symfony.
En Database First, ce dernier est par exemple capable de générer à partir de la base shows un ensemble de classes qui correspondront aux tables de la base de données.
Il est alors possible d'effectuer des requêtes en utilisant Doctrine:
public function indexAction(SeriesRepository $repository)
{
$series = $repository->findBy(['year_start' => '2011']);
return $this->render('series/index.html.twig', [
'series' => $series
]);
}
Puis de les afficher dans la vue:
<ul>
{% for serie in series %}
<li>{{ serie.title }}</li>
{% endfor %}
</ul>
Il est intéressant de remarquer que series
est ici un ensemble d'instances
de la classe Series
.
On peut également créer des entrées à l'aide de persist()
:
<?php
$entityManager = $this->getDoctrine()->getManager();
$user = new User();
$user
->setName('Toto')
->setEmail('toto@toto.com')
->setPassword('totopass')
;
$entityManager->persist($user);
$entityManager->flush();
La plupart des opérations simples peuvent être effectuées sans écrire de requête SQL!
En général, un ORM fournit une API pour effectuer le requêtage via des méthodes, et aussi un langage spécifique.
Par exemple, Doctrine propose le langage DQL pour requêter la base de données:
<?php
// On ne requête ici pas dans les tables mais les "classes" de l'ORM
// Le langage est indépendant du vrai SGBD
$query = $entityManager->createQuery('SELECT s
FROM App:Series s
WHERE s.year_start = "2011"');
$series = $query->getResult();