Tableless

Busca Menu

Alternativa de CMS com Keystone.js

Seja o primeiro a comentar por

Este é o primeiro artigo de uma série sobre o Keystone.js, um framework desenvolvido em Node.js para servir de CMS e também Web Application. Pra começarmos, será necessário nesse primeiro artigo um pouco de teoria, pra termos uma noção.

Aos apressados, o link do repo está disponível no github.

Pra começar, não vou falar detalhadamente sobre sua descrição, pois no site deles está bem explicado, apenas um resumo sobre, e depois, quando formos iniciar o projeto, ficará mais claro.

O Keystone.js utiliza o Express, então para muitos a curva de aprendizado será bem pequena e é um ponto bom, pois a customização (quando necessária) será tranquila. Na interface admin, utilizam o React.js. Este framework me chamou muito a atenção pelo fato de agilizar MUITA coisa no desenvolvimento, e ao mesmo tempo não deixar o desenvolvedor sem saber o que ocorre por baixo dos panos, os módulos de rotas/models/views são bem organizados.

Atualmente, a comunidade Keystone.js está trabalhando na próxima versão (0.4) que terá um rebuild da interface admin com o React e mais novidades.

O projeto que faremos utilizará a versão 0.3.16

A idéia nessa série é desenvolver um CMS (obviamente) onde vamos publicar posts, cadastro e listagem de produtos, e também customizar o Framework (é Javascript!) para termos uma API para posterior consumo.

Showmethecode!

Pré requisitos: Node.js e Mongodb. No terminal, digite:

    $npm install -g generator-keystone
    $mkdir projeto-keystone
    $cd projeto-keystone
    $yo keystone
    $node keystone

Após a instalação via NPM, o generator fará algumas perguntas sobre as engines que deseja utilizar e informações sobre seu projeto (exemplo nome), esta parte fica a seu critério. Itens como template engine, pré-processor, taskers…

Tela Instalação Keystone.JS

Agora você já consegue navegar em http://localhost:3000 pra ter uma noção do que o framework estruturou para você. Nesse momento, repare que já temos um Blog e uma Galeria de imagens (utilizando uma conta temporária da Cloudinary).

Para acessar o admin, navegue em http://localhost:3000/keystone e informe usuário e senha que informou no generator (se não informou nada, é user: user@keystonejs.com pass: admin)

Um pouco sobre as models e rotas

Esta parte é uma mão na roda, dê um check na estrutura de ./models/Post.js e ./models/Gallery.js pra saber do que estamos tratando. O interessante, é que no momento de estruturar as models, você não precisa se preocupar em criar o formulário de cadastro/edição no adminUI (o Keystone.js faz isso conforme vc seta as propriedades da model).

Já as rotas, ficam em ./routes/index.js para serem registradas. Possuímos o arquivo ./routes/middleware.js para interceptar e tratar as requests de acordo com a nossa necessidade (veremos mais adiante).

Uma boa prática que adotamos aqui na Ornito é separar a pasta de rotas em ./routes/api/* e ./routes/views/*, onde, respectivamente, incluiremos os arquivos de API  retornardos de nosso JSON, e ServerViews, que vamos renderizar pela template engine (Jade foi a escolhida).

Cadastro de Produtos

Ao subir o Keystone.js pela primeira vez, ele gerou as models de Post e Galeria para nós, por isso, não teremos muito trabalho nesta parte (já está pronto). Vamos agora focar no código para criação de produtos.

Nosso fluxo de criação: definir Model, definir Rota, definir View.

Vamos criar o arquivo ./models/Produto.js:

// models/Produto.js

var keystone = require('keystone');
var Types = keystone.Field.Types;

/**
 * Model Produto
 * ==========
 */

var Produto = new keystone.List('Produto', {
  map: { name: 'produto' },
  autokey: { path: 'slug', from: 'produto', unique: true }
});

Produto.add({
  produto: { type: String, required: true },
  ativo: { type: Types.Boolean, initial: true, required: true },
  preco: { type: Types.Money, format: '$0.0,00', label: 'Valor do produto', },
  descricao: {
    breve: { type: Types.Textarea, height: 150, label: "Breve descrição" },
    completa: { type: Types.Html, wysiwyg: true, height: 200, label: "Descricao completa" }
  },
  criadoEm: { type: Types.Date, index: true, default: new Date() },
  detalhes: { type: Types.TextArray },
  imagens: { type: Types.CloudinaryImages }
  
});

Produto.schema.virtual('descricao.full').get(function() {
  return this.descricao.completa || this.descricao.breve;
});

