Content clean-up
Kirby doesn't delete fields from content files if you later remove them from your blueprints, rename them, or if you add fields manually with an editor. Especially during development, you therefore often end up with a lot of unused stuff you will probably want to clean up before handing a project over to your client.
This script cleans up your page, file and user content files. It compares fields in the content files with those defined in the blueprint and removes all undefined fields, no matter if they contain content or not.
You can also set the fields to ignore in the $ignore array. By default, we ignore the following fields: uuid, for pages title and slug and for files template, focus and sort.
Create a new cleanup.php file in your site root and copy the script code into it.
Then run the script with http://yourdomain.com/cleanup.php. You can also run the script on the command line or put the code into a route if you cannot call a PHP script in your browser. Delete the script when done.
Before you use this script, make sure you have a backup of your project.
Do not use this script if your project contains fields not defined in blueprints on purpose.
The script only cleans up the saved content. Make sure there are no unsaved changes in the Panel before you run it.
<?php
/**
* Script to clean up unused fields in page, file and user content files
* Compares fields present in the content file
* with fields defined in the blueprint
* and removes all undefined fields
*/
use Kirby\Cms\App as Kirby;
use Kirby\Cms\Html;
use Kirby\Cms\ModelWithContent;
require __DIR__ . '/kirby/bootstrap.php';
$kirby = new Kirby;
// Authenticate as almighty
$kirby->impersonate('kirby');
// Define your collection
$models = $kirby->models();
// Set the fields to be ignored
$ignore = ['uuid', 'title', 'slug', 'template', 'sort', 'focus'];
foreach ($models as $model) {
// Call the script for all languages if multilang
if ($kirby->multilang() === true) {
foreach ($kirby->languages() as $language) {
cleanUp($model, $ignore, $language->code());
}
} else {
cleanUp($model, $ignore);
}
}
function cleanUp(
ModelWithContent $model,
array $ignore = [],
string $lang = 'default'
): void {
$version = $model->version('latest');
// Get the fields stored in the content file for this language
// (without falling back to the default language)
$contentFields = $version->read($lang);
// Skip if there is no content file for this language
if ($contentFields === null) {
return;
}
// Get all field keys from the blueprint
$blueprintFields = array_keys($model->blueprint()->fields());
// Get all field keys that are neither in the blueprint
// nor in the `$ignore` array
$fieldsToBeDeleted = array_diff(
array_keys($contentFields),
$blueprintFields,
array_map(strtolower(...), $ignore)
);
$name = $model->id() ?? $model::class;
if ($fieldsToBeDeleted === []) {
echo Html::tag('p', 'Nothing to clean up in ' . $name . ' (' . $lang . ')');
return;
}
// Remove the fields to be deleted
$data = array_diff_key($contentFields, array_flip($fieldsToBeDeleted));
// Try to write the cleaned-up content back to the content file
try {
$version->replace($data, $lang);
echo Html::tag('p', 'Removed ' . implode(', ', $fieldsToBeDeleted) . ' from ' . $name . ' (' . $lang . ')');
} catch (Exception $e) {
echo Html::tag('p', $name . ': ' . $e->getMessage());
}
}
Instead of echoing the results on screen, you can also write them into a log file if you prefer.