bnomei/kirby-blueprints

PHP类式蓝图,用于Kirby CMS,以提高类型安全和代码补全

4.4.12 2024-09-12 13:04 UTC

This package is auto-updated.

Last update: 2024-09-12 13:05:01 UTC


README

Release Downloads Coverage Maintainability Discord

Kirby Ink - PHP类式蓝图,用于Kirby CMS,以提高类型安全和代码补全。

安装

使用composer

composer require bnomei/kirby-blueprints

商业用途


支持开源!

此插件免费,但如果您将其用于商业项目,请考虑赞助我或捐款。
如果我的工作帮助您赚了钱,我认为我可能也应该得到一点回报,对吧?

善良一点。分享一点。谢谢。

- Bruno
 

箱子里有什么?

此插件介绍了两种新的定义Kirby蓝图的方法。

流畅与命名辅助类

使用PHP文件中的流畅定义命名参数定义来定义蓝图,而不是仅使用Kirby提供的数组定义。您可以使用*::make()-辅助器来创建它们并避免拼写错误。

site/plugins/example/blueprints/fields/description.php

return Field::make(FieldTypes::TEXTAREA)
    ->label('Description')
    ->toArray();

PageModels的PHP属性

在PageModels中定义页面蓝图,并使用公共属性与PHP属性定义字段。您将在模板中的字段上获得自动完成和悬停时的见解。

site/plugins/example/models/ArticlePage.php

#[
    Label([
        'en' => 'Introduction',
        'de' => 'Einleitung',
    ]),
    Type(FieldTypes::TEXT),
]
public Field $introduction;

从文件中的蓝图定义

如何让Kirby知道蓝图定义

目前,如果蓝图存储在site/blueprints文件夹中,Kirby只能从YAML文件加载蓝图。它还可以从PHP文件加载它们,但它们需要在自定义插件中使用所谓的扩展来定义。如果您想看到在未来的更改并能够直接从核心文件夹使用PHP蓝图,请为我建议

本Readme中的示例展示了如何创建各种蓝图。这里没有展示的是如何通过扩展在您自定义插件的index.php中注册它们。有关如何操作的官方文档,请参阅官方文档,或尝试我的自动加载辅助器

以下代码展示了您如何在自定义插件中使用自动加载辅助器。如它的名字所示,该辅助器会自动扫描插件内的文件夹并注册找到的所有扩展(如蓝图、pageModels、snippets等)。这意味着它也会注册来自site/plugins/example/blueprints文件夹的蓝图。

site/plugins/example/index.php

<?php

// load the fields stored as traits
autoloader(__DIR__)->classes();

