sulu / headless-bundle

提供用于将Sulu作为无头内容管理系统使用的控制器和服务的包装

安装次数: 89,346

依赖项: 2

建议者: 0

安全: 0

星标: 44

关注者: 8

分支: 25

公开问题: 21

类型:sulu-bundle

0.10.3 2024-07-24 14:40 UTC

This package is auto-updated.

Last update: 2024-08-24 14:53:03 UTC


README

Official Sulu Bundle Badge

GitHub license Test workflow status Sulu compatibility


SuluHeadlessBundle 提供了用于以无头方式使用 Sulu 内容管理系统的控制器和服务。

为此,该包装包括一个控制器,允许检索 Sulu 页面的纯JSON内容。此外,该包装提供API以访问通过传统模板中的Twig扩展提供的功能,例如导航上下文和片段区域。最后,该包装还包括一个可选的 单页应用程序设置,该设置基于React和MobX构建,并利用了该包装的功能。

SuluHeadlessBundle 与Sulu 从版本2.0开始 兼容。查看 composer.json 中的 require 部分,以找到该包装的 最新要求。请注意,此包装仍在开发中,可能尚未涵盖每个用例。根据社区的反馈,该包装的未来的版本可能会包含破坏性更改。

🚀  安装和使用

安装包装

执行以下 composer 命令,将包装添加到项目的依赖项中

composer require sulu/headless-bundle

启用包装

通过将其添加到项目的 config/bundles.php 文件中注册的包装列表中,启用包装

return [
    /* ... */
    Sulu\Bundle\HeadlessBundle\SuluHeadlessBundle::class => ['all' => true],
];

包含包装的路由

在项目中的新 config/routes/sulu_headless_website.yml 文件中包含包装的路由

sulu_headless:
    type: portal
    resource: "@SuluHeadlessBundle/Resources/config/routing_website.yml"

设置模板的控制器

要提供以JSON格式检索页面内容的API,页面模板的控制器必须设置为该包装中包含的 HeadlessWebsiteController

<?xml version="1.0" ?>
<template xmlns="..." xmlns:xsi="..." xsi:schemaLocation="...">
    <!-- ... -->
    <controller>Sulu\Bundle\HeadlessBundle\Controller\HeadlessWebsiteController::indexAction</controller>
    <!-- ... -->
</template>

如果通过 {pageUrl}.json 以JSON格式请求页面,则该控制器将提供 页面内容作为JSON对象

💡  关键概念

使用HeadlessWebsiteController提供页面内容

SuluHeadlessBundle 的主要用例是 将页面内容作为JSON对象提供。可以通过将页面的模板控制器设置为 Sulu\Bundle\WebsiteBundle\Controller\DefaultController::indexAction 来为每个模板单独启用此功能。当使用 HeadlessWebsiteController 作为模板的控制器时,页面内容可通过 {pageUrl}.json 以JSON对象的形式提供。

除了页面内容外,由 HeadlessWebsiteController 返回的JSON对象还包含 元信息,例如页面模板和页面摘要的数据

{
   "id": "a5181a5a-b030-4933-b3b0-e9faf7ec756c",
   "type": "page",
   "template": "headless-template",
   "content": {
      "title": "Headless Example Page",
      "url": "/headless-example",
      "contacts": [
         {
            "id": 416,
            "firstName": "Homer",
            "lastName": "Simpson",
            "fullName": "Homer Simpson",
            "title": "Dr. ",
            "position": "Nuclear safety Inspector at the Springfield Nuclear Power Plan"
         }
      ]
   },
   "view": {
      "title": [],
      "url": [],
      "contacts": []
   },
   "extension": {
      "seo": {
         "title": "",
         "description": "",
         "keywords": "",
         "canonicalUrl": "",
         "noIndex": false,
         "noFollow": false,
         "hideInSitemap": false
      },
      "excerpt": {
         "title": "",
         "more": "",
         "description": "",
         "categories": [],
         "tags": [],
         "icon": [],
         "images": []
      }
   },
   "author": "2",
   "authored": "2019-12-03T11:01:38+0100",
   "changer": 2,
   "changed": "2020-01-30T07:47:46+0100",
   "creator": 2,
   "created": "2019-12-03T11:01:38+0100"
}

如果请求使用 HeadlessWebsiteController 的页面内容而不带 .json 后缀,则控制器将渲染设置为页面模板的 view 的Twig模板。在这种情况下,在 .json 请求的情况下将返回的数据可在twig模板中通过一个 headless 变量访问。此行为与默认的Sulu WebsiteController 兼容,并允许在用户初始请求后启动利用SuluHeadlessBundle功能的应用程序。

通过ContentTypeResolver将内容数据解析为标量值

内部,Sulu使用ContentType服务来处理页面修改时的页面内容持久化,以及当页面渲染时解析传递给Twig模板的数据。不幸的是,一些ContentType服务会将非标量值(如媒体实体)传递给Twig模板。由于JSON对象必须只包含标量值,SuluHeadlessBundle不能使用现有的ContentType服务来解析页面内容。

为了解决这个问题,SuluHeadlessBundle引入了ContentTypeResolver服务来解析页面内容为标量值。该组件已经包含了针对各种内容类型的ContentTypeResolver服务。如果你的项目包括自定义内容类型或者你对现有的ContentTypeResolver不满意,你可以通过实现ContentTypeResolverInterface并添加sulu_headless.content_type_resolver标签到服务中来注册自己的ContentTypeResolver

通过JSON API提供流行的Sulu功能