Produto.defaultColumns = 'produto, ativo, preco, criadoEm';
Produto.register();

Já criamos a model, então podemos navegar pelo admin, e http://localhost:3000/keystone nos levará para o cadastro de um produto. Preencha as informações do produto.

Agora temos que configurar a rota em ./routes/views/produtos.js , ./routes/index.js e ./routes/middleware.js

// routes/views/produtos.js
var keystone = require('keystone');

exports = module.exports = function(req, res) {
  
  var view = new keystone.View(req, res);
  var locals = res.locals;
  
  // Set locals
  locals.section = 'produtos';
  
  // Load the galleries by sortOrder
  view.query('produtos', keystone.list('Produto').model.find().sort('sortOrder'));
  
  // Render the view
  view.render('produtos');
  
};

A rota views/produtos.js é a responsável por consultar nossa base no mongoDB e retornar os produtos existentes.

// routes/index.js
var keystone = require('keystone');
var middleware = require('./middleware');
var importRoutes = keystone.importer(__dirname);

// Common Middleware
keystone.pre('routes', middleware.initLocals);
keystone.pre('render', middleware.flashMessages);

// Import Route Controllers
var routes = {
  views: importRoutes('./views')
};

// Setup Route Bindings
exports = module.exports = function(app) {
  
  // Views
  app.get('/', routes.views.index);
  app.get('/blog/:category?', routes.views.blog);
  app.get('/blog/post/:post', routes.views.post);
  app.get('/gallery', routes.views.gallery);
  app.all('/contact', routes.views.contact);

  app.get('/produtos', routes.views.produtos);

};

Em routes/index.js registramos igual a um app comum em express com app.get() e apontamos para nossa rota de produtos, a qual vamos consultar os produtos na base.

// routes/middleware.js
var _ = require('underscore');

/**
  Initialises the standard view locals
*/

exports.initLocals = function(req, res, next) {
  
  var locals = res.locals;
  
  locals.navLinks = [
    { label: 'Home',    key: 'home',    href: '/' },
    { label: 'Blog',    key: 'blog',    href: '/blog' },
    { label: 'Gallery',   key: 'gallery',   href: '/gallery' },
    { label: 'Contact',   key: 'contact',   href: '/contact' },
    { label: 'Produtos',    key: 'produtos',    href: '/produtos' }
  ];
  
  locals.user = req.user;
  
  next();
  
};

/**
  Fetches and clears the flashMessages before a view is rendered
*/

exports.flashMessages = function(req, res, next) {
  
  var flashMessages = {
    info: req.flash('info'),
    success: req.flash('success'),
    warning: req.flash('warning'),
    error: req.flash('error')
  };
  
  res.locals.messages = _.any(flashMessages, function(msgs) { return msgs.length; }) ? flashMessages : false;
  
  next();
  
};

/**
  Prevents people from accessing protected pages when they're not signed in
 */

exports.requireUser = function(req, res, next) {
  
  if (!req.user) {
    req.flash('error', 'Please sign in to access this page.');
    res.redirect('/keystone/signin');
  } else {
    next();
  }
  
};

Em routes/middeware.js estamos apenas estruturando os navLinks para exibirmos o link de produtos na home.

E agora vamos exibir nossos produtos em ./templates/views/produtos.jade

//- templates/views/produtos.jade
extends ../layouts/default

block intro
  .container
    h1 Nossos produtos
  
block content
  .container
    if produtos.length
      each item in produtos
        h2= item.produto
          .pull-right.text-muted R$ #{item.preco}

        p= item.descricao.breve
        
        .row.gallery-images
          each image in item.imagens
            .col-xs-6.col-sm-4.col-md-3.gallery-image: img(src=image.limit(300,300)).img-rounded
    else
      h3.text-muted Ainda não temos produtos cadastrados 🙁

Navegue em http://localhost:3000/produtos e verá o resultado 🙂

Página Nossos Produtos com Keystone.js

Neste artigo não foi possível cobrir TODOS os detalhes do Keystone.js. Mas, se você se interessou pelo framework, vale dar uma olhada na documentação sobre tipos de dados, formatos, middlewares, serviços já integrados e tudo mais.

Fiz aqui uma pequena imersão ao framework para mostrar o que ele pode fazer com pouco tempo e dedicação. No próximo artigo vamos ao detalhe do produto, e também começar com nossa API (para produtos e posts).

Repo: https://github.com/victorkurauchi/post-keystone

Até mais.

@victorkurauchi

Publicado no dia