// load every possible extension else with the autoloader
Kirby::plugin('your/example', autoloader(__DIR__)->toArray([
    // add your additional extension definitions here. like...
    'options' => [
        'cache' => true,
    ],
]);

在自定义插件中使用YAML创建蓝图(核心Kirby功能)

这是您当前通过插件扩展在Kirby中定义蓝图的方式。

site/plugins/example/blueprints/fields/description.yml

label: Description
type: textarea

在自定义插件中使用PHP创建蓝图

除了核心的数组定义外,此插件还引入了两种新的定义蓝图的方法。即流畅命名参数定义。最终它们都被转换为Kirby期望的相同的数组定义,但您可以使用*::make()-辅助器来创建它们并避免拼写错误。

Kirby数组定义中的可编程蓝图(核心功能)

如果您在插件或使用自动加载辅助器中注册了它,您可以使用PHP文件定义蓝图。

site/plugins/example/blueprints/fields/description.php

<?php

// "array definition" (same as yaml)
return [
    'label' => 'Description',
    'type' => 'textarea',
];

流畅定义

site/plugins/example/blueprints/fields/description.php

<?php

use Bnomei\Blueprints\Schema\Field;
use Bnomei\Blueprints\Schema\FieldTypes;

// "fluent definition" from this plugin
return Field::make(FieldTypes::TEXTAREA)
    ->label('Description')
    ->toArray();

命名参数定义

site/plugins/example/blueprints/fields/description.php

<?php

use Bnomei\Blueprints\Schema\Field;
use Bnomei\Blueprints\Schema\FieldTypes;

// "named parameter definition" from this plugin
return Field::make(
    type: FieldTypes::TEXTAREA,
    label: 'Description'
)->toArray();

模型上没有PHP属性的动态蓝图

如果您想为蓝图返回动态定义,则需要将PHP-based蓝图返回值包装在回调中。这是因为否则蓝图定义将在Kirby的初始设置期间被解析,您将无法访问Kirby实例中的所有数据(kirby())或任何其他辅助工具(site()/page()),而不会引起问题。但使用闭包将延迟蓝图解析,直到Kirby加载所有插件并准备好渲染页面后。

site/plugins/example/blueprints/fields/version.php

<?php

return fn () => Field::make(FieldTypes::INFO)
    ->text('Kirby v'.kirby()->version())
    ->toArray();

注意:对于模型,您可以使用defer选项。详见下文。

可用的Make辅助工具

根据您要创建的蓝图类型,您可以使用以下辅助工具之一。

  • Field::make()
  • Section::make()
  • Column::make()
  • Tab::make()
  • Page::make()
  • File::make()
  • User::make()
  • Site::make()

动态蓝图

由于蓝图定义在Kirby的初始设置期间被解析,因此有时您无法访问Kirby实例中的所有数据(kirby())或任何其他辅助工具(site()/page())而不会引起问题。如果您需要在蓝图中实现动态行为,请考虑阅读有关system.loadPlugins:after钩子的文档。

还可以查看官方食谱以及与此插件相关的示例

来自PageModel的蓝图定义(插件的新功能)

在Kirby中,您可以通过将模板名称与后缀Page匹配来定义一个类,以创建一个PageModel。在这个例子中,我们将它们放入site/plugins/example/models文件夹中,以便自动加载辅助工具可以找到它们。您也可以将它们放入site/models文件夹中,前提是您稍微调整一下文件名。

site/plugins/example/models/ExamplePage.php

<?php

use Kirby\Cms\Page;

class ExamplePage extends Page
{
    // nothing here yet
}

在PageModel中使用PHP属性创建字段定义

以下示例中出现的许多use语句可能会让您感到困惑。您的IDE很可能会在您使用PHP属性时添加它们,以确保PHP代码知道如何找到它们的定义。

您需要向您的PageModel添加两个特质。

  • 一个用于使其知道我们想要将带有属性注册为蓝图字段的公共属性,并读取页面蓝图定义(HasBlueprintFromAttributes),
  • 另一个用于使这些公共属性返回Kirby字段对象(HasPublicPropertiesMappedToKirbyFields)。

如果不为article模板定义YAML蓝图,则Kirby将有一个空的蓝图定义。但有了PageModel中定义的属性,Kirby将知道我们希望在蓝图中包含哪些字段(由自动加载辅助工具注册)。

site/plugins/example/models/ArticlePage.php

<?php

use Bnomei\Blueprints\Attributes\ExtendsField;
use Bnomei\Blueprints\Attributes\Label;
use Bnomei\Blueprints\Attributes\Type;
use Bnomei\Blueprints\HasBlueprintFromAttributes;
use Bnomei\Blueprints\HasPublicPropertiesMappedToKirbyFields;
use Bnomei\Blueprints\Schema\FieldTypes;
use Kirby\Cms\Page;
use Kirby\Content\Field;

class ArticlePage extends Page
{
    use HasBlueprintFromAttributes;
    use HasPublicPropertiesMappedToKirbyFields;
    
    #[
        Label([
            'en' => 'Introduction',
            'de' => 'Einleitung',
        ]),
        Type(FieldTypes::TEXT),
    ]
    public Field $introduction;
}

为了更加清楚:这将与以下YAML蓝图定义相同。只是我们不需要实际创建它,因为PageModel中的属性会为我们做这件事。

site/plugins/example/blueprints/pages/article.yml

fields:
    introduction:
        label:
            en: Introduction
            de: Einleitung
        type: text

