4: 数据
投票需要存储。Phlo 的 ORM 将表定义为类,对于一个教程应用,JSON 文件驱动程序是完美的:没有数据库服务器,没有凭据,每个表一个 JSON 文件。在本章中,您将定义模型,用选项填充它,并渲染真实记录。
4.1: 模型文件
创建 type.poll.phlo:
@ extends: model
static table = 'poll'
static order = 'votes DESC'
static DB => %JSONDB(data.'poll.json')
static columns = 'id,option,votes'
static total => array_sum(array_map(fn($row) => (int)$row->votes, static::records()))
逐行解释:@ extends: model 使其成为一个模型类。文件名 type.poll.phlo 赋予类名称 type_poll(点变为下划线)。table 和 columns 描述了表:一个 id,选项文本(字符串)和投票计数(整数)。static DB => %JSONDB(data.'poll.json') 将模型指向 JSON 驱动程序;%JSONDB(...) 是 phlo('JSONDB', ...) 的实例简写,而 data 是应用程序的数据路径常量,因此表位于 data/poll.json。计算的静态 total 汇总所有投票;你需要它来计算百分比。
需要记住的一个 JSONDB 特性:记录以普通数据对象的形式返回,因此将计算值(如 total)保留在类中作为静态或在控制器中,并通过静态模型方法而不是在记录实例上进行更改。
4.2: 打开数据库资源
资源是可选的。打开 data/app.json 并列出模型层所需的内容:
{
"resources": [
"DB/DB",
"DB/model",
"DB/JSONDB",
"DB/JSON.result"
]
}
DB/model 是 ORM,DB/JSONDB 是文件驱动,DB/JSON.result 是其结果包装器,而 DB/DB 是共享基础。当你手动编辑 data/app.json 时,你需要自己列出要求;Phlo Control Center 会为你解决这些问题,文本编辑器则不会。
重建并进行 lint:
php www/app.php build::run
php www/app.php build::lint
build::run 打印新的编译文件(+type.poll.php,+model.php,+JSONDB.php,...),而 build::lint 返回 []。
4.3: 播种选项
投票应该在第一次运行时创建自己的选项。在 poll.phlo 中,将 options prop 和 home method 替换为:
route GET poll => $this->home
method home {
$this->seed
view($this, 'Phlo Poll')
}
method seed {
if (type_poll::records()) return
foreach (['Phlo', 'Next.js', 'Laravel', 'Rails'] AS $option){
type_poll::create(option: $option, votes: 0)
}
}
type_poll::records() 获取所有记录;如果存在任何记录,则跳过种子填充。type_poll::create(...) 使用命名参数插入记录。注意 $this->seed 没有括号:没有参数的方法像属性一样被调用。
重新加载 http://localhost/poll 一次,然后查看存储:
docker run --rm -v $(pwd)/app:/app ghcr.io/q-ainl/phlo cat /app/data/poll.json
[
{
"option": "Phlo",
"votes": 0,
"id": 1
},
{
"option": "Next.js",
"votes": 0,
"id": 2
}
]
(加上 Laravel 和 Rails。)该表是一个可读的 JSON 文件;ids 是自动分配的。
4.4: 渲染记录
现在用记录替换静态按钮,并添加一个结果部分。完整的 poll.phlo 视图部分:
prop question = 'Which stack wins?'
method share($votes) => ($total = type_poll::total()) ? round($votes / $total * 100) : 0
view:
<main#app.poll>
<h1>$this->question</h1>
{{ $this->results }}
{{ $this->choices }}
</main>
view results:
<section#results.card>
<foreach type_poll::records() AS $option>
<div.option>
<span.name>$option->option</span>
<span.votes>$option->votes</span>
<div.track>
<div.bar style="width: {{ $this->share($option->votes) }}%"></div>
</div>
</div>
</foreach>
</section>
view choices:
<section.card>
<foreach type_poll::records() AS $option>
<button>$option->option</button>
</foreach>
</section>
share 方法将投票计数转换为百分比;{{ ... }} 在 style 属性内调用它。#results id 在后面很重要:它是你将要就地更新的元素。将条形样式添加到 poll.style.phlo 中,在 <style ns=app> 块内:
.option {
display: grid
grid-template-columns: 1fr auto
gap: 4px 12px
margin-bottom: 14px
}
.track {
grid-column: 1 / -1
height: 8px
border-radius: 4px
background: $border
}
.bar {
height: 100%
border-radius: 4px
background: $primary
transition: width .4s
}
.votes: color: $muted
重新加载 http://localhost/poll:四个选项,投票计数为 0,空条形。是时候让人们投票了。