Bonjour,
Bienvenue dans ce tutoriel pour apprendre à utiliser le framework web Adonis ! Si tu souhaites en savoir plus sur Adonis en 1 coup œil, je t'invite à lire cette page.
Dans cette partie, on va voir ensemble comment créer un article, le lier à un utilisateur et afficher sur une page !
Rappel
Ce tutoriel est la cinquième et dernière partie d'une série de tutoriels qui ont pour objectif de te faire découvrir Adonis au travers la création d'un blog.
Pour lire la partie précédente, c'est par là Création et visualisation des articles - Créer un blog avec Adonis
Tu trouveras aussi sur GiHub l'ensemble du code source du projet !
Sommaire
Ce tutoriel est découpé en différente partie pour t'aider et pour éviter d'avoir des articles trop longs où l'on pourrait se perdre !
Nous allons donc voir ensemble :
- Objectifs et mise en route - Créer un blog avec Adonis
- Création d'un utilisateur - Créer un blog avec Adonis
- Création de l'authentification pour l'utilisateur - Créer un blog avec Adonis
- Création et visualisation des articles - Créer un blog avec Adonis
- Gestion des articles - Créer un blog avec Adonis
Finalement, tu auras un blog fonctionnel !
Gestion des articles
Comme le chapitre précédent, nous allons faire ce chapitre sous la forme d'un exercice. Je vais t'indiquer les étapes à suivre, te donner des indications et des ressources qui peuvent t'aider pour aller au bout puis nous ferons la correction ensemble !
Pour rappel, nous souhaitons réaliser une route articles/create
qui permet de créer un article. Une fois l'article enregistré, nous devons être redirigé sur la page de l'article. Aussi, nous souhaitons avoir un bouton sur les pages des articles qui permet d'éditer l'article. Une fois l'édition terminée, nous devons être redirigé vers l'article.
Ce qu'il faut faire
La liste suivante est l'ensemble des tâches ordonnées t'indiquant ce qu'il y a à faire pour arriver au bout de cette dernière partie :
- Mettre en place le middleware d'authentification
- Créer les différentes méthodes de gestions des articles pour le
controller
- Il ne faut pas oublier de valider les données entrantes sur le serveur
- Il ne faut pas non plus oublier de gérer les erreurs
- Un indice ici
- Créer les différentes vues pour la création et l'édition d'un article
Gestion des articles
Mise en place du middleware d'authentification
Le middleware pour la gestion de l'authentification est fourni par Adonis. Ainsi, il nous suffit de l'ajouter sur les routes que l'on souhaite protéger par l'authentification.
Pour cela, on se rend dans le fichier routes.ts
du dossier start
:
Route.resource('articles', 'ArticlesController').middleware({
create: ['auth'],
store: ['auth'],
edit: ['auth'],
update: ['auth'],
})
La route create
permet d'afficher le formulaire de création d'un article.
La route store
permet de recevoir les informations de la route create pour stocker l'article dans la base de données.
La route edit
permet d'afficher le formulaire de l'édition d'un article.
La route update
permet de recevoir les informations de la route edit pour mettre à jour un article de la base de données.
Ainsi, il est impossible d'accéder à l'une de ses quatres routes si l'utilisateur n'est pas authentifié.
Création du validator
pour les articles
Encore une fois, utilisons la commande :
node ace make:validator article
Dans le validator, on va vouloir l'unicité des titres de nos articles comme on l'a indiqué dans notre migration. On va aussi spécifier la taille maximale du titre et du contenu. Ainsi, le validator
ressemble à cela :
public schema = schema.create({
title: schema.string({ trim: true }, [
rules.maxLength(256),
rules.unique({ table: 'articles', column: 'title' }),
]),
content: schema.string({ trim: true }, [rules.maxLength(1024)]),
})
Ensuite, on personnalise les messages erreurs et le tour est joué !
public messages = {
'title.required': 'Le titre est requis',
'title.string': 'Le titre doit être une chaîne de caractères',
'title.maxLength': 'Le titre doit être moins de 256 caractères',
'title.unique': 'Le titre existe déjà',
'content.required': 'Le contenu est requis',
'content.string': 'Le contenu doit être une chaîne de caractères',
'content.maxLength': 'Le contenu doit être moins de 1024 caractères',
}
Pour en savoir plus : unique, validator, messages
Création des méthodes du controller des articles
On va donc devoir créer une méthode pour chacune de ses routes. Il est simple de s'y repérer puisque le nom de la méthode est le nom de la route.
Pour create
:
public async create({ view }: HttpContextContract) {
return view.render('articles/create')
}
Rien de sorcier ici, on crée la page contenant le formulaire de création. Nous allons créer la vue dans la suite.
Pour store
, on commence par récupérer les informations dont on a besoin pour créer l'article :
const { user } = auth
const { title, content } = await request.validate(ArticleValidator)
Il est possible de récupérer l'utilisateur grâce au middleware précédemment ajouté. En effet, le middleware vérifie l'identité de la personne et l'ajoute dans nos controllers
. On récupère aussi les informations dont on a besoin, validées par le validator
!
Ensuite, on crée un article :
const article = await Article.create({
title,
content,
ownerId: user!.id,
})
Puis on charge le owner avant de rendre la page d'un unique article à l'utilisateur :
await article.load('owner')
return view.render('articles/show', {
article,
})
Pour finir, la méthode ressemble à cela :
public async store({ request, view, auth }: HttpContextContract) {
const { user } = auth
const { title, content } = await request.validate(ArticleValidator)
const article = await Article.create({
title,
content,
ownerId: user!.id,
})
await article.load('owner')
return view.render('articles/show', {
article,
})
}
Pour edit
, nous allons aussi rendre une page d'édition de l'article. Cependant, avant cela, nous devons utiliser le paramètre présent dans l'url pour récupérer l'article et ajouter son contenu au formulaire afin que ce dernier soit prérempli ! Si le formulaire est vide, comment savoir ce qu'il faut mettre à jour ?
public async edit({ view, params }: HttpContextContract) {
const { id } = params
let article: Article
try {
article = await Article.findOrFail(id)
} catch (error) {
console.error(error)
return view.render('errors/not-found')
}
return view.render('articles/edit', {
article,
})
}
En cas d'erreur pour trouver l'article, et comme on a pu faire sur show
, on renvoie l'utilisateur sur une page d'erreur ! Cela permet de gérer le cas où l'utilisateur rendre une mauvaise valeur directement dans l'url !
Update
est la méthode la plus complexe mais qui ne contient que des éléments que l'on a pu déjà aborder. Dans un premier temps, on récupère l'article que l'on veut mettre à jour :
const { id } = params
let article: Article
try {
article = await Article.findOrFail(id)
} catch (error) {
console.error(error)
return view.render('errors/not-found')
}
Ensuite, on extrait des données du formulaire en les passant dans le validator :
const { title, content } = await request.validate(ArticleValidator)
Puis on met à jour l'article et on le sauvegarde :
article.title = title
article.content = content
await article.save()
Ensuite, on charge l'utilisateur dans l'article et on le redirige vers la page de visualisation de son article :
await article.load('owner')
return view.render('articles/show', {
article,
})
Finalement, la méthode complète ressemble à cela :
public async update({ request, view, params }: HttpContextContract) {
const { id } = params
let article: Article
try {
article = await Article.findOrFail(id)
} catch (error) {
console.error(error)
return view.render('errors/not-found')
}
const { title, content } = await request.validate(ArticleValidator)
article.title = title
article.content = content
await article.save()
await article.load('owner')
return view.render('articles/show', {
article,
})
}
Maintenant que tous nos controllers
sont faits, passons à nos 2 vues !
Création des vues pour la gestion des articles
Les pages create
et edit
vont être très similaires. Elles vont toutes les 2 contenir un formulaire, vide pour la première et avec un article pour la seconde.
Pour create
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Créer un article</title>
</head>
<body>
<form action="{{ route('ArticlesController.store') }}" method="post">
<div>
<label for="title">Titre de l'article</label>
<input type="text" name="title" id="title" value="{{ flashMessages.get('title', '') }}">
@if(flashMessages.has('errors.title'))
<div>{{ flashMessages.get('errors.title') }}</div>
@endif
</div>
<div>
<label for="content">Contenu de l'article</label>
<textarea name="content" id="content" cols="30" rows="10" value="{{ flashMessages.get('content', '') }}"></textarea>
@if(flashMessages.has('errors.content'))
<div>{{ flashMessages.get('errors.content') }}</div>
@endif
</div>
<button type="submit">Créer</button>
</form>
</body>
</html>
Comme pour le formulaire d'authentification, on remarque les if
qui permettent de conditionner l'affichage d'un message d'erreur par le serveur.
Pour edit
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Créer un article</title>
</head>
<body>
<form action="{{ route('ArticlesController.update', {id: article.id}) }}?_method=put" method="post">
<div>
<label for="title">Titre de l'article</label>
<input type="text" name="title" id="title" value="{{ flashMessages.get('title', article.title) }}">
@if(flashMessages.has('errors.title'))
<div>{{ flashMessages.get('errors.title') }}</div>
@endif
</div>
<div>
<label for="content">Contenu de l'article</label>
<textarea name="content" id="content" cols="30" rows="10">{{ flashMessages.get('content', article.content) }}</textarea>
@if(flashMessages.has('errors.content'))
<div>{{ flashMessages.get('errors.content') }}</div>
@endif
</div>
<button type="submit">Modifier</button>
</form>
</body>
</html>
Il y a tout de même quelques subtilités dans ce formulaire. Tout d'avoir cela :
{{ flashMessages.get('content', article.content) }}
Cela permet d'afficher le contenu d'un message flash s'il existe, sinon, le contenu de l'article. Ainsi, lorsque la page est chargée, le message flash est vide donc le contenu de l'article va venir remplir le formulaire. Mais supposons que l'utilisateur modifie des informations mais que ces dernières soient rejetées par le validator
, alors il retrouvera dans son formulaire les données qu'il avait saisi et non les données initiales de l'article !
Ensuite, pour mettre à jour un article, il est obligatoire d'utiliser les verbes PUT
ou PATCH
. Cependant, les navigateurs ne les prennent pas en compte. Ainsi, on utilise ce petit tricks pour que cela fonctionne :
<form action="{{ route('ArticlesController.update', {id: article.id}) }}?_method=put" method="post">
La documentation explique très bien cela ! Et pour que cela soit fonctionnel, il faut se rendre dans le fichier app.ts
présent dans config
et passer la clé allowMethodSpoofing
à true
!
Et enfin, nous allons ajouter un lien de modification de l'article lorsque l'utilisateur est connecté dans la vue show.edge
:
@if(auth.isLoggedIn)
<a href="{{ route('ArticlesController.edit', {id: article.id}) }}">Editer l'article</a>
@endif
Pour en savoir plus : create, save, load, spoofing, isLoggedIn
Bonus
La page index.edge
dans le dossier resources/views
ne nous sert pas. Alors on peut supprimer le fichier et dans nos routes, nous allons la rediriger vers nos articles comme cela :
Route.get('/', async ({ response }) => {
return response.redirect('/articles')
})
Si l'on se rend sur http://localhost:3333/, on est alors redirigé vers http://localhost:3333/articles !
Sur notre page index.edge
dans le dossier resources/views/articles
, il est possible de mettre un bouton de création d'un article uniquement lorsque l'utilisateur est connecté ! En effet, même si la route est protégée, on ne souhaite pas que tous nos lecteurs voit un bouton de création d'articles alors que l'action n'est pas possible pour eux. Ajoutons cela à notre fichier :
@if(auth.isLoggedIn)
<a href="{{ route('ArticlesController.create') }}">Créer un article</a>
@endif
Et voilà, le tour est joué. Le lien ne va apparait que lorsque l'utilisateur est connecté grâce à la condition !
Conclusion
Et voilà pour cette cinquième et dernière partie. On a vu plus en détail la création et la gestion de pages protégées par un middleware.
N'hésite pas à commenter si tu as des questions, si ça t'a plus ou même pour me faire des retours !
Et tu peux aussi me retrouver sur Twitter ou sur LinkedIn !
Notre blog est maintenant terminé ! J'espère que ça t'a plus. N'hésite pas à commenter pour me dire ce que tu as pensé de la série !
À bientôt pour de nouveaux tutoriels !
Possibles évolutions
Je n'allais quand même pas te laisser sans un peu de choses à faire. Voici une liste d'amélioration pour ce blog qui vont te permettre de progresser dans la maitrise du framework :
- Mettre en place un draft mode.
- Pour cela, on peut ajouter une propriété à nos articles nommée
isPublished
- Si cette propriété est à false, alors l'article ne sera pas disponible pour les utilisateurs non authentifiés. On utilisera alors un filtre si l'utilisateur n'est pas login. Un peu d'aide ici et là !
- Si la propriété est à true, alors l'article est visible.
- Lors de l'édition et de la création, on peut avoir la possibilité de changer cela via une checkbox.
- Pour cela, on peut ajouter une propriété à nos articles nommée
- Mettre des images aux articles
- Pour cela, tu peux utiliser cela !
- Ajouter du style à ton blog avec un peu de CSS
- Je te conseille alors de te renseigner sur le module de gestion des assets de Adonis
- On peut créer une page de profil pour l'utilisateur connecté et lui permettre de voir l'ensemble des articles qu'il a écrit !
- Cela pourrait t'aider !
- Il est aussi possible de se dire que l'on peut créer une page où les utilisateurs peuvent se connecter et créer des articles, comme sur medium. Mais il ne faudrait alors pas
- Et pour les plus motivé, on peut même imaginer créer un blog
- Les lecteurs entre un pseudo, un message et publie le commentaire
- On peut même imaginer que les lecteurs doivent se créer un compte !
Et voici un bon nombre d'idées pour améliorer le blog et pour te montrer que tu peux dorénavant faire beaucoup de choses sur le web ! Et je suis certains que ton imagination a encore pleins d'idées !
Je peux aussi te conseil d'aller voir ce repo qui contient une application real-world et qui te montre comment faire et en plus, c'est par le créateur d'Adonis !
Bye ! 👋
Top comments (0)