Skip to content

Como Fazer: Fluxo de Trabalho Rascunho → Publicação → Arquivamento

Referência FT: FT305 (NENE2-FT/draftlog) — Máquina de estados do ciclo de vida de artigos: transições unidirecionais rascunho→publicado→arquivado, acesso de escrita apenas para o autor, não-autores veem apenas artigos publicados (rascunhos retornam 404), não é possível editar artigos publicados, listagem pública exclui rascunhos e arquivados, 20 testes / 28 asserções PASS.

Este guia mostra como implementar um ciclo de vida de conteúdo onde os artigos começam como rascunhos, são publicados para se tornarem visíveis e podem ser arquivados para removê-los das listagens públicas.

Schema

sql
CREATE TABLE articles (
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    author_id    INTEGER NOT NULL,
    title        TEXT    NOT NULL,
    body         TEXT    NOT NULL DEFAULT '',
    status       TEXT    NOT NULL DEFAULT 'draft',
    published_at TEXT,
    archived_at  TEXT,
    created_at   TEXT    NOT NULL,
    updated_at   TEXT    NOT NULL,
    CHECK (status IN ('draft', 'published', 'archived')),
    FOREIGN KEY (author_id) REFERENCES users(id)
);

CHECK (status IN (...)) garante que apenas estados conhecidos sejam armazenados. Os timestamps published_at e archived_at registram quando as transições ocorreram.

Máquina de estados

draft ──(POST /publish)──▶ published ──(POST /archive)──▶ archived
TransiçãoPré-condiçãoErro se violada
draft → publishedstatus deve ser 'draft'422
published → archivedstatus deve ser 'published'422
published → draft❌ não permitido
archived → qualquer❌ não permitido
php
// Handler de publicação
if ($article['status'] !== 'draft') {
    return $this->responseFactory->create(['error' => 'only draft articles can be published'], 422);
}

// Handler de arquivamento
if ($article['status'] !== 'published') {
    return $this->responseFactory->create(['error' => 'only published articles can be archived'], 422);
}

Endpoints

MétodoCaminhoAuthDescrição
POST/articlesX-User-IdCriar artigo (começa como rascunho)
GET/articlesListar apenas artigos publicados
GET/articles/{id}X-User-IdObter artigo (verificação de visibilidade)
PUT/articles/{id}X-User-Id (autor)Atualizar rascunho (apenas se for rascunho)
POST/articles/{id}/publishX-User-Id (autor)Publicar
POST/articles/{id}/archiveX-User-Id (autor)Arquivar

Novos artigos começam como rascunho

php
$id = $this->repo->create($actorId, $title, $body);
return $this->responseFactory->create(['id' => $id, 'status' => 'draft'], 201);

O status é sempre 'draft' na criação, independentemente de qualquer campo do corpo. O cliente não pode escolher o status inicial.

Visibilidade — Não-autores veem apenas publicados

php
// Não-autores podem ver apenas artigos publicados
if ($article['status'] !== 'published' && (int) $article['author_id'] !== $actorId) {
    return $this->responseFactory->create(['error' => 'not found'], 404);
}

Artigos não publicados (rascunho ou arquivado) retornam 404 para não-autores. Isso previne:

  • Outros usuários lendo rascunhos não publicados
  • Revelar se um artigo foi arquivado

Não é possível editar artigos publicados

php
// Handler de atualização — apenas rascunhos são editáveis
if ($article['status'] !== 'draft') {
    return $this->responseFactory->create(['error' => 'only draft articles can be edited'], 422);
}
if ((int) $article['author_id'] !== $actorId) {
    return $this->responseFactory->create(['error' => 'forbidden'], 403);
}

Uma vez publicado, o conteúdo do artigo fica congelado. O autor deve despublicar (o que não é suportado aqui) para editar — neste design, publicar é uma barreira unidirecional.

Endpoint de listagem — apenas publicados

php
// Repositório: SELECT WHERE status = 'published' ORDER BY published_at DESC
$articles = $this->repo->listPublished();

O endpoint de listagem filtra para status = 'published' apenas. Rascunhos e artigos arquivados nunca aparecem na listagem pública.

Ações apenas para o autor

Todas as operações de escrita (atualização, publicação, arquivamento) verificam se o ator é o autor do artigo:

php
if ((int) $article['author_id'] !== $actorId) {
    return $this->responseFactory->create(['error' => 'forbidden'], 403);
}

O Que NÃO Fazer

Anti-padrãoRisco
Permitir status no corpo de criaçãoCliente inicia artigo como 'published' contornando o fluxo de revisão
Retornar 403 para GET de rascunho de não-autorRevela que o artigo existe; use 404 para ocultar conteúdo não publicado
Permitir edição de artigos publicadosRetroativamente altera conteúdo ao vivo; viola a confiança do leitor
Permitir transição arquivado → publicadoArtigos arquivados reaparecem inesperadamente
Listar rascunhos na listagem públicaConteúdo não publicado fica exposto antes de estar pronto
Sem CHECK (status IN (...))Inserções diretas no DB podem definir strings de status arbitrárias
Artigos arquivados retornam 200 para não-autoresInforma não-autores que o conteúdo existiu e foi arquivado

Publicado sob a licença MIT.