tbruckmaier/corcel-acf

Corcel 的 Advanced Custom Field (ACF) 插件

1.11 2024-08-22 19:37 UTC

This package is auto-updated.

Last update: 2024-09-22 19:58:11 UTC


README

PHPunit StyleCI Packagist Packagist

在 Laravel 中使用 WordPress 高级自定义字段 (ACF)

此 Corcel 插件允许您使用与 Laravel 框架相同的语法,使用 Eloquent 语法从 WordPress 后端获取由 ACF 插件创建的自定义字段。您可以使用 Eloquent 模型和集合来提高您的开发效率。

有关 Corcel 的工作原理的更多信息,请访问 存储库

安装

安装 Corcel 的 ACF 插件非常简单

composer require tbruckmaier/corcel-acf

此插件需要 Corcel,但请放心,如果它缺失,它也会被安装。

功能

  • 通过 Eloquent 关系加载 ACF 字段
    • 只为帖子加载一次 acf 数据并保存 SQL 查询
    • 支持 eager loading of acf 关系
  • 支持深层封装的字段(例如,在灵活内容中的重复器中的图像)
  • 为不同的 acf 字段返回合适的数据类型(请参阅下表)
    • 未知字段返回一个具有访问原始 db 值的通用类
    • 可以使用自定义类来处理现有和未知字段
  • 可以访问 acf 字段配置和内部属性
  • 完全支持选项页面
  • 支持基于 PHP 和数据库的 ACF 配置
  • toArray()toJson() 在嵌套重复器/其他字段上递归工作(自 1.6 起支持)

基本用法

创建 acf 关系的最简单方法是包含的 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();
    }
}

这会动态创建关系 acf_title()acf_thumbnail()。现在可以访问 acf 字段

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

为了简化,AcfTrait 还包括 getAcfAttribute,它返回辅助类 Acf 的实例(这替代了过时的 corcel acf 插件)。关系也可以这样访问

$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 的 ACF 配置

如果您的 ACF 配置通过 PHP 工作的话,您必须在 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();
    }
}

为了确定用于哪个字段的模型,使用 acf 字段的 type 变量。下面是完整列表。

功能

WordPress 将每个 acf 字段的配置存储在 wp_posts 表中,作为一个自定义帖子类型 acf-field。其中 post_content 包含序列化的配置数组,该数组包含 acf 字段类型(type)和您在创建 acf 字段时可以指定的所有其他内容。其中一些值对于解析很重要,而另一些则仅指定如何在 WordPress 中显示字段。

当在Wordpress中使用acf字段保存帖子时,这些值将作为帖子的元数据存储在表wp_postmeta中。每个acf字段保存两个值:在wp_posts中的对应acf字段名称和实际值。根据acf字段的类型和配置,保存的值可能不同(文本字段值以纯文本形式存储,关系字段(如图片)以附件ID存储,重复和灵活内容字段以一系列不同的字段存储)。

例如,上面的示例字段保存如下

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

使用corcel加载帖子时,无论是否定义了模型的boot()方法中的静态属性$acfRelations,元数据都会自动从数据库中检索。

Acf基类使用这些数据并将它们传递给所有字段(以及可能的子字段),因此不需要额外的查询。只需关系字段(如Image)需要另一个查询来找到正确的Attachment类(尽管如果您只对附件的ID感兴趣,您可以通过$post->acf->thumbnail()->internal_value访问它,并且不需要使用额外的查询)。

这仅添加了一个getAcfAttribute()方法,它返回一个Tbruckmaier\Corcelacf\Acf基类实例(这覆盖了corcel内部对过时的Corcel acf插件的默认支持:[https://github.com/corcel/acf/tree/](https://github.com/corcel/acf/tree/)。如果您想并行使用它,您可以自己定义一个不同名称的getAcfAttribute方法)。

选项页面

ACF选项页面中的字段可以以相同的方式使用,尽管实例化选项页面稍微有些棘手。相关的字段配置存储在具有acf-fieldwp_posts中,并且都是acf-field-group(通过post_parent)的子项。字段值存储在wp_options中,具有特定的前缀(默认为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            |

首先,我们需要找到选项页的acf-field-group。我们可以在Wordpress中编辑字段组时在URL中找到其ID:[https://wp-admin/post.php?post=1016&action=edit](https://wp-admin/post.php?post=1016&action=edit)。它也可以通过其slug或页面标题找到(见下文)

前缀通常在functions.php中设置,默认为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"

如果有人找到了更简单的选项页面解决方案,我愿意听取建议。也许可以通过传递前缀或相反的方式来获取字段组?

高级使用

自定义字段类

您可以使用自己的类为某些字段类型扩展自定义属性和方法。通过运行artisan vendor:publish --provider='Tbruckmaier\Corcelacf\ServiceProvider'发布配置,并在config/corcel-acf.php中填写类名。您可以覆盖现有的字段类型或定义新的类型。

// 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 & two"
$post->acf->my_text_field()->words; // ["one", "&", "two"]

自定义类应扩展Tbruckmaier\Corcelacf\BaseField

定义acf关系

除了使用模型的boot()方法动态创建关系之外,还可以手动定义它们。

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

每当返回Corcel模型(例如,对于图像的Corcel\Model\Attachment类)时,都会考虑corcel类映射配置(见[https://github.com/corcel/corcel#-custom-post-type](https://github.com/corcel/corcel#-custom-post-type))。

预加载

如果您想预加载acf字段,可以使用标准的eloquent语法。如果关系是从$acfRelations创建的,不要忘记传递前缀。

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

字段

以下字段类型受到支持(其他所有内容都返回一个Generic字段)

链接

链接字段对配置的返回值做出反应,因此它返回一个包含titletexturl的数组或只是一个字符串url

该字段具有一个 render() 方法,用于渲染HTML标签。render() 支持自定义链接文本和自定义属性:render('<img src="img.jpg" />', ['class' => 'class-1']) 返回 <a href="#" target="_blank" class="class-1" title="acf title"><img src="img.jpg" /></a>

当将字段作为字符串访问时(如 (string)$post->acf->link() 或在 blade 中 {!! $post->acf->link !!}),会调用 render(),因此返回一个HTML字符串。

重复器和灵活内容

重复器字段返回一个 Collection,其中包含 RepeaterLayout。灵活内容字段返回一个 FlexibleContentLayout。这些模型类似于原始的 Acf 类:当以属性访问字段时,返回字段的解析值,否则返回与上表类似的字段。

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..."

预加载关联关系

如果你有一个包含图像字段的重复器,预先加载图像的 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;
}

分组字段

分组字段返回一个 GroupLayout,其中包含所有分组字段。GroupLayout 类似于 FlexibleContentLayoutRepeaterLayout:通过以属性访问其字段,返回解析值。当以方法访问时,返回类本身。

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

运行测试

要运行phpunit测试,执行 phpunit

./vendor/bin/phpunit

不同php和laravel版本的测试定义在 .github/workflows/ci.yml 中。它们可以通过 act 在本地运行。

许可证

MIT License © Junior Grossi