In v4 I often did something like this:
// 1. load json file containing an array of page contents
// 2. search and update corresponding element in array
// 3. save json file
public function writeContent(array $data, ?string $languageCode = null): bool
{
$patch = array_filter([
'only' => $data['price'] ?? null,
'some' => $data['some'] ?? null,
'fields' => $data['fields'] ?? null,
'allowed' => $data['allowed'] ?? null,
], fn($v) => $v !== null);
$filePath = "path/to/json-file.json";
$allPages = json_decode(file_get_contents($filePath), true);
$itemIndex = array_search(
$this->ref()->value(),
array_column($allPages, 'ref')
);
$allPages[$itemIndex] = array_replace($allPages[$itemIndex], $data);
file_put_contents($filePath, json_encode($allPages));
return false;
}
In Kirby 5, writeContent is deprecated. This is what the changelog has to say:
- Extending the internal
$model->contentFile(),$model->contentFiles(),$model->contentFileDirectory(),$model->contentFileExtension(),$model->contentFileName(),$model->readContent()and$model->writeContent()methods in a page model will no longer have an effect as these methods are no longer called by the core. Please extend the newKirby\Content\PlainTextContentStorageHandlerclass instead and return an instance of your custom class from$model->storage(). Please note that the interface ofPlainTextContentStorageHandleris internal and may change in the future.
(emphasis mine)
Now, I’ve tried extending PlainTextContentStorageHandler but I can’t get it to actually save anything. No “changes” version and no “latest” version. My write() is never called (like, I put a die("hello world") in it and it’s never called).
The storage class roughly looks like this:
class MyStorage extends PlainTextStorage {
protected static array $fileCache = [];
public function __construct(MyPage $page, protected string $ref) {
parent::__construct($page);
}
protected function contentDirectory(VersionId $versionId): string
{
return "path/to";
}
public function contentFile(VersionId $versionId, Language $language): string
{
$filename = match ($versionId->value()) {
VersionId::LATEST => 'json-file.json',
VersionId::CHANGES => 'json-file-changes.json',
};
return $this->contentDirectory($versionId) . '/' . $filename;
}
public function delete(VersionId $versionId, Language $language): void {
throw new Exception('this must never happen');
}
public function exists(VersionId $versionId, Language $language): bool {
// this is what the original code looks like if you only have 1 file and no languages
return $versionId->is(VersionId::LATEST);
}
// new utility function to read and cache the json file
protected function readContentFile(VersionId $versionId, Language $language): array {
$contentFile = $this->contentFile($versionId, $language);
if (!file_exists($contentFile)) {
return [];
}
$json = file_get_contents($contentFile);
if ($json === false) {
return [];
}
return self::$fileCache[$versionId->value()] = json_decode($json, true);
}
public function read(VersionId $versionId, Language $language): array {
if($versionId->is(VersionId::LATEST)) {
$content = self::$fileCache[$versionId->value()] ?? $this->readContentFile($versionId, $language);
$index = array_search($this->ref, array_column($content, 'ref'));
if($index === false) throw new Exception('Page not found: ' . $this->ref);
return $content[$index];
}
// changes version merges the latest version with the changes
$latest = self::$fileCache[VersionId::LATEST] ?? $this->readContentFile(VersionId::latest(), $language);
$changes = self::$fileCache[VersionId::CHANGES] ?? $this->readContentFile(VersionId::changes(), $language);
$index_latest = array_search($this->ref, array_column($latest, 'ref'));
$index_changes = array_search($this->ref, array_column($changes, 'ref'));
if($index_latest === false && $index_changes === false) throw new Exception('Page not found: ' . $this->ref);
if($index_latest === false) return $changes[$index_changes];
if($index_changes === false) return $latest[$index_latest];
return array_replace($latest[$index_latest], $changes[$index_changes]);
}
public function write(VersionId $versionId, Language $language, array $fields): void {
$patch = array_filter([
'only' => $fields['price'] ?? null,
'some' => $fields['some'] ?? null,
'fields' => $fields['fields'] ?? null,
'allowed' => $fields['allowed'] ?? null,
], fn($v) => $v !== null);
$data = self::$fileCache[$versionId->value()] ?? $this->readContentFile($versionId, $language);
$index = array_search($this->ref, array_column($data, 'ref'));
if($index === false) throw new Exception('Apartment not found: ' . $this->ref);
$data[$index] = array_merge($data[$index], $patch);
self::$fileCache[$versionId->value()] = $data;
file_put_contents($this->contentFile($versionId, $language), json_encode($data));
}
}
The model looks like this:
public function storage(): Storage
{
return $this->storage ??= new MyStorage($this, $this->_ref);
}
Is there an example of how to update virtual pages with the panel?