您可能需要一个比仅在根级别上包含字段的更复杂的蓝图定义。为此,您必须创建一个从PageModel返回蓝图的方法。方法名称不重要,但属性Blueprint很重要。该方法需要返回一个包含蓝图定义的数组。请参阅以下示例。

可用属性

您可以在本仓库的 classes/Blueprints/Attributes 文件夹 中找到所有可用的 PHP 属性。它们反映了在 YAML 蓝图中对给定字段设置的属性。对于一些属性,我创建了变体,因为不同的字段使用相同的属性名,但具有不同的含义(如 number 字段中的 maxdate 字段中的 max)并且我想在 PHP 中保持它们的明确性。

Accept, After, Api, Autocomplete, Autofocus, Before, Blueprint, Buttons, Calendar, Columns, ColumnsCount, Converter, Counter, CustomType, DefaultValue, Disabled, Display, EmptyValue, ExtendsField, Fieldsets, Fields, Files, Font, Format, GenericAttribute, Grow, Help, Icon, Image, Info, Inline, Label, Layout, LayoutSettings, Layouts, Link, Marks, Max, MaxDate, MaxRange, MaxTime, MaxLength, Min, MinDate, MinLength, MinRange, MinTime, Multiple, Nodes, Numbered, Options, Path, Pattern, Placeholder, Prepend, Property, Query, Range, Required, Reset, Search, Separator, Size, SortBy, Sortable, Spellcheck, Step, Store, Subpages, Sync, Text, Theme, Time, TimeNotation, Tooltip, Translate, Type, Uploads, When, Width, Wizard

使用此插件的好处

您还可以使用其他插件根据您的常规 Kirby 设置创建类型提示,但这意味着您需要在每次代码更改时更新它们。使用此插件,您可以在 PageModel 中定义字段,并在模板中即时获得代码补全。在大多数 IDE 中,悬停在属性名上会显示您设置的属性,以便于参考。

如果您使用我的 Kirby YAML 蓝图的 Schema,可以减少拼写错误并获取自动补全。但使用 *::make()-辅助程序和 PHP 属性将使您在蓝图本身以及 PHP 代码的其他部分(PageModels、控制器、模板等)中获得代码补全和类型安全。

site/templates/article.php

<?php

// no code completion. your IDE does NOT know
// that `introduction()` method is a Field.

var_dump($page->introduction()->value());


// with this plugin your IDE will know that
// the public property `introduction` is a Field
// and you can use code completion and see the
// set attributes on hovering the property.

var_dump($page->introduction->value()); // <-- property not method!

注意:这还不是完美的。在一个理想的情况下,IDE 会知道您想要一个 textarea 类型的字段,并且只提供适用于该类型字段的方法。但我们目前无法使用 Kirby 做到这一点。但总比没有好。:-)

如何重用蓝图定义

有两种方式可以重用蓝图定义。

  • 一种是通过模仿 YAML 蓝图中的 extends 关键字,但在 PHP PageModel 中将其用作 ExtendsField 属性。您可以从 YAML 和 PHP 蓝图中扩展。
  • 另一种是创建一个包含要重用字段定义的 PHP trait,并将该 trait 应用到多个类中。

通过扩展 YAML/PHP 蓝图重用

site/plugins/example/blueprints/fields/special-date.yml

label: Special Date
type: date

site/plugins/example/models/MemberPage.php

<?php

// omitted the use statements for brevity's sake

class MemberPage extends Page
{
    use HasBlueprintFromAttributes;
    use HasPublicPropertiesMappedToKirbyFields;
    
    #[
        Label('Birthday'),
        ExtendsField('fields/special-date'),
    ]
    public Field $birthday;
}

使用 Traits 重用蓝图

site/plugins/example/classes/HasDescription.php

<?php

// omitted the use statements for brevity's sake

trait HasDescriptionField
{
    #[
        Type(FieldTypes::TEXTAREA),
        Label([
            'de' => 'Beschreibung',
            'en' => 'Description',
        ]),
        Buttons([
            Button::BOLD,
            Button::ITALIC,
            Button::SEPARATOR,
            Button::LINK,
        ]),
        MaxLength(3000),
        Spellcheck(true),
    ]
    public Field $description;
}

