craftcms/element-api

为Craft中的元素创建JSON API

安装次数: 636,067

依赖关系: 9

建议者: 1

安全性: 0

星标: 498

关注者: 14

分支: 57

公开问题: 10

类型:craft-plugin

4.1.0 2024-03-14 13:15 UTC

README

此插件使您能够轻松为Craft CMS中的条目(和其他元素类型)创建JSON API。

它由Phil Sturgeon的出色Fractal包提供支持。

要求

此插件需要Craft CMS 4.3.0+或5.0.0+。

安装

您可以从插件商店或使用Composer安装此插件。

从插件商店

前往项目控制面板中的插件商店,搜索“Element API”。然后在模态窗口中点击“安装”按钮。

使用Composer

打开您的终端并运行以下命令

# go to the project directory
cd /path/to/my-project.test

# tell Composer to load the plugin
composer require craftcms/element-api

# tell Craft to install the plugin
./craft plugin/install element-api

设置

要定义API端点,请在您的config/文件夹中创建一个新的element-api.php文件。此文件应返回一个包含endpoints键的数组,该键定义了您的网站的API端点。

endpoints数组中,键是URL模式,值是定义端点配置的函数。

<?php

use craft\elements\Entry;
use craft\helpers\UrlHelper;

return [
    'endpoints' => [
        'news.json' => function() {
            return [
                'elementType' => Entry::class,
                'criteria' => ['section' => 'news'],
                'transformer' => function(Entry $entry) {
                    return [
                        'id' => $entry->id,
                        'title' => $entry->title,
                        'url' => $entry->url,
                        'jsonUrl' => UrlHelper::url("news/$entry->id.json"),
                        'summary' => $entry->summary,
                    ];
                },
            ];
        },
        'news/<entryId:\d+>.json' => function($entryId) {
            return [
                'elementType' => Entry::class,
                'criteria' => ['id' => $entryId],
                'one' => true,
                'transformer' => function(Entry $entry) {
                    return [
                        'title' => $entry->title,
                        'url' => $entry->url,
                        'summary' => $entry->summary,
                        'body' => $entry->body,
                    ];
                },
            ];
        },
    ]
];

端点配置设置

端点配置数组可以包含以下设置

class

应用于提供请求的Fractal资源的类名。如果没有设置,将默认为craft\elementapi\resources\ElementResource。(所有以下配置设置都是针对该默认类的。)

elementType (必需)

API应关联的元素类型的类名。Craft的内置元素类型类包括

  • craft\elements\Asset
  • craft\elements\Category
  • craft\elements\Entry
  • craft\elements\GlobalSet
  • craft\elements\MatrixBlock
  • craft\elements\Tag
  • craft\elements\User
'elementType' => craft\elements\Entry::class,

criteria

一个参数数组,应在将检索元素的元素查询上设置。

'criteria' => [
    'section' => 'news',
    'type' => 'article',
],

contentType

端点响应应具有的内容类型。

'contentType' => 'application/foo+json',

默认情况下,内容类型将是

  • application/javascript 对于定义JSONP 回调 的端点
  • application/feed+json 对于将序列化器设置为jsonFeed 的端点
  • application/json 对于所有其他内容

transformer

应用于定义每个元素应返回的数据的转换器。如果您没有设置,将使用默认转换器,该转换器包括所有元素的直接属性值,但不包括自定义字段值。

// Can be set to a function
'transformer' => function(craft\elements\Entry $entry) {
    return [
        'title' => $entry->title,
        'id' => $entry->id,
        'url' => $entry->url,
    ];
},

// Or a string/array that defines a Transformer class configuration
'transformer' => 'MyTransformerClassName',

// Or a Transformer class instance
'transformer' => new MyTransformerClassName(),

您的自定义转换器类可能看起来像这样

<?php

use craft\elements\Entry;
use League\Fractal\TransformerAbstract;

class MyTransformerClassName extends TransformerAbstract
{
    public function transform(Entry $entry)
    {
        return [
            // ...
        ];
    }
}

one

是否仅返回第一个匹配的元素。默认设置为false,表示将返回所有匹配的元素。

'one' => true,

paginate

是否应对结果进行分页。默认设置为true,这意味着每个响应中只包含匹配元素的一个子集,并伴随描述分页信息的附加元数据。

'paginate' => false,

elementsPerPage

如果启用分页,则每页应包含的最大元素数量。默认设置为100。

