4: Gegevens
De peiling heeft opslag nodig. Phlo's ORM definieert een tabel als een klasse, en voor een tutorial-app is de JSON-bestuurder perfect: geen database-server, geen inloggegevens, één JSON-bestand per tabel. In dit hoofdstuk definieer je het model, vul je het met opties en render je echte records.
4.1: Het modelbestand
Maak 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()))
Regel voor regel: @ extends: model maakt dit een modelklasse. De bestandsnaam type.poll.phlo geeft de klasse zijn naam, type_poll (punten worden onderstreept). table en columns beschrijven de tabel: een id, de optie tekst (string) en een stemteller (int). static DB => %JSONDB(data.'poll.json') wijst het model naar de JSON-driver; %JSONDB(...) is de instantie-afkorting voor phlo('JSONDB', ...), en data is de constante voor het gegevenspad van de app, zodat de tabel zich bevindt in data/poll.json. De berekende statische total telt alle stemmen op; je hebt het nodig voor percentages.
Een JSONDB-eigenschap om te onthouden: records komen terug als gewone gegevensobjecten, dus houd berekende waarden (zoals total) op de klasse als statics of op de controller, en schrijf wijzigingen via de statische modelmethoden in plaats van op de recordinstantie.
4.2: Zet de database resources aan
Resources zijn opt-in. Open data/app.json en lijst wat de modellaag nodig heeft:
{
"resources": [
"DB/DB",
"DB/model",
"DB/JSONDB",
"DB/JSON.result"
]
}
DB/model is de ORM, DB/JSONDB de besturingsdriver, DB/JSON.result de resultaatwrapper, en DB/DB de gedeelde basis. Wanneer je data/app.json met de hand bewerkt, lijst je zelf de vereisten; het Phlo Control Center lost ze voor je op, de teksteditor doet dat niet.
Herbouw en lint:
php www/app.php build::run
php www/app.php build::lint
build::run print de nieuwe gecompileerde bestanden (+type.poll.php, +model.php, +JSONDB.php, ...), en build::lint retourneert [].
4.3: Zaai de opties
De poll zou zijn eigen opties moeten aanmaken bij de eerste uitvoering. In poll.phlo, vervang de options prop en de home method met:
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() haalt alle records op; als er al records bestaan, wordt het zaaien overgeslagen. type_poll::create(...) voegt een record in met benoemde argumenten. Let op $this->seed zonder haakjes: methods zonder argumenten worden aangeroepen als properties.
Herlaad http://localhost/poll eenmaal, kijk dan naar de opslag:
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
}
]
(Plus Laravel en Rails.) De tabel is een leesbaar JSON-bestand; ids zijn automatisch toegewezen.
4.4: Render de records
Vervang nu de statische knoppen door records en voeg een resultatensectie toe. De volledige poll.phlo view-sectie:
prop question = 'Welke stack wint?'
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>
De share-methode zet een stemteller om in een percentage; {{ ... }} roept het aan binnen de style-attribuut. De #results id is later belangrijk: het is het element dat je ter plaatse zult bijwerken. Voeg de balkstijlen toe aan poll.style.phlo, binnen het <style ns=app> blok:
.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
Herlaad http://localhost/poll: vier opties, stemtellingen op 0, lege balken. Tijd om mensen te laten stemmen.