Prismic.io

Dans un de nos derniers projets, nous avons eu l'occasion de mettre en place un webdoc en utilisant le service Primsmic.io … Une belle occasion pour présenter ce service …

Prismic.io, Le CMS "as a service"

Prismic.io est une solution de CMS en SaaS (Software as a service) : une solution de gestion de contenu hebergée dans le "Cloud". Il existe différents types de prix et différentes fonctionnalités, mais "grosso modo", cette plateforme propose deux services en parallèle :

  • une solution de gestion de contenu avec un back-office élégant et facile d'utilisation ;
  • une API REST, qui permet d'interagir, de récupérer et de mettre en forme les contenus.

Prismic se focalise donc sur la valeur métier de la gestion de contenu, et ne propose pas de site ou de frontend.

Pour certains projets et dans certains contextes, Primsic.io peut devenir une alternative technique et budgétaire qui peut être très intéressante. Pour les équipes de devs, ça évite d'avoir à déployer un CMS et ses fonctions classiques et permet ainsi de se focaliser sur la valeur ajoutée du pojet: l'intégration de l'application finale.

Nous aborderons dans cet article une présentation rapide de la plateforme et une première approche d'utilisation de Prismic.io: comment définir ces contenus ? comment requêter l'API ? Nous finirons avec un petit cas concret.

Notre "Use Case" et ses besoins

Nous allons mettre en place une base de données de vidéos qui sera stockée dans Prismic.io pour,par exemple, alimenter un site de consultation de cette base.

Mais faisons simple, développons un outil de recherche en ligne de commande. Celui-ci permettra de faire des recherches dans la base de vidéos depuis le terminal.

Modélisation

Une vidéo est liée à un auteur ('personne') et à une thématique. L'application doit permettre à l'internaute de naviguer dans les vidéos, en entrant par une thématique ou par une personne. Aussi, une vidéo doit pouvoir être liée à deux autres vidéos, permettant de mettre en place une navigation "Vidéos similaires".

Prismic.io permet de définir ses propres schémas de données et de documents, de façon simple et assez intuitive. Il existe quasiment tous les types de données classiques, et on peut même définir des relations entre les documents.

Les schémas supportent un bon nombre de types de champs: "StructuredText", "Text", "Number", "Select", "Color", "Date", "Image", "Link", "Embed", "Group" ..

