For the complete documentation index, see llms.txt. This page is also available as Markdown.

ACF Values

Use Timber's existing bridge for presentation fields, per call or through the opt-in setting in config/timber.php. PressGang adds no replacement bridge. Existing themes need no changes on upgrade. ACF remains optional; these ACF examples require it, while ordinary Timber metadata access still works without it.

$rows = $post->meta( 'flexible_content', [ 'transform_value' => true ] );

ACF recursively invokes Timber's replacement formatters, including inside groups, repeaters and flexible content. Arrays retain their row/layout structure; object leaves use the existing Timber class maps. Opt in the whole root only when its consumers expect the transformed types.

Return types and boundaries

Verified against Timber 2.5.1 and ACF 6.8.9:

ACF type
Nonempty transformed value

post_object

Timber Post, or PostArrayObject when multiple

relationship

Timber PostArrayObject

image

Timber Image

file

Timber Attachment (or attachment subclass)

gallery

PostArrayObject containing attachment/image posts

taxonomy

Timber Term for select/radio; array of terms otherwise

user

Timber User, or array of users when multiple

date_picker, date_time_picker

DateTimeImmutable

Object-like fields generally return false when empty. Collections are not native PHP arrays. Check empty/deleted selections and callers' return types when migrating; do not assume exact parity with the legacy mapper.

ACF's standard taxonomy type is taxonomy, not term. No custom term type was registered in the audited penarc site. The mapper's historical term case remains unchanged for other consumers.

The switch applies to Timber metadata access, not ordinary get_field(), get_sub_field() or options-page reads. Options and block contexts still use TimberMapper. The options cache remains theme_option_field_objects in group pressgang, invalidated on acf/save_post. It holds ACF-formatted, Timber-unmapped field objects; it is not an unformatted-ID cache. Nested options remain unchanged.

Known upstream limitations

Use one formatted mode per field/entity per request, across PHP, Twig and third-party ACF reads. Include nested sub-fields in that audit.

ACF 6.8.9 shares its formatted-value cache between normal ACF and transformed reads. In live probes, a normal nested-image read followed by a transformed read returned an ACF image array both times. Reversing the order returned a Timber Image both times. transform_value => false alone does not undo a transformed read. Normal WP_Post arrays may still become arrays of Timber posts through Timber's fallback conversion, so successful relationship conversion does not prove that all nested field types escaped the cache issue.

Timber 2.5.1 also lacks exception-safe restoration of its temporary ACF filters. A throwing formatter left the original ACF image formatter removed and Timber's replacement installed. Catching the exception does not make subsequent ACF reads safe. Avoid reentrant transformed reads inside formatting callbacks; PressGang does not provide scope management or vendor patches.

Keep the existing explicit mapper when a request needs normal ACF arrays/date strings as well as Timber objects. These version-specific limitations should be rechecked on dependency upgrades; this change does not fix them upstream.

Opting out and reading IDs

Transformation remains off by default. Opt in or out for a selected root at its meta() call, or enable transform_acf_values after auditing the child theme: query IDs, date strings and image/file arrays have existing consumers. There is no per-type or nested-path switch in this policy. Keep a mixed-use root untransformed and use explicit mapping where necessary.

For normal ACF formatting, use ordinary meta() or transform_value => false consistently from the start of the request. For IDs/storage-oriented input that must coexist with transformed reads, use one of these:

format_value => false alone loses to an enabled transform: Timber calls get_field(..., true) in that path. Storage values are not normal ACF-formatted values: a raw repeater root can be a row count, and unformatted ACF sub-fields can be keyed by field keys. Do not substitute either escape into a template expecting image arrays or formatted dates.

Set transform_acf_values to false to return to the parent default. Remove any local global-transform filter registration too; the setting does not override it.

Worked penarc migrations

These are opt-in edits for a consuming theme, not changes applied to penarc by this parent-theme documentation update. Audit all reads of each selected field first. In the inspected source, these four fields feed presentation getters; query builders using their names compare the current post ID rather than reading these field values. Request-wide plugin behaviour still needs a rendered-page check.

