Are you an LLM? You can read better optimized documentation at /foehn/guide/acf-options-pages.md for this page in Markdown format
ACF Options Pages
Føhn provides #[AsAcfOptionsPage] for creating ACF options pages with type-safe fields using stoutlogic/acf-builder.
Requirements
- ACF Pro installed and active
stoutlogic/acf-builderpackage
bash
composer require stoutlogic/acf-builderBasic Options Page
php
<?php
// app/Options/ThemeSettings.php
namespace App\Options;
use Studiometa\Foehn\Attributes\AsAcfOptionsPage;
use Studiometa\Foehn\Contracts\AcfOptionsPageInterface;
use StoutLogic\AcfBuilder\FieldsBuilder;
#[AsAcfOptionsPage(
pageTitle: 'Theme Settings',
menuTitle: 'Theme',
menuSlug: 'theme-settings',
capability: 'manage_options',
iconUrl: 'dashicons-admin-generic',
)]
final class ThemeSettings implements AcfOptionsPageInterface
{
public static function fields(): FieldsBuilder
{
return (new FieldsBuilder('theme_settings'))
->addText('site_name', ['label' => 'Site Name'])
->addTextarea('footer_text', ['label' => 'Footer Text'])
->addImage('logo', ['label' => 'Site Logo']);
}
}Retrieving Option Values
Using AcfOptionsService
php
<?php
namespace App\ContextProviders;
use Studiometa\Foehn\Attributes\AsContextProvider;
use Studiometa\Foehn\Contracts\ContextProviderInterface;
use Studiometa\Foehn\Services\AcfOptionsService;
#[AsContextProvider(templates: ['*'])]
final readonly class GlobalContextProvider implements ContextProviderInterface
{
public function __construct(
private AcfOptionsService $options,
) {}
public function provide(array $context): array
{
return [
...$context,
'site_name' => $this->options->get('site_name', 'theme-settings'),
'footer_text' => $this->options->get('footer_text', 'theme-settings'),
'logo' => $this->options->get('logo', 'theme-settings'),
];
}
}Using ACF Functions Directly
php
// Get a single field
$siteName = get_field('site_name', 'theme-settings');
// Get all fields
$settings = get_fields('theme-settings');Sub-Pages
Create child pages under a parent options page:
php
<?php
// app/Options/SocialSettings.php
namespace App\Options;
use Studiometa\Foehn\Attributes\AsAcfOptionsPage;
use Studiometa\Foehn\Contracts\AcfOptionsPageInterface;
use StoutLogic\AcfBuilder\FieldsBuilder;
#[AsAcfOptionsPage(
pageTitle: 'Social Media',
parentSlug: 'theme-settings',
capability: 'manage_options',
)]
final class SocialSettings implements AcfOptionsPageInterface
{
public static function fields(): FieldsBuilder
{
return (new FieldsBuilder('social_settings'))
->addUrl('facebook', ['label' => 'Facebook URL'])
->addUrl('twitter', ['label' => 'Twitter URL'])
->addUrl('instagram', ['label' => 'Instagram URL'])
->addUrl('linkedin', ['label' => 'LinkedIn URL']);
}
}Full Configuration
php
#[AsAcfOptionsPage(
pageTitle: 'Advanced Settings',
menuTitle: 'Advanced',
menuSlug: 'advanced-settings',
capability: 'manage_options',
position: 59,
iconUrl: 'dashicons-admin-settings',
redirect: false,
postId: 'advanced_options',
autoload: true,
updateButton: 'Save Settings',
updatedMessage: 'Settings saved successfully!',
)]Options Without Fields
You can create options pages without implementing AcfOptionsPageInterface. This is useful when fields are defined elsewhere or via the ACF UI:
php
<?php
namespace App\Options;
use Studiometa\Foehn\Attributes\AsAcfOptionsPage;
#[AsAcfOptionsPage(
pageTitle: 'External Settings',
menuSlug: 'external-settings',
)]
final class ExternalSettings
{
// Fields defined in ACF UI or imported JSON
}AcfOptionsService API
The AcfOptionsService provides a convenient wrapper around ACF functions:
php
use Studiometa\Foehn\Services\AcfOptionsService;
$options = new AcfOptionsService();
// Get a single field value
$value = $options->get('field_name', 'options-page-slug');
// Get all fields from an options page
$allFields = $options->all('options-page-slug');
// Check if a field has a value
if ($options->has('field_name', 'options-page-slug')) {
// Field has a non-empty value
}
// Get field object with metadata
$fieldObject = $options->getObject('field_name', 'options-page-slug');Using in Twig Templates
twig
{# Get options in a context provider and pass to template #}
<footer class="footer">
<p>{{ footer_text }}</p>
{% if logo %}
<img src="{{ logo.src }}" alt="{{ site_name }}">
{% endif %}
</footer>