我很难确定这是否是本机支持的,或者它是否不是实现此功能的最佳位置-在我的应用程序(Laravel)或API(WordPress)的同一面。
通过slug检索单个页面(/关于我们)很容易:
/wp-json/wp/v2/pages?slug=about-us
但检索子/子页面时会出现问题。考虑一下/关于我们/子页面-这在WordPress中工作得很好,但通过RESTAPI检索页面似乎不可能?
/wp-json/wp/v2/pages?slug=about-us/child-page
/wp-json/wp/v2/pages?slug=%2Fabout-us%2Fchild-page%2F
没有结果。
我可以搜索单独的页面并获得结果,但是如果另一个页面共享该段代码,则可能会发生冲突。
/wp-json/wp/v2/pages?slug=child-page
有
get_page_by_path()
它允许您通过路径搜索页面,这正是我所追求的-我可以使用自定义REST API端点来实现这一点(
https://www.coditty.com/code/wordpress-rest-api-how-to-get-content-by-slug) 但返回的结果不标准,与WP-REST等效值不可比(见下文)
{
"id": 22,
"date": "2017-03-28T13:15:53",
"date_gmt": "2017-03-28T12:15:53",
"guid": {
"rendered": "http://127.0.0.1:8000/?page_id=22"
},
"modified": "2017-03-28T13:15:53",
"modified_gmt": "2017-03-28T12:15:53",
"slug": "test-sub-page",
"status": "publish",
"type": "page",
"link": "http://127.0.0.1:8000/test/test-sub-page/",
"title": {
"rendered": "Test Sub page"
},
"content": {
"rendered": "...",
"protected": false
},
"excerpt": {
"rendered": "....",
"protected": false
},
"author": 1,
"featured_media": 0,
"parent": 7,
"menu_order": 0,
"comment_status": "closed",
"ping_status": "closed",
"template": "",
"meta": [],
"_links": {
"self": [
{
"href": "http://127.0.0.1:8000/wp-json/wp/v2/pages/22"
}
],
"collection": [
{
"href": "http://127.0.0.1:8000/wp-json/wp/v2/pages"
}
],
"about": [
{
"href": "http://127.0.0.1:8000/wp-json/wp/v2/types/page"
}
],
"author": [
{
"embeddable": true,
"href": "http://127.0.0.1:8000/wp-json/wp/v2/users/1"
}
],
"replies": [
{
"embeddable": true,
"href": "http://127.0.0.1:8000/wp-json/wp/v2/comments?post=22"
}
],
"version-history": [
{
"href": "http://127.0.0.1:8000/wp-json/wp/v2/pages/22/revisions"
}
],
"up": [
{
"embeddable": true,
"href": "http://127.0.0.1:8000/wp-json/wp/v2/pages/7"
}
],
"wp:attachment": [
{
"href": "http://127.0.0.1:8000/wp-json/wp/v2/media?parent=22"
}
],
"curies": [
{
"name": "wp",
"href": "https://api.w.org/{rel}",
"templated": true
}
]
}
}
VS公司
{
"ID": 22,
"post_author": "1",
"post_date": "2017-03-28 13:15:53",
"post_date_gmt": "2017-03-28 12:15:53",
"post_content": "...",
"post_title": "Test Sub page",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "test-sub-page",
"to_ping": "",
"pinged": "",
"post_modified": "2017-03-28 13:15:53",
"post_modified_gmt": "2017-03-28 12:15:53",
"post_content_filtered": "",
"post_parent": 7,
"guid": "http://127.0.0.1:8000/?page_id=22",
"menu_order": 0,
"post_type": "page",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw"
}
或者,我可以从我的应用程序中轮询每个段/页面的API,以验证每个页面是否存在,并以这种方式建立数据。。。
SO网友:bosco
遗憾的是,本机并不支持此功能。具体而言,问题在于大多数帖子类型包括page
使用底座WP_REST_Posts_Controller
其中映射了slug
参数设置为post_name__in
WP_Query
参数,这不利于解析层次结构段塞。这个pagename
然而,查询变量确实如此,但每个查询只有一个,这可能就是为什么它还没有用于层次结构帖子类型的REST请求。
有许多解决方案和解决方法。请注意,下面的代码尚未经过彻底测试,JavaScript尤其忽略了重要的身份验证和错误处理实践——它仅用于说明目的。
发出多个请求您的客户端只需遍历每个路径部分并使用_fields
参数,通过仅请求祖先帖子的ID,然后使用该帖子ID作为parent
后续请求的参数:
async function wpse261645_fetchPage( path ) {
const parts = path.split( \'/\' );
const uri = \'/wp-json/wp/v2/pages\';
let parent_id;
for( let i = 0; i < parts.length; i++ ) {
const params = new URLSearchParams( { slug: parts[i] } );
if( i < parts.length - 1 )
params.append( \'_fields\', \'id\' );
if( parent_id )
params.append( \'parent\', parent_id );
const res = await fetch(
`${uri}?${params}`,
{
method: \'GET\',
headers: { \'Content-Type\': \'application/json\' }
}
).then( res => res.json() );
if( i === parts.length - 1 )
return res;
parent_id = res[0].id;
}
}
修改slug
REST参数/QV映射这可能是最有吸引力的解决方案,因为它可以直接有效地解决原始问题,而不需要客户端进行特殊处理。但由于涉及到的运动部件的数量以及我自己对REST API的不熟悉,这有点复杂和实验性——我完全愿意接受建议和改进!
在WP_REST_Posts_Controller
执行查询以检索与请求相对应的项,它通过以下方式运行查询参数和请求对象the rest_{$this->post_type}_query
filter. 我们可以利用此过滤器有选择地重新映射slug
根据所选立柱类型或控制器的需要。
还需要调整slug
相关控制器的参数模式、解析和清理例程/
s或%2F
缓动值或跳闸验证错误。我不认为这会产生任何兼容性问题,因为模式和参数注册中唯一涉及的部分是交换它们的清理回调-更改应该对客户端和发现几乎不可见,并且与原始功能完全向后兼容(除非有人一直依赖REST API进行转换/
s插入-
s) -但我还没有彻底测试过。
解析、架构和清理调整(以保持/
s)
function wpse261645_sanitize_nested_slug( $slug ) {
// Exploding slugs, as one does.
$slug_parts = array_map( \'sanitize_title\', explode( \'/\', $slug ) );
return implode( \'/\', $slug_parts );
}
function wpse261645_parse_nested_slug_list( $slugs ) {
$slugs = wp_parse_list( $slugs );
return array_unique( array_map( \'wpse261645_sanitize_nested_slug\', $slugs ) );
}
function wpse261645_nested_slug_schema( $schema ) {
$schema[\'slug\'][\'arg_options\'][\'sanitize_callback\'] = \'wpse261645_sanitize_nested_slug\';
return $schema;
}
add_filter( \'rest_page_item_schema\', \'wpse261645_nested_slug_schema\' );
function wpse261645_nested_slug_collection_params( $params ) {
$params[\'slug\'][\'sanitize_callback\'] = \'wpse261645_parse_nested_slug_list\';
return $params;
}
add_filter( \'rest_page_collection_params\', \'wpse261645_nested_slug_collection_params\' );
重新映射slug
REST参数查询变量/
在slug
参数,我们可以将值映射到各种不同的查询中:
function wpse261645_remap_slug_param_qv( $args, $request ) {
$slugs = $request->get_param( \'slug\' );
// If the `slug` param was not even set, skip further processing.
if( empty( $slugs ) )
return $args;
// Pull out hierarchical slugs into their own list.
$nested_slugs = [];
foreach( $slugs as $index => $slug ) {
if( strpos( $slug, \'/\' ) !== false ) {
$nested_slugs[] = $slug;
unset( $slugs[ $index ] );
}
}
if( count( $slugs ) ) {
$args[\'post_name__in\'] = $slugs;
if( count( $nested_slugs ) ) {
$args[\'wpse261645_compound_query\'] = true;
$args[\'post__in\'] = array_map( \'url_to_postid\', $nested_slugs );
add_filter( \'posts_where\', \'wpse261645_compound_query_where\', 10, 2 );
}
}
else {
unset( $args[\'post_name__in\'] );
if( count( $nested_slugs ) === 1 )
$args[\'pagename\'] = $nested_slugs[0];
elseif( count( $nested_slugs > 1 ) )
$args[\'post__in\'] = array_map( \'url_to_postid\', $nested_slugs );
}
return $args;
}
add_filter( \'rest_page_query\', \'wpse261645_remap_slug_param_qv\', 10, 2 );
function wpse261645_compound_query_where( $where, $query ) {
global $wpdb;
if( ! isset( $query->query[\'wpse261645_compound_query\'] ) )
return $where;
return preg_replace(
"/ AND ({$wpdb->posts}.post_name IN \\([^)]*\\)) AND ({$wpdb->posts}.ID IN \\([^)]*\\))/",
\' AND ($1 OR $2)\',
$where
);
}
上述逻辑根据
slug
:
任意数量的扁平段塞映射到post_name__in
QV符合标准单个层次结构段塞将映射到pagename
QV,出租WP_Query
本机处理路径分辨率分层段塞列表将通过url_to_postid()
并映射到post__in
QV。查找会增加额外的开销混合层次结构和扁平段塞的列表将映射到post__in
和post_name__in
QV和WHERE子句分别对这些条件进行了修改,而不是对其进行了修订。分层段塞将解析为ID,从而增加额外的开销总之,最有效的查询是传递slug
作为单个层次段塞,或列表中任意数量的扁平段塞。分层或混合段塞列表将导致额外的开销。
作为一个额外的好处,此实现还通过包含/
. 例如。?slug=foobar
将返回所有带有slug的帖子foobar
按照惯例,但是?slug=/foobar
将返回带弹头的立柱foobar
它没有父对象。
对Web路径使用HEAD请求This is a horrible dirty hack 这依赖于RESTAPI之外的约定,这些约定可能会产生大量开销some sites may be prone to disabling 要启动-I strongly recommend against doing this. 在任何旨在分发的代码中都是如此;你最终会遇到很多不开心的用户。
默认情况下,WordPress返回Link
响应内容请求的HTTP头,其中一个是指向与内容对应的资源或集合的REST路由。因此,通过利用WordPress的前端permalink路由,可以在2个请求内解析到REST资源的任何嵌套slug路径:
async function wpse261645_fetchPage( path ) {
let uri = `/wp-json/wp/v2/pages/?slug=${path}`;
if( path.includes( \'/\' ) ) {
const web_res = await fetch( `/${path}`, { method: \'HEAD\' } );
const link_header = web_res.headers.get( \'Link\' ).split( \', \' )
.find( val => val.includes( \' rel="alternate"; type="application/json"\' ) );
uri = link_header.substring( 1, link_header.indexOf( \'>\' ) );
}
return fetch(
uri,
{
method: \'GET\',
headers: { \'Content-Type\': \'application/json\' }
}
).then( res => res.json() );
}