Lien du dernier cycle hébergé
(seulement accessible avec le VPN de l'IUT)
Dans le cadre de mon projet de fin de 2e année en MMI, j’ai développé seul un réseau social reprenant les fonctionnalités essentielles de Twitter et d’Instagram. Ce projet m’a permis de mobiliser mes compétences en design UX/UI, gestion de projet et en partie de la stratégie de communication, mais principalement en développement web full stack (front-end et back-end).
L’objectif était de concevoir une plateforme permettant aux utilisateurs de publier du contenu textuel ou visuel (photos ou vidéos), de les partager et d’interagir entre eux en mettant des commentaires. Chaque utilisateur dispose d’un profil personnalisé où sont regroupées ses publications, et peut s’abonner à d’autres comptes afin de suivre leur contenu plus facilement.
Tout au long du projet, j'ai pensé à veiller sur la sécurité de chaque utilisateur. Il est en effet possible de bloquer les autres utilisateurs et les administrateurs de la plateforme peuvent censurer des contenus ou désactiver un compte.
Afin de démontrer ma maîtrise des frameworks, j’ai utilisé React et TailwindCSS pour la partie front-end (client) et Symfony pour la partie back-end (serveur). Pour faciliter l’intégration entre les deux environnements durant le développement, j’ai mis en place une architecture basée sur Docker, assurant une communication fluide et un déploiement simplifié. entre le serveur, le client et la base de données.
Le déroulement du projet
Le projet s’est déroulé sur 5 semaines.Pour les 4 premières semaines, j'avais au minimum de 8 heures par semaine dédiées à son avancement avec des cours en parallèle. La dernière semaine était entièrement consacrée à la finalisation du projet. Les cours en développement front-end et back-end ont été essentiels pour acquérir les compétences techniques nécessaires à la bonne réalisation de cette application.
Les différentes étapes de ce projet ont été divisés en cycles et chaque cycle contient des petites itérations. Pour compléter un cycle, il faut avoir fini toutes les petites itérations et il n'est pas possible de passer au cycle suivant sans avoir fini le cycle précédent en l'ayant hébergé. Pour ma part, j'ai réussi à terminer les cycles A, B et C.
Les maquettes (UI/UX)
Ensemble des composants (dernier cycle réalisé)
Pour déterminer quels composants je vais avoir besoin d'utiliser dans mon application, je suis tout d'abord passé sur Figma pour créer des maquettes pour ensuite les recréer au pixel près (ou presque, si je voyais des améliorations possible je les faisais directement). Chaque composant a des variantes différentes pour prévoir les changements d'état et animations. Le framework React utilise ce même système aussi, chaque composant React peut contenir des fonctions permettant de créer plusieurs variantes ou états d'un composant pour ne pas se retrouver avec trop de compsants faisant quasiment la même chose. Cette étape de la conception de la maquette m'a permis de valider l'apprentissage critique AC21.05 : Cartographier les expériences utilisateur : points de contact, points de friction et de satisfaction, carte d’empathie. et en partie l'AC22.01 : Co-concevoir un produit ou un service (proposition de valeur, fonctionnalités...).
J'ai ensuite placé ces compsants sur des pages en format mobile pour créer une maquette fonctionnelle. N'ayant pas de maquette bureau, j'ai développé l'application en mobile first (en faisant d'abord la version mobile) et ensuite j'ai adapté le format bureau pour avoir un rendu correcte. J'ai réalisé une maquette au début de chaque site avant de commencer le développement. En finissant un cycle, je réalisais la maquette du cycle suivant en complétant petit à petit la page des composants. Dans le monde professionnel, la maquette permet de montrer au client une version "croquis" du projet avant d'entamer la aprtie développement, permettant des éventuelles retouches. J'ai validé les apprentissages critiques AC21.04 : Identifier et décrire les parcours client à partir d’enquêtes de terrain et AC22.02 : Produire une recommandation ergonomique à partir des tests utilisateurs (sur système fonctionnel, prototype ou maquette interactive).
En réalisant des maquettes de ce type dans la vie professionnelle, j'anticiperais l’interface utilisateur pour ensuite valider les choix de design. Cette étape a été intégrée dans une démarche d’amélioration continue : les maquettes ont évolué en fonction des retours et des contraintes identifiées au cours du développement. Cela m’a permis de mieux planifier les tâches, d’optimiser l’ergonomie et de gagner du temps en phase de codage. Cette phase m'a également permis de valider l'apprentissage critique AC25.01 – Gérer un projet avec une méthode d’amélioration continue.
Maquette fonctionnelle du dernier cycle réalisé
Le développement front-end (client)
la structure des fichiers
Structure de la partie front.
Le dossier public contient les fichiers statiques du projet, notamment les assets tels que les photos de profil et les arrière-plans des utilisateurs (ces fichiers sont ensuite transférés et gérés côté back-end, comme expliqué plus loin).
Le dossier src regroupe l’ensemble du code source, avec des fichiers .tsx correspondant aux composants React et des fichiers .ts dédiés aux fonctions et à la logique du projet.
Pour faciliter la configuration de l’application, j’ai utilisé un fichier .env afin d’y définir l’URL de l’API. Cette méthode permet de centraliser les variables d’environnement et de les réutiliser dans tout le projet, rendant ainsi le code plus modulable et facile à maintenir. Modifier l’adresse de l’API ne nécessite alors qu’un simple changement dans ce fichier, sans avoir à intervenir manuellement dans chaque fichier source.
Les fichiers de configuration (fichiers .json et .js) sont générés automatiquement lors de l’initialisation du projet via NPM (Node Package Manager). Ils contiennent la liste des dépendances, les scripts de gestion du projet, ainsi que diverses informations nécessaires au bon fonctionnement de l’application. NPM est un gestionnaire de paquets utilisé pour installer, gérer et exécuter des modules JavaScript. Pour démarrer un projet React avec TypeScript, j’ai utilisé la commande npx create-react-app nom-du-projet --template typescript pour créer le projet React et npm run dev pour lancer le projet en local pour développer dessus.
Pour faciliter le déploiement et l’environnement de développement, j’ai utilisé Docker et configuré un fichier docker-compose.yml. Ce fichier permet de définir et de gérer plusieurs services (appelés conteneurs) nécessaires au bon fonctionnement de l’application, comme le front-end, le back-end, ou encore une base de données, à partir d’un seul point de configuration. Ce fichier permet notamment de lancer automatiquement le serveur React pour le front-end, démarrer le serveur Symfony pour le back-end (via PHP et Apache/Nginx), configurer les ports, volumes, et variables d’environnement nécessaires et Assurer une communication fluide entre les services via un réseau interne Docker.
Le dossier src est organisé de manière modulaire pour garantir une meilleure lisibilité et maintenabilité du code. Ce dossier contient les sous-dossiers suivants :
assets : contient les éléments liés aux visuels et aux composants graphiques tels que les icônes.
components : regroupe l’ensemble des pages et des composants complexes de l’application.
context : contient un fichier AuthContext, qui centralise les fonctions nécessaires à la gestion de l’authentification (connexion, déconnexion, utilisateur actif, etc.).
interfaces : stocke les interfaces TypeScript, notamment les props des composants, afin d’assurer un typage clair et cohérent.
ui : regroupe les composants d’interface utilisateur réutilisables (boutons, champs, etc.), souvent légers et purement visuels.
utils : contient des fonctions utilitaires, dont une fonction cn permettant de gérer dynamiquement les variantes de styles CSS via des classes conditionnelles.
Cette organisation permet une séparation claire des responsabilités et favorise la réutilisabilité du code au sein du projet. En triant mon projet de manière propre, je contribue à la validation de l'apprentissage critique AC24.04 – Modéliser les traitements d’une application Web et AC25.01 – Gérer un projet avec une méthode d’amélioration continue.
Contenu du dossier src.
Extraits des codes du framework utilisé
Structure du composant profil (profil.tsx)
Avant la déclaration de chaque composant, j’importe tous les éléments externes nécessaires à son fonctionnement. Cela inclut notamment :
des hooks React comme useParams, useEffect et useState,
des composants personnalisés comme NavigationBar, Post et ProfileHead,
le hook personnalisé useAuth, provenant du fichier de contexte dédié à la gestion de l’authentification.
Dans la fonction Profile, je commence par initialiser les variables nécessaires. Je récupère ensuite les informations de l’utilisateur connecté (ainsi que son token) à partir du hook useAuth, afin d’assurer le bon affichage et la sécurité des données liées au profil.
J'utilise le hook useEffect pour récupérer le statut de blocage entre l’utilisateur connecté et l’utilisateur dont on visite le profil. Cette logique se déclenche à chaque fois que l’userId ou le token change.
Pour optimiser les performances, je vérifie si les données sont déjà présentes dans le localStorage avant d’effectuer un appel à l’API.
Ensuite, je fais un appel API sécurisé (avec un Bearer token) pour obtenir le statut de blocage en temps réel. Si les données retournées sont valides, je mets à jour les états React isBlocked et blockedMe pour gérer dynamiquement le rendu de la page. Les nouveaux résultats sont stockés dans le localStorage avec des clés spécifiques à l’utilisateur (isBlocked_userId et blockedMe_userId) afin d’être réutilisés.
Ensuite, deux hooks useEffect sont utilisés pour récupérer dynamiquement les informations liées à un profil utilisateur. Le premier est exécuté à chaque fois que l’userId change. Il effectue une requête HTTP GET pour récupérer les informations publiques du profil visité, comme le nom d’utilisateur, la biographie, la localisation, le nombre d’abonnés et abonnements, l'avatar et la bannière. Les chemins relatifs des images sont convertis en URLs absolues pour pouvoir les afficher correctement. Ces données sont ensuite stockées dans l’état local profileData.
Ce second effet est déclenché une fois que l’userId est défini avec un token d’authentification disponible et que les données du profil ont été chargées. Il vérifie si l’utilisateur connecté suit le profil visité, en envoyant une requête sécurisée via un token JWT (JSON Web Token). Le résultat de data.is_following est stocké dans l’état isFollowing, utilisé ensuite pour afficher dynamiquement le bon bouton (s'abonner / se désabonner).
Ce dernier useEffect charge les publications d’un utilisateur dès que son profil est affiché. Si l’utilisateur est bloqué ou s’il nous a bloqué, les publications ne sont pas affichées. L’appel API est protégé par un token et les publications sont ensuite enregistrées dans l’état local posts.
la fonction handleFollowToggle permet à l’utilisateur de s'abonner ou se désabonner d’un autre profil que le sien. Une requête POST ou DELETE est envoyée en fonction de l’état actuel (isFollowing). Si la requête réussit, l’état isFollowing est mis à jour, ainsi que le compteur d’abonnés dans les données du profil.
La fonction handleBlockToggle gère le blocage ou déblocage d'un utilisateur. Lorsque l’utilisateur clique sur le bouton de blocage ou déblocage, une requête est envoyée à l’API. L’action, dynamiquement choisie, est POST pour bloquer et DELETE pour débloquer. En cas de succès, les états React isBlocked et blockedMe sont mis à jour. Le localStorage est synchronisé pour limiter les appels API à l’avenir. Si l’utilisateur est bloqué, une requête supplémentaire est faite pour le désabonner automatiquement (le supprimer de la liste des abonnements et abonnés), assurant une cohérence dans la logique du réseau social.
La condition if (!profileData) return <p>Loading...</p>; permet d’éviter que le composant s’exécute avant que les données du profil soient disponibles. Elle renforce la stabilité du rendu et améliore l'expérience utilisateur en lui indiquant que des données sont en train de charger plutôt que de lui laisser devant une page blanche.
Le bloc "return" représente l’interface complète d’un profil utilisateur, avec navigation, en-tête personnalisé, et liste de publications. La page commence par le composant <NavigationBar /> (barre de navigation), commun à toutes les pages de l'application. Le conteneur principal est centré (items-center) et avec un flex vertical (flex-col) pour une bonne organisation responsive.
Le composant <ProfileHead /> affiche toutes les informations de la tête du profil : pseudo, bio, avatar, bannière, etc. Il reçoit aussi des fonctions comme handleFollowToggle et handleBlockToggle pour gérer les interactions en direct depuis l’UI. L’attribut isCurrentUser permet de modifier l’affichage si l’utilisateur regarde son propre profil.
Si l’utilisateur consulté a bloqué l’utilisateur connecté, un message empêche tout accès à son contenu. Cette vérification conditionnelle remplace l'affichage d'une erreur 404 et permet une expérience sécurisée et logique.
Si l’utilisateur n’a aucune publication, un message le précise. Sinon, chaque post est rendu dynamiquement via le composant <Post />, avec passage de toutes les données nécessaires : contenu, auteur, date, fichiers.
Le message "Loading more posts..." est affiché si le chargement est en cours, ce qui améliore la réactivité visuelle.
En utilisant le chargement conditionnel pour le chargement des pages et commentaires, pré-chargement asynchrone avec useEffect pour récupérer les données seulement quand on en a besoin et l'affichage optimisé selon si le contenu à afficher est une image ou une vidéo, j'ai répondu à la validation des apprentissages critiques AC24.05 : Optimiser une application web (référencement et temps de chargement) et AC25.05 Maitriser les codes des productions écrites et orales professionnelles.
Requêtes API (composant Post.tsx)
Le composant Post.tsx contient le contenu d'une publication ainsi que les fonctionnalités d'intéraction correspondantes, comme la liker (aimer), mettre un commentaire, la modifier ou la supprimer. La première fonction permet d’afficher dynamiquement le nombre de likes d’un post, de savoir si l’utilisateur connecté l’a liké, et d’interagir avec l’API pour ajouter ou retirer un like. Dans cet exemple, le back-end est le localhost:8080 mais pour le déploiement j'ai utilisé un fichier .env pour appliquer une base URL dans tous les fichiers faisant appel à l'API (voir partie déploiement).
La fonction handleLike empêche l’utilisateur de liker une publication en affichant une alerte s'il n'est pas connecté. L’interface est instantanément mise à jour sans attendre la réponse du serveur. Une requête POST ou DELETE est envoyée selon l’action (POST pour like ou DELETE pour unlike). Cette pratique ne re-télécharge pas les données, permettant d'améliorer la performance.
La fonction handleDeletePost est la fonction qui permet à un utilisateur de supprimer ses propores publications. En cliquant sur le bouton "supprimer" (icône poubelle), une boîte de confirmation native est affichée (utilisation de window.confirm). Si l'utilisateur confirme, elle envoie une requête DELETE à l'API pour supprimer le post et recharge la page automatiquement si la suppression est réussie. Mettre un message de confirmation permet à l'utilisateur de revenir sur sa décision s'il change d'avis avant de confirmer.
La fonction handleEditPost renvoie l'utilisateur vers la page lui permettant de modifier la publication en cliquant sur l'icône "modifier (icône crayon). Il prend en paramètre l'ID de la publication sélectionné pour indiquer à l'API quelle publication il faudra modifier. J'ai choisi cette méthode plutôt que la modification sur la page des publications pour permet une séparation claire entre lecture et édition.
La fonction NavigateToProfile redirige l'utilisateur vers le profil de l’auteur de la publication. Il suffit de cliquer sur son nom ou son avatar.
La fonction updateCommentCount Récupère tous les commentaires d’un post depuis l’API et met à jour de manière dynamique le nombre de commentaires affichés, sans avoir besoin de recharger la page si des nouveaux commentaires sont publiés.
Mon application utilise une logique d'appel API avec GET pour récupérer des données, POST pour publier des données et DELETE pour supprimer des données. J'utilise également un affichage conditionnel selon les utilisateurs (si l'utilisateur est propriétaire du profil et publications affichés) et j'organise le flux de données sous un format identique au MVC (Model View Controller) pour récupérr les données, gérer les intéractions et ensuite afficher au client. Cette étape m'a permis de valider l'apprentissage critique AC24.04 – Modéliser les traitements d’une application Web.
Côté intégration des publications (composant Post.tsx)
Pour concevoir une interface moderne, responsive et rapidement modifiable, j’ai choisi d’utiliser Tailwind CSS pour le style de la page. Tailwind permet une intégration directe du style dans les classes HTML, facilitant le prototypage rapide et l’uniformité visuelle, tout en gardant le contrôle facile sur chaque élément.
Cette première partie de la publication affiche le contenu textuel et multimédia avec un support des images et vidéos et la navigation vers le profil de l’auteur via l’avatar ou le pseudo. Les classes tailwind (ex: p-5, rounded-4xl, text-xl) eont utilisées pour le design.
Cette partie gère l'affichage conditionnel des icônes de modification et suppression si l’utilisateur connecté est l’auteur et la fonctionnalité de liker un post ou d’afficher les commentaires. Des composants d'UI comme le Like, CommentIcon, DeleteIcon et PostComment sont utilisés pour ne pas surcharger le composant principal.
Résultat final
Je suis satisfait du résultat que j'ai pu obtenir de l'interface de mon application web. Rendre mon application responsive et adapté à tous les utilisateurs m'a permis de valider l'apprentissage critique AC24.01 : Produire des pages et applications Web responsives.
Proposer à l'utilisateur l'option de supprimer ses publications, commentaires ou de changer sa photo de profil ou l'enlever permet de répondent au respect du RGPD (Règlement Général sur la Protection des Données): la réglementation européenne sur le traitement des données de manière égalitaire sur tout le territoire de l'Union européenne. Veiller à ce que mon application respecte cette réglementation m'a permis de valider l'apprentissage critique AC25.06 : Prendre en compte les contraintes juridiques.
La base de données (SQL)
Les tables de la base de données
Sur PHPMyAdmin, j'ai créé une base de données en utiliusant Doctrine (qui sera explqué dans la partie back-end). Cette base contient les tables suivantes :
comment : les données pour les commentaires (id, id utilisateur, id publication, contenu, date)
doctrine_migrate_versions : une liste des migrations Doctrine ainsi que leurs dates.
follow : les données des abonnements (id utilisateur qui s'abonne, id utilisateur abonné).
likes : les données sur les likes. (id, id utilisateur, id publication, date)
post : les publications (id, contenu, date de création, id utilisateur, chemins vers les fichiers).
token : les tokens reliés à leurs utilisateurs (token, ID utilisateur, date, booléan sur la validité).
user : les données sur les utilisateurs (id, pseudo, adresse mail, mod de passe hashé, booléan sur la vérification par mail, rôles, abnnière, photo de profil, localisation, biographie, site web).
user_block : les données sur les blocages (ID utilisateur qui bloque, ID utilisateur bloqué, date).
La structuration de cette base est le résultat d’une analyse des besoins fonctionnels du projet. Elle respecte les principes de modélisation relationnelle (clé primaire, relations, intégrité des données) et permet une extensibilité facile en cas de nouvelles fonctionnalités. Cette structure m'a permis d'implémenter tous les cycles depuis le cycle A sans avoir à modifier les tables du cycle précédent. La création de la base de données du projet m'a permis de valider les apprentissage critique AC24.04 – Modéliser les traitements d’une application Web et AC25.01 – Gérer un projet avec une méthode d’amélioration continue (en complément des maquettes).
Le développement back-end (serveur/API)
La structure des fichiers
La structure de la partie back-end de mon projet est la suivante :
Ressources : contient le fichier SQL que j’ai utilisé pour des exports, sauvegardes ou réinitialisations de la base.
bin : contient le fichier console nécessaire pour exécuter les commandes Symfony et Doctrine (créer des entités, lancer des migrations, etc.).
config : dossier central de configuration de Symfony, contenant les réglages globaux de l'application (services.yaml, routes.yaml, etc.) et les configurations spécifiques des packages (Doctrine, Mailer, CORS, etc)
migrations : contient les migrations Doctrine : des fichiers PHP générés automatiquement lorsque je crée ou modifie une entité. Ces migrations permettent de créerou modifier la base de données sans écrire manuellement de SQL.
public : Contient les fichiers accessibles publiquement via l'URL, comme les avatars d'utilisateur, les bannières, les médias associés aux publications (images/vidéos). C’est également ici que Symfony place le point d’entrée principal : index.php.
src : dossier principal du code source :
Controller : les classes contrôleurs (ex : PostController.php, UserController.php).
Entity : les entités Doctrine (représentation objet des tables de la BDD).
Form : gérer l'inscription d'un utilisateur en vérifiant son email, l'acceptation des conditions et la sécurité du mot de passe.
Repository : les classes de requêtes personnalisées liées aux entités.
Security : les classes responsables de la gestion de l'authentification, de la vérification d'email et du comportement lors des connexions réussies ou échouées.
Service : mise à jour du profil et ajout de fichiers.
templates : contient des fichiers Twig utilisés au tout début pour tester l'enregistrement via des pages HTML. Tout se fait désormais via le front-end React, donc ces fichiers ne sont plus utilisés, mais ont servi pour la phase de prototypage.
.env / .env.dev : fichiers de configuration des variables d’environnement (env.dev est celui utilisé pour la phase de développement). Ces fichiers permettent la connexion à la base de données, la configuration CORS (accès depuis le front local) et la configuration du mailer utilisé dans les tests. Mailer est uun outil qui permet de recevoir des mails en local dans le projet.
composer.json / composer.lock : liste des dépendances PHP nécessaires au projet (Symfony, Doctrine, etc.). composer.lock verrouille les versions utilisées pour garantir la cohérence entre environnements.
symfony.lock : fichier généré par Symfony qui fixe les versions des composants utilisés.
Comme pour la partie front-end, mon organisation permet de séparer clairement les fonctions et responsabilités et favorise la réutilisabilité du code. Je contribue de nouveau à la validation de l'apprentissage critique AC24.04 – Modéliser les traitements d’une application Web et AC25.01 – Gérer un projet avec une méthode d’amélioration continue.
Le fichier .env
Le fichier .env configure la connexion à la base de données MySQL avec Symfony en utilisant DATABASE_URL. Il configure également le serveur SMTP pour l’envoi de mails en utilisant MAILER_DSN. La gestion du CORS autorise les appels d’API depuis localhost. Cette configuration m'a permis de valider l'apprentrissage critique AC24.06 : Configurer une solution d’hébergement adaptée aux besoins.
Extraits des codes du back-end
Créer une entité en utilisant Doctrine
Tout d'abord, la commande php bin/console make:entity [Entity] crée l'entité de base. Dans mon cas, pour créer l'entity User j'ai utilisé php bin/console make:entity User dans la console du backend (clic-droit sur le conteneur backend et sélection de "Attach shell"). Ensuite, le suivant s'affiche dans la console :
New property name (press <return> to stop adding fields): username
Field type (enter ? to see all types) [string]: string
Field length [255]: 20
Can this field be null in the database (nullable) (yes/no) [no]: no
Cela signifie que je vais créer une propriété username dans la table User sous forme de string de 20 caractère ne pouvant pas être nul. Par défaut (en tapant la touche entrée sans rien saisir) cela crée un string de 255 caractères ne pouvant pas être nul.
Une fois avoir saisi tous les champs souhaités, il suffit d'appuyer sur la touche entrée. Pour ensuite ajouter d'autres propriétés, il suffit juste de relancer la commance php bin/console make:entity User et refaire exactement la même chose.
Pour ajouter des relations, il suffit de créer une table avec un champ ayant un type comme ManyToOne. Par exemple, pour ajouter une relation entre la table Token et la table User, j'ai fait la commande php bin/console make:entity Token et j'ai ajouté une propriété user avec une relation ManyToOne vers User.
Pour exécuter cette migration, j'ai utilisé la commande php bin/console make:migration et ensuite php bin/console doctrine:migrations:migrate. Maintenant, nous pouvons voir que l'entité User est créé avec les propriété que j'ai demandées :
La fonction getUsername est un getter et la fonction setUsername est un setter. Le getter permet de récupérer le pseudo et le setter permet d'ajouter un pseudo. Ces fonctions peuvent ensuite être utilisées dans les controllers.
Exemple d'un Controller
Le fichier Controller\UserController.php regroupe l'ensemble des routes (endpoints) de l'API liées aux actions utilisateur.
Prenons l'exemple de la route /users/{id}follow qui permet à un utilisateur connecté de suivre un autre utilisateur via une requête POST. Cette route utilise une fonction FollowUser qui récupère l'utilisateur à suivre et l'utilisateur actuel. En utilisant $this->getUser(), il est possible d'identifier l’utilisateur actuellement connecté.
Pour empêcher un utilisateur de se suivre lui-même, une erreur 400 est retournée si l'utilisateur essaye de se suivre lui-même (même si le front bloque cela automatiquement).
Si une relation de suivi existe déjà entre les deux utilisateurs, une erreur 400 est renvoyée pour éviter les doublons (même si encore une fois le front contient déjà une boucle pour déterminer s'il faut utiliser la route pour follow ou unfollow).
Un nouvel enregistrement est créé dans la table follow pour représenter cette relation et l'objet Follow est persisté avec Doctrine et enregistré en base via flush().
Si tout se passe bien, une réponse JSON avec un message de succès est retournée.
Exemple d'un Repository
Le fichier Repository\UserRepository.php contient trois méthodes destinées à interagir avec la base de données pour récupérer des utilisateurs selon des critères précis.
La fonction findByEmail cherche un utilisateur dont l’adresse email correspond à celle passée en paramètre. Elle utilise un QueryBuilder pour construire la requête SQL et retourne soit un objet User, soit null si aucun utilisateur ne correspond.
findByUsername utilise le même principe mais en utilisant le pseudo d'un utilisateur en paramètre.
En développant la partie back-end de mon application, j'ai géré la logique de l'utilisateur. J'ai aussi montré une logique de récupération de données complexe en utilisant des fonctions comme findFollowedUserIds. Les méthodes comme followUser dans le contrôleur illustrent une modélisation métier claire, comme la vérification de l'existence de l’utilisateur, l'interdiction de se suivre soi-même, la vérification si déjà suivi et la création et persistance du lien Follow. Cette partie m'a permis de valider l'apprentissage critique AC24.04 : Modéliser les traitements d’une application Web.
L'utilisation d'un système de vérification par mail et un token sécurisé ainsi que la sécurisation des données personnelles (données du profil) stockées dans une base protégée m'ont permis de valider l'apprentissage critique AC25.06 : Prendre en compte les contraintes juridiques.
Aperçu sur le dashboard d'administrateur (backoffice)
Pour permettre aux administrateurs de modifier les informations d'un compte utilisateur (pseudo, mail mais pas le mot de passe) ainsi que lui bloquer l'accès ou censurer des posts, j'ai implémenté un dashboard d'administrateur. L'accès à cette interface est bloquée si l'utilisateur connecté ne possède pas le rôle "ROLE_ADMIN". Si l'administrateur a fini de faire des modifications, il peut cliquer sur un bouton pour se rediriger vers la page d'accueil.
La boucle qui bloque l'accès au dashboard pour les utilisateurs n'ayant pas le rôle "ROLE_ADMIN".
Le bouton dans les paramètres donnant accès au dashboard.
L’implémentation du dashboard administrateur renforce la sécurité de l’application en permettant de modérer les comptes ou contenus ne respectant pas les règles, afin de protéger les autres utilisateurs. Son implémentation m'a permis de valider les apprentissages critiques AC24.02 — Mettre en place ou développer un back office et AC25.06: Prendre en compte les contraintes juridiques.
Le déploiement (sur mmi.unilim.fr)
Une fois le développement terminé, j’ai généré une version optimisée de mon front-end pour la production grâce à la commande npm run build. Cette étape prépare les fichiers pour un déploiement performant et garantit un rendu fluide de l’application côté client. Cette commande génère un dossier dist contenant tous les fichiers nécessaires au déploiement du front-end sur un serveur. Je me suis ensuite connecté à mmi.unilim.fr en utilisant le protocole SFTP grâce à l'application FileZilla.
Pour héberger le back-end, j'ai cloné le projet sur le serveur en utilisant le protocole SSH (Secure Shell) sur VSC (Visual Studio Code). J'ai mis un fichier .env.local à la racine avec le contenu suivant :
APP_ENV=dev : nécessaire pour les droits sur le serveur).
DATABASE_URL : adapté à l’utilisateur MySQL de l’université)
CORS_ALLOW_ORIGIN : limité à https://mmi.unilim.fr
MAILER_DSN avec le SMTP smtp.unilim.fr.
FRONT_URL défini selon le domaine de production
J'ai ensuite installé les dépendances en utilisant la commande composer install --optimize-autoloader et lancé les migrations Symfony (commandes php bin/console make:migration et php bin/console doctrine:migrations:migrate) pour générer la structure.
Pour configurer Apache, j'ai, ajouté un fichier .htaccess dans le dossier public du dossier backend, avec une RewriteBase pointant vers le chemin de l’utilisateur :
En adaptant la configuration selon l’environnement (dev/prod), en gérant les variables sensibles via des fichiers .env.local, en utilisant un fichier .htaccess pour assurer la compatibilité avec le serveur Apache, et en déployant l’application sur un hébergement mutualisé avec ses contraintes spécifiques (droits, accès restreint, etc.), j’ai validé l’apprentissage critique AC24.06 : Configurer une solution d’hébergement adaptée aux besoins.
Bilan personnel
Ce projet m'a permis de comprendre comment fonctionne une application web en utilisant des frameworks plutôt qu'utiliser du pur HTML, CSS, JS et PHP. Malgré les problèmes que j'ai eu pour déployer mon site sur un serveur, comme les problèmes de chemins, j'ai développé un réseau social en me conformant aux standards du Web et aux normes d'accessibilité en veillent à vérifier que mon site soit sobre et responsive, et en veillant à la sécurité des systèmes et des données pour garantir la sécurité des utilisateurs et en respectant le droit et leur vie privée.