Guias

O que uma rota devolve

A página padrão, seu próprio HTML, JSON, XML, texto, CSV, arquivos e PDF, e como fazer cada um.

Cada rota é uma tela em branco. Uma rota é uma pasta com um index.tpl, e o que esse template produz é a resposta, nada mais. O Nimbly não acrescenta layout, invólucro ou formato próprio.

  • Para ter a página padrão, com o head, os assets e a edição na página, termine o template com [#html#].
  • Para ter seu próprio documento HTML, escreva-o.
  • Para ter JSON, XML, texto simples, CSV, um arquivo ou um PDF, produza isso.

Todos usam os mesmos shortcodes, dados, templates, bibliotecas e permissões. Um site pode misturá-los à vontade: um site público com a shell padrão, uma landing page com documento próprio, um feed JSON, um sitemap e um download só para membros, lado a lado.

A rota devolve Como
A página padrão [#html#] no final do index.tpl
Seu próprio documento HTML Escreva o documento no index.tpl; veja Seu próprio HTML
JSON [#http-header json#] e o JSON
XML, um feed [#http-header xml#] e o XML
Texto simples [#http-header txt#] e o texto
CSV [#http-header csv#] e as linhas
Um arquivo Uma biblioteca que envia os cabeçalhos e o conteúdo do arquivo
Um PDF O módulo de PDF, a partir de um template HTML
Um redirecionamento [#redirect url#]
Um código de status [#http-header 404#], 403 ou 500

O endereço

Uma pasta de rota pode conter um ponto, então o endereço pode ter uma extensão:

ext/uri/projects.json/index.tpl     /projects.json
ext/uri/feed.xml/index.tpl          /feed.xml
ext/uri/export/projects.csv/        /export/projects.csv

Uma rota dinâmica funciona do mesmo jeito, por exemplo ext/uri/api/projects/(slug)/, que responde JSON para um projeto. Veja Roteamento.

O tipo de conteúdo

[#http-header type#] no início de um template define o Content-Type:

Tipo Content-Type
json application/json
xml application/xml; charset=UTF-8
txt text/plain; charset=UTF-8
csv application/csv; charset=UTF-8
css text/css
js application/javascript

Para qualquer outro tipo, ou para cabeçalhos extras, use uma biblioteca com a função header() do PHP, como mostrado abaixo.

JSON

ext/uri/projects.json/index.tpl:

[#http-header json#]
[#data projects filter=published:1 sort=year|numeric|desc#]
[#fmt var=data.projects empty={} type=json#]

[#fmt ... type=json#] codifica uma variável; [#get project.title json#] codifica um único valor, com aspas e escape. As quebras de linha entre os shortcodes não fazem mal: o JSON permite espaços em branco.

Para um formato próprio, escreva-o no template:

[#http-header json#][#data .config.site#]{"site": [#get data.config.site.name json#], "projects": [#data-count projects#]}

Quando a resposta precisa de lógica, monte-a numa biblioteca:

<?php
// ext/lib/project-stats.php

function project_stats_sc($params)
{
    load_library('data');
    $projects = data_read('projects') ?: [];
    header('Content-Type: application/json');
    return json_encode([
        'total' => count($projects),
        'per_sector' => array_count_values(array_column($projects, 'sector')),
    ]);
}

ext/uri/stats.json/index.tpl tem uma linha: [#project-stats#].

XML e feeds

ext/uri/feed.xml/index.tpl:

[#http-header xml#]<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Fieldwork projects</title>
    [#data projects filter=published:1 sort=year|numeric|desc limit=20#]
    [#repeat data.projects tpl=feed-item#]
  </channel>
</rss>

com um template por item, ext/uri/feed.xml/feed-item.tpl:

<item>
  <title>[#fmt var=item.title type=plain#]</title>
  <link>[#base-url#]/projects/[#item.title_slug#]</link>
  <description>[#fmt var=item.description type=plain#]</description>
</item>

Todo site já responde sitemap.xml e robots.txt. Um recurso acrescenta seus registros ao sitemap com uma configuração sitemap no seu .meta.

Texto simples

[#http-header txt#][#data .config.site#][#get data.config.site.name#]
Projects: [#data-count projects#]

CSV

[#http-header csv#]title,sector,year
[#data projects filter=published:1#][#repeat data.projects tpl=csv-row#]

com csv-row.tpl contendo uma linha por registro. O admin também exporta qualquer recurso para usuários com export-<resource>.

Arquivos

Os arquivos enviados à biblioteca de mídia são servidos em /download/<uuid>, com o tipo e o nome guardados.

Para servir um arquivo com suas próprias regras, como um documento só para membros ou um arquivo gerado na hora, deixe uma biblioteca enviá-lo:

<?php
// ext/lib/member-document.php

function member_document_sc($params)
{
    load_library('access');
    if (!access_by_feature('view-member-documents')) {
        http_response_code(403);
        exit();
    }
    $path = $GLOBALS['SYSTEM']['file_base'] . 'ext/data/.member-files/handbook.pdf';
    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename="Member handbook.pdf"');
    header('Content-Length: ' . filesize($path));
    readfile($path);
    exit();
}

ext/uri/members/handbook.pdf/index.tpl contém [#member-document#]. O mesmo padrão envia um ZIP, uma imagem gerada na hora ou um arquivo de calendário: os cabeçalhos e os bytes são seus.

Documentos PDF

O módulo de PDF transforma em PDF o HTML dos seus próprios templates, guarda o resultado em cache e o envia como download:

./nimbly module:install pdf
load_library('pdf');
$pdf = pdf_render_cached_document([
    'paper_size' => 'a4',
    'orientation' => 'portrait',
    'pages' => [$html],
]);
pdf_send_download($pdf, 'Project-overview.pdf');
exit();

A chave do cache cobre o documento e o renderizador, então um texto ou template alterado gera um novo PDF sozinho.

Imagens

As imagens enviadas são servidas em /img/<uuid>/<size>, no tamanho que o endereço pede e em WebP: 800w para uma largura, 400x400c para um recorte. Nos templates, [#get-img-html#] escreve o markup responsivo. Veja Mídia e edição inline.

Redirecionamentos e códigos de status

[#redirect [#base-url#]/en/#]
[#http-header 404#][#http-header json#]{"error": "not found"}

Uma rota dinâmica que não aceita uma requisição cai na página 404 do site, que ext/uri/errors/404/ pode substituir.

A API REST

Todo recurso também responde em /api/v1/<resource> com JSON, para listar, ler, criar, alterar e excluir, com as permissões de quem chama. Use-a para apps e integrações, e escreva rotas próprias para o que ela não cobre. Veja a referência da API REST.

Ver esta página em Markdown