The existing shape is:

These replacements preserve the getters' native-array contracts. Timber performs the conversion; the remaining normalization materializes collections, removes null members for missing targets, and reindexes the result. This matters in the actual dataset: post 20166's other_arc_staff collection contains a null member. ?: [] handles empty fields, and the array branch also accepts Timber's fallback conversion of an already formatted WP_Post array. That branch does not fix the cache problem for other field types.

src/Traits/HasAuthors.php:

src/Traits/HasArticleRelationships.php:

src/Controllers/ResearchProjectController.php:

Remove the unused use PressGang\ACF\TimberMapper; import from each edited file. Do not pass the transformed collection back into to_timber_posts(): that helper accepts native arrays and otherwise returns an empty array. It remains supported, with its existing filtering and reindexing behaviour, for unmigrated consumers.

These examples were compared with the existing helper on live selections: staff_authors on post 23295, contact_staff_members on 10460, other_arc_staff on 20166 and arc_lead on 20169. All four preserved the selected IDs and order after normalization. The comparison isolated the ACF value cache between modes; it does not demonstrate safe mixed-mode reads.

If callers accept collections and selections cannot contain missing targets, a direct transformed read with ?: [] and an iterable return type can omit normalization. That is a deliberate contract change, not the compatibility example above. Keeping the existing mapper is also a supported choice.

For an audited nested presentation root, Twig can use the same upstream argument:

All supported descendants transform, including dates and files. Do not turn this on until the macros receiving those rows expect those types.

Evidence and performance

Live WP-CLI probes on penarc (PHP 8.3.8, Timber 2.5.1, ACF 6.8.9) demonstrated:

Read
Result

Post 17, flexible_content[2].people

PostArrayObject, 9 StaffMember objects

Post 65513, flexible_content[0].cards[0].image

Timber Image, ID 49660

Post 20184, lead_collaborators[0].partner_organisation

Timber Term, ID 3345

Post 66610, themes

false; stored fixture is empty

Existing themes schema with an in-memory row

PostArrayObject containing ResearchProject 20184

In-memory group containing relationship/date

PostArrayObject and DateTimeImmutable

Independent probes started with a fresh request-local ACF value store; the in-memory fixtures did not write content. Do not clear ACF's caches in application code as a workaround.

The database contained 1,524 published publications and 46 events. Three fields on 24 publications (72 calls) took a median 1.799 ms for warm normal ACF reads, versus 3.623 ms transformed. Across all publications (4,572 calls), warm times were 118.715 ms versus 225.346 ms. Both warm modes issued zero additional SQL. Timber performs 9 field-type lookups and 36 filter mutation calls for each transformed read, including scalar fields and cache hits.

A cold source-post read of a 27-item relationship used 2 queries with normal ACF versus 28 transformed; iterating the transformed collection added 6 queries. The numeric-ID path eagerly creates posts. Source-post metadata priming alone does not eliminate related-object N+1 queries. Transform only fields needed for the displayed page and measure rendered listings, not just warmed CLI loops.

See Timber's ACF documentation for the upstream API. The runtime findings above qualify its per-call opt-out advice for the versions tested.

Configuring an audited child theme

Keep existing Twig settings when adding this option to config/timber.php (child configuration replaces the parent file):

The setting defaults to false. Replace a local TransformAcfValues snippet and its registration with this configuration. Call PressGang\Bootstrap\Config::clear_cache() after changing configuration. Transformation uses Timber's existing bridge and class maps; ACF remains optional. Hook registration is harmless without ACF and no ACF functions are called by this configuration handler.

Missing references may still produce null collection entries. Keep presentation checks for missing values; this option does not add filtering or repair data.

Use raw_meta() for stored query IDs. Setting transform_value to false per call opts out of Timber transformation, but ACF's shared formatted cache still applies as described above. Disable the configuration option to stop registering its policy; false does not override filters installed by another plugin or theme.

Last updated