PHPackages                             tbruckmaier/corcel-acf - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. tbruckmaier/corcel-acf

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

tbruckmaier/corcel-acf
======================

Advanced Custom Field (ACF) plugin for Corcel

1.12.4(5mo ago)3020.9k—0%5[3 issues](https://github.com/tbruckmaier/corcel-acf/issues)MITPHPPHP &gt;=7.0.0|^8.0CI passing

Since Jan 23Pushed 5mo ago1 watchersCompare

[ Source](https://github.com/tbruckmaier/corcel-acf)[ Packagist](https://packagist.org/packages/tbruckmaier/corcel-acf)[ Docs](http://github.com/tbruckmaier/corcel-acf)[ RSS](/packages/tbruckmaier-corcel-acf/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (5)Versions (27)Used By (0)

Corcel ACF Plugin
=================

[](#corcel-acf-plugin)

[![PHPunit](https://github.com/tbruckmaier/corcel-acf/actions/workflows/ci.yml/badge.svg)](https://github.com/tbruckmaier/corcel-acf/actions/workflows/ci.yml)[![StyleCI](https://camo.githubusercontent.com/8c81e78e7bbef87a3b908c38ed18e59f6fa31b74eca940967aab290d62c52cf3/68747470733a2f2f6769746875622e7374796c6563692e696f2f7265706f732f3136363035333437312f736869656c643f6272616e63683d6d6173746572)](https://github.styleci.io/repos/166053471)[![Packagist](https://camo.githubusercontent.com/3bac37c574074e35fe814542b040b9397fe938f005ad897c9bf5b6fe586b9ec5/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f74627275636b6d616965722f636f7263656c2d6163662e737667)](https://github.com/tbruckmaier/corcel-acf/releases)[![Packagist](https://camo.githubusercontent.com/50ed6e5dac51060d4d3ca2af19ce38c65d69c999c64802fea33d6331c68473f0/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f74627275636b6d616965722f636f7263656c2d6163662e737667)](https://packagist.org/packages/tbruckmaier/corcel-acf)

> Use Wordpress Advanced Custom Fields (ACF) in laravel.

This Corcel plugin allows you to fetch WordPress custom fields created by the [ACF](http://advancedcustomfields.com) plugin using the same syntax of Eloquent, from the [Laravel Framework](http://laravel.com). You can use Eloquent models and Collections to improve your development, using the WordPress backend with any PHP application.

For more information about how Corcel works please visit [the repository](http://github.com/jgrossi/corcel).

- [Installation](#installation)
- [Features](#features)
- [Usage](#usage)
    - [Functionality](#functionality)
    - [Fields](#fields)
    - [Custom field types](#custom-field-types)
- [Running Tests](#running-tests)
- [Licence](#licence)

Installation
============

[](#installation)

To install the ACF plugin for Corcel is easy:

```
composer require tbruckmaier/corcel-acf

```

Corcel is required for this plugin, but don't worry, if it's missing it will be installed as well.

Features
========

[](#features)

- loads ACF fields via eloquent relations
    - loads acf data for a post only once and save sql queries
    - supports eager loading of acf relations
- supports deeply encapsulated fields (e.g. a image in a repeater in a flexible content)
- return suitable data types for the different acf fields (see table below)
    - unknown fields return a generic class with access to the raw db values
    - custom classes can be used for existing &amp; unknown fields
- possible to access acf field config and internal attributes
- full support for option page
- support for PHP- &amp; database-based ACF config
- `toArray()` and `toJson()` work recursively even on nested repeaters/ other fields (since 1.6)

Basic usage
===========

[](#basic-usage)

The easiest way to create the acf relations is the included trait:

```
use \Corcel\Models\Post as BasePost;
use Tbruckmaier\Corcelacf\AcfTrait;

class Post extends BasePost
{
    use AcfTrait;

    public static function boot()
    {
        self::addAcfRelations(['title', 'thumbnail']);
        parent::boot();
    }
}
```

This dynamically creates the relationships `acf_title()` and `acf_thumbnail()`. Acf fields can now be accessed:

```
use Corcel\Models\Post;
use Corcel\Models\Attachment;
use Tbruckmaier\Corcelacf\Models\Text;
use Tbruckmaier\Corcelacf\Models\Image;

$post = Post::find(1);

// post has a text field named "title" and a image field called "thumbnail"

$post->acf_title; // an instance of Text::class
$post->acf_title->value; // "Example title"
$post->acf_thumbnail; // Image::class
$post->acf_thumbnail->value; // an instance of Attachment::class representing the specified image
```

To make things easier, `AcfTrait` also includes `getAcfAttribute`, which returns an instance of the helper class `Acf` (this replaces the outdated corcel acf plugin from ). Relation can also be accessed like:

```
$post->acf->title; // (string) the parsed value, for instance "Example Page #1"
$post->acf->title(); // an instance of the underlying Text::class
$post->acf->title()->value; // the parsed value, $post->acf->title is a short version of this
$post->acf->title()->internal_value; // the unparsed value, for text's this is the same as value
$post->acf->title()->config; // the acf field config array defined in wordpress, sth like ['type' => 'text', 'instructions' => 'The site title', ...]

$post->acf->thumbnail; // an instance of Attachment::class representing the specified image
$post->acf->thumbnail(); // an instance of the underlying Image::class
$post->acf->thumbnail()->value; // again the same Attachment::class
$post->acf->thumbnail()->internal_value; // the unparsed value from the `postmeta` table, in this case the attachment id
$post->acf->thumbnail()->config; // the thumbnail acf field config array (['type' => 'image', ...])
```

PHP-based ACF config
--------------------

[](#php-based-acf-config)

If your ACF configuration works [via PHP](https://www.advancedcustomfields.com/resources/register-fields-via-php/), you can must pass the config array in `AcfTrait`:

```
// wordpress functions.php

acf_add_local_field_group(array(
    'key' => 'group_1',
    'title' => 'My Group',
    'fields' => array (
        array (
            'key' => 'field_1',
            'label' => 'Sub Title',
            'name' => 'sub_title',
            'type' => 'text',
        )
    ),
    'location' => array (
        array (
            array (
                'param' => 'post_type',
                'operator' => '==',
                'value' => 'post',
            ),
        ),
    ),
));
```

```
// laravel model

use \Corcel\Models\Post as BasePost;
use Tbruckmaier\Corcelacf\AcfTrait;

class Post extends BasePost
{
    use AcfTrait;

    public static function boot()
    {
        self::addAcfRelations([
            'field_1' => [
                'key' => 'field_1',
                'label' => 'Sub Title',
                'name' => 'sub_title',
                'type' => 'text',
            ],
        ]);
        parent::boot();
    }
}
```

To determine which model is used for which field, the acf field's `type` variable is used. See below for a full list.

Functionality
-------------

[](#functionality)

Wordpress stores the config of each acf field in the table `wp_posts` as a custom post type `acf-field`. `post_content` contains the serialized config array, which contains the acf field type (`type`) and everything else you can specify when creating acf fields. Some of these values are important for parsing, some only specify things on how to display the field in wordpress itself.

When a post is saved in Wordpress with acf fields, those values are stored as the post's meta data in the table `wp_postmeta`. Each acf field saves two values: the according acf field name in `wp_posts` and the actual value. Depending on the acf field type and configuration, the saved value differs (text field values are stored as plain text, relation fields like image as the attachment id, and repeater &amp; flexible content fields as a whole bunch of different fields).

For instance, the example fields from above are saved like this:

```
| meta_key   | meta_value          |
|:-----------|:--------------------|
| _title     | field_5bdae4fb72c4a |
| title      | Example Page #1     |
| _thumbnail | field_5c3b6543480d6 |
| thumbnail  | 5                   |

```

When loading a post with corcel, the meta data is automatically retrieved from the database anyway. If the static propery `$acfRelations` is defined in the model's `boot()` method,

The base `Acf` class uses this data and passes it to all fields (and possibly subfields), so no extra queries are needed. Just relational fields like `Image` need another query to find the correct `Attachment` class (though if you are only interested in the attachment's id, you can access `$post->acf->thumbnail()->internal_value` and no additional query is used).

This just adds a `getAcfAttribute()` method, which returns an instance of the base `Tbruckmaier\Corcelacf\Acf` class (this overwrites the corcel-internal default support for the outdated Corcel acf plugin: . If you would like to use it in parallel, you can define the `getAcfAttribute` method by yourself with a different name)

Option page
-----------

[](#option-page)

Fields in ACF option pages can be used the same way, though it is a bit more tricky to instantiate the option page. The relevant field configs are stored in `wp_posts` with post\_type `acf-field` and are all children of a `acf-field-group` (via `post_parent`). The field values are stored in `wp_options`, with a certain prefix (defaults to `options`).

```
wp_options

| option_name                            | option_value        |
|:---------------------------------------|:--------------------|
| _options_page-title                    | field_5bdae4fb72c4a |
| options_page-title                     | My page             |
| _options_page-description              | field_5891ef34058bf |
| options_page-title                     | My description      |
| _additional-options-my-repeater        | field_5c3b6543480d6 |
| additional-options-my-repeater         | 2                   |
| _additional-options-my-repeater_0_text | field_58737273acc78 |
| additional-options-my-repeater_0_text  | Entry #1            |
| _additional-options-my-repeater_1_text | field_58737273acc78 |
| additional-options-my-repeater_1_text  | Entry #2            |

```

First of all, we need to find the option page's `acf-field-group`. We can find its id in the url when editing the field group in Wordpress: `/wp-admin/post.php?post=1016&action=edit`. It can also be found by its slug or page title (see below)

The prefix is normally set in `functions.php` and defaults to `options`.

```
// functions.php:

// no parameters result in the prefix "options"
acf_add_options_page();

// another option page with a different prefix
acf_add_options_page([
    'post_id' => 'additional-options',
]);

// laravel:

use Tbruckmaier\Corcelacf\OptionPage;

// get the option page's field group by id, take it from the url for instance
$optionPage = OptionPage::find(1016);

// ... or find it by its title. This is not the title given to acf_add_options_page(), but the field group name.
$optionPage1 = OptionPage::byTitle('Page option fields')->first();

// load the option data from the database
$optionPage->loadOptions();

// alternatively with a custom prefix
$optionPage1->loadOptions('additional-options');

// get a option
$pageTitle = $optionPage->getOption('page-title'); // "My page"

// or the underlying Field
$pageTitle = $optionPage->getOptionField('page-title'); // Text::class

// works with all fields:
$myRepeater = $optionPage1->getOption('my-repeater'); // Collection
$myRepeater->first()->text; // "Entry #1"
```

If anyone stumbles upon an easier solution for option pages, I am open for suggestions. Maybe there is a way to get the field group by passing the prefix, or the other way round?

Advanced usage
--------------

[](#advanced-usage)

### Custom field classes

[](#custom-field-classes)

You can use your own classes for certain field types to extend them with custom attributes &amp; methods. Publish the configuration via `artisan vendor:publish --provider='Tbruckmaier\Corcelacf\ServiceProvider'` and fill in the class names in `config/corcel-acf.php`. You can overwrite existing field types or define new ones:

```
// config/corcel-acf.php
    'classMapping' => [
        'text' => CustomText::class,
        'google_maps' => GoogleMapsField::class,
    ]

// CustomText.php
class CustomText extends \Tbruckmaier\Corcelacf\BaseField
{
    public function getValueAttribute()
    {
        return htmlentities($this->internal_value);
    }

    public function getWordsAttribute()
    {
        return explode(' ', $this->internal_value);
    }
}

// Usage
$post->acf->my_text_field(); // CustomText::class
$post->acf->my_text_field; // "one &amp; two"
$post->acf->my_text_field()->words; // ["one", "&", "two"]
```

The custom classes should extend `Tbruckmaier\Corcelacf\BaseField`

### Defining acf relations

[](#defining-acf-relations)

Instead of using the model's `boot()` method to create relationships on the fly, one can also define them manually:

```
use Corcel\Models\Post as BasePost;
use Tbruckmaier\Corcelacf\AcfTrait;

class Post extends BasePost
{
    use AcfTrait;

    public function thumbnail()
    {
        return $this->hasAcf('thumbnail');
    }
}

$post = Post::find(1);
$post->thumbnail; // Image::class
$post->thumbnail->value; // Attachment
```

Whenever Corcel models are returned (for instance an `Corcel\Model\Attachment` class for an image), the corcel class mapping config is considered (see ).

### Eager loading

[](#eager-loading)

If you want to eager-load acf fields, you can use the standard eloquent syntax. If the relationships are created from `$acfRelations`, do not forget to pass the prefix:

```
$posts = Post::all()->load('acf_thumbnail');
```

Fields
------

[](#fields)

The following field types are supported (everything else just returns a `Generic` field):

FieldInternal classParsed response\_\_toString()TextText`string`TextareaText`string`NumberText`string`E-mailText`string`URLText`string`PasswordText`string`WYSIWYG (Editor)Text`string`oEmbedText`string`ImageImage`Corcel\Model\Attachment`FileFile`Corcel\Model\Attachment`GalleryGallery`Collection` of `Corcel\Model\Attachment`SelectChoice`string` or `array`CheckboxChoice`string` or `array`RadioChoice`string`True/FalseBoolean`boolean`Post ObjectPost`Corcel\Model\Post` or `Collection` of `Post`RelationshipRelationship`Collection` of `Post`Page LinkPageLink`string`LinkLink`array` or `string`HTML tag or urlTaxonomyTerm`Corcel\Term` or `Collection` of `Term`UserUser`Corcel\User`Date PickerDateTime`Carbon\Carbon`Date Time PickerDateTime`Carbon\Carbon`Time PickerDateTime`Carbon\Carbon`Color PickerText`string`GroupGroup`GroupLayout`RepeaterRepeater`Collection` of `RepeaterLayout`Flexible ContentFlexibleContent`Collection` of `FlexibleContentLayout`(everything else)Genericstring### Link

[](#link)

The link field reacts on the configured return value, so it returns either an array with `title`, `text` and `url` or just the `url` as string.

The field has a `render()` method, which renders a html tag. `render()` supports custom link text and custom attributes: `render('', ['class' => 'class-1'])` returns ``

When accessing the field as string (`(string)$post->acf->link()` or in blade `{!! $post->acf->link !!}`), `render()` is called, so a html string is returned.`

### Repeater &amp; Flexible Content

[](#repeater--flexible-content)

Repeater and flexible content fields return a `Collection` of `RepeaterLayout` respectively `FlexibleContentLayout`. These models act like the original `Acf` class: when accessing fields as attributes, the parsed value of the field is returned, otherwise a field like in the table above.

```
use Corcel\Models\Post;
use Tbruckmaier\Corcelacf\Models\Text;
use Tbruckmaier\Corcelacf\Models\Repeater;
use Tbruckmaier\Corcelacf\Models\FlexibleContent;
use Tbruckmaier\Corcelacf\Support\RepeaterLayout;
use Tbruckmaier\Corcelacf\Support\FlexibleContentLayout;

$post = Post::find(1);

$post->acf->main_repeater(); // Repeater
$repeaterFields = $post->acf->main_repeater; // Collection of RepeaterLayout
$repeaterFields->first()->title(); // Text::class
$repeaterFields->first()->title; // parsed response "Main repeater title #1"
$repeaterFields->get(1)->title(); // Text::class
$repeaterFields->get(1)->title; // "Main repeater title #2"

$post->acf->main_content(); // FlexibleContent
$fcLayouts = $post->acf->main_content; // Collection of FlexibleContentLayout

$fcLayouts->get(0)->getType(); // layout type of the first block, for example "text_with_image"
$fcLayouts->get(0)->text(); // Text::class
$fcLayouts->get(0)->text; // "Text of the first content block"
$fcLayouts->get(0)->image(); // Image::class
$fcLayouts->get(0)->image; // Attachment::class (linked image)

$fcLayouts->get(1)->getType(); // layout type of the second block, for example "accordion"
$fcLayouts->get(1)->accordion_title; // "Accordion #1"
$fcLayouts->get(1)->accordion_items(); // Repeater::class
$fcLayouts->get(1)->accordion_items; // Collection of RepeaterLayouts
$fcLayouts->get(1)->accordion_items->first()->title; // "First accordion element"
$fcLayouts->get(1)->accordion_items->first()->content; // "First accordion content..."
```

#### Eager loading relationships

[](#eager-loading-relationships)

If you have a repeater which includes an image field, it may be wise to preload the image's `Attachment` relation (otherwise an own query is fired for each attachment):

```
use Tbruckmaier\Corcelacf\Models\Repeater;

$post = Post::find(1);

// fires 2 queries per iteration
foreach ($post->acf->main_repeater as $layout) {
    $layout->foo_image->attachment;
    $layout->bar_image->attachment;
}

// preloads all attachment relations, fires 2 queries in total
foreach ($post->acf->main_repeater()->load('foo_image.attachment', 'bar_image.attachment')->value as $layout) {
    $layout->foo_image->attachment;
    $layout->bar_image->attachment;
}
```

### Group field

[](#group-field)

A group field returns a `GroupLayout`, which contains all grouped fields. `GroupLayout` acts like a `FlexibleContentLayout` or a `RepeaterLayout`: by accessing its fields as attributes, the parsed value is returned. When accessing them as methods, the class itself is returned.

```
use Corcel\Models\Post;
use Tbruckmaier\Corcelacf\Models\Group;
use Tbruckmaier\Corcelacf\Models\Text;
use Tbruckmaier\Corcelacf\Models\Repeater;
use Tbruckmaier\Corcelacf\Support\GroupLayout;

$post = Post::find(1);

$post->acf->header_fields(); // Group
$post->acf->header_fields; // GroupLayout

$post->acf->header_fields->title; // "site title"
$post->acf->header_fields->title(); // Text::class
```

Running Tests
=============

[](#running-tests)

To run the phpunit tests, execute `phpunit`:

```
./vendor/bin/phpunit

```

Tests with different php &amp; laravel versions are defined in `.github/workflows/ci.yml`. They can be run locally via [act](https://github.com/nektos/act)

Licence
=======

[](#licence)

[MIT License](http://jgrossi.mit-license.org/) © Junior Grossi

###  Health Score

54

—

FairBetter than 97% of packages

Maintenance68

Regular maintenance activity

Popularity38

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity76

Established project with proven stability

 Bus Factor1

Top contributor holds 54.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~100 days

Recently: every ~47 days

Total

26

Last Release

173d ago

PHP version history (2 changes)v1.0.0PHP &gt;=7.0.0

1.6.2PHP &gt;=7.0.0|^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/d50182e1a3dc5a22b4e2b108c6ca456acf2458cdb892159feba78ad5f249bcf2?d=identicon)[tbruckmaier](/maintainers/tbruckmaier)

---

Top Contributors

[![jgrossi](https://avatars.githubusercontent.com/u/1175275?v=4)](https://github.com/jgrossi "jgrossi (209 commits)")[![tbruckmaier](https://avatars.githubusercontent.com/u/4941331?v=4)](https://github.com/tbruckmaier "tbruckmaier (149 commits)")[![tlaverdure](https://avatars.githubusercontent.com/u/1731025?v=4)](https://github.com/tlaverdure "tlaverdure (8 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (5 commits)")[![jakobbuis](https://avatars.githubusercontent.com/u/949674?v=4)](https://github.com/jakobbuis "jakobbuis (4 commits)")[![omzy](https://avatars.githubusercontent.com/u/8927010?v=4)](https://github.com/omzy "omzy (2 commits)")[![makkinga](https://avatars.githubusercontent.com/u/3294611?v=4)](https://github.com/makkinga "makkinga (1 commits)")[![ivometz](https://avatars.githubusercontent.com/u/15211791?v=4)](https://github.com/ivometz "ivometz (1 commits)")[![chrisk-7777](https://avatars.githubusercontent.com/u/55621012?v=4)](https://github.com/chrisk-7777 "chrisk-7777 (1 commits)")[![SamuelNCarvalho](https://avatars.githubusercontent.com/u/5971923?v=4)](https://github.com/SamuelNCarvalho "SamuelNCarvalho (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/tbruckmaier-corcel-acf/health.svg)

```
[![Health](https://phpackages.com/badges/tbruckmaier-corcel-acf/health.svg)](https://phpackages.com/packages/tbruckmaier-corcel-acf)
```

###  Alternatives

[corcel/acf

Advanced Custom Field (ACF) plugin for Corcel

132343.9k7](/packages/corcel-acf)[leocavalcante/swoole-futures

Futures + async/await for PHP's Swoole concurrency run-time.

1032.5k](/packages/leocavalcante-swoole-futures)[hexbit/router

Wordpress Router

352.8k](/packages/hexbit-router)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
