getpop / component-model-configuration
将配置层添加到组件模型中
Requires
- php: ^8.1
- getpop/engine: ^1.0.6
Requires (Dev)
- phpstan/phpstan: ^1.8
- phpunit/phpunit: ^10.2
- rector/rector: ^0.18
- squizlabs/php_codesniffer: ^3.6
Conflicts
- gatographql/extension-demo: <1.0.6
- gatographql/external-dependency-wrappers: <1.0.6
- gatographql/gatographql: <1.0.6
- gatographql/plugin-utils: <1.0.6
- gatographql/testing-schema: <1.0.6
- getpop/access-control: <1.0.6
- getpop/application: <1.0.6
- getpop/application-wp: <1.0.6
- getpop/cache-control: <1.0.6
- getpop/component-model: <1.0.6
- getpop/componentrouting: <1.0.6
- getpop/definitionpersistence: <1.0.6
- getpop/definitions: <1.0.6
- getpop/definitions-base36: <1.0.6
- getpop/definitions-emoji: <1.0.6
- getpop/dom-crawler: <1.0.6
- getpop/engine-wp: <1.0.6
- getpop/engine-wp-bootloader: <1.0.6
- getpop/filestore: <1.0.6
- getpop/graphql-parser: <1.0.6
- getpop/guzzle-http: <1.0.6
- getpop/loosecontracts: <1.0.6
- getpop/mandatory-directives-by-configuration: <1.0.6
- getpop/markdown-convertor: <1.0.6
- getpop/multisite: <1.0.6
- getpop/resourceloader: <1.0.6
- getpop/resources: <1.0.6
- getpop/root: <1.0.6
- getpop/root-wp: <1.0.6
- getpop/site: <1.0.6
- getpop/site-builder-api: <1.0.6
- getpop/site-wp: <1.0.6
- getpop/spa: <1.0.6
- getpop/static-site-generator: <1.0.6
- graphql-by-pop/graphiql: <1.0.6
- graphql-by-pop/graphql-clients-for-wp: <1.0.6
- graphql-by-pop/graphql-endpoint-for-wp: <1.0.6
- graphql-by-pop/graphql-request: <1.0.6
- graphql-by-pop/graphql-server: <1.0.6
- graphql-by-pop/graphql-voyager: <1.0.6
- phpunit-for-gatographql/dummy-schema: <1.0.6
- phpunit-for-gatographql/dummy-wp-schema: <1.0.6
- phpunit-for-gatographql/gatographql: <1.0.6
- phpunit-for-gatographql/gatographql-testing: <1.0.6
- phpunit-for-gatographql/webserver-requests: <1.0.6
- phpunit-for-gatographql/wpfaker-schema: <1.0.6
- pop-api/api: <1.0.6
- pop-api/api-clients: <1.0.6
- pop-api/api-endpoints: <1.0.6
- pop-api/api-endpoints-for-wp: <1.0.6
- pop-api/api-graphql: <1.0.6
- pop-api/api-mirrorquery: <1.0.6
- pop-api/api-rest: <1.0.6
- pop-backbone/php-hooks: <1.0.6
- pop-backbone/wp-data-parser: <1.0.6
- pop-cms-schema/categories: <1.0.6
- pop-cms-schema/categories-wp: <1.0.6
- pop-cms-schema/comment-mutations: <1.0.6
- pop-cms-schema/comment-mutations-wp: <1.0.6
- pop-cms-schema/commentmeta: <1.0.6
- pop-cms-schema/commentmeta-wp: <1.0.6
- pop-cms-schema/comments: <1.0.6
- pop-cms-schema/comments-wp: <1.0.6
- pop-cms-schema/custompost-categories-wp: <1.0.6
- pop-cms-schema/custompost-category-mutations: <1.0.6
- pop-cms-schema/custompost-mutations: <1.0.6
- pop-cms-schema/custompost-mutations-wp: <1.0.6
- pop-cms-schema/custompost-tag-mutations: <1.0.6
- pop-cms-schema/custompost-tags-wp: <1.0.6
- pop-cms-schema/custompost-user-mutations: <1.0.6
- pop-cms-schema/custompost-user-mutations-wp: <1.0.6
- pop-cms-schema/custompostmedia: <1.0.6
- pop-cms-schema/custompostmedia-mutations: <1.0.6
- pop-cms-schema/custompostmedia-mutations-wp: <1.0.6
- pop-cms-schema/custompostmedia-wp: <1.0.6
- pop-cms-schema/custompostmeta: <1.0.6
- pop-cms-schema/custompostmeta-wp: <1.0.6
- pop-cms-schema/customposts: <1.0.6
- pop-cms-schema/customposts-wp: <1.0.6
- pop-cms-schema/media: <1.0.6
- pop-cms-schema/media-wp: <1.0.6
- pop-cms-schema/menus: <1.0.6
- pop-cms-schema/menus-wp: <1.0.6
- pop-cms-schema/meta: <1.0.6
- pop-cms-schema/metaquery-wp: <1.0.6
- pop-cms-schema/page-mutations: <1.0.6
- pop-cms-schema/pagemedia-mutations: <1.0.6
- pop-cms-schema/pages: <1.0.6
- pop-cms-schema/pages-wp: <1.0.6
- pop-cms-schema/post-categories: <1.0.6
- pop-cms-schema/post-categories-wp: <1.0.6
- pop-cms-schema/post-category-mutations: <1.0.6
- pop-cms-schema/post-category-mutations-wp: <1.0.6
- pop-cms-schema/post-mutations: <1.0.6
- pop-cms-schema/post-tag-mutations: <1.0.6
- pop-cms-schema/post-tag-mutations-wp: <1.0.6
- pop-cms-schema/post-tags: <1.0.6
- pop-cms-schema/post-tags-wp: <1.0.6
- pop-cms-schema/postmedia-mutations: <1.0.6
- pop-cms-schema/posts: <1.0.6
- pop-cms-schema/posts-wp: <1.0.6
- pop-cms-schema/queriedobject: <1.0.6
- pop-cms-schema/queriedobject-wp: <1.0.6
- pop-cms-schema/schema-commons: <1.0.6
- pop-cms-schema/schema-commons-wp: <1.0.6
- pop-cms-schema/settings: <1.0.6
- pop-cms-schema/settings-wp: <1.0.6
- pop-cms-schema/tags: <1.0.6
- pop-cms-schema/tags-wp: <1.0.6
- pop-cms-schema/taxonomies: <1.0.6
- pop-cms-schema/taxonomies-wp: <1.0.6
- pop-cms-schema/taxonomymeta: <1.0.6
- pop-cms-schema/taxonomymeta-wp: <1.0.6
- pop-cms-schema/taxonomyquery: <1.0.6
- pop-cms-schema/taxonomyquery-wp: <1.0.6
- pop-cms-schema/user-avatars: <1.0.6
- pop-cms-schema/user-avatars-wp: <1.0.6
- pop-cms-schema/user-roles: <1.0.6
- pop-cms-schema/user-roles-wp: <1.0.6
- pop-cms-schema/user-state: <1.0.6
- pop-cms-schema/user-state-mutations: <1.0.6
- pop-cms-schema/user-state-mutations-wp: <1.0.6
- pop-cms-schema/user-state-wp: <1.0.6
- pop-cms-schema/usermeta: <1.0.6
- pop-cms-schema/usermeta-wp: <1.0.6
- pop-cms-schema/users: <1.0.6
- pop-cms-schema/users-wp: <1.0.6
- pop-schema/directive-commons: <1.0.6
- pop-schema/extended-schema-commons: <1.0.6
- pop-schema/http-requests: <1.0.6
- pop-schema/schema-commons: <1.0.6
- pop-sites-wassup/comment-mutations: <1.0.6
- pop-sites-wassup/contactus-mutations: <1.0.6
- pop-sites-wassup/contactuser-mutations: <1.0.6
- pop-sites-wassup/custompost-mutations: <1.0.6
- pop-sites-wassup/custompostlink-mutations: <1.0.6
- pop-sites-wassup/flag-mutations: <1.0.6
- pop-sites-wassup/form-mutations: <1.0.6
- pop-sites-wassup/gravityforms-mutations: <1.0.6
- pop-sites-wassup/highlight-mutations: <1.0.6
- pop-sites-wassup/newsletter-mutations: <1.0.6
- pop-sites-wassup/notification-mutations: <1.0.6
- pop-sites-wassup/post-mutations: <1.0.6
- pop-sites-wassup/postlink-mutations: <1.0.6
- pop-sites-wassup/share-mutations: <1.0.6
- pop-sites-wassup/socialnetwork-mutations: <1.0.6
- pop-sites-wassup/stance-mutations: <1.0.6
- pop-sites-wassup/system-mutations: <1.0.6
- pop-sites-wassup/user-state-mutations: <1.0.6
- pop-sites-wassup/volunteer-mutations: <1.0.6
- pop-wp-schema/block-content-parser: <1.0.6
- pop-wp-schema/block-metadata-for-wp: <1.0.6
- pop-wp-schema/blocks: <1.0.6
- pop-wp-schema/commentmeta: <1.0.6
- pop-wp-schema/comments: <1.0.6
- pop-wp-schema/custompostmeta: <1.0.6
- pop-wp-schema/customposts: <1.0.6
- pop-wp-schema/media: <1.0.6
- pop-wp-schema/menus: <1.0.6
- pop-wp-schema/meta: <1.0.6
- pop-wp-schema/pages: <1.0.6
- pop-wp-schema/posts: <1.0.6
- pop-wp-schema/schema-commons: <1.0.6
- pop-wp-schema/taxonomymeta: <1.0.6
- pop-wp-schema/usermeta: <1.0.6
- pop-wp-schema/users: <1.0.6
This package is auto-updated.
Last update: 2024-09-21 22:15:09 UTC
README
将配置级别添加到组件层次结构中,通过它可以扩展数据API到应用程序
安装
通过Composer
composer require getpop/component-model-configuration
开发
源代码托管在GatoGraphQL monorepo中,位于SiteBuilder/packages/component-model-configuration
。
用法
初始化组件
\PoP\Root\App::stockAndInitializeModuleClasses([([ \PoP\ConfigurationComponentModel\Module::class, ]);
架构设计和实现
配置
在函数下添加配置值
function getImmutableConfiguration($component, &$props)
function getMutableonmodelConfiguration($component, &$props)
function getMutableonrequestConfiguration($component, &$props)
例如
// Implement the components properties ... function getImmutableConfiguration($component, &$props) { $ret = parent::getImmutableConfiguration($component, $props); switch ($component->name) { case self::COMPONENT_SOMENAME: $ret['description'] = __('Some description'); $ret['classes']['description'] = 'jumbotron'; break; } return $ret; }
请注意,配置接收$props
参数,因此可以打印通过props设置的配置值。通过initModelProps
初始化immutable
和mutable on model
配置值,而通过initRequestProps
初始化mutable on request
配置值。
// Implement the components properties ... function getImmutableConfiguration($component, &$props) { $ret = parent::getImmutableConfiguration($component, $props); switch ($component->name) { case self::COMPONENT_SOMENAME: $ret['showmore'] = $this->getProp($component, $props, 'showmore'); $ret['class'] = $this->getProp($component, $props, 'class'); break; } return $ret; } function initModelProps($component, &$props) { switch ($component->name) { case self::COMPONENT_SOMENAME: $this->setProp($component, $props, 'showmore', false); $this->appendProp($component, $props, 'class', 'text-center'); break; } parent::initModelProps($component, $props); }
客户端缓存
为了在客户端缓存所有组件的配置,并使其可重用,我们只需将所有响应深度合并在一起。例如,如果第一个请求带来了这个响应
{ "component1": { configuration: { class: "topcomponent" }, components: { "component2": { configuration: { class: "some-class" } } } } }
并且第二个响应带来了这个响应
{ "component3": { configuration: { class: "topcomponent" }, components: { "component4": { configuration: { class: "another-class" } } } } }
然后深度合并响应将得到
{ "component1": { configuration: { class: "topcomponent" }, components: { "component2": { configuration: { class: "some-class" } } } }, "component3": { configuration: { class: "topcomponent" }, components: { "component4": { configuration: { class: "another-class" } } } } }
然后我们可以完美地重用从"component1"开始的配置以重新打印第一个请求,以及从"component3"开始的配置以重新打印第二个请求。
这很简单,但是从现在开始事情变得更复杂。如果组件的后代不是静态的,而是可以依赖于上下文而改变,比如请求的URL或其他输入,会发生什么?例如,我们可能有一个名为"single-post"的组件,根据请求对象的帖子类型改变其子组件,选择"layout-post"或"layout-event"组件,这样组件层次结构就会从这样
"single-post" components "layout-post"
变成这样
"single-post" components "layout-event"
同样,即使在同一组件层次结构中,组件的属性值也可能因不同的URL而变化。例如,一个名为"post-layout"的组件可以有一个值为"post-{id}"的属性"class",其中"{id}"是请求的帖子的id,这样我们就可以为特定的帖子添加样式,例如.post-37 { background-color: red; }
和.post-224 { background-color: green; }
。然后,id为37和224的帖子,尽管它们具有相同的组件层次结构,但它们的配置将交替出现
"single-post" components "layout-post" configuration class: "post-37"
变成这样
"single-post" components "layout-post" configuration class: "post-224"
让我们来看看上述两种情况在深度合并结果时会发生什么。例如,如果第一个请求带来了这个响应
{ "component1": { configuration: { class: "topcomponent" }, components: { "component2": { configuration: { class: "some-class" } } } } }
并且第二个响应带来了这个响应
{ "component1": { configuration: { class: "topcomponent" }, components: { "component3": { configuration: { class: "another-class" } } } } }
然后深度合并响应将得到
{ "component1": { configuration: { class: "topcomponent" }, components: { "component2": { configuration: { class: "some-class" } }, "component3": { configuration: { class: "another-class" } } } } }
如我们所见,合并第二个请求的响应后,相同的数据(class: "topcomponent"
)没有影响合并后的对象,新的信息被附加到现有对象中,但没有覆盖任何数据。然而,原始的第一个响应中只有"component1"具有子组件"component2",但在合并后,"component1"有两个子组件,"component2"和"component3"。然后,如果再次加载第一个响应的URL并重用缓存的配置,下面"component1"将打印"component2"和"component3"而不是应该只打印"component2"。
为了解决这个问题,可以在配置中显式地添加一个“descendants”属性,以声明其子组件,这样就可以知道哪些组件必须被渲染,忽略其余部分,尽管它们的数据仍然是合并后的JSON对象的一部分。然后,第一和第二次响应将如下所示
{ "component1": { configuration: { class: "topcomponent", descendants: ["component2"] }, components: { "component2": { configuration: { class: "some-class" } } } } } { "component1": { configuration: { class: "topcomponent", descendants: ["component3"] }, components: { "component3": { configuration: { class: "another-class" } } } } }
合并后的配置将如下所示
{ "component1": { configuration: { class: "topcomponent", descendants: ["component3"] }, components: { "component2": { configuration: { class: "some-class" } }, "component3": { configuration: { class: "another-class" } } } } }
但是,现在缓存的“descendants”属性值已经被第二次响应的值覆盖,带来了之前提到的第二个问题关于不同的属性值。然后,如果再次加载第一个响应的URL并重用缓存的配置,它将在“component1”下打印“component3”而不是应该的“component2”。
关于不同属性的问题源于配置值不仅根据组件层次结构设置,还根据请求的URL设置。例如,以下组件层次结构
"single-post" components "layout-post"
可以产生以下两种不同的配置输出
"single-post" components "layout-post" configuration class: "post-37"
和
"single-post" components "layout-post" configuration class: "post-224"
解决方案是在不同请求中深度合并配置,不覆盖不同的属性,无论是在组件层次结构还是URL级别,将配置分成3个单独的部分:“immutable”(不可变)、“mutableonmodel”(其中“model”相当于“组件层次结构”)和“mutableonrequest”。配置中的每个属性必须放在这三个部分中的 exactly 1 个部分,如下所示
- immutable: 包含永远不会改变的属性,例如
class: "topcomponent"
- mutableonmodel: 包含可以根据组件层次结构更改的属性,例如
descendants: ["component2"]
- mutableonrequest: 包含可以根据请求的URL更改的属性,例如
class: "post-37"
按照这个方案,第一次请求可能产生以下响应
{ immutable: { "single-post": { configuration: { class: "topcomponent" } } }, mutableonmodel: { "single-post": { configuration: { descendants: ["layout-post"] } } }, mutableonrequest: { "single-post": { components: { "layout-post": { configuration: { class: "post-37" } } } } } }
如我们所见,由于来自三个部分的属性没有重叠,因此将请求的三个部分合并会产生完整的配置
{ "single-post": { configuration: { class: "topcomponent", descendants: ["layout-post"] }, components: { "layout-post": { configuration: { class: "post-37" } } } } }
接下来,客户端的缓存被保持为3个单独的对象,每个对象对应一个子部分,其中“mutableonmodel”和“mutableonrequest”在适当的键下存储其数据:“mutableonmodel”在名为“modelInstanceId”的键下,它代表组件层次结构的散列,而“mutableonrequest”在请求的URL下。上面的请求将像这样被缓存(假设“modelInstanceId”的值为“bwKtq*8H”,URL为“/posts/some-post/”)
immutable => { "single-post": { configuration: { class: "topcomponent" } } } mutableonmodel => { "bwKtq*8H": { "single-post": { configuration: { descendants: ["layout-post"] } } } } mutableonrequest => { "/posts/some-post/": { "single-post": { components: { "layout-post": { configuration: { class: "post-37" } } } } } }
如果随后我们获得第二次请求的响应,缓存将更新如下(假设“modelInstanceId”的值为“6C7Lu$\3”,URL为“/posts/some-event/”)
immutable => { "single-post": { configuration: { class: "topcomponent" } } } mutableonmodel => { "bwKtq*8H": { "single-post": { configuration: { descendants: ["layout-post"] } } }, "6C7Lu$\3": { "single-post": { configuration: { descendants: ["layout-event"] } } } } mutableonrequest => { "/posts/some-post/": { "single-post": { components: { "layout-post": { configuration: { class: "post-37" } } } } }, "/posts/some-event/": { "single-post": { components: { "layout-event": { configuration: { class: "post-45" } } } } } }
如我们所见,“immutable”包含结构的共同部分,而“mutableonmodel”和“mutableonrequest”包含差异。因此,此方案识别共同数据并只存储一次,并且所有不同的条目都分别存储和可访问。如果在组件层次结构中大部分配置没有变化,那么在“immutable”下存储的信息将是存储信息的主体,从而成功减少缓存的数据量。
最后,对于任何请求的“modelInstanceId”和URL,我们可以从三个部分获得三个单独的分支,并将它们全部合并在一起,从缓存中重新创建整个配置。
合并也可以在服务器端进行:如果不需要在客户端缓存配置,则可以通过将参数 dataoutputmode=combined
添加到URL中来避免处理三个子部分的额外复杂性。
PHP版本
要求
- PHP 8.1+ 用于开发
- PHP 7.2+ 用于生产
支持的PHP特性
查看 在 GatoGraphQL/GatoGraphQL
中支持的PHP特性列表
预览降级到PHP 7.2
通过 Rector(dry-run 模式)
composer preview-code-downgrade
标准
通过 PHP CodeSniffer 检查编码标准,运行
composer check-style
自动修复问题,运行
composer fix-style
变更日志
请参阅 CHANGELOG 了解最近的变化。
测试
要执行 PHPUnit,运行
composer test
静态分析
要执行 PHPStan,运行
composer analyse
报告问题
要报告错误或请求新功能,请在 GatoGraphQL monorepo 问题跟踪器 上进行。
贡献
我们欢迎在 GatoGraphQL monorepo(该包的源代码托管处)上的贡献。
请参阅 CONTRIBUTING 和 CODE_OF_CONDUCT 了解详细信息。
安全性
如果您发现任何安全相关的问题,请通过电子邮件 leo@getpop.org 而不是使用问题跟踪器进行报告。
致谢
许可
GNU 通用公共许可证 v2(或更高版本)。有关更多信息,请参阅 许可文件。