site/plugins/example/models/BlogpostPage.php

<?php

// omitted the use statements for brevity's sake

class BlogpostPage extends Page
{
    use HasBlueprintFromAttributes;
    use HasPublicPropertiesMappedToKirbyFields;
    
    use HasDescriptionField; // <-- re-use the trait
}

从 PageModel 创建的页面蓝图定义

您将使用 *::make()-辅助程序在 PHP 蓝图文件中创建蓝图定义。如果您想使 PageModel 中的基于属性的字段工作,您需要在同一 PageModel 中直接定义一个完整的页面蓝图。为什么需要这样做?因为基于属性的字段需要注入到蓝图定义中,而这只能在定义蓝图的本机 PageModel 中完成。

这将使您获得不需要任何蓝图文件的好处。

site/plugins/example/models/ArticlePage.php

<?php

// use statements omitted for brevity's sake

class ProductPage extends \Kirby\Cms\Page
{
    use HasBlueprintFromAttributes;
    use HasPublicPropertiesMappedToKirbyFields;
    
    // this is the same trait as the example above
    use HasDescriptionField;

    #[
        CustomType('qrcode'),
        Property('Custom key', 'custom data'),
    ]
    public Kirby\Content\Field $qrcode;

    #[
        Type(FieldTypes::EMAIL),
        Placeholder('Email Field from Property')
    ]
    public Kirby\Content\Field $email;

    #[
        Blueprint
    ]
    public static function nameOfThisMethodDoesNotMatterOnlyTheAttribute(): array
    {
        return Page::make(
            title: 'Product',
            status: PageStatus::make(
                draft: 'Beer',
                unlisted: 'Wine',
                listed: 'Whiskey',
            ),
            icon: Icon::PIN,
            image: PageImage::make(
                back: 'black',
                icon: '📝',
                query: 'page.cover.toFile()'
            ),
            options: PageOptions::make()
                ->preview('{{ page.url }}#product'),
            navigation: PageNavigation::make()
                ->sortBy('date desc'),
            tabs: [
                Tab::make(
                    label: 'Shop',
                    icon: Icon::CART,
                    columns: [
                        Column::make()
                            ->width(1 / 3)
                            ->fields([
                                'price' => [
                                    'type' => 'number',
                                    'label' => 'Price',
                                ],
                                'email' => true, // from PHP
                            ]),
                        Column::make(
                            width: 2 / 3,
                            fields: [
                                // generic
                                'intro' => Field::make(
                                    type: FieldTypes::TEXTAREA,
                                    label: 'Introduction',
                                    // for custom props use a method with attributes or...
                                    properties: [
                                        Ink::MAXLENGTH => 3000,
                                        Ink::SPELLCHECK => false,
                                        Ink::BUTTONS => false,
                                    ],
                                )
                                // OR
                                // ->property(Ink::MAXLENGTH, 3000)
                                // ->property(Ink::SPELLCHECK, false)
                                // ->property(Ink::BUTTONS-, false)
                                // OR
                                // ->maxLength(3000)
                                // ->spellcheck(false)
                                // ->buttons(false)
                                ,
                                // from methods with attributes
                                'qrcode' => true,
                                'description' => true,
                            ]
                        ),
                    ],
                ),
                Tab::make(label: 'Badger')
                    ->icon(Icon::BADGE),
            ],
        )->toArray();
    }
}

这里有一个需要注意的特殊行为。您可以通过通过名称引用它们并将它们的值设置为 true 来使蓝图扩展由属性定义的字段。但这只能在 PageModel 中定义的蓝图中工作,而不是 PHP 蓝图文件中的。

Column::make()
    ->width(1 / 3)
    ->fields([
        'price' => [
            'type' => 'number',
            'label' => 'Price',
        ],
        // will be expanded to the field definition from the
        // attributes set on the `public Field $email` property.
        'email' => true, 
    ]),

