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: Switch on the database resources

Resources are opt-in. Open data/app.json and list what the model layer needs:

{
    "resources": [
        "DB/DB",
        "DB/model",
        "DB/JSONDB",
        "DB/JSON.result"
    ]
}

DB/model is the ORM, DB/JSONDB the file driver, DB/JSON.result its result wrapper, and DB/DB the shared base. When you edit data/app.json by hand you list requirements yourself; the Phlo Control Center resolves them for you, the text editor does not.

The next request rebuilds all of this automatically; the CLI shows exactly what transpiles:

php www/app.php build::run
php www/app.php build::lint

build::run prints the new transpiled files (+type.poll.php, +model.php, +JSONDB.php, ...), and build::lint returns [].

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 the records

Now replace the static buttons with records, and add a results section. The full poll.phlo view section:

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>

The share method turns a vote count into a percentage; {{ ... }} calls it inside the style attribute. The #results id matters later: it is the element you will update in place. Add the bar styles to poll.style.phlo, inside the <style> block:

.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

Reload http://localhost/poll: four options, vote counts at 0, empty bars. Time to let people vote.

We gebruiken essentiële cookies om deze site te laten werken. Met uw toestemming gebruiken we ook analytics om de site te verbeteren.