'elementsPerPage' => 10,

pageParam

用于标识请求页面的查询字符串参数名称。默认设置为'page'

'pageParam' => 'pg',

请注意,不能将其设置为'p',因为这将是Craft用来检查请求路径的参数。

resourceKey

元素应在响应数据中嵌套的键。默认情况下,这将设置为'data'

'resourceKey' => 'entries',

meta

应包含在响应数据中的任何自定义元值。

'meta' => [
    'description' => 'Recent news from Happy Lager',
],

serializer

应使用序列化器来格式化返回的数据。

可能的值有

  • 'array' (默认) – 使用ArraySerializer来格式化数据。
  • 'dataArray' – 使用DataArraySerializer来格式化数据。
  • 'jsonApi' – 使用JsonApiSerializer来格式化数据。
  • 'jsonFeed' – 基于以下链接的JSON Feed V1.1格式化数据(请参阅下面的JSON Feed示例):[JSON Feed V1.1](https://www.jsonfeed.org/version/1.1/)。
  • 自定义序列化器实例。

includes

对于当前请求,应包含的包含名称(如果有的话)。

'includes' => (array)Craft::$app->request->getQueryParam('include'),

请注意,此设置需要一个预置以处理包含的包含的自定义转换器类。

class MyTransformerClassName extends TransformerAbstract
{
    protected $availableIncludes = ['author'];

    public function includeAuthor(Entry $entry)
    {
        return $this->item($entry->author, function(User $author) {
            return [
                'id' => $author->id,
                'name' => $author->name,
            ];
        });
    }

    // ...
}

excludes

对于当前请求,应排除的包含名称,否则它们将被包含(例如,如果它们被列为默认包含),如果有的话。

'excludes' => 'author',

includes一样,此设置需要一个自定义转换器类。

callback

如果设置此值,则返回JSONP响应,并使用此设置值作为回调函数,内容类型为application/javascript

例如,如果您将其设置为

'callback' => 'foo',

则响应将如下所示

foo({ /* ... */ });

请注意,如果您设置此值,则忽略jsonOptionspretty设置。

jsonOptions

将传递给json_encode()$options参数值,以准备响应。默认将传递JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE

'jsonOptions' => JSON_UNESCAPED_UNICODE,

pretty

jsonOptions添加JSON_PRETTY_PRINT的快捷方式。

'pretty' => true,

cache

输出是否应该被缓存,以及缓存多久。

可能的值有

  • true (默认) – 结果根据Craft配置设置中的cacheDuration指定的持续时间进行缓存,或者直到相关元素被保存或删除。
  • false – 结果永远不会被缓存。
  • 整数 – 结果缓存指定秒数。
  • interval spec字符串 – 结果根据指定持续时间进行缓存。

请注意,当缓存温暖时,不会触发onBeforeSendData事件。

'cache' => 'PT1M', // one minute

cacheKey

如果需要自定义,则响应应缓存的键。

动态URL模式

URL模式可以包含动态子模式,格式为<subpatternName:regex>,其中subpatternName是子模式的名称,regex是有效的正则表达式。例如,URL模式“news/<entryId:\d+>.json”将匹配类似news/100.json的URL。您也可以在正则表达式中使用{handle}{slug}标记,这些将被替换为匹配处理和元素短语的适当正则表达式模式。

URL模式中的任何子模式匹配都将映射到端点配置函数的参数。例如,如果URL模式包含一个entryId子模式,那么您可以在端点配置函数中添加一个$entryId参数,匹配URL子模式的任何内容都将传递给$entryId

'news/<entryId:\d+>.json' => function($entryId) {
    return [
        'elementType' => craft\elements\Entry::class,
        'criteria' => ['id' => $entryId],
        'one' => true,
    ];
},

设置默认配置设置

您可以通过在endpoints键旁边添加一个defaults键来指定端点配置设置的默认值(不在它内部)。

use craft\elements\Entry;

return [
    'defaults' => [
        'elementType' => Entry::class,
        'elementsPerPage' => 10,
        'pageParam' => 'pg',
        'transformer' => function(Entry $entry) {
            return [
                'title' => $entry->title,
                'id' => $entry->id,
                'url' => $entry->url,
            ];
        },
    ],

    'endpoints' => [
        'news.json' => function() {
            return [
                'criteria' => ['section' => 'news'],
            ]
        },
        'news/<entryId:\d+>.json' => function($entryId) {
            return [
                'criteria' => ['id' => $entryId],
                'one' => true,
            ];
        },
    ]
];

示例

以下是几个端点示例以及它们的响应示例。

分页条目索引端点

'ingredients.json' => function() {
    return [
        'criteria' => ['section' => 'ingredients'],
        'elementsPerPage' => 10,
        'transformer' => function(craft\elements\Entry $entry) {
            return [
                'title' => $entry->title,
                'url' => $entry->url,
                'jsonUrl' => UrlHelper::url("ingredients/$entry->slug.json"),
            ];
        },
        'pretty' => true,
    ];
},
{
    "data": [
        {
            "title": "Gin",
            "url": "/ingredients/gin",
            "jsonUrl": "/ingredients/gin.json"
        },
        {
            "title": "Tonic Water",
            "url": "/ingredients/tonic-water",
            "jsonUrl": "/ingredients/tonic-water.json"
        },
        // ...
    ],
    "meta": {
        "pagination": {
            "total": 66,
            "count": 10,
            "per_page": 10,
            "current_page": 1,
            "total_pages": 7,
            "links": {
                "next": "/ingredients.json?p=2"
            }
        }
    }
}

单个条目端点

'ingredients/<slug:{slug}>.json' => function($slug) {
    return [
        'criteria' => [
            'section' => 'ingredients',
            'slug' => $slug
        ],
        'one' => true,
        'transformer' => function(craft\elements\Entry $entry) {
            // Create an array of all the photo URLs
            $photos = [];
            foreach ($entry->photos->all() as $photo) {
                $photos[] = $photo->url;
            }

            return [
                'title' => $entry->title,
                'url' => $entry->url,
                'description' => (string)$entry->description,
                'photos' => $photos
            ];
        },
        'pretty' => true,
    ];
},
{
    "title": "Gin",
    "url": "/ingredients/gin",
    "description": "<p>Gin is a spirit which derives its predominant flavour from juniper berries.</p>",
    "photos": [
        "/images/drinks/GinAndTonic1.jpg"
    ]
}

JSON源

以下是使用Element API为您的站点设置JSON源版本1.1)的方法。