Sulu内容管理系统提供各种服务和Twig扩展来简化复杂网站的开发和渲染。当以无头方式提供网站内容时,这些功能不可用,因此SuluHeadlessBundle包含控制器来提供用于访问这些功能的JSON API

API以门户URL注册,因此它们的路径以Web空间的URL为前缀。如果你为你的Web空间配置了特定语言的URL,API URL将类似于以下格式:

  • https://example.org/en/api/...

导航

/api/navigations/{contextKey}

示例:/api/navigations/main?depth=2&flat=false&excerpt=true

搜索

/api/search?q={searchTerm}

示例:/api/search?q=CMS

片段区域

/api/snippet-areas/{area}

示例:/api/snippet-areas/settings?includeExtension=true

参考单页应用程序实现

SuluHeadlessBundle完全与前端无关,不要求使用特定的技术或框架。尽管如此,该组件在Resources/js-website目录中包含一个独立且可选的单页应用程序设置,允许你快速启动项目,并作为利用组件功能的参考实现。

提供的参考实现基于React作为渲染库,并使用MobX进行状态管理。它围绕一个中央的viewRegistry单例构建,允许你将React组件注册为特定类型资源(例如特定模板的页面)的视图。该应用程序包含一个路由器,它将拦截浏览器的导航,加载请求资源的JSON数据,并用加载数据渲染相应的视图。

Reference Frontend Implementation

要使用提供的单页应用程序设置,你需要在你的Twig模板中包含以下行以初始化和启动应用程序:

{% block content %}
    {# ... #}

    {# define container element for rendering single page application #}
    <div id="sulu-headless-container"></div>
    
    {# initialize application with json data of current page to prevent second request on first load #}
    <script>window.SULU_HEADLESS_VIEW_DATA = {{ headless|json_encode|raw }};</script>
    <script>window.SULU_HEADLESS_API_ENDPOINT = '{{ sulu_content_path('/api') }}';</script>
    
    {# start single page application by including built javascript code #}
    <script src="/build/headless/js/index.js"></script>
{% endblock %}

此外,你还需要将以下文件添加到你的项目中以设置单页应用程序:

assets/headless/package.json
{
  "name": "my-frontend-application",
  "main": "src/index.js",
  "private": true,
  "scripts": {
    "build": "webpack src/index.js -o ../../public/build/headless/js/index.js --module-bind js=babel-loader -p --display-modules --sort-modules-by size",
    "watch": "webpack src/index.js -w -o ../../public/build/headless/js/index.js  --module-bind js=babel-loader --mode=development --devtool source-map"
  },
  "dependencies": {
    "sulu-headless-bundle": "file:../../vendor/sulu/headless-bundle/Resources/js-website",
    "core-js": "^3.0.0",
    "loglevel": "^1.0.0",
    "mobx": "^4.0.0",
    "mobx-react": "^5.0.0",
    "prop-types": "^15.7.0",
    "react": "^16.8.0",
    "react-dom": "^16.8.0",
    "whatwg-fetch": "^3.0.0",
    "history": "^4.10.1"
  },
  "devDependencies": {
    "@babel/core": "^7.6.0",
    "@babel/plugin-proposal-class-properties": "^7.5.5",
    "@babel/plugin-proposal-decorators": "^7.6.0",
    "@babel/preset-env": "^7.6.0",
    "@babel/preset-react": "^7.0.0",
    "babel-eslint": "^10.0.3",
    "babel-loader": "^8.0.6",
    "webpack": "^4.40.2",
    "webpack-cli": "^3.3.8"
  }
}
assets/headless/webpack.config.js
const path = require('path');
const nodeModulesPath = path.resolve(__dirname, 'node_modules');

/* eslint-disable-next-line no-unused-vars */
module.exports = (env, argv) => {
    return {
        resolve: {
            modules: [nodeModulesPath, 'node_modules'],
        },
        resolveLoader: {
            modules: [nodeModulesPath, 'node_modules'],
        },
    };
};
assets/headless/babel.config.js
module.exports = {
    presets: ['@babel/env', '@babel/react'],
    plugins: [
        ['@babel/plugin-proposal-decorators', {'legacy': true}],
        ['@babel/plugin-proposal-class-properties', {'loose': true}]
    ]
};
assets/headless/src/index.js
import { startApp } from 'sulu-headless-bundle';
import viewRegistry from 'sulu-headless-bundle/src/registries/viewRegistry';
import HeadlessTemplatePage from './views/HeadlessTemplatePage';

// register views for rendering page templates
viewRegistry.add('page', 'headless-template', HeadlessTemplatePage);

// register views for rendering article templates
// viewRegistry.add('article', 'headless-template', HeadlessTemplateArticle);

// start react application in specific DOM element
startApp(document.getElementById('sulu-headless-container'));
assets/headless/src/views/HeadlessTemplatePage.js
import React from 'react';
import { observer } from 'mobx-react';

@observer
class HeadlessTemplatePage extends React.Component {
    render() {
        const serializedData = JSON.stringify(this.props.data, null, 2);

        return (<pre>{ serializedData }</pre>);
    }
}

export default HeadlessTemplatePage;

最后,你可以在assets/headless目录中执行npm installnpm run build来构建你的前端应用程序。

❤️  支持和贡献

Sulu内容管理系统是一个由各种合作公司支持的社区驱动型开源项目。我们致力于完全透明的开发过程,并非常欢迎任何贡献

如果您有任何问题,我们很乐意在官方的 Slack频道 中欢迎您。如果您发现了错误或缺少特定功能,请自由地在该 sulu/SuluHeadlessBundle 仓库上 提交新问题,并附上相应的标题和描述。

📘  许可协议

Sulu内容管理系统是根据MIT许可协议发布的。