Escopos e paginação
O filtro com nome, reaproveitável e encadeável — e a página que traz o total que a tela precisa.
Duas peças pequenas que decidem se uma listagem de verdade é escrita uma vez ou dez.
Escopo: o filtro com nome#
ativos, do_mes, sem_pagamento — o filtro que aparece em dez lugares e, escrito dez vezes, diverge em um deles. Quando a regra muda (“ativo agora exclui suspenso”), há um lugar só para mudar; e o nome documenta a intenção, que um onde solto não faz.
dataforge
adopt Arcane.Forge as Forge
db := Forge.memoria()
Forge.limpar_modelos()
Pedido := Forge.modelo("Pedido", {
"id": "Serial", "cliente_id": "Inteiro",
"status": "Texto", "total": "Decimal"
})
Forge.ligar(Pedido, db)
Pedido.migrar()
Pedido.escopo("abertos", lambda q => q.onde("status", "aberto"))
Pedido.escopo("grandes", lambda q => q.onde("total", ">", 100.0))
Pedido.criar({"cliente_id": 1, "status": "aberto", "total": 250.0})
Pedido.criar({"cliente_id": 1, "status": "aberto", "total": 30.0})
Pedido.criar({"cliente_id": 2, "status": "pago", "total": 900.0})
out $"abertos: {len(Pedido.usar('abertos').buscar())}"
out $"grandes: {len(Pedido.usar('grandes').buscar())}"
// Encadear e o que separa um escopo de um atalho.
ambos := Pedido.usar("grandes", Pedido.usar("abertos")).buscar()
out $"abertos E grandes: {len(ambos)}"
assert len(ambos) is 1
Forge.fechar(db)Paginação#
Uma listagem sem teto é a forma mais comum de uma aplicação travar. E uma paginação sem total não desenha: a tela não sabe quantos botões pôr, e a saída comum é buscar tudo para contar — exatamente o que a paginação existe para evitar.
dataforge
adopt Arcane.Forge as Forge
db := Forge.memoria()
Forge.limpar_modelos()
Post := Forge.modelo("Post", {"id": "Serial", "titulo": "Texto"})
Forge.ligar(Post, db)
Post.migrar()
cycle i from 1 to 25:
Post.criar({"titulo": $"Post {i}"})
p := Post.paginar(pagina := 2, tamanho := 10)
out $"pagina {p['pagina']} de {p['paginas']}"
out $"{len(p['linhas'])} linhas, de {p['total']} no total"
out $"anterior: {p['tem_anterior']} proxima: {p['tem_proxima']}"
assert p["total"] is 25
assert p["paginas"] is 3
// Pagina fora da faixa e VAZIA, e nao erro: '?pagina=999' e um
// favorito de seis meses atras, e nao um ataque.
fora := Post.paginar(pagina := 999)
assert len(fora["linhas"]) is 0
assert fora["total"] is 25
Forge.fechar(db)| Decisão | Sem ela |
|---|---|
devolve total e paginas | a tela busca tudo para contar |
| página fora da faixa é vazia | um link antigo quebra a listagem |
tamanho tem teto de 500 | ?tamanho=999999 derruba a página |
| aceita uma consulta pronta | paginar um escopo exigiria repetir o filtro |
dataforge
adopt Arcane.Forge as Forge
db := Forge.memoria()
Forge.limpar_modelos()
Post := Forge.modelo("Post", {"id": "Serial", "titulo": "Texto", "status": "Texto"})
Forge.ligar(Post, db)
Post.migrar()
Post.escopo("publicados", lambda q => q.onde("status", "publicado"))
cycle i from 1 to 12:
Post.criar({"titulo": $"P{i}",
"status": "publicado" given i % 2 is 0 otherwise "rascunho"})
// Paginar SOBRE um escopo: o total ja e o do escopo.
p := Post.paginar(pagina := 1, tamanho := 4,
consulta := Post.usar("publicados"))
out $"publicados: {p['total']} em {p['paginas']} pagina(s)"
assert p["total"] is 6
Forge.fechar(db)Continue em Relações e Carga antecipada.