Catégories
JavaScript Vue.js

Nuxt.js + Adonis.js / Chapitre 05 : Les controllers d’Adonis.js

(Article mis à jour le 7 octobre 2019)

Nouveau chapitre sur les controllers d’Adonis.js

Dans ce nouveau chapitre de notre tutoriel sur Nuxt.js + Adonis.js, nous allons parler de la mise en place des controllers d’Adonis.js et plus particulièrement du controller ‘Language’.

Voici ce que nous souhaitons réaliser d’ici la fin de ce chapitre :

Controllers d'Adonis.js
Le but de ce chapitre

Previously in « Nuxt.js + Adonis.js » 😀

Dans le précédent épisode, nous avons vu comment mettre en place les premiers modèles de notre application. La suite logique est la mise en place des premiers controllers d’Adonis.js pour commencer à mettre tous ces éléments en relation.

Sommaire du tutoriel

Utilisation des controllers d’Adonis.js

Bon cette fois-ci, on plonge et on met les mains dans le cambouis. Pour commencer, nous allons nous créer un petit controller qui sera responsable de nous récupérer les languages en base de données via le modèle Language que nous avons vu au chapitre précédent. Il pourra également les mettre à jour, les créer et évidemment de les supprimer.

À vrai dire, si vous souhaitez en savoir plus sur les controllers d’Adonis.js, je vous encourage, comme toujours, à aller jeter un œil à la documentation ! Et oui, la doc ce n’est pas fait pour les chiens…

Controller d'Adonis.js : dog reading books

Donc, nous allons utiliser Adonis en ligne de commande pour nous prémacher le travail. N’oubliez pas qu’un développeur efficace se doit d’être fainéant d’utiliser les outils à sa disposition ! 😀

Créer des controllers d’Adonis.js via la commande make:controller

La commande make:controller permet de créer facilement les squelettes des controllers d’Adonis.js

Afin de créer un nouveau controller, nous utilisons la commande make:controller d’Adonis en ligne de commande.

adonis make:controller LanguageController

Grâce à cette commande, on peut voir qu’Adonis propose de créer un controller dédié aux requêtes HTTP ou pour les Websocket.

Par conséquent, si vous souhaitez changer le type de controller, il vous suffit de descendre ou monter avec les flèches haut et bas de votre clavier.

L’option sélectionnée est en bleu avec le symbole supérieur (>) devant elle.

Nous choisissons donc ‘For HTTP requests’ qui est sélectionné par défaut en appuyant sur la touche Entrée.

Adonis vient de créer pour nous le controller demandé.

Mettre à jour les controllers d’Adonis.js

Ensuite, occupons nous de récupérer les différents langages présents en base de données et de les afficher sur une page dédiée.

Pour cela, il suffit de remplacer le code généré par défaut :

Controllers d'Adonis.js : Code LanguageController.js généré par défaut par Adonis
Code LanguageController.js généré par défaut par Adonis

Par le suivant :

'use strict'

const Language = use('App/Models/Language')

class LanguageController {
  async home () {
    const languages = await Language.all()
    return languages.toJSON()
  }
}

module.exports = LanguageController

Mise à jour du fichier de routes

Pour que ce controller puisse être appelé via l’API depuis notre frontend Nuxt, il faut en définir la route. Hop, direction notre fichier start/routes.js et nous ajoutons le bloc suivant :

Route.group(() => {
  Route.get('languages', 'LanguageController.get')
}).prefix('api')

Nous utilisons ici la notion de group qui permettra par la suite de grouper plusieurs routes concernant la partie languages (pour le moment je vous l’accorde c’est un petit peu overhead !)

Ensuite, on préfixe ces routes avec api et voilà notre serveur AdonisJs est prêt à recevoir des appels GET de notre frontend sous NuxtJs via l’url http://127.0.0.1:3333/api/languages

Ce controller avec sa fonction get sera appelé en mode API par notre fichier languages.vue du répertoire /pages de notre frontend Nuxt.

