The second code Snippet is that actual used one with a Files Section instead of a Files Field like sonja recommended to use.
Here is the Code of the Virtual Pages Child:
<?php
class VoicesPage extends Kirby\Cms\Page
{
public function children()
{
$voices = [];
foreach (Db::select('voices') as $voice) {
$voices[] = [
'slug' => $voice->slug(),
'num' => $voice->status() === 'listed' ? 0 : null,
'template' => 'voice',
'model' => 'voice',
'content' => [
'title' => $voice->title() ?? 'New Voice',
'shortname' => $voice->shortname(),
'image' => $voice->image(),
'status' => is_null($voice->status()) ? 'draft' : $voice->status()
]
];
}
return Pages::factory($voices, $this);
}
}
and the code for the Parent
<?php
class VoicePage extends Kirby\Cms\Page
{
public function changeSlug(string $slug, string $languageCode = null)
{
// always sanitize the slug
$slug = Str::slug($slug);
$data['slug'] = $slug;
if ($comment = Db::first('voices', '*', ['slug' => $this->slug()])) {
if (Db::update('voices', $data, ['slug' => $this->slug()])) {
return $this;
};
}
return $this;
}
protected function changeStatusToDraft()
{
$data['status'] = 'null';
if ($comment = Db::first('voices', '*', ['slug' => $this->slug()])) {
return Db::update('voices', $data, ['slug' => $this->slug()]);
}
return $this;
}
protected function changeStatusToListed(int $position = null)
{
// create a sorting number for the page
$num = $this->createNum($position);
// don't sort if not necessary
if ($this->status() === 'listed' && $num === $this->num()) {
return $this;
}
$data['status'] = 'listed';
if ($comment = Db::first('voices', '*', ['slug' => $this->slug()])) {
return Db::update('voices', $data, ['slug' => $this->slug()]);
}
if ($this->blueprint()->num() === 'default') {
$this->resortSiblingsAfterListing($num);
}
return $this;
}
protected function changeStatusToUnlisted()
{
if ($this->status() === 'unlisted') {
return $this;
}
$data['status'] = 'unlisted';
if ($comment = Db::first('voices', '*', ['slug' => $this->slug()])) {
return Db::update('voices', $data, ['slug' => $this->slug()]);
}
$this->resortSiblingsAfterUnlisting();
return $this;
}
public function changeTitle(string $title, string $languageCode = null)
{
$data['shortname'] = $title;
if ($comment = Db::first('voices', '*', ['slug' => $this->slug()])) {
if (Db::update('voices', $data, ['slug' => $this->slug()])) {
return $this;
};
}
return $this;
}
public function writeContent(array $data, string $languageCode = null): bool
{
$data['shortname'] = $data['title'];
unset($data['title']);
if ($voice = Db::first('voices', '*', ['slug' => $this->slug()])) {
return Db::update('voices', $data, ['slug' => $this->slug()]);
} else {
$data['slug'] = $this->slug();
return Db::insert('voices', $data);
}
}
public function delete(bool $force = false): bool
{
return Db::delete('voices', ['slug' => $this->slug()]);
}
public function isDraft(): bool
{
return in_array($this->content()->status(), ['listed', 'unlisted']) === false;
}
public function title(): Kirby\Cms\Field
{
return $this->shortname()->excerpt(100)->or('New voice');
}
}