sulu / headless-bundle
提供用于将Sulu作为无头内容管理系统使用的控制器和服务的包装
Requires
- php: ^7.3 || ^8.0
- sulu/sulu: ^2.4 || ^2.5@dev
- symfony/config: ^4.4 || ^5.4 || ^6.3 || ^7.0
- symfony/dependency-injection: ^4.4 || ^5.4 || ^6.3 || ^7.0
- symfony/framework-bundle: ^4.4 || ^5.4 || ^6.3 || ^7.0
- symfony/http-foundation: ^4.4 || ^5.4 || ^6.3 || ^7.0
- symfony/http-kernel: ^4.4 || ^5.4 || ^6.3 || ^7.0
Requires (Dev)
- coduo/php-matcher: ^5.0 || ^6.0
- handcraftedinthealps/code-coverage-checker: ^0.2.1
- handcraftedinthealps/zendsearch: ^2.0
- jackalope/jackalope-doctrine-dbal: ^1.3.4 || ^2.0
- jangregor/phpstan-prophecy: ^1.0
- php-cs-fixer/shim: ^3.0
- phpspec/prophecy: ^1.8
- phpspec/prophecy-phpunit: ^2.0
- phpstan/phpstan: ^1.0
- phpstan/phpstan-doctrine: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- phpstan/phpstan-symfony: ^1.0
- phpunit/phpunit: ^9.6 || ^10.0
- symfony/browser-kit: ^4.4 || ^5.4 || ^6.3 || ^7.0
- symfony/console: ^4.4 || ^5.4 || ^6.3 || ^7.0
- symfony/dotenv: ^4.4 || ^5.4 || ^6.3 || ^7.0
- symfony/error-handler: ^4.4 || ^5.4 || ^6.3 || ^7.0
- symfony/monolog-bundle: ^3.1
- thecodingmachine/phpstan-strict-rules: ^1.0
Conflicts
- doctrine/orm: < 2.6.3
- zendframework/zend-code: < 3.3.1
README
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数据,并用加载数据渲染相应的视图。
要使用提供的单页应用程序设置,你需要在你的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 install
和npm run build
来构建你的前端应用程序。
❤️ 支持和贡献
Sulu内容管理系统是一个由各种合作公司支持的社区驱动型开源项目。我们致力于完全透明的开发过程,并非常欢迎任何贡献。
如果您有任何问题,我们很乐意在官方的 Slack频道 中欢迎您。如果您发现了错误或缺少特定功能,请自由地在该 sulu/SuluHeadlessBundle 仓库上 提交新问题,并附上相应的标题和描述。
📘 许可协议
Sulu内容管理系统是根据MIT许可协议发布的。