Lowify
Un clone low-cost de Spotify : pages dynamiques avec PHP, HTML et une base de données.
Le but de cet atelier est de créer une première application PHP en utilisant des utilitaires de gestion de page et de base de données. Vous allez créer un clone "Low-cost" de Spotify, nommé Lowify.
Préparation
Attention
Si vous aviez déjà un projet en cours, assurez-vous de bien stopper les conteneurs Docker des projets précédents pour éviter les conflits de ports.
Vous trouverez la procédure ici : Changer de projet
Lancer la stack Docker
Rendez-vous dans le dossier racine du dépôt git puis dans 004-lowify. En suite, lancez la stack docker avec la commande suivante :
docker compose up -d --buildVous pouvez en suite vérifier que tout fonctionne en allant à l'adresse http://localhost:80.
Attention
Si vous avez une erreur HTTPS, acceptez simplement le certificat auto-généré.
Attention
Pour ceux qui ont le problème de page qui ne se raffraichissent pas sur Windows
Vous allez devoir changer votre configuration docker pour ce projet. Pour se faire :
- Allez dans le dossier du projet
- Ouvrez le fichier
docker-compose.yml - Dans la section port, vous devriez avoir des notations comme suit :
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3- Changez les ports de gauche (ceux avant les
:) pour des ports libres sur votre machine, par exemple :
ports:
- "8080:80" # HTTP
- "444:443" # HTTPS
- "444:443/udp" # HTTP/3Lorsque dans la documentation il est fait référence à http://localhost ou https://localhost, vous devrez utiliser les ports que vous avez configurés : https://localhost:444 pour accéder à votre projet.
Ouvrir le projet dans PHPStorm
Ouvrez PHPStorm, puis :
- Si vous êtes déjà dans un projet :
File>Open- Sélectionnez le dossier racine du dépôt git >
004-lowify/app
- Si vous n'êtes pas dans un projet :
Open- Sélectionnez le dossier racine du dépôt git >
004-lowify/app
Configurer l'interpréteur PHP
Pour éviter que vous n'ayez à installer PHP sur votre machine, nous allons utiliser l'interpréteur PHP fourni par Docker.
Pour cela :
- Allez dans
File>Settings>Languages & Frameworks>PHP - Cliquez sur les
...à côté du champCLI Interpreter - Cliquez sur le
+en haut à gauche pour ajouter un nouvel interpréteur - Sélectionnez
From Docker, Vagrant, WSL, Remote... - Sélectionnez
Docker Compose - Si le champ
Serverest vide, cliquez sur les...à côté et laissez les options par défaut, puis cliquez surOK - Dans le champ
Configuration file, cliquez sur les..., retirez tout le contenu et sélectionnez le fichierdocker-compose.ymlà la racine du dossier actuel :004-lowify/docker-compose.yml - Dans le champ
Service, sélectionnezphp - Cliquez sur
OKpour valider l'ajout de l'interpréteur - Cliquez sur
ApplypuisOKpour fermer la fenêtre des paramètres
Introduction
Architecture des pages
L'application Lowify va être structurée de la manière suivante :
index.phpsera la page d'accueil de l'application, elle listera les tops tendances et permettra une recherche.artists.phplistera tout les artistes disponibles.artist.phpsera la page de détail d'un artiste. Elle permettra d'afficher ses détails, de lister ses "tops titres" et ses albums.album.phppermettra d'afficher le détail d'un album et de ses titres.search.phpaffichera les résultats de recherches.error.phpsera affiché lors d'une erreur de code métier.
Architecture des données
La base de donnée sera constituée de 3 tables pour garder le modèle simple.
Astuce
Pour une application "réelle", ce modèle n'est pas viable.
Il faudrait par exemple :
- Gérer les covers localement et non via des URLs externes, avec une table
media. - Gérer les utilisateurs et leurs playlists.
- Ajouter des tables de jointures pour gérer les relations "many to many" (ex : plusieurs artistes par chansons).
- Etc.
Installation
J'ai préparé un script d'installation pour vous permettre de créer la base de données et d'insérer des données de test.
Astuce
Les données ont été générées via Faker PHP et sont totalement fictives.
Les images sont issues de Lorem Picsum.
Une fois votre stack docker lancée :
- Rendez-vous sur https://localhost/install
- Cliquez sur le bouton pour lancer l'installation
- Vérifiez que tout est "OK" dans le résultat de l'installation
Consignes générales
Les consignes suivantes sont à garder en tête tout au long de l'atelier :
- Toutes les pages doivent utiliser la classe
HTMLPagepour générer le HTML. - Toutes les pages doivent utiliser la classe
DatabaseManagerpour interagir avec la base de données. - Je ne donne pas d'information sur le design des pages, vous êtes libre de faire ce que vous voulez, du moment que les fonctionnalités sont présentes et utiles.
- Vous pouvez créer des fichiers CSS et JS supplémentaires si nécessaire, et les inclure dans les pages via la classe
HTMLPage. - Vous pouvez créer des fonctions utilitaires supplémentaires si nécessaire, mais essayez de garder le code simple et lisible.
- Si d'aventure, vous utilisez des fonctions utilitaires sur plusieurs pages, créez un fichier
inc/utils.inc.phpet incluez-le dans les pages concernées.
- Si d'aventure, vous utilisez des fonctions utilitaires sur plusieurs pages, créez un fichier
- Le plan de vos scripts devrait être le suivant :
- Inclusion des fichiers nécessaires (
require_once) - Initialisation de la page (
HTMLPage) - Initialisation de la base de données (
DatabaseManager) - Récupération des données nécessaires
- Génération du contenu HTML
- Affichage de la page
- Inclusion des fichiers nécessaires (
- Vous ne devez pas injecter de code PHP comme des boucles et conditions directement dans le HTML.
- Vous devriez préparer des variables PHP contenant le HTML nécessaire, puis les injecter dans une variable globale de contenu
$htmlvia la méthodesetContent()de la classeHTMLPage.
- Vous devriez préparer des variables PHP contenant le HTML nécessaire, puis les injecter dans une variable globale de contenu
- Vous devrez gérer les erreurs de code métier en redirigeant vers la page
error.phpavec un message d'erreur approprié.- Par exemple, si un artiste n'existe pas, vous devez rediriger vers
error.phpavec un message "Artiste non trouvé".
- Par exemple, si un artiste n'existe pas, vous devez rediriger vers
Recommandé - Configurer PHPStorm avec la base de données
Pour faciliter le développement, vous pouvez configurer PHPStorm pour qu'il puisse se connecter à la base de données MySQL.
Vous pourrez ainsi naviguer dans les tables et voir les données directement depuis l'IDE.
Pour cela :
- Ouvrez le panneau "Database" (
View>Tool Windows>Database) - Cliquez sur le
+en haut à gauche >Data Source>MySQL - Configurez la connexion avec les paramètres suivants :
- Host:
localhost - Port:
3306 - User:
lowify - Password:
lowifypassword - Database:
lowify
- Host:
- Cliquez sur
Test Connectionpour vérifier que tout fonctionne - Cliquez sur
OKpour enregistrer la connexion
Vous pourrez en suite aller explorer les tables dans le panneau "Database", et ouvrir une console SQL pour exécuter des requêtes.
Listage des artistes
Nous allons commencer par implémenter la page artists.php qui listera tous les artistes disponibles dans la base de données.
Nous allons procéder en deux étapes :
- Afficher la page à l'aide de la classe
HTMLPage. - Récupérer les artistes depuis la base de données à l'aide de la classe
DatabaseManager.
Astuce
Vous aurez besoin des documentations des classes HTMLPage et DatabaseManager qui sont disponibles sur la page principale du cours.
Commençons par créer le fichier artists.php à la racine du dossier app.
Consigne
- Créez le fichier
artists.phpà la racine du dossierapp. - Dans ce fichier, incluez le fichier
inc/page.inc.php. Vous utiliserez la fonction require_once pour cela. - Créez une instance de la classe
HTMLPageavec le titre "Lowify - Artistes". - Initialisez vos CSS et JS si nécessaire.
- Ajoutez le contenu HTML de la page (un simple titre H1 suffira pour l'instant) à l'aide de la méthode
setContent(). - Affichez la page à l'aide de la méthode
render().
Une fois que vous avez terminé, rendez-vous sur http://localhost/artists.php pour vérifier que la page s'affiche correctement avec le titre "Lowify - Artistes" et le contenu HTML que vous avez ajouté.
Ensuite, nous allons récupérer les artistes depuis la base de données et les afficher dans la page.
Consigne
- Dans le fichier
artists.php, incluez le fichierinc/database.inc.phpà l'aide de la fonction require_once. - Créez une instance de la classe
DatabaseManager.dsn:mysql:host=mysql;dbname=lowify;charset=utf8mb4username:lowifypassword:lowifypassword
- Utilisez la méthode
executeQuery()pour récupérer tous les artistes depuis la tableartist. - Générez le contenu HTML pour afficher la liste des artistes. Pour chaque artiste, affichez son nom et sa cover.
- Mettez à jour le contenu HTML de la page avec la liste des artistes.
Une fois que vous avez terminé, rendez-vous sur http://localhost/artists.php pour vérifier que la liste des artistes s'affiche correctement avec leurs noms et covers.
Détail d'un artiste
Maintenant que nous avons une page qui liste tous les artistes, nous allons créer la page artist.php qui affichera le détail d'un artiste spécifique.
Cette page devra afficher les informations suivantes :
- Nom de l'artiste
- Biographie
- Cover
- Nombre d'auditeurs mensuels formatté
- Top titres (les 5 chansons les mieux notées de l'artiste) avec leurs noms, durées et notes
- Albums (tous les albums de l'artiste) avec leurs noms, covers et année de sortie
Pour savoir quel artiste afficher, la page recevra un paramètre GET id qui contiendra l'identifiant de l'artiste.
Consigne
- Créez le fichier
artist.phpà la racine du dossierapp. - Importez les fichiers nécessaires et initialisez la page et la base de données comme dans l'exercice précédent.
- Récupérez le paramètre GET
idpour savoir quel artiste afficher. - Utilisez la méthode
executeQuery()pour récupérer les informations de l'artiste depuis la tableartist. - Récupérez les 5 chansons les mieux notées de l'artiste depuis la table
song.- Ordonnées par le champ
noteen ordre décroissant et limité à 5 résultats. - Vous devrez aussi récupérer les covers de chaque chanson en joignant la table
album.
- Ordonnées par le champ
- Récupérez tous les albums de l'artiste depuis la table
album.- Ordonnés par le champ
release_dateen ordre décroissant.
- Ordonnés par le champ
- Générez le contenu HTML pour afficher les informations de l'artiste.
- Vous devrez formatter le nombre d'auditeurs mensuels en mode
1kpour1 000,1.5Mpour1 500 000, etc.
- Vous devrez formatter le nombre d'auditeurs mensuels en mode
- Générez le contenu HTML pour afficher les top titres et les albums.
- Pour chaque top titre, affichez son nom, sa durée et sa note.
- La durée doit être formatée en
mm:ss(minutes:secondes). - La cover de chaque titre doit être la cover de l'album auquel il appartient.
- Générez le contenu HTML pour afficher les albums.
- Pour chaque album, affichez son nom, sa cover et son année de sortie.
- Mettez à jour le contenu HTML de la page avec les informations de l'artiste, les top titres et les albums.
- Affichez la page à l'aide de la méthode
render(). - Modifiez le fichier
artists.phppour que chaque nom d'artiste soit un lien vers la pageartist.php?id=ARTIST_ID.
Gestion des erreurs
Actuellement, notre page artist.php ne gère pas les erreurs. Par exemple, si un utilisateur essaie d'accéder à un artiste qui n'existe pas, la page affichera une erreur PHP.
Nous allons améliorer cela en redirigeant vers la page error.php avec un message d'erreur approprié lorsque l'artiste n'existe pas.
Consigne
- Créez le fichier
error.phpà la racine du dossierapp. - Dans ce fichier, incluez le fichier
inc/page.inc.phpet initialisez la page comme dans les exercices précédents. - Récupérez le paramètre GET
messagequi contiendra le message d'erreur à afficher. - Générez le contenu HTML pour afficher le message d'erreur et une redirection vers la page d'accueil.
- Mettez à jour le contenu HTML de la page avec le message d'erreur.
- Affichez la page à l'aide de la méthode
render(). - Dans le fichier
artist.php, après avoir récupéré les informations de l'artiste, vérifiez si l'artiste existe.- Si l'artiste n'existe pas (le tableau retourné est vide OU il y a eut une erreur), redirigez vers
error.phpavec un message d'erreur approprié.
- Si l'artiste n'existe pas (le tableau retourné est vide OU il y a eut une erreur), redirigez vers
Affichage d'un album
Nous allons maintenant créer la page album.php qui affichera le détail d'un album spécifique :
- Nom de l'album
- Nom de l'artiste avec un lien vers sa page
- Cover
- Date de sortie
- Liste des titres de l'album avec
- Nom
- Durée formatée en
mm:ss - Note
Comme pour la page artiste, la page album recevra un paramètre GET id qui contiendra l'identifiant de l'album à afficher.
Consigne
- Créez le fichier
album.phpà la racine du dossierapp. - Dans ce fichier, incluez le fichier
inc/page.inc.phpet initialisez la page comme dans les exercices précédents. - Récupérez le paramètre GET
idpour savoir quel album afficher. - Utilisez la méthode
executeQuery()pour récupérer les informations de l'album depuis la tablealbum.- Si l'album n'existe pas, redirigez vers
error.phpavec un message d'erreur approprié.
- Si l'album n'existe pas, redirigez vers
- Récupérez les informations de l'artiste associé à l'album depuis la table
artist. - Récupérez tous les titres de l'album depuis la table
song.- Ordonnés par le champ
idpour garder l'ordre d'ajout.
- Ordonnés par le champ
- Générez le contenu HTML pour afficher les informations de l'album, de l'artiste et la liste des titres.
- Mettez à jour le contenu HTML de la page avec les informations de l'album.
- Affichez la page à l'aide de la méthode
render(). - Modifiez le fichier
artist.phppour que chaque nom d'album soit un lien vers la pagealbum.php?id=ALBUM_ID.
Astuce
Si vous êtes bon en SQL, vous pourriez récupérer toutes les informations en 2 voir une seule requête.
Accueil et recherche
Maintenant que nous avons les pages pour afficher les artistes et les albums, nous allons créer la page d'accueil index.php qui affichera :
- Un champ de recherche permettant de rechercher des artistes, albums et chansons par nom.
- Les 5 artistes les plus populaires ("Top trending" basé sur le nombre d'auditeurs mensuels).
- Les 5 albums les plus récents ("Top sorties" basé sur la date de sortie).
- Les 5 albums les mieux notés ("Top albums" basé sur la note moyenne des chansons de l'album).
Nous allons déjà implémenter l'affichage des sections "Top trending", "Top sorties" et "Top albums".
Consigne
- Créez le fichier
index.phpà la racine du dossierapp. - Importez les fichiers nécessaires et initialisez la page et la base de données comme dans les exercices précédents.
- Récupérez les 5 artistes les plus populaires depuis la table
artist.- Ordonnés par le champ
monthly_listenersen ordre décroissant et limité à 5 résultats.
- Ordonnés par le champ
- Récupérez les 5 albums les plus récents depuis la table
album.- Ordonnés par le champ
release_dateen ordre décroissant et limité à 5 résultats.
- Ordonnés par le champ
- Récupérez les 5 albums les mieux notés.
- Vous devrez calculer la note moyenne des chansons de chaque album dans une sous-requête.
- Ordonnés par la note moyenne en ordre décroissant et limité à 5 résultats.
- Générez le contenu HTML pour afficher les sections "Top trending", "Top sorties" et "Top albums".
- Mettez à jour le contenu HTML de la page avec les sections générées.
- Affichez la page à l'aide de la méthode
render().
Nous allons maintenant implémenter la fonctionnalité de recherche dans l'accueil.
Consigne
- Dans le fichier
index.php, dans le HTML de la page, ajoutez un formulaire de recherche avec un champqueryet un bouton de soumission. - Créez le fichier
search.phpà la racine du dossierapp. - Importez les fichiers nécessaires et initialisez la page et la base de données comme dans les exercices précédents.
- Récupérez le paramètre GET
queryqui contiendra la chaîne de recherche. - Utilisez la méthode
executeQuery()pour rechercher des artistes, albums et chansons dont le nom contient la chaîne de recherche.- Pour chaque type (artiste, album, chanson), vous pouvez effectuer une requête similaire à celle plus bas.
- Générez le contenu HTML pour afficher les résultats de la recherche, regroupés par type (artistes, albums, chansons).
- Pour chaque artiste, vous afficherez son nom et sa cover.
- Pour chaque album, vous afficherez son nom, sa cover, son année de sortie et le nom de l'artiste.
- Pour chaque chanson, vous afficherez son nom, la durée formatée en
mm:ss, la note, le nom de l'album et le nom de l'artiste.
- Mettez à jour le contenu HTML de la page avec les résultats de la recherche.
- Affichez la page à l'aide de la méthode
render(). - Au clic sur les artistes et albums, redirigez vers leurs pages respectives (
artist.php?id=ARTIST_IDetalbum.php?id=ALBUM_ID). - Modifiez le formulaire de recherche dans
index.phppour qu'il soumette les données verssearch.php.
Astuce
Voici un exemple de requête SQL pour récupérer les artistes via une recherche sur leurs nom :
SELECT *
FROM artist
WHERE (
MATCH(name) AGAINST(:search IN NATURAL LANGUAGE MODE) OR
name LIKE :search
)Cette méthode de recherche est rudimentaire et ne devrait pas être utilisée en production.
A la place, utilisez un moteur de recherche dédié comme MeiliSearch ou Typesense.
Bonus
Modification du schéma de base de données
Pour effectuer les parties suivantes, vous allez avoir besoin de modifier le schéma de la base de données.
Voici la requête SQL pour faire les modifications nécessaires :
ALTER TABLE `song`
ADD COLUMN `is_liked` tinyint(1) NOT NULL DEFAULT 0
AFTER `duration`;
DROP TABLE IF EXISTS `playlist`;
DROP TABLE IF EXISTS `x_playlist_song`;
CREATE TABLE `playlist` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`duration` int NOT NULL DEFAULT 0,
`nb_song` int NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE = InnoDB AUTO_INCREMENT = 379 DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
CREATE TABLE `x_playlist_song` (
`id` int NOT NULL AUTO_INCREMENT,
`song_id` int NOT NULL,
`playlist_id` int NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`),
KEY `song_id` (`song_id`),
KEY `playlist_id` (`playlist_id`),
CONSTRAINT `xps_fk_1` FOREIGN KEY (`song_id`) REFERENCES `song` (`id`),
CONSTRAINT `xps_fk2` FOREIGN KEY (`playlist_id`) REFERENCES `playlist` (`id`)
) ENGINE = InnoDB AUTO_INCREMENT = 379 DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;Gestion des chansons likées
Nous allons gérer la fonctionnalité de "liker" une chanson.
Consigne
- Créez une page
like_song.phpà la racine du dossierapp. - Importez les fichiers nécessaires pour manipuler la base de donnée.
- Récupérez le paramètre GET
idqui contiendra l'identifiant de la chanson à liker/deliker. - Récupérez la chanson depuis la table
songen utilisant l'identifiant. Gérez le cas où la chanson n'existe pas en redirigeant verserror.php. - Si la chanson existe, inversez la valeur du champ
is_liked(0 devient 1 et 1 devient 0). - Mettez à jour la chanson dans la base de données avec la nouvelle valeur de
is_liked. - Redirigez l'utilisateur vers la page précédente (Vous pouvez utiliser
header('Location: ' . $_SERVER['HTTP_REFERER']);).
Gestion des playlists
Nous allons maintenant implémenter la gestion des playlists.
Consigne
- Créez une page
playlists.phpà la racine du dossierapp.- Cette page affichera la liste des playlists existantes avec un lien pour créer une nouvelle playlist.
- Chaque playlist affichera son nom, le nombre de chansons et la durée totale formatée en
hh:mm:ss. - Les playlist pourront être supprimés via un bouton de suppression.
- Créez une page
playlist.phpà la racine du dossierapp.- Cette page affichera le détail d'une playlist spécifique.
- Elle recevra en paramètre GET l'identifiant de la playlist à afficher.
- La page affichera le nom de la playlist, le nombre de chansons, la durée totale formatée en
hh:mm:sset la liste des chansons avec leurs noms, durées formatées enmm:ss, notes, et un bouton pour supprimer la chanson de la playlist.
- Créez une page
create_playlist.phpà la racine du dossierapp.- Cette page contiendra un formulaire pour créer une nouvelle playlist en saisissant son nom.
- Lors de la soumission du formulaire, la nouvelle playlist sera ajoutée à la base de données.
- Créez une page
add_to_playlist.phpà la racine du dossierapp.- Cette page permettra d'ajouter une chanson à une playlist.
- Elle recevra en paramètre GET l'identifiant de la chanson à ajouter.
- La page affichera un formulaire avec la possibilité de choisir une playlist existante pour y ajouter la chanson.
- Lors de la soumission du formulaire, la chanson sera ajoutée à la playlist sélectionnée (via
x_playlist_song), et la durée et le nombre de chansons de la playlist seront mis à jour.
- Créez une page
remove_from_playlist.phpà la racine du dossierapp.- Cette page permettra de supprimer une chanson d'une playlist.
- Elle recevra en paramètre GET l'identifiant de la chanson et de la playlist.
- La chanson sera supprimée de la playlist dans la table
x_playlist_song, et la durée et le nombre de chansons de la playlist seront mis à jour.
- Créez une page
delete_playlist.phpà la racine du dossierapp.- Cette page permettra de supprimer une playlist.
- Elle recevra en paramètre GET l'identifiant de la playlist à supprimer.
- La playlist sera supprimée de la base de données ainsi que toutes les associations avec les chansons dans la table
x_playlist_song.
- Partout où il y a des chansons d'affichés (détail artiste, détail album, recherche, etc.), ajoutez un lien ou un bouton pour ajouter la chanson à une playlist (redirigeant vers
add_to_playlist.php?id=SONG_ID).