为什么叫 Ink?

因为在所有这些 *::make()-辅助程序之上,它还引入了新的 Ink::*-辅助程序,可以从 PageModel 中创建蓝图定义。而且它简短易记。

<?php

use ...;

class ElephantPage extends Page
{
    use HasInk;

    #[
        Label('Left Ear'),
        Type(FieldTypes::TEXT),
    ]
    public Field $leftEar;

    #[
        Label('Right Ear'),
        Type(FieldTypes::TAGS),
    ]
    public Field $rightEar;

    #[
        Blueprint
    ]
    public static function elephantsBlueprint(): array
    {
        // DANGER: do not use kirby() or site() or page() here
        // $user = kirby()->user(); // will cause issues with blueprint loading

        return Ink::page(
            title: 'Elephant',
            columns: [
                Ink::column(2 / 3)->fields([
                    'leftEar',
                    Ink::field(Ink::BLOCKS)
                        ->label('Trunk')
                        ->property(Ink::EMPTY, '🐘'),
                    'rightEar',
                ]),
                Ink::column(1 / 3)->sections([
                    Ink::fields()->fields([
                        Ink::field(Ink::TEXT)
                            ->label('User')
                            ->property(Ink::PLACEHOLDER, '{{ user.nameOrEmail }} ({{ user.role.name }})'),
                    ]),
                    Ink::info()
                        ->label('Kirby Version')
                        ->theme(Ink::INFO)
                        ->text('{{ kirby.version }}'),
                    Ink::files()
                        ->label('Files'),
                ]),
            ],
        )->toArray();
    }
}

PHP 基于蓝图的缓存和延迟加载

如果启用了缓存,它将只编译一次蓝图,然后使用缓存的版本。缓存将持续到设置的时间。

#[
    // cache for 120 seconds
    Blueprint(cache: 120) 
    
    // cache with default 60 seconds
    Blueprint()
    Blueprint(cache: null) 
    
    // disable
    Blueprint(cache: 0) 
    Blueprint(cache: false) 
]

默认值可以在您的插件中的 config.php 文件中使用 bnomei.blueprints.expire 选项设置。

如果 defer 设置为 true,它将在 kirby 实例准备就绪后编译蓝图。这意味着您可以在蓝图中使用 kirby()/site()/page() 辅助函数或查询站点的内容。通常,当使用 defer 时禁用缓存是有意义的,因为缓存可能会干扰您试图创建的动态行为。

#[
    // load with system.loadPlugins:after hook and no cache
    Blueprint(defer: true, cache: 0) 
]

PHP 和 YAML 基于蓝图的缓存

如果您没有使用属性来定义您的蓝图定义,您仍然可以使用 traits 模型启用缓存。一个用于启用缓存,另一个用于定义是否应尽可能解决所有字段。

解决字段意味着将所有元数据写入,同时使用模型实例提取它们。所以如果您在蓝图中使用 Kirby 查询字符串语言或其他动态值(如 Janitor 或一些 SEO 插件所做的),您可能希望跳过解决。但是...如果您有一个非常复杂的蓝图,包含许多字段,并且希望加快蓝图加载速度,您可能希望解决它们。特别是当使用大量布局、列和块时,解决可能是一个好主意。

site/models/example.php

<?php

class ExamplePage extends \Kirby\Cms\Page {

    // can be used safely to speed up all kind of blueprints
    use \Bnomei\Blueprints\HasBlueprintCache;
    
    // not recommended for dynamic blueprints
    use \Bnomei\Blueprints\HasBlueprintCacheResolve; 

}

缓存将使用您的插件 config.php 文件中定义的 bnomei.blueprints.expire 选项中的持续时间。

免责声明

本插件“原样提供”,不提供任何保证。请在自己的风险下使用它,并在将其用于生产环境之前始终自行测试。如果您发现任何问题,请创建新问题

许可证

MIT

不鼓励在任何推广种族主义、性别歧视、恐同、动物虐待、暴力或其他任何形式仇恨言论的项目中使用此插件。