5: 投票
没有投票的投票是一个列表。在本章中,每个选项都变成一个表单,一个 POST route 接收投票,计数增加,重定向重新渲染页面。纯 HTTP,尚未使用 JavaScript:这个版本在任何浏览器中都能工作,所有功能都关闭。
5.1: 投票表单
在 poll.phlo 中,将 choices 视图中的每个按钮变成一个小表单:
view choices:
<section.card>
<foreach type_poll::records() AS $option>
<form method=post action="/poll/vote/$option->id">
<button>$option->option</button>
</form>
</foreach>
</section>
每个表单都提交到其自己的 URL:/poll/vote/1、/poll/vote/2,依此类推。id 在路径中传递,因此表单不需要隐藏字段。请注意 action 值周围的引号:包含变量或斜杠的属性必须加引号。第 3 章中的 form: display: inline 规则使按钮保持在同一行。
重新加载 http://localhost/poll:看起来相同,但每个按钮现在都提交一个表单。点击一个按钮会返回 404,因为该路由尚不存在。
5.2: POST 路由
在 poll.phlo 中添加路由,靠近 GET 路由:
route POST poll vote $id {
if (!$option = type_poll::record(id: (int)$id)) return false
type_poll::change('id=?', (int)$id, votes: $option->votes + 1)
location('/poll')
}
从上到下阅读:
route POST poll vote $id匹配POST /poll/vote/<id>。路径中的$segment是一个变量,可以在块内使用。type_poll::record(id: ...)获取一条记录或返回 null。返回确切的false表示路由未命中:未知的 id 会被忽略,最终返回 404,而不是破坏数据。type_poll::change('id=?', $id, votes: ...)通过模型更新记录:一个 where 子句,它的值,然后是命名列。使用 JSONDB 时,您总是通过静态模型方法进行写入。location('/poll')进行重定向。POST、重定向、GET:自 2004 年以来安全刷新投票。
重新加载并点击 Phlo。页面重新加载,计数为 1,进度条跳到 100%。点击另一个选项,观察百分比重新分配。检查 data/poll.json 以查看磁盘上的投票情况。
5.3: 读取有效负载而不是
路径中的 id 是一种风格。另一种是请求体,您可以通过 %payload 读取。首先在 data/app.json 中启用资源:
{
"resources": [
"DB/DB",
"DB/model",
"DB/JSONDB",
"DB/JSON.result",
"payload"
]
}
基于负载的相同路由版本如下所示:
route POST poll vote @option {
$id = (int)%payload->option
if (!$option = type_poll::record(id: $id)) return false
type_poll::change('id=?', $id, votes: $option->votes + 1)
location('/poll')
}
@option 验证请求体:负载必须恰好包含键 option,否则路由不匹配。请求体中的键永远不会绑定为参数;您通过 %payload->option 读取它们。匹配的表单将是每个选项一个表单,形式为 <button name=option value="$option->id">。
这两种风格都是惯用的。对于本教程,请保留来自 X.2 的 $id 变体;它可以顺利过渡到 async 版本。
5.4: 你现在拥有的是什么
再运行一次检查:
php www/app.php build::run
php www/app.php build::lint
两者都停留在 []。你有一个完整的、可工作的投票:服务器渲染、刷新安全、零 JavaScript。投票几次,观察 data/poll.json 的变化,并注意到唯一让人感觉像2004年的事情:每次投票时的完整页面重新加载。这是下一个章节。