Il est à noter que ce fichier n’existe pas encore, vous avez raison ! Nous allons donc le créer.

Créer la vue associée

Nous avons besoin d’une vue associée à notre controller, à cette fin, côté frontend NuxtJs dans le répertoire /pages, vous allez créer le fichier languages.vue.

Dans votre éditeur favori, ajoutez le code suivant à ce fichier nouvellement créé :

<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        Meet The Coders
      </h1>
      <h2 class="subtitle">
        Languages page
      </h2>
      <div class="links">
        <a
          href="#"
          target="_self"
          class="button--green"
        >
          Find Coders
        </a>
        <a
          href="#"
          target="_self"
          class="button--grey"
        >
          Find Coders by languages
        </a>
      </div>

      <h1 class="coder-list">
        All languages
      </h1>

    </div>
  </div>
</template>

<script>
import axios from 'axios'
import Logo from '~/components/Logo.vue'
export default {
  layout: 'main',
  components: {
    Logo
  },
  async asyncData ({ req }) {
    const ApiURL = process.env.apiUrl
    const { data } = await axios.get(ApiURL + '/languages')
    return {
      languages: data
    }
  }
}
</script>

<style>

.blank {
  width: 100px;
  height: 100px;
  border: 1px solid lightgray;
  border-radius: 5px;
}

.coder-list {
  margin-top: 1em;
}

.container {
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

h1 {
  margin-bottom: 1em;
  font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
  'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  display: block;
  font-weight: 300;
  font-size: 48px;
  color: #35495e;
  letter-spacing: 1px;
}

.links {
  padding-top: 15px;
}

.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}

.title {
  font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
    'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #35495e;
  letter-spacing: 1px;
}

</style>

Enfin, enregistrez votre fichier avec un petit ctrl + s.

Grâce à yarn qui a dû, en arrière plan, s’occuper de mettre à jour vos fichiers, il ne vous reste plus qu’à vous rendre à l’adresse http://127.0.0.1:3000/languages pour voir ce que cela donne.

Controllers d'Adonis.js : La nouvelle page Languages de notre application Meet The Coders... encore un peu vide ;-)
La nouvelle page Languages de notre application Meet The Coders… encore un peu vide 😉

Rien de bien folichon pour le moment, mais nous allons améliorer tout cela. Mais avant cela quelques explications sur notre nouveau fichier languages.vue.

