Dev Deep Dive: How to Update Hidden Fields in Statamic Using the EntrySaving Event
As a Statamic developer, there comes a time when you need to keep data in sync that isn't visible to your editors. Hidden fields are beneficial for storing search indexes, calculated values, or data aggregated from complex field structures, such as replicators.
Over the past few projects, I've reached for Statamic's EntrySaving event to handle this. Today, I want to share why this is the right place to handle hidden field updates—and how to implement it without slowing down your sites or risking infinite save loops.
Let's dig in.
Why Use Hidden Fields in Statamic?
In most Statamic builds, your blueprints include fields that your content editors interact with daily, such as titles, images, content blocks, taxonomy terms, and more. But there's often data you need to store behind the scenes:
Search keywords aggregated from nested fields
Machine-readable IDs or slugs
Data used for integrations (e.g., serialized JSON configs)
Composite values calculated from multiple fields
Hidden fields let you store that data without cluttering your UI.
However, these hidden fields don't update themselves. You need a mechanism to keep them in sync whenever the entry changes.
The Wrong Way: Saving Inside EntrySaved
One of the classic mistakes I see is developers using the EntrySaved event and then calling $entry->save()
from inside the listener:
use Statamic\Events\EntrySaved;
class UpdateHiddenField
{
public function handle(EntrySaved $event)
{
$entry = $event->entry;
$entry->set('hidden_field', 'some value');
$entry->save();
}
}
Don't do this.
Here's why:
EntrySaved fires after the entry has already been written to disk. So changing the entry's data at that point means you have to save again.
Calling save() inside EntrySaved triggers the event again, leading to infinite loops and 504 errors if not handled carefully.
It's unnecessary overhead, especially on bulk updates.
Trust me—more than once, I've watched servers grind to a halt from infinite save loops because of this pattern.
The Right Way: EntrySaving
The correct place to handle hidden fields is the EntrySaving event. This event fires just before Statamic writes your entry to disk. If you modify the entry object during this event, Statamic persists those changes as part of the same save operation. No double-saving. No recursion. No loops.
For this example, let's say we have a Products collection. Inside our products, we have a replicator field that stores Product model numbers with some other product model data.
First, we would need to set up a hidden field in our Products blueprint. If we added a hidden field called model_numbers
Next, we need to set up a new class for our Listener. So we run: php artisan make:listener UpdateHiddenField
. This will set up the file in App/Listeners. Here's a clean example of how to class's handle method:
namespace App\Listeners;
use Statamic\Events\EntrySaving;
class UpdateHiddenField
{
public function handle(EntrySaving $event)
{
$entry = $event->entry;
if ($entry->collectionHandle() !== 'products') {
return;
}
// Aggregate values from nested fields
$keywords = [];
$replicator = $entry->get('replicator_field_name');
if (is_array($replicator)) {
foreach ($replicator as $block) {
if (
isset($block['type']) &&
$block['type'] === 'model'
) {
$raw = $block['model_number'] ?? null;
if ($raw) {
$models = array_map('trim', explode(',', $raw));
$keywords = array_merge($keywords, $models);
}
}
}
}
if (!empty($keywords)) {
$entry->set('model_numbers', implode(' ', $keywords));
}
}
}
This listener:
✅ Runs before the save
✅ Updates your hidden field in memory
✅ Let Statamic handle writing it to disk
Registering Your Listener
Don't forget to wire your listener in your EventServiceProvider:
protected $listen = [
\Statamic\Events\EntrySaving::class => [
\App\Listeners\UpdateHiddenField::class,
],
];
A Note About Bulk Updates
A word of caution: Maybe you are writing this class after you already have a bunch of Products in your system and you retroactively need to update this field. if you run a bulk update via an artisan command or in Tinker, make sure you save each entry after setting the field. The listener updates the entry object in memory, but you still have to persist that entry:
$entries = \Statamic\Facades\Entry::query()
->where('collection', 'products')
->get();
foreach ($entries as $entry) {
$entry->save();
}
That's the safe way to ensure every hidden field gets updated without triggering recursion.
Benefits of This Approach
✅ Performance: No extra save operations.
✅ Safety: No risk of infinite loops.
✅ Maintainability: Keeps logic in one place.
✅ Clean code: Doesn't mix hidden logic into controllers or blueprints.
This technique has saved me dozens of hours across projects and kept my sites stable during large-scale content migrations and imports.
Final Thoughts
Updating hidden fields automatically is one of those subtle pieces of polish that takes your Statamic builds from good to great. Doing it the right way with EntrySaving ensures your data stays in sync without wrecking performance or your server.
If you're working on a Statamic project that requires hidden search indexes, aggregated data, or computed fields, hook into the save flow in this way.
Happy coding—and may your listeners never loop!
Categories
- Development
Tags:
- Statamic
- Code