decor-6-site/kirby/src/Content/Changes.php
2026-04-07 18:09:15 +02:00

205 lines
4.1 KiB
PHP

<?php
namespace Kirby\Content;
use Kirby\Cache\Cache;
use Kirby\Cms\App;
use Kirby\Cms\Files;
use Kirby\Cms\ModelWithContent;
use Kirby\Cms\Pages;
use Kirby\Cms\Users;
use Kirby\Toolkit\A;
/**
* The Changes class tracks changed models
* in the Site's changes field.
*
* @package Kirby Content
* @author Bastian Allgeier <bastian@getkirby.com>
* @link https://getkirby.com
* @copyright Bastian Allgeier
* @license https://getkirby.com/license
*/
class Changes
{
protected App $kirby;
public function __construct()
{
$this->kirby = App::instance();
}
/**
* Access helper for the cache, in which changes are stored
*/
public function cache(): Cache
{
return $this->kirby->cache('changes');
}
/**
* Returns whether the cache has been populated
*/
public function cacheExists(): bool
{
return $this->cache()->get('__updated__') !== null;
}
/**
* Returns the cache key for a given model
*/
public function cacheKey(ModelWithContent $model): string
{
return $model::CLASS_ALIAS . 's';
}
/**
* Verify that the tracked model still really has changes.
* If not, untrack and remove from collection.
*
* @template T of \Kirby\Cms\Files|\Kirby\Cms\Pages|\Kirby\Cms\Users
* @param T $tracked
* @return T
*/
public function ensure(Files|Pages|Users $tracked): Files|Pages|Users
{
foreach ($tracked as $model) {
if ($model->version('changes')->exists('*') === false) {
$this->untrack($model);
$tracked->remove($model);
}
}
return $tracked;
}
/**
* Return all files with unsaved changes
*/
public function files(): Files
{
$files = new Files([]);
foreach ($this->read('files') as $id) {
if ($file = $this->kirby->file($id)) {
$files->add($file);
}
}
return $this->ensure($files);
}
/**
* Rebuilds the cache by finding all models with changes version
*/
public function generateCache(): void
{
$models = [
'files' => [],
'pages' => [],
'users' => []
];
foreach ($this->kirby->models() as $model) {
if ($model->version('changes')->exists('*') === true) {
$models[$this->cacheKey($model)][] = (string)($model->uuid() ?? $model->id());
}
}
foreach ($models as $key => $changes) {
$this->update($key, $changes);
}
}
/**
* Return all pages with unsaved changes
*/
public function pages(): Pages
{
/**
* @var \Kirby\Cms\Pages $pages
*
* Always pass at least two arguments even if the
* data is empty so that `$site->find()` always
* returns a collection, not a single page
*/
$pages = $this->kirby->site()->find(
false,
false,
...$this->read('pages')
);
return $this->ensure($pages);
}
/**
* Read the changes for a given model type
*/
public function read(string $key): array
{
return $this->cache()->get($key) ?? [];
}
/**
* Add a new model to the list of unsaved changes
*/
public function track(ModelWithContent $model): void
{
$key = $this->cacheKey($model);
$changes = $this->read($key);
$changes[] = (string)($model->uuid() ?? $model->id());
$this->update($key, $changes);
}
/**
* Remove a model from the list of unsaved changes
*/
public function untrack(ModelWithContent $model): void
{
// get the cache key for the model type
$key = $this->cacheKey($model);
// remove the model from the list of changes
$changes = A::filter(
$this->read($key),
fn ($id) => $id !== (string)($model->uuid() ?? $model->id())
);
$this->update($key, $changes);
}
/**
* Update the changes field
*/
public function update(string $key, array $changes): void
{
$changes = array_unique($changes);
$changes = array_values($changes);
$this->cache()->set($key, $changes);
$this->cache()->set('__updated__', time());
}
/**
* Return all users with unsaved changes
*/
public function users(): Users
{
/**
* @var \Kirby\Cms\Users $users
*
* Always pass at least two arguments even if the
* data is empty so that `$users->find()` always
* returns a collection, not a single user
*/
$users = $this->kirby->users()->find(
false,
false,
...$this->read('users')
);
return $this->ensure($users);
}
}