Les schémas ("Document Masks" dans l'interface) sont définis via un fichier JSON, que l'on peut éditer depuis le BackOffice Prismic. La Killing Feature à mon goût, est de pouvoir prévisualiser le formulaire d'édition/création en même temps que l'on définit le schéma de nos documents.

Backoffice.io

Les schémas

Les documents de type "video" sont définis ainsi:

{
  "Video" : {
    "titre" : {
      "type" : "Text",
      "fieldset" : "Titre",
      "config" : {
        "placeholder" : "Titre..."
      }
    },
    "description" : {
      "fieldset" : "Description...",
      "type" : "StructuredText"
    },
    #lien vers la video dailymotion
    "dailymotion" : {
      "type" : "Text",
      "fieldset" : "ID vidéo dailymotion"
    },
    #auteur
    "personne" : {
      "fieldset" : "Personne",
      "type" : "Link",
      "config" : {
        "select" : "document",
        "masks" : [ "personne" ],
        "placeholder" : "Personne"
      }
    },
    #thematique
    "theme" : {
      "fieldset" : "Theme",
      "type" : "Link",
      "config" : {
        "select" : "document",
        "masks" : [ "theme" ],
        "placeholder" : "Theme"
      }
    },
    #video liée 1
    "video_1" : {
      "fieldset" : "Video 1",
      "type" : "Link",
      "config" : {
        "select" : "document",
        "masks" : [ "video" ],
        "placeholder" : "Video liée 1"
      }
    },
    #video liée 2
    "video_2" : {
      "fieldset" : "Video 2",
      "type" : "Link",
      "config" : {
        "select" : "document",
        "masks" : [ "video" ],
        "placeholder" : "Video liée 2"
      }
    }
  }
}

Les "Documents" de type personne sont définis ainsi:

{
  "Personne" : {
    "nom" : {
      "type" : "StructuredText",
      "fieldset" : "Nom",
      "config" : {
        "placeholder" : "Nom...",
        "single" : "heading1"
      }
    },
    "prenom" : {
      "type" : "Text",
      "fieldset" : "Prenom",
      "config" : {
        "placeholder" : "Prenom..."
      }
    },
    "photo" : {
      "fieldset" : "Photo...",
      "type" : "Image"
    },
    "photo_miniature" : {
      "fieldset" : "Photo miniature...",
      "type" : "Image"
    },
    "bio" : {
      "fieldset" : "Biographie...",
      "type" : "StructuredText"
    }
  }
}

Les "Documents" de type theme sont définis ainsi:

{
  "Theme" : {
    "titre" : {
      "type" : "StructuredText",
      "config" : {
        "placeholder" : "Titre...",
        "single" : "heading1"
      }
    },
    "citation" : {
      "type" : "Text",
      "config" : {
        "placeholder" : "Citation..."
      }
    },
    "position" : {
      "fieldset" : "Position",
      "type" : "Number"
    },
    "class_css" : {
      "type" : "Select",
      "config" : {
        "label" : "Classe CSS",
        "options" : [ "engagement", "quotidien", "deportation", "apres-resistance" ]
      }
    }
  }
}

Type de champs

Notre exemple se veut simple, mais on peut mettre en place des choses intéréssantes en jouant sur les types de champs.

  • On peut définir le wording et l'interface des formulaires d'édition.
  • Sur les champs "Images", on peut définir différents formats de "Thumbnails".
  • Les relations: On peut aller assez loin dans la typologie des relations entre documents.

Utilisation de l'API

Pour interroger la base des documents, l'API REST met à disposition un langage de prédicats qui permet de filter et rechercher les données souhaitées.

La doc, en anglais, est assez claire là-dessus :)

  • Il est possbile de requêter sur des propriétés communes et standards à tous les documents (type, slug ...) ( préfixe document ). Exemple:
    Le prédicat [[:d = at(document.type, "video")] permet de récuperer tous les documents de type video.
  • Il est possbile de requêter sur des propriétés propres au type de document ( prefixe _my.type_de_document ). Exemple:
    Le prédicat [[:d = at(_my.video.titre, "titre")] permet de récuperer la vidéo dont le titre est égal "titre".

    Il est bien entendu possible d'enchaîner les prédicats. Par exemple, pour rechercher les vidéos appartenant au thème 'policier' associées à la personne "Keyser Söze"; nous pouvons utiliser la requête suivante:

[[:d = at(document.type, "video")][:d = at(my.video.theme, "policier")][:d = at(my.video.personne, "Keyser Söze")]]

Il est aussi possible de mettre en place du tri et de jouer sur la pagination des résultats.

Notre outil: rechercher des vidéos depuis un outil en ligne de commande

Il s'agit de développer un petit outil qui permettra depuis son terminal de rechercher des videos dans notre base prismic.io.

Pour nos essais, nous avons décidé d'utiliser l'API Node.js (il existe d'autres kits pour d'autres langages).

Pré-requis

Vous pouvez installer le kit Prismic.io pour NodeJs depuis npm. D'autre part, pour notre exemple, nous utiliserons deux autres modules:

  • 'optimistic' : qui nous aidera à gérer les options dans l'appel de notre script
  • 'Q': qui nous permettra de gérer les appels à l'API via des promises.

voici notre fichier package.json

#//package.json
{
  "name": "plop-plop",
  "version": "0.0.1",
  "description": "",
  "main": "index.js",
  "author": "lanetscouade",
  "license": "ISC",
  "dependencies": {
    "prismic.io": "*",
    "q": "*",
    "optimist": "0.6.1"
  }
}

Pour installer toutes les dépendances, il suffit d'utiliser npm :

$ npm install

Maintenant le code de notre petit outil

#//index.js
#!/usr/bin/env node
/**
 * il faut installer les dépéndances (via npm) Q
 */
'use strict';
//modules
var Q = require('q'),
  prismic = require('prismic.io').Prismic,
  argv = require('optimist');
//custom config
var apiUrl = "mon_api",
    apiKey = "ma_key"

function PrismicQuery() {
  /**
   * [buildVideoQuery description]
   * @param {[type]} theme    [description]
   * @param {[type]} personne [description]
   */
  this.buildVideoQuery = function(theme, personne) {

    var predicat = "[";
    predicat += "[:d = at(document.type, \"video\")]";
    //ca pourrait être plus zzzoli
    if (theme) {
      //filtre sur le thème
      predicat += "[:d = at(my.video.theme, \"" + theme + "\")]"
    }
    if (personne) {
      //filtre sur la personne
      predicat += "[:d = at(my.video.personne, " + personne + ")]"
    }
    predicat += "]";
    return predicat;
  }
  /**
   * [query description]
   * @param  {[type]} predicat [description]
   * @return {[type]}          [description]
   */
  this.query = function(predicat) {

    if (!PrismicQuery.api) {
      throw "Query not initialized";
    }
    var query = PrismicQuery.api.form('everything').query(predicat);
    //page size - nombres de résultats
    query.set('pageSize', 60);
    //repository
    query.ref(PrismicQuery.api.master());
    //here we are
    query.submit(function(error, data) {
      //ruuuude boy..affichage brute de décoffrage ;)
      console.log(data);
    });
  }

  /**
   * [init description]
   * @return {[type]}
   * nous utilisons ici une promesse (Q) afin de s'assurer que l'initialisation est bien réalisée avant d'utiliser l'API
   */
  this.init = function() {

    var deferred = Q.defer();
    prismic.Api(apiUrl, function(error, api) {
        if (api) {
          PrismicQuery.api = api;
          deferred.resolve(api);
        }
      },
      apiKey,
      null
    );
    return deferred.promise;

  }
  return this;

}

/**
 * Main
 */

/**
 * on définit les options de notre outil
 */
var args = argv.usage(
  'Search videos on prismic.io\nUsage: $0 -t theme -p person')
  .string('t').describe('t', 'search videos for a theme ( optional )')
  .string('p').describe('p', 'search videos for a person ( optional )')
  .argv;

if (args.help) {
  argv.showHelp();
  return (1);
}

//....

var provider = new PrismicQuery(),
    myQuery = provider.buildVideoQuery(args.t, args.p);

provider.init().then(function() {
  provider.query(myQuery);
});

Pour utiliser cet outil:

$ node index.js [-t type]type [-p personne]

Conclusion

Voilà un premier apercu de l'API Prismic.io. Il reste pas mal de choses intéressantes qui n'ont pu être traitées dans cet article. Nous aurions pu notamment parler du formatage des résultats. Ici nous avons une sortie des videos en JSON brut … mais les librairies fournissent des helpers pour formatter correctement les données.

Il y a aussi pas mal de fonctionnalités (plutôt orientées "métier" et éditeurs) que je n'ai pas eu le temps ni les moyens (c'est payant :)) de tester : la gestion des rôles utilisateurs et des workflows. Dans l'avenir, il serait top de mettre en place des webhooks (pour être alerté lors de la modification de contenu).

À bon entendeur, salut !

Merci