import axios from 'axios'
import Logo from '~/components/Logo.vue'
export default {
  layout: 'main',
  components: {
    Logo
  },
  async asyncData ({ req }) {
    const ApiURL = process.env.apiUrl
    const { data } = await axios.get(ApiURL + '/languages')
    return {
      languages: data
    }
  }

Premièrement, nous importons la bibliothèque Axios qui va nous permettre de faire des appels au controller LanguagesController.js (sur notre serveur AdonisJs) que nous avons défini un peu plus haut.

C’est ce que nous faisons d’ailleurs avec la méthode asyncData via un axios.get.

Ensuite, l’application va aller interroger en arrière plan l’url http://127.0.0.1:3333/api/languages

Enfin, on arrive dans le controller LanguageController et sa fonction get. Cette dernière fait une requête vers notre base de données, via le modèle app\Models\Language.js, dans le but de récupérer tous les langages présents en base de données.

La fonction get du controller LanguageController retourne alors un set de data à notre fichier languages.vue. Ce set data vient alors alimenter la variables languages.

Améliorer notre page Languages

Là-dessus, nous allons à présent voir comment implémenter ce jeu de données dans notre page afin d’afficher l’ensemble des langages de programmation enregistrés en base de données, comme ci-dessous :

Controllers d'Adonis.js : Notre objectif 
 La page Languages "améliorée" de notre application Meet The Coders
Notre objectif : La page Languages « améliorée » de notre application Meet The Coders

La future version de notre page est déjà un peu plus sexy, non ?

Pour cela, nous allons commencer à l’implémenter à l’aide de Bulma (framework CSS) retenu pour ce tutoriel.

Implémenter les migrations manquantes

Mais juste avant cela nous allons mettre en place les migrations pour compléter notre modèle de données afin de pouvoir arriver au résultat présenter ci-dessus.

Concrètement qu’allons nous ajouter comme champs à notre table ‘languages’ ?

  • un champ picture : pour stocker l’image principale du langage et qui sera utilisée dans le header de la card Bulma
  • un champ logo : pour stocker l’image de logo du langage en petit format

En ce qui concerne ces deux champs, le type string sera retenu.

Nous créons tout d’abord la migration pour le champ picture :

Controllers d'Adonis.js : Migration pour ajouter le champ picture à la table languages
Migration pour ajouter le champ picture à la table languages

A noter, Adonis vous propose, lorsque vous demandez la création d’une migration, de sélectionner ou de créer une nouvelle table.

Dans notre cas, nous choisissons de sélectionner la table.

Sitôt que le fichier de migration est généré, nous n’avons plus qu’à le remplir.

'use strict'

/** @type {import('@adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')

class LanguagesAddPictureFieldSchema extends Schema {
  up () {
    this.alter('languages', (table) => {
      table.string('picture').nullable()
    })
  }

  down () {
    this.table('languages', (table) => {
      table.dropColumns('picture')
    })
  }
}

module.exports = LanguagesAddPictureFieldSchema

Une fois le fichier de migration sauvegardé, on peut ensuite lancer la migration :

Controllers d'Adonis.js : Lancement de la migration avec Adonis.js
Lancement de la migration avec Adonis.js

Il suffit ensuite d’effectuer la même opération pour le champ logo.

adonis make:migration languages_add_picture_logo

Comme précédemment, on remplit notre fichier de migration :

'use strict'

/** @type {import('@adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')

class LanguagesAddLogoFieldSchema extends Schema {
  up () {
    this.alter('languages', (table) => {
      table.string('logo').nullable()
    })
  }

  down () {
    this.table('languages', (table) => {
      table.dropColumns('logo')
    })
  }
}

module.exports = LanguagesAddLogoFieldSchema

Et on n’oublie pas évidemment de lancer la migration ! 😉

adonis migration:run

Nota : la migration aurait pu être faite avec un seul fichier de migration, bien évidemment. C’est juste que pour ma part, je l’ai fait en deux temps 🙂

Importer les données sans se fatiguer

Comme je me tue à vous le répéter un développeur se doit d’être fainéant d’économiser ses neurone, aussi je vous livre ci-dessous les fichiers vous permettant d’importer les données nécessaires à ce chapitre.

Premièrement, le fichier csv à importer dans votre table languages de votre base de données MeetTheCoders sous PostGresql.

Deuxièmement, une gallerie contenant les images et les logos des langages de programmation que vous trouverez dans la base de données après avoir importer le fichier CSV fourni juste au-dessus.

Ces images sont à placer dans le répertoire resources/static.

Améliorer la vue

Dans le fichier languages.vue, vous allez ajouter sous la balise h1 > All languages, le code suivant.

      <div class="container is-fluid">
        <div class="columns is-multiline">
          <div v-for="(language, index) in languages" :key="language.id+'_'+index" class="column is-one-quarter">
            <div class="card">
              <div class="card-image">
                <figure class="image is-4by3">
                  <img :src="language.picture" :alt="language.title">
                </figure>
              </div>
              <div class="card-content has-background-light">
                <div class="media">
                  <div class="media-left">
                    <figure class="image is-48x48">
                      <img :src="language.logo" :alt="language.title">
                    </figure>
                  </div>
                  <div class="media-content">
                    <p class="title is-4">
                      <a :href="language.link" target="_blank">{{ language.title }}</a>
                    </p>
                  </div>
                </div>

                <div class="content">
                  <div class="tags has-addons">
                    <span class="tag is-light">Used by</span>
                    <span class="tag is-primary">42</span>
                    <span class="tag is-dark">coders</span>
                  </div>
                  {{ language.description }}
                  <br>
                  <small class="has-text-primary has-background-white-ter">Updated at {{ language.updated_at }}</small><br>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

On se sert ici du composant Card de Bulma.

Dans les grandes lignes, voici quelques petites explications sur les modifications apportées :

  • on utilise l’instruction vue.js v-for pour boucler sur l’ensemble des languages de programmation présents en base de données et les injecter dans notre composant Card de Bulma ( <div v-for="(language, index) in languages" :key="language.id+'_'+index" class="column is-one-quarter"> )
  • on « bind » le champ language.picture sur la balise img. A savoir, on a stocké le nom du fichier image sous forme de string en base de données et on a placé le fichier image dans le répertoire resources/static de notre projet. Les fichiers picture et logo ont étés récupérés sur internet et retouchés pour respecter les ratios évoqués, notamment le 4/3 pour l’image principale du langage (is-4by3)
  • on « bind » également le champ language.title sur les balises alt de notre picture et de notre logo
  • on « bind » (décidemment) le champ language.link sur la balise href en lui associant comme texte le language.title
  • On crée enfin un bloc de tags associés, ce qui nous génère un petit composant sympathique (mais statique pour le moment) afin d’indiquer proprement le nombre de codeurs utilisant ce langage de programmation
Petit composant informatif, créé à partir des tags Bulma
Petit composant informatif, créé à partir des tags Bulma

Voici ce que nous obtenons à cet instant :

Notre page Languages commence à avoir de la gueule !
On progresse !

Là, on commence à se faire plaisir.

Vous trouvez cet article intéressant ?

Ecrire ces articles est passionnant 😉, aussi je souhaite y consacrer plus de temps et votre soutien est capital pour cela.
Si vous souhaitez m’encourager et récompenser mon travail, vous pouvez me payer une bière 🍺 ou tout autre montant de votre choix *
Mille mercis 🙏 et maintenant la suite de cet article.
Bonne journée !

* Pas de panique : Le paiement est sécurisé par Paypal

Et l’esthétique, bon sang !

Mais il y a deux petits soucis, la hauteur entre les « cards » qui n’est pas équivalente et tous les langages ont 42 codeurs associés.

Pour le premier point, nous allons effectuer une première optimisation. On va tronquer tous les textes descriptifs à la même longueur. C’est parti !

Chérie… ça va couper !

Attention chérie, ça va couper !

Pour tronquer nos textes descriptifs, nous allons nous créer un petit filtre dédié.

Dans le fichier languages.vue, avant l’appel à async asyncData, nous ajoutons le filtre suivant :

  filters: {
    textTruncate (str, length, ending) {
      if (length == null) {
        length = 75
      }
      if (ending == null) {
        ending = '...'
      }
      if (str.length > length) {
        return str.substring(0, length - ending.length) + ending
      } else {
        return str
      }
    }
  },

Une fois que ce filtre est défini, nous pouvons l’utiliser à notre guise dans notre vue.

En l’occurrence, nous allons nous en servir pour tronquer le champ language.description.

Pour cela remplacer la ligne de code suivante :

{{ language.description }}

par celle-ci :

{{ language.description | textTruncate }}

On peut voir ci-dessous que cela a amélioré notre affichage, même si il n’est pas encore parfait :

La fonction textTruncate nous a aidé à rendre plus homogène nos composants Cards
La fonction textTruncate nous a aidé à rendre plus homogène nos composants Cards

La bonne solution viendra d’une discussion autour d’une issue de Bulma.

En ajoutant deux classes CSS, nous pourrons solutionner le problème de taille équivalente de card avec Bulma.

.bm--card-equal-height {
   display: flex;
   flex-direction: column;
   height: 100%;
}
.bm--card-equal-height .card-footer {
   margin-top: auto;
}

Il ne vous restera plus ensuite qu’à affecter la nouvelle classe sur votre bloc card :

<div class="card bm--card-equal-height">
   <!-- content -->
</div>

Afin d’améliorer encore un peu plus l’esthétique, on va centrer notre composant informatif à base de tags Bulma. Pour cela, il suffit d’ajouter la classe is-centered au conteneur parent.

<div class="tags has-addons is-centered">
   <span class="tag is-warning">Used by</span>
   <span class="tag is-primary">42</span>
   <span class="tag is-dark">coders</span>
</div>
Et voilà, nos cards sont parfaitement alignées à présents !

42 n’est pas la réponse universelle !

Bon ce 42 de partout, je ne sais pas vous, mais moi ça me donne la nausée !

Oui d’accord mais on n’a pas encore pluggué nos utilisateurs, donc comment s’est y donc que l’on fait, chef ?

Bon, on va ruser un peu en attendant de plugguer tout cela correctement.

Un bon petit coup de random devrait nous aider à rendre tout cela plus réaliste.

On se créé un petite ‘methods’ toute simple :

  methods: {
    randomNumber () {
      return Math.floor(Math.random() * (100)) + 1
    }
  }

Et là encore, on l’utilise directement dans le code de notre vue, en remplaçant :

<div class="tags has-addons">
   <span class="tag has-background-info has-text-white">Used by</span>
   <span class="tag is-primary">42</span>
   <span class="tag is-dark">coders</span>
</div>

par :

<div class="tags has-addons">
   <span class="tag has-background-info has-text-white">Used by</span>
   <span class="tag is-primary">{{ randomNumber() }}</span>
   <span class="tag is-dark">coders</span>
</div>

Il y a quoi au menu ?

Tout cela c’est bien joli mais on y accède comment à notre page, hormis en tapant directement l’url à la mano ? hum ?

Ok, ok, on va y remédier de ce pas en ajoutant le menu à notre header. Pour cela vous modifier le fichier main.vue, en lui ajoutant la ligne suivante :

<li><a href="/languages">Languages</a></li>

au bloc qui compose notre menu :

    <header>
      <a id="logo" href="/">Meet The Coders</a>
      <nav>
        <ul>
          <li><a href="/">Coders</a></li>
          <li><a href="/languages">Languages</a></li>
          <li><a href="/login">Login</a></li>
          <li><a href="/signup">Signup</a></li>
        </ul>
      </nav>
    </header>

Et là nous pouvons voir que nous avons atteint notre objectif :

Notre page languages, telle que nous la souhaitions !
Notre page languages, telle que nous la souhaitions !

Conclusion de ce chapitre sur les controllers d’Adonis.js

Et bien, nous avons vu quelques aspects sympathiques dans ce chapitre. Le point essentiel étant la compréhension des controllers d’Adonis.js.

Mais nous avons également vu l’utilisation du framework CSS Bulma.

Nous avons abordé également la mise en place de filters et de methods dans nos fichiers vue.

Nous aurons encore pas mal de travail sur les controllers d’Adonis.js pour mettre en place l’ensemble de notre mécanique, ici nous n’avons que survolé le concept.

Vous pouvez retrouver l’ensemble des sources sur les github dédiés à ce tutoriel :

Dans le prochain chapitre, nous parlerons d’authentification.

Si cet article vous a plu, je vous serais super reconnaissant de lui attribuer une note.

Je vous serai également éternellement reconnaissant de vous abonner à notre newsletter.

Enfin si vous avez constaté des erreurs, des oublis ou tout autre motif pour que je sois pendu haut et court, je vous encourage à utiliser les commentaires pour me le signaler.

Nota : vous pouvez aussi utiliser les commentaires aussi pour m’encourager 😀 !

Aidez nous à nous améliorer en évaluant cet article :
[Total: 0 Moyenne: 0]

Par Aldew

Absolument passionné depuis toujours par l'informatique et la programmation, je souhaite via ce blog vous faire passer un peu du savoir que j'ai accumulé depuis des années.
Et même, pourquoi pas, vous aider à faire le premier pas dans le développement web.

Vous êtes prêts ?
Alors allons y ensemble et n'oubliez pas que les débutants sont vraiment les bienvenus.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.