请注意,photosbodysummarytags是虚构的自定义字段。

'feed.json' => function() {
    return [
        'serializer' => 'jsonFeed',
        'elementType' => craft\elements\Entry::class,
        'criteria' => ['section' => 'news'],
        'transformer' => function(craft\elements\Entry $entry) {
            $image = $entry->photos->one();
    
            return [
                'id' => (string)$entry->id,
                'url' => $entry->url,
                'title' => $entry->title,
                'content_html' => (string)$entry->body,
                'summary' => $entry->summary,
                'image' => $image ? $image->url : null,
                'date_published' => $entry->postDate->format(\DateTime::ATOM),
                'date_modified' => $entry->dateUpdated->format(\DateTime::ATOM),
                'authors' => [
                    ['name' => $entry->author->name],
                ],
                'language' => $entry->getSite()->language,
                'tags' => array_map('strval', $entry->tags->all()),
            ];
        },
        'meta' => [
            'description' => 'Recent news from Happy Lager',
        ],
        'pretty' => true,
    ];
},
{
    "version": "https://jsonfeed.org/version/1",
    "title": "Happy Lager",
    "home_page_url": "http://domain.com/",
    "feed_url": "http://domain.com/feed.json",
    "description": "Craft demo site",
    "items": [
        {
            "id": "24",
            "url": "http://domain.com/news/the-future-of-augmented-reality",
            "title": "The Future of Augmented Reality",
            "content_html": "<p>Nam libero tempore, cum soluta nobis est eligendi ...</p>",
            "date_published": "2016-05-07T00:00:00+00:00",
            "date_modified": "2016-06-03T17:43:36+00:00",
            "author": {
                "name": "Liz Murphy"
            },
            "tags": [
                "augmented reality",
                "futurism"
            ]
        },
        {
            "id": "4",
            "url": "http://domain.com/news/barrel-aged-digital-natives",
            "title": "Barrel Aged Digital Natives",
            "content_html": "<p>Nam libero tempore, cum soluta nobis est eligendi ...</p>",,
            "date_published": "2016-05-06T00:00:00+00:00",
            "date_modified": "2017-05-18T13:20:27+00:00",
            "author": {
                "name": "Liz Murphy"
            },
            "tags": [
                "barrel-aged"
            ]
        },
        // ...
    ]
}