Yoast SEO 开发者文档

Yoast SEO 开发者文档中文翻译,介绍扩展与集成接口。

Overview

Welcome to the Yoast Developer portal, the place to find any technical information related to our software.

This documentation is aimed at advanced end-users, developers, plugin/theme authors, and those curious about the mechanics of search engine optimization.

Explore core features

Browse technical specifications

Learn how our features work, how to customize the outputs, and how best to integrate into or out of other systems.

Integrating

Want to integrate Yoast SEO into your platform? Check out our documentation on integrating, and familiarize yourself with all of our features and controls.

Yoast SEO for Shopify

Explore our APIs

Metadata Api

对于简单的网站,或者当 SEO 不是首要考虑因素时,插件和主题通常直接将元标签输出或回显到文档的 <head> 部分。

然而,更高级的 SEO 通常要求元标签具备上下文,能够感知页面上的其他元标签,或涉及复杂的内部逻辑。例如,规范 URL 标签的值可能会受到元机器人标签的存在或值的影响。对于运行复杂或多个插件和主题的网站,标准化和安全性也同样重要。在没有框架的情况下,管理这类挑战会变得越来越繁琐。

这就是为什么我们提供了一种正式的方式来管理每个页面 <head> 中元标签的构建和输出。我们为每个标签使用一个 presenter(Abstract_Indexable_Presenter 类的扩展),您可以轻松修改或扩展它。

可用呈现器

默认情况下,Yoast SEO 附带以下用于输出元标签的呈现器。

通用呈现器

呈现器 标签格式 过滤器
Title_Presenter <title>%s</title> wpseo_title
Meta_Description_Presenter <meta name="description" content="%s" /> wpseo_metadesc
Canonical_Presenter <link rel="canonical" href="%s" /> wpseo_canonical
Robots_Presenter <meta name="robots" content="%s" /> wpseo_robots
Meta_Author_Presenter <meta name="author" content="%s" /> wpseo_meta_author

Webmaster presenters

Presenter Tag format Filter
Baidu_Presenter <meta name="baidu-site-verification" content="%s" /> n/a
Bing_Presenter <meta name="msvalidate.01" content="%s" /> n/a
Google_Presenter <meta name="google-site-verification" content="%s" /> n/a
Pinterest_Presenter <meta name="p:domain_verify" content="%s" /> n/a
Yandex_Presenter <meta name="yandex-verification" content="%s" /> n/a

X presenters

Presenter Tag format Filter
Creator_Presenter <meta name="twitter:creator" content="%s" /> n/a
Description_Presenter <meta name="twitter:description" content="%s" /> wpseo_twitter_description
Image_Presenter <meta name="twitter:image" content="%s" /> wpseo_twitter_image
Site_Presenter <meta name="twitter:site" content="%s" /> wpseo_twitter_site
Title_Presenter <meta name="twitter:title" content="%s" /> wpseo_twitter_title

OpenGraph presenters

Presenter Tag format Filter
Article_Author_Presenter <meta property="article:author" content="%s" /> wpseo_opengraph_author_facebook
Article_Modified_Time_Presenter <meta property="article:modified_time" content="%s" /> n/a
Article_Published_Time_Presenter <meta property="article:published_time" content="%s" /> n/a
Article_Publisher_Presenter <meta property="article:publisher" content="%s" /> wpseo_og_article_publisher
Description_Presenter <meta property="og:description" content="%s" /> wpseo_opengraph_desc
Locale_Presenter <meta property="og:locale" content="%s" /> wpseo_og_locale
Site_Name_Presenter <meta property="og:site_name" content="%s" /> wpseo_opengraph_site_name
Title_Presenter <meta property="og:title" content="%s" /> wpseo_opengraph_title
Type_Presenter <meta property="og:type" content="%s" /> wpseo_opengraph_type
Url_Presenter <meta property="og:url" content="%s" /> wpseo_opengraph_url
Image_Presenter <meta property="og:image" content="%s" /> wpseo_opengraph_image

Deprecated Presenters

Presenter Tag format Filter Deprecated from
Googlebot_Presenter <meta name="googlebot" content="%s" /> wpseo_googlebot
Bingbot_Presenter <meta name="bingbot" content="%s" /> wpseo_bingbot
FB_App_ID_Presenter <meta property="fb:app_id" content="%s" /> Yoast SEO 15.5 (Dec 2020)

编辑现有元标签

有时您可能会遇到需要编辑 Yoast SEO 输出的某个元标签的情况。 为此,您应使用相应元数据展示器的相关过滤器来实现。

关于使用呈现器和过滤器的注意事项

遗留过滤器

请注意 这些过滤器未来可能会变更,因为它们尚未通过 Presenter 类进行封装。

过滤器 描述
wpseo_opengraph_image_size 用于操作 Open Graph 分享所用图像尺寸的过滤器。
- 仅当您需要手动确定 og:image 标签的自定义图像尺寸时使用此过滤器。
尺寸可以是字符串值(例如 thumbnail)或宽度/高度值数组
(例如 [ 200, 100 ])。
- 如果图像小于过滤器指定的尺寸,仍将被拒绝使用。
- 使用后,保存新特色图像或更新带有特色图像的文章时将始终强制执行此尺寸。
- 若要将此尺寸应用于使用过滤器前创建的文章,请重新索引站点。
- 可通过命令执行:wp yoast index --reindex。参见 Yoast SEO WP CLI:重新索引。或安装Yoast Test Helper来重置索引数据。
- 若站点从未建立索引,只需前往 Yoast SEO->工具 并点击 开始 SEO 数据优化。

示例

以下将展示我们提供的一些过滤器使用示例。

操作元机器人标签输出

/**
 * 将 max-video-preview 修改为 100。
 *
 * @param string $output       原始输出字符串的字符串表示。
 * @param object $presentation 包含必要数据的呈现对象。
 *
 * @return string Googlebot 元标签。
 */
function change_robots_video_preview_settings( $output, $presentation ) {
    $robots = $presentation->robots;

    $values = \array_map( function( $item ) {
        if ( strpos( $item, 'max-video-preview' ) !== false ) {
            $item = 'max-video-preview:100';
        }

        return $item;
    }, $robots );

    return \implode( ', ', $values );
}

add_filter( 'wpseo_robots', 'change_robots_video_preview_settings', 10, 2 );

Appending a string to a category's title

/**
 * Changes title for a specific category.
 *
 * @param string $title        The current title.
 * @param object $presentation The presentation object containing the necessary data.
 *
 * @return string The altered title tag.
 */
function change_category_title( $title, $presentation ) {
	$categories = \get_the_category( $presentation->model->object_id );

	foreach ( $categories as $category ) {
		if ( $category->slug === 'books' ) {
			return sprintf( '%s - %s', $title, $category->name );
		}

		return $title;
	}
}

add_filter( 'wpseo_title', 'change_category_title', 10, 2 );

Adding meta tags

Adding your own meta tag(s) is as simple as creating your own class which extends Abstract_Indexable_Presenter. To get started, you'll need to understand the following:

Combined, this should lead to code that looks something like this:

use Yoast\WP\SEO\Presenters\Abstract_Indexable_Presenter;

/**
 * Adds a custom my_meta_tag tag.
 */
class My_Custom_Presenter extends Abstract_Indexable_Presenter {
    /**
     * This output the full meta tag you want to add.
     */
    public function present() {
        return '<meta name="my_meta_tag" content="' . esc_attr( $this->get() ) . '" />';
    }

    /**
     * Returns the value of our new tag.
     *
     * @return string The value of our meta tag.
     */
    public function get() {
        return ( $this->presentation->open_graph_locale === 'nl_NL' ) ? 'Dutch' : 'Not dutch';
    }
}

For simple tags, you can use Abstract_Indexable_Tag_Presenter, which simply defines the string format and variable placeholder for the tag. A simple example would look like this:

use Yoast\WP\SEO\Presenters\Abstract_Indexable_Tag_Presenter;

/**
 * Adds a custom my_meta_tag tag.
 */
class My_Custom_Presenter extends Abstract_Indexable_Tag_Presenter {

	/**
	 * The tag format including placeholders.
	 *
	 * @var string
	 */
	protected $tag_format = '<meta name="my_meta_tag" content="%s" />';

	/**
	 * Returns the value of our new tag.
	 *
	 * @return string The value of our meta tag.
	 */
	public function get() {
		return ( $this->presentation->open_graph_locale === 'nl_NL' ) ? 'Dutch' : 'Not dutch';
	}
}

这种更简单的格式要求您指定标签格式,并将值应出现的位置替换为 %s,因为它将被 sprintf 到该位置。这假设您的 get 函数返回一个需要属性转义的值。如果它实际上是一个 URL,您可以通过将展示器的 $escaping 类属性设置为 'url' 来更改转义逻辑。

由于 Abstract_Indexable_Tag_Presenter 本身继承自 Abstract_Indexable_Presenter,您在那里拥有的所有工具在这里也同样可用。

注册你的呈现器

无论你选择上述哪种方法,都可以通过过滤器注册你的呈现器:

/**
 * 将我们的自定义呈现器添加到呈现器数组中。
 *
 * @param array $presenters 当前的呈现器数组。
 *
 * @return array 添加了自定义呈现器后的呈现器数组。
 */
function add_my_custom_presenter( $presenters ) {
	$presenters[] = new My_Custom_Presenter();

	return $presenters;
}

add_filter( 'wpseo_frontend_presenters', 'add_my_custom_presenter' );

移除元标签

可以通过使用 wpseo_frontend_presenters 过滤器来移除元标签。在下面的示例中,我们将从列表中移除规范 URL 展示器。

/**
 * 从展示器中移除规范 URL。
 *
 * @param array $presenters 已注册的展示器。
 *
 * @return array 剩余的展示器。
 */
function remove_canonical_presenter( $presenters ) {
    return array_map( function( $presenter ) {
        if ( ! $presenter instanceof Canonical_Presenter ) {
            return $presenter;
        }
    }, $presenters );
}

add_action( 'wpseo_frontend_presenters', 'remove_canonical_presenter' );

Overview

Yoast SEO exposes several APIs, which are designed to help advanced users, developers, and integrators to access and manipulate our data and outputs.

Rest Api

:::caution

下文描述的 'raw' json 数据格式仅在 Yoast SEO 16.7 及以上版本中可用。

:::

Yoast SEO 包含一个 REST API,可返回站点上任意 URL 的所有 SEO 元数据(元标签、schema.org 数据等)。这使得无头 WordPress 站点能够非常便捷地利用 Yoast SEO 来管理其所有 SEO 事宜。

Yoast SEO REST API 语法

您可以通过两种方式获取页面或 URL 的 SEO 元数据:使用 WordPress 原生 WP REST API 查询对象,或查询特定 URL。

1. 将 SEO 元数据作为 WordPress 原生 WP REST API 响应的一部分

许多无头 WordPress 实现利用 WordPress 原生的 WP REST API 来获取特定文章或对象的数据。

通常,这涉及向类似 https://yoast.com/wp-json/wp/v2/posts/607(通过 ID 获取特定文章)、https://yoast.com/wp-json/wp/v2/posts?slug=wordpress-seo(通过 slug 获取特定文章)或 https://yoast.com/wp-json/wp/v2/types/yoast_events(特定自定义文章类型归档)* 的 URL 发出请求。

请注意,请求自定义文章归档应使用文章类型名称*,这可能与归档的slug不同(例如,yoast_events 对比 /events/)。*

当 Yoast SEO 激活时,我们会在这些响应中附加两个额外字段:yoast_head 和 yoast_head_json。

2. 查询特定 URL

在某些情况下,查询特定 URL(而非对象标识符)可能更简便或更可取。您可以通过向 /wp-json/yoast/v1/get_head?url= 发送 GET 请求来实现,适用于任何 URL。例如:

https://yoast.com/wp-json/yoast/v1/get_head?url=https://yoast.com/wordpress-seo/

此操作将查询 URL https://yoast.com/wordpress-seo/,并返回以下 JSON 属性:html、json 和 status。

理解和使用我们的数据

在上述两种方法中,我们以两种格式返回 SEO 数据。

  1. yoast_head/html 属性包含一个经过转义的、预制的数据块,其中包含该页面所需的所有元标签(以及 schema.org 输出)。这与我们通常在文章或页面的 <head> 中输出的内容相同。

  2. yoast_head_json/json 属性提供原始数据,作为每个标签的一系列键/值对(以及更复杂的对象)。

status 属性返回一个 HTTP 状态码(通常是 200 或 404)。

原始数据中返回的许多属性被分解为特定的字段和值。您可以使用这些来查询特定属性,和/或构建您自己的响应。 例如,og_image 属性包含 width、height、size、path、alt、pixels 和各种其他属性,而不仅仅是图像的 URL。

我们建议尽可能使用预制方法,因为它利用我们的内部逻辑来保证标签和值的最佳组合。 在使用原始数据时,我们建议使用预制数据作为参考,以确保您的实现是正确的。

修改输出

我们所有的元数据都可以通过元数据 API进行自定义。 通过筛选每个标签对应的呈现器,可以修改预制和原始两种响应。

常见问题

我能使用此 API 更新 数据吗?

Yoast REST API 目前是只读的,尚不支持通过 POST 或 PUT 调用来更新数据。

为什么 API 对存在的页面返回 404 状态?

在某些情况下,您可能会发现 API 对存在的文章、资源或 URL 返回 404 状态。

这通常发生在资源数据未在我们的 可索引 系统中可用时。通常可以通过以下任一方式解决:

请注意,Yoast SEO 也会为非 200 状态场景(包括合法的 404 错误)返回完整响应,以便您能有效“优化”这些场景。

Can I disable the API?

You can easily disable this API by going to the Site features section of your Yoast SEO Settings menu, and disabling the feature toggle.

Example output

The following code is the result of a query to https://yoast.com/wp-json/yoast/v1/get_head?url=https://yoast.com/site-structure-the-ultimate-guide/. It contains the raw html, the structured json, and a status (of 200).

{
    "html": "<!-- This site is optimized with the Yoast SEO Premium plugin v23.5-RC1 (Yoast SEO v23.5-RC1) - https://yoast.com/product/yoast-seo-premium-wordpress/ -->\n<title>Site structure: the ultimate guide &#8226; Yoast</title>\n<meta name=\"description\" content=\"Your site structure is vital for users and SEO. Our complete guide will guide you through all the steps to create a sound site structure.\" class=\"yoast-seo-meta-tag\" />\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" />\n<link rel=\"canonical\" href=\"https://yoast.com/site-structure-the-ultimate-guide/\" class=\"yoast-seo-meta-tag\" />\n<meta property=\"og:locale\" content=\"en_US\" class=\"yoast-seo-meta-tag\" />\n<meta property=\"og:type\" content=\"article\" class=\"yoast-seo-meta-tag\" />\n<meta property=\"og:title\" content=\"Site structure: the ultimate guide\" class=\"yoast-seo-meta-tag\" />\n<meta property=\"og:description\" content=\"Your site structure is vital for users and SEO. Our complete guide will guide you through all the steps to create a sound site structure.\" class=\"yoast-seo-meta-tag\" />\n<meta property=\"og:url\" content=\"https://yoast.com/site-structure-the-ultimate-guide/\" class=\"yoast-seo-meta-tag\" />\n<meta property=\"og:site_name\" content=\"Yoast\" class=\"yoast-seo-meta-tag\" />\n<meta property=\"article:publisher\" content=\"https://www.facebook.com/yoast\" class=\"yoast-seo-meta-tag\" />\n<meta property=\"article:author\" content=\"https://www.facebook.com/mariekerakt\" class=\"yoast-seo-meta-tag\" />\n<meta property=\"article:published_time\" content=\"2023-05-03T11:35:45+00:00\" class=\"yoast-seo-meta-tag\" />\n<meta property=\"article:modified_time\" content=\"2024-08-21T08:37:24+00:00\" class=\"yoast-seo-meta-tag\" />\n<meta property=\"og:image\" content=\"https://yoast.com/app/uploads/2020/08/tuesday.png\" class=\"yoast-seo-meta-tag\" />\n\t<meta property=\"og:image:width\" content=\"1200\" class=\"yoast-seo-meta-tag\" />\n\t<meta property=\"og:image:height\" content=\"675\" class=\"yoast-seo-meta-tag\" />\n\t<meta property=\"og:image:type\" content=\"image/png\" class=\"yoast-seo-meta-tag\" />\n<meta name=\"author\" content=\"Marieke van de Rakt\" class=\"yoast-seo-meta-tag\" />\n<meta name=\"twitter:card\" content=\"summary_large_image\" class=\"yoast-seo-meta-tag\" />\n<meta name=\"twitter:creator\" content=\"@mariekerakt\" class=\"yoast-seo-meta-tag\" />\n<meta name=\"twitter:site\" content=\"@yoast\" class=\"yoast-seo-meta-tag\" />\n<meta name=\"twitter:label1\" content=\"Written by\" class=\"yoast-seo-meta-tag\" />\n\t<meta name=\"twitter:data1\" content=\"Marieke van de Rakt\" class=\"yoast-seo-meta-tag\" />\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" class=\"yoast-seo-meta-tag\" />\n\t<meta name=\"twitter:data2\" content=\"19 minutes\" class=\"yoast-seo-meta-tag\" />\n<script type=\"application/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https://yoast.com/site-structure-the-ultimate-guide/#article\",\"isPartOf\":{\"@id\":\"https://yoast.com/site-structure-the-ultimate-guide/\"},\"author\":{\"name\":\"Marieke van de Rakt\",\"@id\":\"https://yoast.com/#/schema/person/1159f46828cf5ce5fa8e3b89a1af9327\"},\"headline\":\"Site structure: the ultimate guide\",\"datePublished\":\"2023-05-03T11:35:45+00:00\",\"dateModified\":\"2024-08-21T08:37:24+00:00\",\"mainEntityOfPage\":{\"@id\":\"https://yoast.com/site-structure-the-ultimate-guide/\"},\"wordCount\":3920,\"commentCount\":2,\"publisher\":{\"@id\":\"https://yoast.com/#organization\"},\"image\":{\"@id\":\"https://yoast.com/site-structure-the-ultimate-guide/#primaryimage\"},\"thumbnailUrl\":\"https://yoast.com/app/uploads/2020/08/site_structure_ultimate_guide_2400x1350.jpg\",\"keywords\":[\"Internal linking\",\"Site structure\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https://yoast.com/site-structure-the-ultimate-guide/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https://yoast.com/site-structure-the-ultimate-guide/\",\"url\":\"https://yoast.com/site-structure-the-ultimate-guide/\",\"name\":\"Site structure: the ultimate guide &#8226; Yoast\",\"isPartOf\":{\"@id\":\"https://yoast.com/#website\"},\"primaryImageOfPage\":{\"@id\":\"https://yoast.com/site-structure-the-ultimate-guide/#primaryimage\"},\"image\":{\"@id\":\"https://yoast.com/site-structure-the-ultimate-guide/#primaryimage\"},\"thumbnailUrl\":\"https://yoast.com/app/uploads/2020/08/site_structure_ultimate_guide_2400x1350.jpg\",\"datePublished\":\"2023-05-03T11:35:45+00:00\",\"dateModified\":\"2024-08-21T08:37:24+00:00\",\"description\":\"Your site structure is vital for users and SEO. Our complete guide will guide you through all the steps to create a sound site structure.\",\"breadcrumb\":{\"@id\":\"https://yoast.com/site-structure-the-ultimate-guide/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https://yoast.com/site-structure-the-ultimate-guide/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https://yoast.com/site-structure-the-ultimate-guide/#primaryimage\",\"url\":\"https://yoast.com/app/uploads/2020/08/site_structure_ultimate_guide_2400x1350.jpg\",\"contentUrl\":\"https://yoast.com/app/uploads/2020/08/site_structure_ultimate_guide_2400x1350.jpg\",\"width\":2400,\"height\":1350},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://yoast.com/site-structure-the-ultimate-guide/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https://yoast.com/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Site structure\",\"item\":\"https://yoast.com/tag/site-structure/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Site structure: the ultimate guide\"}]},{\"@type\":\"WebSite\",\"@id\":\"https://yoast.com/#website\",\"url\":\"https://yoast.com/\",\"name\":\"Yoast\",\"description\":\"SEO for everyone\",\"publisher\":{\"@id\":\"https://yoast.com/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https://yoast.com/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\",\"copyrightHolder\":{\"@id\":\"https://yoast.com/#organization\"}},{\"@type\":[\"Organization\",\"Brand\"],\"@id\":\"https://yoast.com/#organization\",\"name\":\"Yoast\",\"url\":\"https://yoast.com/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https://yoast.com/#/schema/logo/image/\",\"url\":\"https://yoast.com/app/uploads/2020/09/Yoast_Icon_SocialMedia_500x500.png\",\"contentUrl\":\"https://yoast.com/app/uploads/2020/09/Yoast_Icon_SocialMedia_500x500.png\",\"width\":500,\"height\":500,\"caption\":\"Yoast\"},\"image\":{\"@id\":\"https://yoast.com/#/schema/logo/image/\"},\"sameAs\":[\"https://www.facebook.com/yoast\",\"https://x.com/yoast\",\"https://www.instagram.com/yoast/\",\"https://www.linkedin.com/company/1414157/\",\"https://www.pinterest.com/yoast/\",\"https://www.youtube.com/yoast\",\"https://en.wikipedia.org/wiki/Yoast\",\"https://g.co/kgs/4H5sG2\",\"https://g.co/kgs/a9XfUu\"],\"numberOfEmployees\":116,\"founder\":{\"@type\":\"Person\",\"name\":\"Joost de Valk\",\"sameAs\":\"https://en.wikipedia.org/wiki/Joost_de_Valk\"},\"foundingDate\":\"2010-05-01\",\"slogan\":\"SEO for Everyone\",\"description\":\"Yoast helps you with your website optimization, whether it be through our widely used SEO software or our online SEO courses: we&#039;re here to help.\",\"legalName\":\"Yoast BV\",\"parentOrganization\":{\"@type\":\"Organization\",\"name\":\"Newfold Digital\",\"description\":\"Newfold Digital is a leading web presence solutions provider serving millions of small-to-medium businesses globally.\",\"url\":\"https://newfold.com/\",\"sameAs\":[\"https://newfold.com/\"],\"logo\":\"https://yoast.com/app/uploads/2022/01/newfold-logo.png\"},\"memberOf\":{\"@type\":\"Organization\",\"name\":\"World Wide Web Consortium (W3C)\",\"description\":\"The World Wide Web Consortium (W3C) is an international community that develops open standards to ensure the long-term growth of the Web.\",\"url\":\"https://w3.org/\",\"sameAs\":[\"https://w3.org/\"],\"logo\":\"https://www.w3.org/Icons/w3c_main.png\"}},{\"@type\":\"Person\",\"@id\":\"https://yoast.com/#/schema/person/1159f46828cf5ce5fa8e3b89a1af9327\",\"name\":\"Marieke van de Rakt\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https://yoast.com/#/schema/person/image/\",\"url\":\"https://secure.gravatar.com/avatar/1d83533e299c379140f9fcc2cb0015cb?s=96&d=mm&r=g\",\"contentUrl\":\"https://secure.gravatar.com/avatar/1d83533e299c379140f9fcc2cb0015cb?s=96&d=mm&r=g\",\"caption\":\"Marieke van de Rakt\"},\"description\":\"Marieke was Head of Strategy and former CEO at Yoast. During her time at our company she wrote a lot. Topics that she loves writing about include SEO copywriting, site structure and readability.\",\"sameAs\":[\"https://yoast.com/\",\"https://www.facebook.com/mariekerakt\",\"https://www.instagram.com/mgarakt/\",\"https://www.linkedin.com/in/mariekerakt\",\"https://x.com/mariekerakt\",\"https://marieke.blog/\",\"https://profiles.wordpress.org/mariekerakt/\",\"https://yoast.com/about-us/team/marieke-van-de-rakt/\"],\"honorificSuffix\":\"PhD\",\"gender\":\"female\",\"jobTitle\":\"Head of Strategy\",\"worksFor\":{\"@id\":\"https://yoast.com/#organization\"}}]}</script>\n<!-- / Yoast SEO Premium plugin. -->",
    "json": {
        "title": "Site structure: the ultimate guide &#8226; Yoast",
        "description": "Your site structure is vital for users and SEO. Our complete guide will guide you through all the steps to create a sound site structure.",
        "robots": {
            "index": "index",
            "follow": "follow",
            "max-snippet": "max-snippet:-1",
            "max-image-preview": "max-image-preview:large",
            "max-video-preview": "max-video-preview:-1"
        },
        "canonical": "https://yoast.com/site-structure-the-ultimate-guide/",
        "og_locale": "en_US",
        "og_type": "article",
        "og_title": "Site structure: the ultimate guide",
        "og_description": "Your site structure is vital for users and SEO. Our complete guide will guide you through all the steps to create a sound site structure.",
        "og_url": "https://yoast.com/site-structure-the-ultimate-guide/",
        "og_site_name": "Yoast",
        "article_publisher": "https://www.facebook.com/yoast",
        "article_author": "https://www.facebook.com/mariekerakt",
        "article_published_time": "2023-05-03T11:35:45+00:00",
        "article_modified_time": "2024-08-21T08:37:24+00:00",
        "og_image": [
            {
                "width": 1200,
                "height": 675,
                "url": "https://yoast.com/app/uploads/2020/08/tuesday.png",
                "type": "image/png"
            }
        ],
        "author": "Marieke van de Rakt",
        "twitter_card": "summary_large_image",
        "twitter_creator": "@mariekerakt",
        "twitter_site": "@yoast",
        "twitter_misc": {
            "Written by": "Marieke van de Rakt",
            "Est. reading time": "19 minutes"
        },
        "schema": {
            "@context": "https://schema.org",
            "@graph": [
                {
                    "@type": "Article",
                    "@id": "https://yoast.com/site-structure-the-ultimate-guide/#article",
                    "isPartOf": {
                        "@id": "https://yoast.com/site-structure-the-ultimate-guide/"
                    },
                    "author": {
                        "name": "Marieke van de Rakt",
                        "@id": "https://yoast.com/#/schema/person/1159f46828cf5ce5fa8e3b89a1af9327"
                    },
                    "headline": "Site structure: the ultimate guide",
                    "datePublished": "2023-05-03T11:35:45+00:00",
                    "dateModified": "2024-08-21T08:37:24+00:00",
                    "mainEntityOfPage": {
                        "@id": "https://yoast.com/site-structure-the-ultimate-guide/"
                    },
                    "wordCount": 3920,
                    "commentCount": 2,
                    "publisher": {
                        "@id": "https://yoast.com/#organization"
                    },
                    "image": {
                        "@id": "https://yoast.com/site-structure-the-ultimate-guide/#primaryimage"
                    },
                    "thumbnailUrl": "https://yoast.com/app/uploads/2020/08/site_structure_ultimate_guide_2400x1350.jpg",
                    "keywords": [
                        "Internal linking",
                        "Site structure"
                    ],
                    "inLanguage": "en-US",
                    "potentialAction": [
                        {
                            "@type": "CommentAction",
                            "name": "Comment",
                            "target": [
                                "https://yoast.com/site-structure-the-ultimate-guide/#respond"
                            ]
                        }
                    ]
                },
                {
                    "@type": "WebPage",
                    "@id": "https://yoast.com/site-structure-the-ultimate-guide/",
                    "url": "https://yoast.com/site-structure-the-ultimate-guide/",
                    "name": "Site structure: the ultimate guide &#8226; Yoast",
                    "isPartOf": {
                        "@id": "https://yoast.com/#website"
                    },
                    "primaryImageOfPage": {
                        "@id": "https://yoast.com/site-structure-the-ultimate-guide/#primaryimage"
                    },
                    "image": {
                        "@id": "https://yoast.com/site-structure-the-ultimate-guide/#primaryimage"
                    },
                    "thumbnailUrl": "https://yoast.com/app/uploads/2020/08/site_structure_ultimate_guide_2400x1350.jpg",
                    "datePublished": "2023-05-03T11:35:45+00:00",
                    "dateModified": "2024-08-21T08:37:24+00:00",
                    "description": "Your site structure is vital for users and SEO. Our complete guide will guide you through all the steps to create a sound site structure.",
                    "breadcrumb": {
                        "@id": "https://yoast.com/site-structure-the-ultimate-guide/#breadcrumb"
                    },
                    "inLanguage": "en-US",
                    "potentialAction": [
                        {
                            "@type": "ReadAction",
                            "target": [
                                "https://yoast.com/site-structure-the-ultimate-guide/"
                            ]
                        }
                    ]
                },
                {
                    "@type": "ImageObject",
                    "inLanguage": "en-US",
                    "@id": "https://yoast.com/site-structure-the-ultimate-guide/#primaryimage",
                    "url": "https://yoast.com/app/uploads/2020/08/site_structure_ultimate_guide_2400x1350.jpg",
                    "contentUrl": "https://yoast.com/app/uploads/2020/08/site_structure_ultimate_guide_2400x1350.jpg",
                    "width": 2400,
                    "height": 1350
                },
                {
                    "@type": "BreadcrumbList",
                    "@id": "https://yoast.com/site-structure-the-ultimate-guide/#breadcrumb",
                    "itemListElement": [
                        {
                            "@type": "ListItem",
                            "position": 1,
                            "name": "Home",
                            "item": "https://yoast.com/"
                        },
                        {
                            "@type": "ListItem",
                            "position": 2,
                            "name": "Site structure",
                            "item": "https://yoast.com/tag/site-structure/"
                        },
                        {
                            "@type": "ListItem",
                            "position": 3,
                            "name": "Site structure: the ultimate guide"
                        }
                    ]
                },
                {
                    "@type": "WebSite",
                    "@id": "https://yoast.com/#website",
                    "url": "https://yoast.com/",
                    "name": "Yoast",
                    "description": "SEO for everyone",
                    "publisher": {
                        "@id": "https://yoast.com/#organization"
                    },
                    "potentialAction": [
                        {
                            "@type": "SearchAction",
                            "target": {
                                "@type": "EntryPoint",
                                "urlTemplate": "https://yoast.com/?s={search_term_string}"
                            },
                            "query-input": {
                                "@type": "PropertyValueSpecification",
                                "valueRequired": true,
                                "valueName": "search_term_string"
                            }
                        }
                    ],
                    "inLanguage": "en-US",
                    "copyrightHolder": {
                        "@id": "https://yoast.com/#organization"
                    }
                },
                {
                    "@type": [
                        "Organization",
                        "Brand"
                    ],
                    "@id": "https://yoast.com/#organization",
                    "name": "Yoast",
                    "url": "https://yoast.com/",
                    "logo": {
                        "@type": "ImageObject",
                        "inLanguage": "en-US",
                        "@id": "https://yoast.com/#/schema/logo/image/",
                        "url": "https://yoast.com/app/uploads/2020/09/Yoast_Icon_SocialMedia_500x500.png",
                        "contentUrl": "https://yoast.com/app/uploads/2020/09/Yoast_Icon_SocialMedia_500x500.png",
                        "width": 500,
                        "height": 500,
                        "caption": "Yoast"
                    },
                    "image": {
                        "@id": "https://yoast.com/#/schema/logo/image/"
                    },
                    "sameAs": [
                        "https://www.facebook.com/yoast",
                        "https://x.com/yoast",
                        "https://www.instagram.com/yoast/",
                        "https://www.linkedin.com/company/1414157/",
                        "https://www.pinterest.com/yoast/",
                        "https://www.youtube.com/yoast",
                        "https://en.wikipedia.org/wiki/Yoast",
                        "https://g.co/kgs/4H5sG2",
                        "https://g.co/kgs/a9XfUu"
                    ],
                    "numberOfEmployees": 116,
                    "founder": {
                        "@type": "Person",
                        "name": "Joost de Valk",
                        "sameAs": "https://en.wikipedia.org/wiki/Joost_de_Valk"
                    },
                    "foundingDate": "2010-05-01",
                    "slogan": "SEO for Everyone",
                    "description": "Yoast helps you with your website optimization, whether it be through our widely used SEO software or our online SEO courses: we&#039;re here to help.",
                    "legalName": "Yoast BV",
                    "parentOrganization": {
                        "@type": "Organization",
                        "name": "Newfold Digital",
                        "description": "Newfold Digital is a leading web presence solutions provider serving millions of small-to-medium businesses globally.",
                        "url": "https://newfold.com/",
                        "sameAs": [
                            "https://newfold.com/"
                        ],
                        "logo": "https://yoast.com/app/uploads/2022/01/newfold-logo.png"
                    },
                    "memberOf": {
                        "@type": "Organization",
                        "name": "World Wide Web Consortium (W3C)",
                        "description": "The World Wide Web Consortium (W3C) is an international community that develops open standards to ensure the long-term growth of the Web.",
                        "url": "https://w3.org/",
                        "sameAs": [
                            "https://w3.org/"
                        ],
                        "logo": "https://www.w3.org/Icons/w3c_main.png"
                    }
                },
                {
                    "@type": "Person",
                    "@id": "https://yoast.com/#/schema/person/1159f46828cf5ce5fa8e3b89a1af9327",
                    "name": "Marieke van de Rakt",
                    "image": {
                        "@type": "ImageObject",
                        "inLanguage": "en-US",
                        "@id": "https://yoast.com/#/schema/person/image/",
                        "url": "https://secure.gravatar.com/avatar/1d83533e299c379140f9fcc2cb0015cb?s=96&d=mm&r=g",
                        "contentUrl": "https://secure.gravatar.com/avatar/1d83533e299c379140f9fcc2cb0015cb?s=96&d=mm&r=g",
                        "caption": "Marieke van de Rakt"
                    },
                    "description": "Marieke was Head of Strategy and former CEO at Yoast. During her time at our company she wrote a lot. Topics that she loves writing about include SEO copywriting, site structure and readability.",
                    "sameAs": [
                        "https://yoast.com/",
                        "https://www.facebook.com/mariekerakt",
                        "https://www.instagram.com/mgarakt/",
                        "https://www.linkedin.com/in/mariekerakt",
                        "https://x.com/mariekerakt",
                        "https://marieke.blog/",
                        "https://profiles.wordpress.org/mariekerakt/",
                        "https://yoast.com/about-us/team/marieke-van-de-rakt/"
                    ],
                    "honorificSuffix": "PhD",
                    "gender": "female",
                    "jobTitle": "Head of Strategy",
                    "worksFor": {
                        "@id": "https://yoast.com/#organization"
                    }
                }
            ]
        }
    },
    "status": 200
}

Surfaces Api

在 Yoast SEO 14.0 中,我们引入了一种将 Yoast SEO 集成到您代码中的正式方法。我们添加了一个称为表面的功能,名为 YoastSEO()。这个表面让您能轻松访问 Yoast SEO 提供的众多功能。

轻松获取当前页面的 SEO 数据

当前页面的所有 SEO 数据都可以通过我们的 surface 轻松访问。

例如,以下代码将页面标题作为变量提供:

$title = YoastSEO()->meta->for_current_page()->title;

此代码直接输出页面的元描述:

echo YoastSEO()->meta->for_current_page()->description;

此代码输出当前页面的预计阅读时间。

echo (string) YoastSEO()->meta->for_current_page()->estimated_reading_time_minutes, " minutes";

current_page surface 暴露了我们拥有的关于当前页面的所有数据,它们的工作方式都相同;这是一个很长的列表:

Variable Type Description
canonical string The canonical URL for the current page.
description string The meta description for the current page, if set.
title string The SEO title for the current page.
id string The requested object ID.
site_name string The site name from the Yoast SEO settings.
wordpress_site_name string The site name from the WordPress settings.
site_url string The main URL for the site.
company_name string The company name from the Knowledge Graph settings.
company_logo_id int The attachment ID for the company logo.
site_user_id int If the site represents a 'person', this is the ID of the accompanying user profile.
site_represents string Whether the site represents a 'person' or a 'company'.
site_represents_reference array false
breadcrumbs_enabled bool Whether breadcrumbs are enabled or not.
schema_page_type string The Schema page type.
main_schema_id string Schema ID that points to the main Schema thing on the page, usually the webpage or article Schema piece.
page_type string The Schema page type.
meta_description string The meta description for the current page, if set.
robots array An array of the robots values set for the current page.
googlebot array The meta robots values we specifically output for Googlebot on this page.
rel_next string The next page in the series, if any.
rel_prev string The previous page in the series, if any.
open_graph_enabled bool Whether OpenGraph is enabled on this site.
open_graph_publisher string The OpenGraph publisher reference.
open_graph_type string The og:type.
open_graph_title string The og:title.
open_graph_description string The og:description.
open_graph_images array The array of images we have for this page.
open_graph_url string The og:url.
open_graph_site_name string The og:site_name.
open_graph_article_publisher string The article:publisher value.
open_graph_article_author string The article:author value.
open_graph_article_published_time string The article:published_time value.
open_graph_article_modified_time string The article:modified_time value.
open_graph_locale string The og:locale for the current page.
schema array The entire Schema array for the current page.
twitter_card string The X card type for the current page.
twitter_title string The X card title for the current page.
twitter_description string The X card description for the current page.
twitter_image string The X card image for the current page.
twitter_creator string The X card author for the current page.
twitter_site string The X card site reference for the current page.
source array The source object for most of this page data.
breadcrumbs array The breadcrumbs array for the current page.
estimated_reading_time_minutes int The estimated reading time in minutes for the content.
post_author string The name of the post author

无论你需要的是OpenGraph描述还是robots数组,这里都已涵盖。请习惯打开你最喜欢的IDE,输入YoastSEO()->meta->for_current_page()->,然后查看类型提示以获取你所需的精确数据片段。

已弃用的属性

变量名 类型 描述 弃用版本
open_graph_fb_app_id string Facebook 应用 ID。 Yoast SEO 15.5 (2020年12月)

对于其他页面

获取任何页面的数据,其工作方式与获取当前页面数据几乎完全相同。您只需要提供一个 ID 或一个 URL。

例如,获取 ID 为 2 的文章的规范 URL 值。

YoastSEO()->meta->for_post( 2 )->canonical;

例如,获取 URL 为 https://www.example.com/example-page/ 的页面的标题:

YoastSEO()->meta->for_url( 'https://www.example.com/example-page/' )->title;

注意:如果某个 URL 在我们的 Indexables 表中不存在,则此方法将返回 false。

访问我们的辅助函数

有时您需要的不仅仅是页面的原始 SEO 数据。例如,您可能需要知道当前文章类型是否应该被索引。我们的 post_type 辅助函数可以解决这个问题:

YoastSEO()->helpers->post_type->is_indexable( get_post_type() );

这将返回一个简单的 布尔值。

如果您想要获取可索引的文章类型列表,应该使用:

$public_post_types = YoastSEO()->helpers->post_type->get_public_post_types();

同样的方法也适用于分类法:

YoastSEO()->helpers->taxonomy->is_indexable( 'category' );

我们提供了相当多的这类辅助函数,并非所有函数对您都同样有用。但请在您的 IDE 中查看我们提供了哪些函数,这同样是一个相当长的列表!

Using Apis Classes

Yoast SEO ships with a variety of APIs and classes that you can utilize in your own code. However, there are a few things you need to keep in mind when you plan on using these APIs and classes.

Class <class name> doesn't exist

This usually happens whenever you try to use one of the Yoast SEO classes before it has been made available to PHP / WordPress. This is, in part, due to the loading order within WordPress. To overcome this, you should load your plugin by hooking into the proper hook. An example of this, is as follows:

class MyCustomPlugin {
 // Your custom plugin code.
}

function loadMyCustomPlugin() {
	new MyCustomPlugin();
}

if ( ! wp_installing() ) {
	add_action( 'plugins_loaded', 'loadMyCustomPlugin' );
}

Because the above example hooks into the plugins_loaded hook, WordPress has completedly loaded all activated plugins and our classes are then freely available to use in your own code.

Extending YoastSEO.js' dataset

One of the other APIs we ship, is that of YoastSEO.js. If you're attempting to extend the data that gets passed through this library, make sure you check out the dedicated page we've written for it.

Changing Location Post Type

自 13.2 版本起,您或您的开发者可以使用 wpseo_local_post_type 过滤器来更改默认的地点文章类型。此过滤器接受一个参数:$post_type(必需)。 当您为地点定义了自定义文章类型时,此过滤器尤其有用。

:::caution

如果更改文章类型,请确保重新索引 Yoast SEO 的可索引内容。

:::

在您的自定义插件或主题的 functions.php 文件中,添加以下代码行,并将默认值更改为您所需的文章类型。

/** 
 * 更改用于地点的 Yoast SEO: Local 文章类型。
 *
 * @param string $post_type 我们要替换的文章类型。
 *
 * @return string 我们的地点文章类型。
 */
function my_custom_locations_post_type( $post_type ) {
    return 'my_custom_locations_post_type';
}

add_filter( 'wpseo_local_post_type', 'my_custom_locations_post_type', 10, 1 );

Changing Location Url Google Maps

Yoast SEO: Local and Yoast SEO: Local SEO for WooCommerce include the ability to add a Google map for your location(s). The URL in the map, by default, points to the Yoast SEO location page on your website. In some cases, you may prefer to link to a different URL.

As of version 6.2, you or your developer can change the URL with the yoast_seo_local_change_map_location_url filter. This filter takes 2 params: $url (required) and $location_id.

You will find an example of how to use the filter below.

Single location

add_filter( 'yoast_seo_local_change_map_location_url', 'yoast_seo_local_change_map_location_url', 10, 1 );

/**
 * Changes the location of the Google Maps URLs for your location.
 *
 * @param string $url The URL we're replacing.
 *
 * @return string The maps URL.
 */
function yoast_seo_local_change_map_location_url( $url ) {
   return 'https://www.yoast.com';
}

Multiple locations

add_filter( 'yoast_seo_local_change_map_location_url', 'yoast_seo_local_change_map_location_url', 10, 2 );

/**
 * Changes the location of the Google Maps URLs for your location, per location.
 *
 * @param string $url         The URL we're replacing.
 * @param int    $location_id The ID of the location we're changing the map for.
 *
 * @return string The maps URL.
 */
function yoast_seo_local_change_map_location_url( $url, $location_id ) {
    // Change this to the post ID you want to match against.
    if ( $location_id === 1 ) {
        $url = 'https://www.yoast.com';
    }

    return $url;
}

Changing Organization Url In Schema

自 13.9 版本起,您或您的开发者可以使用 yoast-local-seo-schema-organization-url 过滤器来更改 Schema 中 Organization 部分的默认 URL。此过滤器接受一个参数:$url(必需)。 当您组织的 URL 与网站首页 URL 不匹配时,此过滤器尤其有用。

在您的自定义插件或主题的 functions.php 文件中,添加以下代码行,并将默认值更改为您所需的 URL。

/** 
 * 更改 Schema 中主要 Organization 部分的 URL。
 *
 * @param string $url 我们要替换的 URL。
 *
 * @return string Schema 中 Organization 部分的 URL。
 */
function my_custom_organization_url( $url ) {
    return 'https://mycustomurl.tld';
}

add_filter( 'yoast-local-seo-schema-organization-url', 'my_custom_organization_url', 10, 1 );

Enhancing Search Results

Local SEO 插件的一项隐藏功能是使用您的位置内容增强网站的内部搜索结果。在极少数情况下,增强的搜索结果可能会与其他插件或主题功能产生冲突。因此,Local SEO 7.1 版本引入了开发者过滤器来禁用这些增强功能。

这些过滤器应放置在您主题的 function.php 文件中。如果您不确定该文件的位置,请咨询您的主题开发者。

我们建议在添加过滤器或编辑主题文件之前,先对您的网站进行备份。

过滤器接受布尔值 true 或 false。

要禁用对位置元字段的搜索,请使用:

add_filter( 'yoast_local_seo_enhanced_search_enabled', '__return_false' );

要同时禁用对已找到位置的标记,请使用:

add_filter( 'yoast_local_seo_enhanced_search_result_enabled', '__return_false' );

Subscription Api

Yoast 合作伙伴可使用订阅 API 在 MyYoast 中创建订阅。订阅有助于通过 Yoast 插件的许可证检查。

:::tip 若您希望成为 Yoast 合作伙伴,请联系 partners@yoast.com。 :::

我们提供 Swagger 文档供您探索 API,可提供查看凭证。

配置器

配置器是获准在 MyYoast 中创建订阅的 Yoast 合作伙伴。

我们为每位配置器创建一个账户,配置器可使用该账户的凭证调用 API 端点。

该 API 使用基本身份验证。如果您是配置器但尚未获得调用配置 API 的适当凭证,请联系我们。

客户端 API

为协助配置人员,我们使用 swagger-codegen 为 API 生成了客户端。现有:

这些客户端的自述文件说明了如何安装和使用它们。

订阅

订阅为客户的指定站点提供插件在特定时间段内的有效许可。

订阅状态

订阅可能存在以下五种状态:

创建订阅

您可以使用创建端点来创建新订阅。

创建订阅时,您应同时发送客户希望将其订阅连接到的网站。订阅将作为其连接网站的有效许可证。

如果您知道客户的名字和姓氏,也应一并发送。我们会在沟通中使用这些姓名来称呼客户。

创建订阅时,如果客户尚未拥有账户,系统将为其创建一个 MyYoast 账户。通过此账户,客户可以访问 Yoast SEO Academy 及其高级内容。 随后我们将为客户创建订阅,并将此新订阅作为 API 调用的响应返回。 如果客户账户是新创建的,客户还将收到一封电子邮件,通知他们已为其创建账户,并且可以设置密码。

如果订阅应授予用户访问课程的权限(例如 Yoast SEO Premium 订阅),这些课程可通过 MyYoast 的课程标签页访问。

续订订阅

您可以使用续订端点来续订现有订阅。

续订订阅会延长订阅期限。延长的时长取决于产品类型。 大多数产品提供一年的访问权限。部分产品提供一个月访问权限。这已为您的供应账户配置或即将配置。

续订订阅会将订阅的结束日期设置为未来的一个新日期。 如果订阅的结束日期已过,则额外时长将从请求日期开始计算。

已取消或已退款的订阅无法再续订。

使订阅许可证失效

有三种主动方式可使订阅失效:

取消订阅

若已知客户不会续订订阅,或客户不应再持有订阅,则应使用取消端点。

该订阅的状态将被设为 pending-cancel。在截止日期前它仍是有效许可,之后将变为 cancelled。

若需客户立即失去访问权限,可在请求体中设置 immediately 为 true。此时订阅将立即设为 cancelled 状态,无论剩余订阅时长多少。处于 cancelled 状态的订阅不再被视为有效许可。

已取消的订阅无法再次续订。

订阅退款

如需标记客户购买已退款,应使用退款端点。 对订阅执行退款操作意味着相关授权将立即失效。

已退款的订阅无法续期。

订阅过期与取消

若订阅的结束日期已过,该订阅即将过期。

若订阅状态为 active,其状态将被设为 expired,且不再视为有效许可。此操作每 30 分钟自动执行一次。

对于状态为 pending-cancel 且结束日期已过的订阅,同样适用。此类订阅将被设为 cancelled。

许可证检查

客户网站上的 Yoast 插件在访问插件页面或高级设置页面时,会发送请求以检查该站点是否拥有该插件的有效许可证。此请求会被缓存。

当站点在 MyYoast 中拥有任何授予该产品访问权限的 active(激活)或 pending-cancel(待取消)订阅时,即视为对该产品拥有有效许可证。

许可证检查的响应将始终包含用户有权访问的插件最新版本的下载 URL。

将网站关联至订阅。

您可以在创建订阅时关联网站,或使用 API 的 set-site 端点。

获取插件

客户获得订阅后,即可访问关联产品。

客户有权访问的任何插件均可下载。订阅 API 的任何响应中始终包含该订阅的下载链接。

若订阅包含课程访问权限(例如 Yoast SEO Premium 插件订阅),用户可通过 MyYoast 的课程页面访问这些课程。

当客户获得课程访问权限时,我们也会发送邮件通知。若客户同时获得多门课程访问权限,将发送单封邮件列出所有可访问课程。

Hooks

Yoast News SEO 插件提供了一些可用于修改其功能的钩子,具体如下:

头部标签

Yoast\WP\News\head

此操作允许在输出头部标签前运行额外的代码。

Yoast\WP\News\head_display_noindex

参数:$display_noindex (布尔值), $post (WP_Post)

此过滤器允许阻止为特定文章输出 noindex 元标签。

站点地图

wpseo_news_sitemap_content

参数:$str (字符串)

此过滤器允许向新闻 XML 站点地图添加额外内容。 $str 中添加的输出默认为空字符串。

Yoast\WP\News\publication_language

参数:$publication_lang (字符串), $item (Indexable)

此过滤器允许更改新闻 XML 站点地图中特定文章的发布语言。

Yoast\WP\News\sitemap_name

参数:$sitemap_name (字符串)

此过滤器允许更改新闻 XML 站点地图的名称。

Yoast\WP\News\skip_build_item

参数:$skip_build_item (布尔值), $item_id (整数)

此过滤器允许跳过在新闻 XML 站点地图中构建特定项目。

Hooks

Yoast Video SEO 插件提供了一些可用于修改其功能的钩子,具体如下:

输出

wpseo_video_{$type}_details

参数:$vid (数组), $post (WP_Post)

此过滤器允许更改视频嵌入识别功能的输出。 $vid 数组至少包含 player_loc 或 content_loc、thumbnail_loc 和 type。 您需要知道类型才能过滤输出,但您可以使用此过滤器来添加例如时长信息。

wpseo_video_family_friendly

参数:$family-friendly (布尔值), $post_id (整数)

此过滤器允许更改视频的家庭友好状态。

wpseo_video_rating

参数:$rating (浮点数), $post_id (整数)

此过滤器允许更改视频的评分。评分是介于 0 到 5 之间的值。

wpseo_video_item

参数:$str (字符串), $post_id (整数)

此过滤器允许向视频 XML 站点地图添加额外内容。 添加到 $str 中的输出默认为空字符串。

wpseo_video_yandex_support

参数:$yandex_on (布尔值)

此过滤器允许禁用视频 XML 站点地图中特定于 Yandex 的元标签。

输入

wpseo_video_index_content

参数:$content (字符串), $vid (数组), $post (WP_Post)

允许在内容被索引之前更改文章内容。 如果输入内容需要修改、添加内容等,此过滤器非常有用。

Adding Custom Assessments

除了在 Yoast SEO 已有的评估中添加自定义输入字段外,您还可以编写自己的评估来分析文章、页面或术语的各个方面。

注意: 本示例将使用 ES6 语法,如果您尚未使用 ES6 或未使用 Babel 或 Webpack 等编译器,请确保调整代码以适应您的语法。

基础概念

编写自定义评估的首要步骤之一是确保 Yoast SEO 能够识别其存在。实现这一目标的方法是编写自定义评估、一个能够接入 Yoast SEO 的 Web Worker,以及一些额外的代码将它们整合起来。以下步骤将引导您了解创建此代码的基础知识。

Creating the plugin

First, create a file in your own plugin's js/ directory (i.e. js/MyCustomAssessmentPlugin.js).

In this file, you'll have to ensure that, at a minimum, the following is present:

/* global YoastSEO: true, myCustomAssessmentPluginL10n */

class MyCustomAssessmentPlugin {
    /**
     * The constructor.
     */
	constructor() {
		// Ensure YoastSEO.js is present and can access the necessary features.
		if ( typeof YoastSEO === "undefined" || typeof YoastSEO.analysis === "undefined" || typeof YoastSEO.analysis.worker === "undefined" ) {
			return;
		}

		this.registerWorker();
	}

	/**
	 * Registers the custom worker.
	 *
	 * @returns {void}
	 */
	registerWorker() {
		const worker = YoastSEO.analysis.worker;

		worker.loadScript( myCustomAssessmentPluginL10n.script_url ).then(
			() => worker.sendMessage( "initialize", myCustomAssessmentPluginL10n, "MyCustomAssessmentPlugin" )
		);
	}
}

/**
 * Adds eventListener on page load to load the Yoast Extended.
 */
if ( typeof YoastSEO !== "undefined" && typeof YoastSEO.analysis !== "undefined" ) {
	new MyCustomAssessmentPlugin();
}
else {
	jQuery( window ).on(
		'YoastSEO:ready',
		function() {
			new MyCustomAssessmentPlugin();
		}
	);
}

The above code checks whether all necessary Yoast SEO code is available and registers our custom worker and code to be used within Yoast SEO.

Note: The last part in the above JavaScript is used to properly load the JavaScript class after YoastSEO.js is done initializing. Please make sure you don't forget to add this.

Next, we'll create the webworker itself. In js/, create a file named CustomWorker.js and add the following code:

/* global analysisWorker */
import CustomAssessment from "./CustomAssessment";

class CustomWorker {
	/**
	 * Constructs a worker to be run inside the analysis web worker.
	 */
	constructor() {
		this._worker = analysisWorker;
	}

    /**
     * Registers the worker.
     */
	register() {
		this._worker.registerMessageHandler( "initialize", this.initialize.bind( this ), "MyCustomAssessmentPlugin" );
	}

    /**
     * Initializes the worker.
     *
     * @param {Object} settings The settings to pass along to the assessment.
     */
	initialize( settings ) {
		this.customAssessment = new CustomAssessment( settings );

		this._worker.registerAssessment( "customAssessment", this.customAssessment, "MyCustomAssessmentPlugin" );
	}
}

const customWorker = new CustomWorker();

customWorker.register();

What this code does, is hook into the webworkers messaging system and registers itself. Additionally, it takes our custom assessment (which we'll create later on) and registers it as well. Finally, we create a new instance of the class and immediately call the register method as we don't need to do any kind of conditional checks.

Finally, we'll create the assessment itself. In js/, create a file named CustomAssessment.js and add the following code:

import { AssessmentResult, Assessment } from 'yoastseo';

export default class CustomAssessment extends Assessment {
	/**
	 * The constructor.
	 *
	 * @param {Object} settings The settings to pass along.
	 */
	constructor( settings ) {
		super();
		this.settings = settings;
	}

	/**
	 * Executes the assessment and return its result.
	 *
	 * @param {Paper}      paper      The paper to run this assessment on.
	 * @param {Researcher} researcher The researcher used for the assessment.
	 * @param {Object}     i18n       The i18n-object used for parsing translations.
	 *
	 * @returns {AssessmentResult} The result of the assessment.
	 */
	getResult( paper, researcher, i18n ) {

		// Check for the occurance of the word Yoast in the title.
		const title = paper.getTitle();
		const matches = (title.match(/Yoast/gi) || []).length;

		const assessmentResult = new AssessmentResult();
		const { score, text } = this.score( matches );

		assessmentResult.setScore( score ) ;
		assessmentResult.setText( text );

		return assessmentResult;
	}

	/**
	 * Determines the score based on the amount of matches.
	 *
	 * @param {number} matches The amount of matches.
	 *
	 * @returns {Object} The score object.
	 */
	score( matches ) {

		if ( matches === 0 ) {
			return{
				score: 0,
				text: "Yoast isn't present in your title",
			};
		}

		if ( matches >= 5 ) {
			return{
				score: -10,
				text: "Yoast occurs too many times in your title",
			};
		}

		return{
			score: 50,
			text: "Yoast is present in your title",
		};
	}
}

The above code is the actual assessment itself. We take an instance of the Paper class, which then gets passed through the webworker that analyzes the Paper's title property. Within this title, we look for the word 'Yoast' and count the amount of instances that we can find. Based on this, we return a score (which determines whether a bullet is grey, red, orange or green) and a feedback message so the user knows why the score is what it is.

Registering the plugin with WordPress

Next, we'll have to make sure our custom JavaScript gets loaded. To achieve this, we need to make sure that our custom plugin is registered and loaded by WordPress. The main importance is that the above JavaScript file loads at the right time. Therefor, it's necessary to implement a chain of hooks that'll run once WordPress loads your plugin.

Assuming you have a plugin file present (i.e. MyCustomPlugin.php), your file might look a little something like this:

/**
 * Plugin Name: My Custom Plugin
 */

class MyCustomPlugin {

	/**
	 * MyCustomPlugin constructor.
	 */
	public function __construct() {
		// ...
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
	}

	/**
	 * Enqueues the plugin file.
     * 
     * @return void
	 */
	public function enqueue_scripts() {
		global $pagenow;

		if ( ( $pagenow == 'post.php' ) || ( get_post_type() == 'post' ) ) {
			wp_enqueue_script( 'my-custom-assessment-plugin', plugins_url( 'dist/my-custom-assessment.min.js', __FILE__ ), [], '1.0', true );
			wp_localize_script( 'my-custom-assessment-plugin', 'myCustomAssessmentPluginL10n', $this->localize_extended_script() );
		}
	}

	/**
	 * Localizes a set of data to be used in JavaScript.
	 *
	 * @return array The data to localize.
	 */
	public function localize_extended_script() {
		return [
			'script_url'  => plugins_url( 'dist/my-custom-assessment-worker.min.js', __FILE__ ),
		];
	}
}

/**
 * Loads the plugin.
 */
function loadMyCustomPlugin() {
	new MyCustomPlugin();
}

if ( ! wp_installing() ) {
	add_action( 'plugins_loaded', 'loadMyCustomPlugin', 20 );
}

测试代码

最后一步是测试代码。请确保您的插件已安装并启用,然后创建一篇新文章。 打开 SEO 分析,查看分析结果中是否显示“标题中未包含 Yoast”的提示信息。现在将 Yoast 添加到文章标题中,观察分析结果的变化。

Adding Custom Data Analysis

Yoast SEO 配备了一套强大的工具,不仅能帮助您提升 SEO,还能通过分析您正在处理的文章或页面内容来帮助您写出更好的文本。

在某些情况下,插件开发者可能希望向分析中添加一些额外数据,他们认为这些数据对其整体可读性和 SEO 评分有额外的重要性。Yoast SEO 允许这种分析的扩展。

这对于那些以我们的插件可能无法原生检测的方式(例如通过自定义数据结构、函数和短代码)添加页面内容的插件也很有用,他们希望我们将这些内容纳入分析。

注意: 在本示例中,我们将使用 ES6 语法,因此如果您尚未使用 ES6 或不使用像 Babel 或 Webpack 这样的编译器,请确保调整代码以适应您自己的语法。

基础概念

在将自定义数据添加到分析之前,需要确保 Yoast SEO 能够识别这些数据的存在。实现这一目标的方法是编写自定义 JavaScript 插件。以下步骤将引导您了解创建此类插件的基本原理。

Creating the plugin

First, create a file in your own plugin's js/ directory (i.e. js/MyCustomDataPlugin.js).

In this file, you'll have to ensure that, at a minimum, the following is present:

/* global YoastSEO */

class MyCustomDataPlugin {
    constructor() {
       // Ensure YoastSEO.js is present and can access the necessary features.
       if ( typeof YoastSEO === "undefined" || typeof YoastSEO.analysis === "undefined" || typeof YoastSEO.analysis.worker === "undefined" ) {
            return;
        }

        YoastSEO.app.registerPlugin( "MyCustomDataPlugin", { status: "ready" } );
        
        this.registerModifications();
    }

    /**
	 * Registers the addContent modification.
	 *
	 * @returns {void}
	 */
	registerModifications() {
		const callback = this.addContent.bind( this );

        // Ensure that the additional data is being seen as a modification to the content.
		YoastSEO.app.registerModification( "content", callback, "MyCustomDataPlugin", 10 );
	}

	/**
	 * Adds to the content to be analyzed by the analyzer.
	 *
	 * @param {string} data The current data string.
	 *
	 * @returns {string} The data string parameter with the added content.
	 */
	addContent( data ) {
        data += "Hello, I'm some additional data!";

		return data ;
	}
}

/**
 * Adds eventlistener to load the plugin.
 */
if ( typeof YoastSEO !== "undefined" && typeof YoastSEO.app !== "undefined" ) {
  new MyCustomDataPlugin();
} else {
  jQuery( window ).on(
    "YoastSEO:ready",
    function() {
      new MyCustomDataPlugin();
    }
  );
}

The above code adds "Hello, I'm some additional data!" to the text analysis, but you're not limited to just sending hard-coded strings to your custom plugin. For example, you could add a custom input field and read its contents.

Note: The last part in the above JavaScript is used to properly load the JavaScript class after YoastSEO.js is done initializing. Please make sure you don't forget to add this.

Registering the plugin with WordPress

The next step to make things work, is to make sure your JavaScript code is properly loaded. To achieve this, we'll have to ensure our plugin is properly registered and loaded by WordPress. The main importance is that the above JavaScript file is loaded at the right time. Therefor, it's necessary to implement a chain of hooks that'll run once WordPress loads your plugin.

Assuming you have a plugin file present (i.e. MyCustomPlugin.php), your file might look a little something like this:

<?php
// ...
class MyCustomPlugin {

    /**
     * MyCustomPlugin constructor.
     */
    public function __construct() {
        // ...
        add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
    }
    
    /** 
     * Enqueues the plugin file.
     */
    public function enqueue_scripts() {
        wp_enqueue_script( 'my-custom-plugin', plugins_url( 'js/MyCustomDataPlugin.js', __FILE__ ), [], '1.', true );
    }

}

/** 
 * Loads the plugin.
 */
function loadMyCustomPlugin() {
    new MyCustomPlugin();
}

if ( ! wp_installing() ) {
	add_action( 'plugins_loaded', 'loadMyCustomPlugin', 20 );
}

测试代码

最后一步是测试代码。请确保您的插件已正确加载并创建一篇新文章。 作为测试,您可以将关键词设置为“additional”,并查看 SEO 分析是否在“引言中的关键短语”结果中正确检测到它。

Adding Custom Language Analysis

我们的软件支持多种语言。如果您希望添加自定义语言分析,分析文件需要在 window 对象上可用。有两种方法可以引入正确的语言分析 JS 文件:

  1. 自行创建自定义语言文件并将其分配给 window 对象。例如:window.yoast.researcher = specific researcher;
  2. 按照我们的方式配置 webpack;这样 webpack 会为您生成文件。

Changing Enhanced Slack Sharing

As of Yoast SEO 15.2 we've added the option to enhance the way URLs are displayed when they're shared on Slack. For more info on that feature, please see the help page: enhanced Slack sharing.

The data shown is a set of two labels with attached data, which will be output as string. You can filter this data by simply filtering the array through the wpseo_enhanced_slack_data filter.

For example:

class Yoast_SEO_Enhance_Slack_Data {

	/**
	 * Class constructor
	 */
	public function __construct() {
		add_filter( 'wpseo_enhanced_slack_data', [ $this, 'generate_slack_data' ], 10, 2 );
	}

	/**
	 * Change Enhanced Slack sharing data labels.
	 *
	 * @param array                  $data         The Slack labels + data.
	 * @param Indexable_Presentation $presentation The indexable presentation object.
	 *
	 * @return array The Slack labels + data.
	 */
	public function generate_slack_data( array $data, Indexable_Presentation $presentation ) {
		if ( is_singular( 'jobs' ) ) {
			$hours    = get_post_meta( $presentation->model->object_id, 'jobs_hours', true );
			$location = get_post_meta( $presentation->model->object_id, 'jobs_location', true );
			$data = [
				'Location' => $location,
				'Hours'    => $hours,
			];
		}

		return $data;
	}
}

Customizing Attachment Parsing

Yoast SEO 有多种方式处理文章中添加的附件。我们添加了多个过滤器以确保在各种场景下的性能。

场景

Not so many attachments < 10.000

The normal implementation which implements a file scan with WP_HTML_Tag_Processor is most likely performant enough.

A huge amount of attachments > 75.000 and a fast disk

The normal implementation which implements a file scan with WP_HTML_Tag_Processor is most likely performant enough.

附件数量巨大(超过 75,000 个)且磁盘速度慢

如果在保存文章时遇到性能问题,以下过滤器可以帮助加速处理过程。

add_filter( 'wpseo_indexable_forced_included_post_types', function( $post_types ) {
    $post_types[] = 'attachment';
    return $post_types;
} );
add_action( 'init', function( $post_types ) {
    add_filter( 'wpseo_indexable_excluded_post_types', function( $post_types ) {
       $filtered_post_types = [];
       foreach ( $post_types as $post_type ) {
          if ( $post_type !== 'attachment' ) {
             $filtered_post_types[] = $post_type;
          }
       }
       return $filtered_post_types;
    } );
} );
add_filter('wpseo_force_creating_and_using_attachment_indexables', '__return_true'  );

使用这组过滤器组合,系统可以为附件创建索引对象,而无需同时启用媒体归档页面。首次启用这些过滤器时,您需要在工具页面运行 SEO 优化。 此场景中使用的过滤器在 Indexables: Filters 中有详细说明。

大量附件超过 75,000 个,内容扫描会降低性能。

如果新的内容扫描解决方案或可索引对象解决方案导致性能下降,也可以选择恢复使用正则表达式。

add_filter('wpseo_force_skip_image_content_parsing', '__return_true'  );

当使用的图片或相册实现与 WordPress 的 HTML 结构不同时

某些相册和图片插件使用的 HTML 标记与 WordPress 不同。我们的内容扫描实现依赖于在图片标签的类中找到图片 ID。 有两个过滤器允许用户更改此设置,以支持不同类型的标记。以下示例基于 envira-gallery 插件。该插件使用 id 字段结合 envira-gallery-image-<image_ID> 格式,而不是类字段。

add_filter( 'wpseo_image_attribute_containing_id', 'image_attribute_containing_id', 10 );

/**
 * 过滤器 'wpseo_image_attribute_containing_id' - 允许过滤用于从图片标签中提取图片 ID 的属性。
 *
 * 默认为 "class",这是 WordPress 原生存储图片 ID 的位置,格式为 `wp-image-<ID>`。
 *
 * @api string 用于提取图片 ID 的属性。
 */
function image_attribute_containing_id() {
    return 'id';
}

add_filter( 'wpseo_extract_id_pattern', 'extract_id_pattern', 10 );

/**
 * 过滤器 'wpseo_extract_id_pattern' - 允许过滤用于从类/属性名称中提取图片 ID 的正则表达式模式。
 *
 * 默认为从核心的 `wp-image-<ID>` 原生格式(在图片类中)提取图片 ID 的模式。
 *
 * @api string 用于从类名中提取图片 ID 的正则表达式模式。如果应返回整个类/属性,则为空字符串。
 */
function extract_id_pattern() {
    return '/(?<!\S)envira-gallery-image-(\d+)(?!\S)/i';
}

Deprecations

我们有时会弃用过滤器和操作。本文档重点介绍 Yoast SEO 中的弃用项,并尝试为您指出替代方案。

Yoast SEO 14.0 中弃用的过滤器和操作

过滤器

wpseo_twitter_taxonomy_image

现在您始终可以通过 wpseo_twitter_image 过滤器来筛选任何页面(包括分类页面)的 X 图片。

wpseo_twitter_metatag_key

我们移除了此过滤器且未提供替代方案。如需添加更多值,请根据此文档添加新的元数据呈现器。

操作

wpseo_opengraph

我们移除了此操作且未提供替代方案。如需添加更多值,请根据此文档添加新的元数据呈现器。

wpseo_twitter

我们移除了此操作且未提供替代方案。如需添加更多值,请根据此文档添加新的元数据呈现器。

Disabling Primary Category

自 Yoast SEO 3.1 版本起,我们新增了为文章设置主要分类的功能。 但在某些情况下,您可能不希望使用此功能。因此,我们添加了一个过滤器,允许您完全禁用它。

请将以下代码添加到您主题的 functions.php 文件中。

add_filter( 'wpseo_primary_term_taxonomies', '__return_empty_array' );

Disabling Yoast Seo

自 Yoast SEO 14.0 起,我们已更改您与 Yoast SEO 输出交互的方式。 但在某些情况下,您可能希望禁用特定文章上的 Yoast SEO 输出。

请将以下代码添加到您主题的 functions.php 文件中。

add_action( 'template_redirect', 'remove_wpseo' );

/**
 * 在前端为特定文章、页面或自定义文章类型移除 Yoast SEO 的输出。
 */
function remove_wpseo() {
	if ( is_single ( 1 ) ) {
		$front_end = YoastSEO()->classes->get( Yoast\WP\SEO\Integrations\Front_End_Integration::class );

		remove_action( 'wpseo_head', [ $front_end, 'present_head' ], -9999 );
	}
}

以上示例将禁用 ID 为 1 的文章的输出。您也可以调整代码以适应多种不同情况:

if ( is_page ( 1 ) ) { //... }
if ( is_single( [ 123456, 234567, 345678 ] ) ) { //... }
if ( is_singular( 'my_custom_posttype' ) ) { //... }

Filtering Yoast Blocks

自 Yoast SEO 8.2 起,我们添加了前两个结构化数据块:常见问题解答块和操作指南块。

可用区块

目前,Yoast SEO 中提供以下区块:

区块 命名空间
常见问题解答块 yoast/faq-block
操作指南块 yoast/how-to-block

示例

虽然 WordPress 允许您使用 allowed_block_types 过滤器来筛选区块,但此过滤器存在一些限制。 筛选区块的更好方法是使用 JavaScript 实现。请注意,这也意味着您需要通过自己的插件或主题(例如 functions.php)中的 wp_enqueue_script 方法注册一个 JavaScript 文件,如下所示:

// MyCustomPlugin.php
<?php
// 您的插件代码在此处。

wp_enqueue_script( 
	'my-custom-blocks-filter', 
	plugins_url( 'js/MyCustomBlocksFilter.js', __FILE__ ), 
	[], 
	'1.0', 
	true 
);

假设您只想从 Yoast 结构化数据块列表中加载常见问题解答块,并希望过滤掉操作指南块:

// js/MyCustomBlocksFilter.js
const hiddenBlocks = [ 'yoast/how-to-block' ];

wp.blocks.getBlockTypes().forEach( function( block ) {
	if ( block.name && hiddenBlocks.includes( block.name ) ) {
		wp.blocks.unregisterBlockType( block.name );
	}
} );

反转 if 语句的逻辑(即对 hiddenBlocks.includes( block.name ) 取反),将允许您限制您确实希望加载的区块。

Wp Get Environment Type In Yoast Seo

自 WordPress 5.5 起,可以使用新的 wp_get_environment_type() 函数来获取网站的当前环境。这尤其使得主题和插件的作者能够轻松地了解网站的环境,并可能为每种环境类型提供特定的功能。

Yoast SEO 利用此功能来启用或禁用插件的某些选项,这些选项对于每种环境类型可能相关或不相关。

使用 wp_get_environment_type() 获取网站环境

wp_get_environment_type() 的使用相当简单直接。该函数返回的值由全局变量 WP_ENVIRONMENT_TYPE 定义。

此变量的可能值为 local、development、staging 和 production。

因此,会对函数的返回值进行条件检查,并根据结果执行给定的指令。

以下是实际应用中的示例:

<?php
switch ( wp_get_environment_type() ) {
    case 'local':
    case 'development':
        do_nothing();
        break;
    
    case 'staging':
        do_staging_thing();
        break;
    
    case 'production':
    default:
        do_production_thing();
        break;
}

一些快速细节。如果 wp_get_environment_type() 返回 development,WP_DEBUG 常量的值将被设置为 true,前提是未在 wp-config.php 文件中预先定义。

WordPress 的另一项功能,应用程序密码,也与 wp_get_environment_type() 返回的值相关联。当此值为 local 时,默认可用。

Yoast SEO 如何使用 wp_get_environment_type()?

在非生产环境中

在非实时网站上 - if ( wp_get_environment_type() !== 'production' ) - ,有几项功能是不必要的。因此,在这些情况下,插件不会获取 Ryte 数据,不会执行 Ryte 站点健康检查,会禁用数据跟踪,并禁用关于 XML 站点地图的搜索引擎 ping 通知。

Assessment Markers Filter

在某些编辑器中,Yoast SEO 的 SEO 和可读性分析里的评估标记可能无法正常工作。为避免混淆,可以禁用这些标记。

使用方法

禁用评估标记

以下示例展示了如何使用此过滤器来禁用评估标记。

<?php
add_filter( 'wpseo_enable_assessment_markers', '__return_false' );

Capability Roles Filter

Yoast SEO ships with a variety of custom capabilities that are assigned to various roles, which allow for granular control over what features are available for a particular role. However, in some cases you might want to expand or limit these capabilities. To support this, we’ve introduced a filter named {$capability}_roles, where {$capability} needs to be replaced with the name of one of the capabilities that is registered by Yoast SEO.

Default roles

Yoast SEO has the following two default roles and their respective capabilities. These roles can be seen as an extension to the default WordPress role of Editor. As such, they inherit all capabilities of the Editor role as well.

wpseo_editor

wpseo_manager

Usage

Adding roles

The example below shows how you can use the filters to add one or multiple roles to a specific capability.

<?php

/**
 * Adds the editor and wpseo_editor roles to the view_site_health_checks capability.
 * 
 * @param array $roles The currently allowed roles.
 *
 * @return array The allowed roles.
 */
function add_site_health_roles( $roles ) {
	return array_merge( $roles, [ 'editor', 'wpseo_editor' ] );
}

add_filter( 'view_site_health_checks_roles', 'add_site_health_roles' );

Removing roles

The example below shows how you can use the filters to remove roles from a specific capability.

<?php

/**
 * Removes the wpseo_editor role from the view_site_health_checks_roles capability.
 * 
 * @param array $roles The currently allowed roles.
 *
 * @return array The allowed roles.
 */
function remove_site_health_roles( $roles ) {
	return array_filter( $roles, function( $role ) {
		if ( $role !== 'wpseo_editor' ) {
			return $role;
		}
  } );
}

add_filter( 'view_site_health_checks_roles', 'remove_site_health_roles' );

Applicable capabilities

Currently, the following capabilities are registered and can be altered with the filter:

Change Metabox Prio Filter

默认情况下,Yoast SEO 元框的优先级为 high。这意味着它将显示在编辑器正下方。如有需要,可以降低优先级,使其显示在其他元框下方。

元框优先级可接受的值如下(按显示顺序排列)[^1]:

在大多数情况下,core 优先级已足以将元框置于重要元框下方,例如在使用 WooCommerce 时。

降低元框优先级

以下示例展示了如何降低元框的优先级。

add_filter( 'wpseo_metabox_prio', 'lower_yoast_metabox_priority' );

/**
 * 将 Yoast SEO 元框的优先级降低为 'core'。
 *
 * @param string $priority 当前优先级。
 *
 * @return string $priority 可能调整后的优先级。
 */
function lower_yoast_metabox_priority( $priority ) {
  return 'core';
}

[^1]: 根据 add_meta_box 的文档。

Cornerstone Post Types Filter

Yoast SEO 允许您将公开的(自定义)文章类型标记为核心内容。在某些情况下,您可能希望限制可被标记的内容。 wpseo_cornerstone_post_types 过滤器允许您修改哪些文章类型能够被标记为核心内容。

使用方法

移除文章类型

下面的示例展示了如何使用过滤器来移除某个特定的自定义文章类型,使其无法被标记为核心内容。

<?php

/**
 * 移除 'my_custom_post_type',使其不允许被标记为核心内容。
 *
 * @param array $post_types 当前包含的文章类型。
 *
 * @return array 剩余的文章类型。
 */
function remove_custom_post_type_from_cornerstone( $post_types ) {
	if ( isset( $post_types['my_custom_post_type'] ) ) {
		unset( $post_types['my_custom_post_type'] );
	}

	return $post_types;
}

add_filter( 'wpseo_cornerstone_post_types', 'remove_custom_post_type_from_cornerstone' );

Disable Opcache Reset On Upgrade

Yoast SEO 每次升级到新版本时都会运行 opcache_reset(),以处理过时和新的 PHP 文件。

同时,还提供了 Yoast\WP\SEO\should_invalidate_opcache 过滤器,可用于禁用此行为。

使用方法

以下示例展示了如何使用过滤器在 Yoast SEO 升级时禁用 opcache 重置。

<?php

add_filter( 'Yoast\WP\SEO\should_invalidate_opcache', '__return_false' );

Markdown Enabled Filter

Yoast SEO 可以在内容分析中提供 Markdown 支持。默认情况下此功能是禁用的,除非启用了 Jetpack 的 Markdown 模块。可以将其扩展到其他在内容中解析 Markdown 的插件。

使用方法

启用 Markdown 支持

下面的示例展示了如何使用此过滤器来启用 Markdown 支持。

<?php
add_filter( 'wpseo_is_markdown_enabled', '__return_true' );

Primary Term Taxonomies Filter

Yoast SEO 允许您将分类设置为文章和页面的主要术语。在某些情况下,您可能希望允许用户使用额外的自定义分类法作为主要术语。 wpseo_primary_term_taxonomies 过滤器允许您修改哪些分类法可以被指定为主要术语。

使用方法

添加分类法

下面的示例展示了如何使用过滤器将分类法添加到潜在主要术语列表中。

<?php

/**
 * 添加自定义分类,使其可作为主要术语被选择。
 *
 * @param array  $taxonomies     当前可用作主要术语的分类法。
 * @param string $post_type      当前文章类型。
 * @param array  $all_taxonomies 所有已注册的分类法。
 *
 * @return mixed 可用作主要术语的分类法。
 */
function add_additional_primary_term_taxonomies( $taxonomies, $post_type, $all_taxonomies ) {
	if ( $post_type === 'my_custom_post_type' && isset( $all_taxonomies['my_custom_category'] ) ) {
		$taxonomies['my_custom_category'] = $all_taxonomies['my_custom_category'];
	}

	return $taxonomies;
}

add_filter( 'wpseo_primary_term_taxonomies', 'add_additional_primary_term_taxonomies', 10, 3 );

移除分类法

下面的示例展示了如何使用过滤器移除特定分类法,使其无法被选为主要术语。

<?php

/**
 * 移除自定义分类,使其不可作为主要术语被选择。
 *
 * @param array  $taxonomies     当前可用作主要术语的分类法。
 * @param string $post_type      当前文章类型。
 * @param array  $all_taxonomies 所有已注册的分类法。
 *
 * @return mixed 可用作主要术语的分类法。
 */
function remove_primary_term_taxonomies( $taxonomies, $post_type, $all_taxonomies ) {
	if ( isset( $taxonomies['my_custom_category'] ) ) {
		unset( $taxonomies['my_custom_category'] );
	}

	return $taxonomies;
}

add_filter( 'wpseo_primary_term_taxonomies', 'remove_primary_term_taxonomies', 11, 3 );

Shortcodes Filter

As of Yoast SEO 21.4, we have removed our default analysis of shortcodes. As of Yoast SEO 21.7, we have introduced a filter to enable the content analysis for the shortcodes provided through this filter. When enabling this filter, we will be able to parse and expand the provided shortcodes, and use our analysis to detect any favorable or infavorable content. However, note that we are unable to highlight analysis results, and therefore, we disable these buttons when this filter is activated.

Example

You can use our shortcode filter yoast.analysis.shortcodes through a JavaScript implementation. Please note that this means you have to register a JavaScript file via the wp_enqueue_script method in your own plugin or theme (e.g., in functions.php), as follows:

// functions.php or MyCustomPlugin.php
<?php
wp_enqueue_script(
    'my-shortcodes-filter',
    get_template_directory_uri() . '/js/MyShortcodesFilter.js',
    [ WPSEO_Admin_Asset_Manager::PREFIX . 'analysis' ],
    '1.0',
    true
);

Let's say that you would like to use our content analysis to detect images added through the gallery and su_custom_gallery (from the Shortcodes Ultimate plugin) shortcodes:

// js/MyShortcodesFilter.js
const getShortcodes = (shortcodes) => {
  shortcodes.push( "gallery", "su_custom_gallery" );
  return shortcodes;
};

wp.hooks.addFilter( "yoast.analysis.shortcodes", "getShortcodes", getShortcodes );

Yoast Seo Usage Tracking Filter

Yoast SEO 具备使用情况追踪功能,用于收集基础站点与服务器数据、插件使用情况、站点上安装的其他插件以及当前使用的主题。 通过 wpseo_enable_tracking 过滤器,您可以修改 Yoast SEO 是否追踪这些数据。 在 Yoast SEO 免费版中,仅当用户在配置向导中明确同意时才会启用使用情况追踪。而在我们的附加组件或 Yoast SEO Premium 中,默认会启用数据追踪。

使用方法

启用 Yoast SEO 数据追踪

以下示例展示了如何使用过滤器来启用 Yoast SEO 数据追踪。

<?php

/**
 * 启用 Yoast SEO 数据追踪
 */
add_filter( 'wpseo_enable_tracking', '__return_true' ); 

Deprecations

我们有时会弃用过滤器和操作。本文档重点介绍 Yoast SEO Premium 中的弃用项,并尝试指引您找到替代方案。

Yoast SEO Premium 12.9 中弃用的过滤器和操作

过滤器

wpseo_premium_post_redirect_slug_change

页面或文章别名更改时的自动重定向现在可以通过 Yoast\WP\SEO\post_redirect_slug_change 过滤器实现。

wpseo_premium_term_redirect_slug_change

分类项别名更改时的自动重定向现在可以通过 Yoast\WP\SEO\term_redirect_slug_change 过滤器实现。

Disable Table Of Content Block

我们在 Yoast SEO Premium 21.5 版本中添加了一个过滤器,用于禁用“目录”区块。当你完全未使用该区块时,这可以有效防止在注册“目录”区块时自动为标题生成 HTML 锚点。

请在你的主题 functions.php 文件中添加如下代码片段:

add_filter( 'Yoast\WP\SEO\disable_table_of_content_block', '__return_true' );

Disabling Autoloading Redirect Options

自 Yoast SEO Premium 17.7 版本起,用于计算重定向目标的两个选项(wpseo-premium-redirects-export-plain 和 wpseo-premium-redirects-export-regex)已设置为自动加载,以减少两次查询。 如果重定向数量变得庞大,您可能希望禁用这些选项的自动加载,例如当您使用对象缓存且自动加载的选项超出最大对象大小限制时。 仅在数据库中将这两个选项的 autoload 值改为 no 是不够的,因为 Yoast SEO Premium 会强制这些选项再次自动加载。

在 Yoast SEO Premium 20.13 版本中,我们引入了 Yoast\WP\SEO\redirects_options_autoload 来帮助您更改此行为。您可以将以下代码添加到主题的 functions.php 文件中,以防止 Yoast SEO Premium 强制自动加载重定向选项:

add_filter( 'Yoast\WP\SEO\redirects_options_autoload', '__return_false');

请注意,仅此操作不足以停止自动加载:您仍需在数据库中将这两个选项的 autoload 值改为 no。 您也可以在重定向管理器中添加/删除重定向,或编辑一个重定向(只要它确实被更改且未按原样保存),但如果这是一个 普通 重定向,只有 wpseo-premium-redirects-export-plain 的自动加载会改变,对于 正则表达式 重定向也是如此。

除了 autoload 的值外,该过滤器还接受两个参数:重定向的类型(plain 或 regex)以及当前定义的重定向数组。 因此,例如,您可以确保 plain 重定向的选项不会被自动加载,同时保持 regex 重定向的默认行为。在主题的 functions.php 文件中添加如下代码片段:

function yoast_set_autoload_no_plain( $autoload, $type, $redirects ) {
	if ( $type === 'plain' ) {
		return false;
	}
	return $autoload;
}

add_filter( 'Yoast\WP\SEO\redirects_options_autoload', 'yoast_set_autoload_no_plain', 3, 10);

Disabling Automatic Redirects Notifications

Yoast SEO Premium 的重定向管理器包含自动创建重定向和重定向请求通知功能。在极少数情况下,您可能希望关闭其中某些功能。

禁用自动创建重定向

Yoast SEO Premium 会监控 URL 变更并自动创建重定向。在大多数情况下,这是防止访问者遇到 404 错误页面的理想行为。但是,您可以通过将以下代码添加到主题的 functions.php 文件来禁用此功能:

文章和页面

add_filter('Yoast\WP\SEO\post_redirect_slug_change', '__return_true' );

分类法(分类、标签等)

add_filter('Yoast\WP\SEO\term_redirect_slug_change', '__return_true' );

注意:如果您看到已创建重定向的通知,但您并未更改 URL 或别名,这通常是由冲突引起的。请按照本文中的步骤检查是否存在冲突。

禁用重定向通知

当您删除网站上的内容时,我们会显示提醒通知,建议为已删除的项目添加重定向。我们强烈建议为已删除的项目添加重定向。但是,您可以通过将以下代码添加到主题的 functions.php 文件来禁用此功能。请注意,这只会隐藏通知消息,因此重定向仍可能在后台静默创建。

文章或页面:移至回收站

add_filter('Yoast\WP\SEO\enable_notification_post_trash', '__return_false' );

文章或页面:更改 URL

add_filter('Yoast\WP\SEO\enable_notification_post_slug_change', '__return_false' );

分类法:移至回收站

add_filter('Yoast\WP\SEO\enable_notification_term_delete', '__return_false' );

分类法:更改 URL

add_filter('Yoast\WP\SEO\enable_notification_term_slug_change', '__return_false' );

Hiding Version Number

我们在 Yoast SEO Premium 2.3.3 版本中添加了一个过滤器,允许您在 HTML 源代码中隐藏版本号。

请将以下代码添加到您主题的 functions.php 文件中。

add_filter( 'wpseo_hide_version', '__return_true' );

Orphaned Content Count Cache Filters

Yoast SEO Premium 允许您禁用孤立内容计数缓存,或更改其失效方式。 wpseo_premium_orphaned_count_cache 过滤器允许您完全禁用缓存。 wpseo_premium_orphaned_count_cache_invalidation_method 过滤器允许您更改缓存失效的方式。

使用方法

禁用缓存

以下示例展示了如何使用过滤器禁用孤立计数缓存。

<?php

add_filter( 'wpseo_premium_orphaned_count_cache', '__return_false' );

更改缓存失效方式

以下示例展示了如何使用过滤器更改缓存失效的方式。

有两种选项:

要将失效方法更改为 delete,请使用以下代码:

<?php

add_filter( 'wpseo_premium_orphaned_count_cache_invalidation_method', function() {
    return 'delete';
} );

Cheatsheets

以下是一些实用的资源和速查表,涵盖多个主题,可能会派上用场!

CSS

其他

Integrating

本文档是描述集成 Yoast SEO 所需技术规范和要求的中心。

自动优化

当软件安装并激活后,必须自动且无需用户干预地满足以下标准。

系统行为

集成

元数据与输出

元标签

所有页面和内容类型都应按照以下标准输出元数据:

结构化数据

所有页面都应按照以下标准输出结构化数据:

控制与分析

对于所有文章、页面和内容类型,用户必须能够:

与我们的 Yoast SEO 内容分析集成

当前设置

目前,我们的许多分析深度集成于 WordPress 或 Shopify 集成中。 在此,我们说明 Yoast SEO 插件如何与(默认)区块编辑器、经典编辑器以及 Elementor 集成。 出于可理解的原因,我们专注于免费开源 WordPress 插件 Yoast SEO 的相关部分。

PHP 集成

在 PHP 端,我们使用 Metabox 的概念来处理内容分析的不同部分。这个命名略有不当,因为它们既用于我们的 Yoast SEO metabox(在经典编辑器和区块编辑器中),也用于 Yoast SEO 侧边栏(在区块编辑器、Elementor 编辑器和 Shopify 应用中)。更多详情请参考文章相关代码和分类法相关代码。我们目前正在解耦 metabox 和侧边栏,请持续关注相关更新。

Elementor 集成展示了如何将这些 Metabox 单元集成到其他页面(本例中是 Elementor 编辑文章页面,参见 Elementor_Edit_Conditional),并实现了 Integration_Interface。它还展示了如何加载额外的脚本和样式(参见 register_hooks)。

JavaScript 集成

在 JavaScript 端,起点是 packages/js/src/post-edit.js(GitHub 链接),它初始化了元框及其行为(initTabs、initAdminMedia、initAdmin、initializeInsights、initializeAiGenerator)、Redux 存储的初始化(initEditorStore),以及与编辑器各部分的集成:

在 term-edit.js(GitHub 链接)中,我们为分类术语提供了非常相似的集成,但省略了不适用于术语的元素的集成。

对于与其他编辑器的集成,例如 packages/js/src/elementor.js 中的 Elementor,我们采用了非常相似的方法(参见此代码)。不过存在一些细微差别:

未来规划

在(希望不久的)将来,我们期望逐步将分析工具与其各自的后端解耦,并创建一个“独立”编辑器。 我们的目标是使其易于在任何内容编辑环境中实现,只需将其“指向”相关字段(并根据用户设置和控件“连接”相应的后端行为)。

资源

请注意,这些资源应被视为正在进行中的工作,在生产环境中可能不稳定。 如果您遇到问题或错误,我们鼓励您在相应的代码仓库中创建问题,和/或贡献修复和补丁。

Overview

本页面列出了我们在 Yoast 开发过程中使用的各种流程、技术和标准。

以下列表可用于了解如何在您自己的开发实践中运用这些不同元素。

工具与设置

指南与规范

资源

Productivity Tips And Tricks

以下是几件你可以做或学习的事情,让软件开发工作变得更轻松。这是一个不断增长的列表,欢迎随时补充!

注意:大部分说明假设你在使用 OSX 系统。

PhpStorm

Keyboard shortcuts

Shortcut Description
⌘ + e Opens the 'recent files' window. Select by typing the name of the file or moving up and down with arrow keys. Or press the return key directly for the previous file.
⌥ + ↑ Hard to explain, just give it a try... Selects, or extends, the selection of text in your editor. Pressing it repeatedly, increases the scope in which code is selected. Can be used to select a word, a statement or variable, a line, a function or a class and so on.
⌘ + ⌥ + l Reformats your code according to what you have setup in your code style settings (PhpStorm → Preferences → Editor → Code Style → {your language}). A very useful tool for preventing your colleagues from requesting changes on your pull requests just for a code style error.
⌘ + 9 Opens the 'version control' panel. Here you can check out the history of a branch. View which files are changed in what commit. And albeit with a little more effort, open the changed files in the editor (it defaults to changes window).
^ + g Multiple selection: select next occurrence. Find the next occurrence of the selection and adds a cursor there too. Very handy for a quick refactor in one file. Also you can just ⌥ + click to add an extra cursor.

Sublime Text

实现类似 PHPStorm 的代码自动缩进功能

打开菜单栏中的快捷键设置:Sublime Text → Preferences → Keybindings

这会打开两个文件。编辑右侧的文件(这是用户专属配置文件)。在 [] 之间添加这行代码,然后重启 Sublime。

{ "keys": ["command+option+l"], "command": "reindent", "args": {"single_line": false} }

现在 ⌘ + ⌥ + l 组合键就能正确缩进你的代码了。

iTerm 2

自然文本编辑

在 Mac 上的普通文本编辑器中,您可以使用 ⌘ + ← 或 ⌘ + → 快速浏览文本。要在 iTerm 2 中启用此功能,请转到 Preferences → Profiles → Keys。然后,点击 "Presets" 下拉菜单并选择 "Natural Text Editing"。

Git

删除已合并的分支

(此操作仅删除本地分支,不会以任何方式影响远程仓库。)

将以下行添加到你的 ~/.bash_profile 或 ~/.zshrc 文件中。仅当你在使用 Z-shell(或 echo $SHELL 输出类似 /bin/zsh 的内容)时才使用后者。

alias delete-merged='git branch --merged | grep -v "\*" | grep -v main | grep -v trunk | grep -v develop | xargs -n 1 git branch -d'

现在,你可以在任何 git 仓库的终端中使用 delete-merged 命令来删除已合并到 trunk、develop 或 main 分支的分支。

别名提示

这是一篇关于 Git 别名的好文章:https://haacked.com/archive/2014/07/28/github-flow-aliases/

Autojump

Autojump 是一个极佳的工具,能让你在终端中飞速跳转到所需目录。

如果你已安装 brew(强烈推荐安装),运行 brew install autojump。然后,如果你使用 oh-my-zsh,只需在 ~/.zshrc 文件中找到 plugins 变量,并将 autojump 添加到列表中。重新加载配置文件或重启终端后,即可使用 j 命令跳转到安装 autojump 后访问过的任何目录。

示例

无需输入 cd ~/Dev/yoast.com/site/web/app/themes/yoast-com,您可以直接键入 j yoast-c。它仅需路径或目录名的一小部分即可识别目标位置。

若 autojump 选择了错误目录,只需再次运行该命令。此时它将选取次优匹配项。您可通过在该目录下执行 j -i [权重值] 来提升当前目录的权重。运行 j -s 可查看所有已索引目录的当前权重。

命令行

例如要将 SSH 密钥上传至 GitHub,可使用命令 cat ~/.ssh/id_rsa.pub | pbcopy 再粘贴到 GitHub 界面。若需将外部数据导入脚本,pbpaste 非常实用,例如 pbpaste | ./awesome-data-processing-script.php。

sudo 便捷性

Defaults timestamp_timeout=15

Generating Unit Test Template

src 文件夹中的每个类都应在 tests/unit 文件夹中有一个对应的单元测试。由于设置新的单元测试文件需要一些样板代码,我们创建了一个方便的工具来为您生成单元测试模板。

使用单元测试模板生成器

您可以通过在插件仓库的根目录中调用以下 Composer 脚本来创建单元测试模板:

composer generate-unit-test 'Fully\Qualified\Class\Name'

此命令需要一个必需参数:您要为其生成单元测试模板的类的完全限定类名。请注意,此完全限定类名应放在引号内,以确保命令行能正确处理反斜杠。

调用生成器的结果

调用生成器将在 tests/unit 目录中创建一个新的单元测试文件。该文件包含一个基本的单元测试。其设置方法会创建被测试类及其模拟依赖项的实例,并将它们都设置在单元测试类上,以便于即将实现的测试用例使用。

常见异常及解决方案

[ErrorException] file_put_contents(...php): failed to open stream: No such file or directory

生成器目前尚不支持在单元测试文件夹中创建目录。如果您在 src 文件夹的新目录中添加了类,请确保在单元测试文件夹中也创建了同名的目录。

[RuntimeException] 单元测试已存在于路径 "tests/unit/..."

这意味着指定路径已存在该类的单元测试。您可以将新的测试用例添加到错误消息中给出的路径对应的文件中。

[ReflectionException] Class YoastWPSEO... does not exist

请注意错误消息中的类名缺少反斜杠。

这很可能意味着完全限定类名没有放在引号内。请将类名放在引号内,以确保命令行能正确处理其中的反斜杠。

[ReflectionException] Class Yoast\WP\SEO... does not exist

这意味着找不到要为其生成单元测试的给定类。请确保您提供了完全限定类名,即类名前加上其命名空间。同时请确保您已在 src 文件夹中创建了您的类。

Running Unit Tests Code Style Checks And Linters

在 Yoast,我们使用多种工具来确保代码遵循特定标准,这让我们能更有信心地发布产品并减少错误。

这些工具包括:

PHP

单元测试

针对我们的 PHP 代码,我们使用 PHPUnit 来测试代码是否符合预期行为。在运行任何测试之前,请确保您已阅读 PHPUnit 设置指南。

同时请务必阅读关于 在多站点环境中运行测试 的部分。

Running unit test with code coverage

Running unit tests with code coverage is very similar to 'just' running unit tests, with one difference: While a test runs, all your code gets analyzed to see what parts of your code base are covered by the tests.

The results of this analysis are then stored as a report that can later be used to determine what parts of your code might need more tests. Please note that running unit tests with code coverage takes up more time, as analyzing your code requires extra resources.

To configure your system to be able to run with code coverage, follow these steps:

  1. Run pecl install xdebug in your terminal to install XDebug.
  2. To run the tests including the coverage, click Coverage button in PhpStorm in the upper right corner of PhpStorm.
Troubleshooting

XDebug won't load

If you get an error "Failed loading /usr/local/Cellar/php@7.1/7.1.26/lib/php/20160303/xdebug.so" or something similar, it is possible your extension directory needs to be configured so PHP knows where to look for it.

Follow these steps to configure your extension directory:

  1. Run php --ini in your terminal to find your php.ini file.
  2. Open the php.ini file in your preferred editor.
  3. To find out what the proper extension directory is, run: pecl config-get ext_dir in a separate terminal window.
  4. Now uncomment and update the extension_dir in your php.ini:extension_dir = /usr/local/lib/php/pecl/<php_api_version> and replace the pathname with the one you found in the previous step.

Sometimes the unit tests will run on a different PHP version than the one used for code coverage. This is because of a quirk in PhpStorm.

To fix this, under Edit Configurations... -> Command line -> Interpreter, you can specify an interpreter. However, running with code coverage always uses the <Default project interpreter>, even when you have specified another interpreter. In case you get fatal errors when running with code coverage, take a look at the default project interpreter (Preferences -> Languages & Frameworks -> PHP) and ensure that this one is also properly configured.

XDebug doesn't have the right SSL permissions

If you get an error regarding SSL permissions, please run brew install openssl and try to install XDebug again.

集成测试

如果您对搭建和运行集成测试感兴趣,请务必查阅我们的指南:为插件设置集成测试。

其他插件

我们的大多数其他插件也包含各种测试,用于检查 PHP 代码。要运行这些测试,请在终端中使用以下命令:

代码检查

检查 PHP 语法错误

免费版

高级版

其他方法

要检查语法错误,请运行 find -L . -path ./vendor -prune -o -path ./node_modules -prune -o -name '*.php' -print0 | xargs -0 -n 1 -P 4 php -l

编码规范

在我们的代码库中使用代码检查工具时,我们综合采用了以下多种编码标准:

运行代码检查工具

免费版

高级版

新闻模块

视频模块

WooCommerce 模块

JavaScript

单元测试

对于我们的 JavaScript 代码,我们使用 Jest 和 Jasmine 来测试代码的预期行为。请确保在尝试运行以下命令之前已运行 yarn,以便安装所有必需的 NPM 包。

要运行测试,请在终端中使用以下命令:

覆盖率

要运行测试(包括覆盖率),请在终端中使用以下命令:

代码检查

自动运行代码检查工具

要启用此自动功能,请通过以下路径在设置中选择 Automatic ESlint configuration 选项:

PhpStorm -> Preferences -> Editor -> Inspections -> JavaScript -> Code quality tools -> ESLint

手动运行代码检查工具

要在终端中运行代码检查工具,请使用以下命令:

可用性

wordpress-seo wordpress-seo-premium wpseo-news wpseo-video wpseo-woocommerce
PHPUnit ✅ ✅ ✅ ✅ ✅
PHPLint ✅ ✅ ✅ ✅ ✅
PHPCS ✅ ✅ ✅ ✅ ✅
JavaScript 测试 ✅ ✅ ❌ ❌ ❌
ESLint ✅ ✅ ❌ ✅ ❌

Setup Plugin Integration Tests

要运行我们任何 WordPress 插件的集成测试,你需要满足以下条件:

  1. 能够访问你想要测试的 PHP 版本。
  2. 一份包含基础测试用例的 wordpress-develop 仓库副本。
  3. 一个带有空数据库的 MySQL 服务器。

安装 PHP

如果尚未安装,您可以按照 PHP 安装文档 在您的机器上安装 PHP。 如果因任何原因无法/不想在本地安装 PHP,您也可以选择使用 Docker 容器运行测试,但该过程本文档不作说明。

克隆 wordpress-develop 仓库

克隆 WordPress/wordpress-develop。 集成测试会在您启动测试时,从这个克隆仓库加载 WordPress 及其所有函数和 API。这意味着我们可以针对您本地安装的 WordPress 版本进行测试,这正是“集成”部分。 它还为我们的测试提供了一些实用类(例如,测试用例)。

设置 MySQL 服务器

有多种方式可以满足此要求。这里我们概述了 Yoast 团队喜欢使用的几个选项。您可以根据个人需求/偏好选择其中之一。

选项 1:使用 wordpress-develop Docker 环境提供的 MySQL 服务器

此选项的额外好处是,您还可以获得一个用于为 WordPress 本身编写补丁和单元测试的独立设置。

暴露 Docker MySQL 实例

我们将本地暴露 Docker MySQL 实例,以便 PHPUnit 测试可以访问它。

暴露 MySQL 端口

在你之前克隆的 wordpress-develop 本地副本中,你需要暴露 MySQL 端口,这将允许我们稍后连接到它。

在 wordpress-develop 目录中,添加一个 docker-compose.override.yml 文件,并设置以下内容:

services:
  mysql:
    ports:
      - "3306:3306"

这将把 3306 端口暴露给本地环境。

编辑 hosts 文件

在终端中运行 sudo nano /etc/hosts 来编辑你的 hosts 文件,并添加以下内容:

127.0.0.1 localhost
10.254.254.254 mysql

使数据库可访问

为了使你的数据库可访问,请运行以下命令:

sudo ifconfig lo0 alias 10.254.254.254
sudo ifconfig lo:0 10.254.254.254
netsh interface ipv4 add address "Loopback Pseudo-Interface 1" 10.254.254.254 255.255.255.255

启动容器

如果您已完成上述所有步骤,现在可以启动容器了!

在终端中,进入 wordpress-develop 目录,运行以下命令:

npm install
npm run build:dev
npm run env:start
npm run env:install

当您想停止容器时,只需执行:

npm run env:stop

选项 2:使用 Local by Flywheel 开发环境提供的 MySQL 服务器

如果你使用 Local 进行开发,这可能是一个简单的替代方案。

  1. 在 Local 中启动你的开发站点。
  2. 切换到 "Database" 选项卡。
  3. 在这里,你应该能看到一个 "Open Adminer" 链接。点击它。
  4. 在 Adminer 中,靠近左上角有一个 "SQL command" 链接。点击它。
  5. 在文本区域输入 CREATE DATABASE `plugins-integration-test` 并点击 "Execute"。

现在你已经有了数据库,接下来需要告诉测试套件如何连接到它:

  1. 在你的 wordpress-develop 克隆仓库中,找到 wp-tests-config.php 文件。如果它还不存在,请复制 wp-tests-config-sample.php 并将其命名为 wp-tests-config.php。
  2. 找到如下所示的代码块:
    	define( 'DB_NAME', 'youremptytestdbnamehere' );
    	define( 'DB_USER', 'yourusernamehere' );
    	define( 'DB_PASSWORD', 'yourpasswordhere' );
    	define( 'DB_HOST', 'localhost' );
    	define( 'DB_CHARSET', 'utf8' );
    	define( 'DB_COLLATE', '' );
    
  3. 将 DB_NAME 的值改为 plugins-integration-test。
  4. 将 DB_USER、DB_PASSWORD 和 DB_HOST 的值更改为你的 Local by Flywheel 站点 "Database" 选项卡中列出的值。

Tying it all together

The last step is to tie it all together! Open the folder where you cloned wordpress-seo.

In order to ensure that unit tests can properly run, you need to add the following two constants:

You can add those to the phpunitxml.dist file in the root of the wordpress-seo repository. If you followed our earlier recommendations your will be adding the constants as below:

  <php>
    <env name="WP_DEVELOP_DIR" value="/Users/<your name>/Projects/WordPress/wordpress-develop/" />
    <env name="WP_PLUGIN_DIR" value="/Users/<your name>/Projects/Yoast" />
  </php>

:::caution Note the trailing slash in WP_DEVELOP_DIR and the absence of it in WP_PLUGIN_DIR. :::

Now, you can (finally!) run the integration tests via:

composer test-wp

That's all! Congratulations! You now have a working setup to run the integration tests for the Yoast SEO plugins.

故障排除

MySQL 连接冲突

如果遇到数据库连接错误,请确保没有其他 MySQL 进程在后台运行。可以在终端中执行 brew services stop mysql 命令来停止 MySQL 服务。

访问管理后台(wordpress-develop docker)

Docker 设置实际上运行的是一个完整的本地 WordPress 站点。出于调试目的,您可以访问此测试环境的管理后台。默认情况下,访问 WordPress 管理后台的凭据是:

访问数据库(wordpress-develop docker)

如需访问数据库以检查某些内容(例如是否已创建所有数据库表),可在您选择的数据库工具中使用以下凭据:

Setup

本页描述了我们在 Yoast 设置开发环境的过程。

:::note 本指南假设您使用 Mac OSX 和 PHP 7.4+ 环境。 :::

开始之前

在开始为 Yoast 插件做贡献之前,请确保您已查阅我们的开发工具页面,其中列出了需要使用的各种工具(除非另有说明)。

项目组织建议

通常我们建议创建一个 ~/Projects/Yoast 文件夹来集中管理所有内容。

:::note 请勿在 ~/Documents、~/Desktop 或 ~/Downloads 目录下创建 Yoast 文件夹,否则在使用 plugin-development-docker 等容器化环境时可能引发错误。 :::

创建该文件夹后,您有两种选择:

:::note 通常更推荐使用符号链接方案,因为单一数据源能降低操作失误的风险。 :::

按环境逐个克隆插件

如果您选择使用 Local 作为开发环境,可以按照以下步骤将插件克隆到 Local 站点的插件目录中。

:::note 这些步骤假设您已经安装了 Local,并在其中设置了用于插件开发的站点。 :::

在 Mac 上

将插件克隆到单一目录并创建符号链接

如果您使用 Local 作为首选开发环境,可以按照以下步骤将插件克隆到独立目录,并创建指向 Local 站点插件目录的符号链接。

在 Mac 上

设置 PHPUnit

在 PhpStorm 中启用 PHPUnit

在 PhpStorm 中完成设置,请遵循以下步骤:

配置 PHPUnit 以配合 WordPress 和插件工作

为确保单元测试能够正常运行,您需要添加以下两个常量:

示例:

WP_DEVELOP_DIR=/Users/<your name>/Projects/WordPress/wordpress-develop/
WP_PLUGIN_DIR=/Users/<your name>/Projects/Yoast

:::caution 请注意 WP_DEVELOP_DIR 末尾的斜杠,而 WP_PLUGIN_DIR 则没有。 :::

:::info 如果您已将 WP_DEVELOP_DIR 和 WP_PLUGIN_DIR 设置为环境变量,可以跳过以下部分。 :::

在 PhpStorm 中配置 PHPUnit:转到 编辑配置... -> PHPUnit,并确保配置与以下截图类似:

运行多站点测试

我们部分插件包含仅在多站点环境下运行的测试。要执行这些测试,请选择您的 PHPUnit 配置,并在设置界面的命令行部分添加值为 1 的 WP_MULTISITE 参数。

安装插件

现在开始安装插件。首先,如果你还没有操作,请在 /plugins/ 文件夹中克隆 wordpress-seo 仓库。

然后,在你克隆仓库的目录中运行以下命令:

之后,你可以运行 yarn start 来监听变更并自动构建 JavaScript。

其他插件

我们还提供了其他几个插件。所有公开可访问的代码库都可以在 GitHub 上找到。

启用可索引化索引

为测试生成模拟数据

您可以使用 Yoast WP CLI Faker 来生成模拟数据,例如文章、分类和用户。

接下来做什么?

克隆仓库后,你可以在浏览器中访问 basic.wordpress.test 查看开发环境。在 IDE 中打开插件目录,就可以开始开发了!

Tools

本页介绍了 Yoast 在开发各类插件和库时使用的工具。

:::note 以下说明假设您使用的是 OSX 系统。 :::

通用工具

Homebrew

Homebrew 是 OSX 的包管理器,类似于 apt、yum 和 pip。它允许你从命令行轻松安装和更新软件。

安装 Homebrew 非常简单。只需打开终端(应用程序/实用工具/终端)并运行以下命令:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

构建工具

在 Mac 上进行开发时,建议安装一些额外的开发工具(Xcode 命令行工具、autoconf、automake 和 libtool),以便从源代码构建代码:

xcode-select --install
brew install autoconf automake libtool

Iterm2 与 Oh-My-Zsh(可选)

iTerm2 是 Mac OSX 默认终端应用的替代品。我们建议安装它,因为它在终端操作中提供了许多额外功能。

Oh-My-Zsh 是一个命令行 shell,它添加了一些在普通 shell 中难以实现的功能。它还让你能更轻松地管理 zsh 配置。

你可以通过以下命令安装两者:

brew install --cask iterm2
sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

安装完成后,你需要通过运行 chsh -s /bin/zsh 将 shell 从 bsh 切换到 zsh。否则,你将无法体验 Oh-My-Zsh 的所有炫酷功能。

示例:

Git / GitHub

在 Yoast,我们使用 Git 进行版本控制。不了解 Git 如何工作?请查看 此教程。我们的 Git 仓库都托管在 GitHub 上。如果您还没有账户,请前往 创建账户。

通过运行以下命令安装 Git:

brew install git

然后,进一步阅读 如何配置本地 Git 设置 和 如何将 SSH 密钥添加到 GitHub。

最后一个建议:添加全局 git 忽略文件,以免意外将 .DS_Store 等系统文件添加到仓库中。

本地环境

在 Yoast,我们使用 Local 作为开发环境的基础。

遇到通过 Local 配置站点的问题?如果你的芯片组与 Local(或任何其他软件)开发时所用的不同,安装 Rosetta 的这个命令会有所帮助:

softwareupdate --install-rosetta

Sequel Pro

要以简单快捷的方式管理和搜索数据库,Sequel Pro 是推荐工具。您可以从其官网下载最新版本。

要了解如何连接到 Docker 数据库,请查阅 Plugin Development Docker 仓库中的连接说明。

gettext

gettext 是一个 GNU 国际化 (i18n) 和本地化 (l10n) 库。在撰写本文时,构建 wordpress-seo 插件需要此软件包。

brew install gettext
brew link gettext --force

项目工具

PHP

由于插件使用 PHP 编写,需要在本地安装 PHP。我们目前使用 PHP 7.4 进行插件开发。由于该版本未包含在 brew 官方源中,需要先添加非官方的 Homebrew PHP 仓库再安装 PHP:

brew tap shivammathur/php
brew install php@7.4

PECL

PHP 扩展通过 PECL 分发。PECL 随 PHP 一同安装,但你可能需要将其添加到计算机的路径中以便访问。 找到 PHP 的 bin 目录(通常遵循以下模式),并按如下方式将其添加到 PATH 变量中:

export PATH=$PATH:/usr/local/Cellar/php@7.4/7.4.33_4/bin/ 

Brew PHP Switcher(可选)

:::note 目前仅适用于 Homebrew。 :::

Brew PHP Switcher 是一个简单的 shell 脚本,可快速在 PHP 主要版本之间切换 Apache 和 CLI 配置。如果你需要同时维护使用新版或旧版 PHP 功能构建的多个产品或项目,这将非常有用。

假设你当前运行的是最新版 PHP,并希望降级到较早版本,可以按以下步骤操作:

Composer

Composer 是一个 PHP 包管理器,在我们的 WordPress 插件中用于轻松管理代码依赖。要安装它,请在终端中运行以下命令:

brew install composer

安装 Composer 后,您可以在包含 composer.json 文件的插件目录中运行以下命令来安装所有必要的依赖项:

composer install

:::note 要更新依赖项,您可以运行 composer update。但通常无需这样做——我们使用 composer.lock 中列出的固定版本。 :::

在 Local 环境中使用 XDebug

如果您使用 Local 作为开发环境,XDebug 应该已经安装在您的本地站点上。要在 IDE 中启用 XDebug,请按照以下步骤操作。

确保以下路径已包含在您的 PHP 包含路径中:

您可以使用这些步骤将这些路径包含到您的 PHPStorm 项目中。

确保这些路径映射到服务器上的正确路径:

为避免 PHPStorm 在每次请求时因断点而频繁中断,请禁用 Break at first line in PHP scripts。同时禁用 Force break at first line when no path mapping specified 和 Force break at first line when a script is outside the project。 这些设置可以通过打开 Settings (cmd ,) -> PHP -> Debug 找到。

您可以使用这些步骤在您的 PHPStorm 项目中添加这些路径映射。

通过 NVM 安装 Node

Node.js® 是一个基于 Chrome V8 JavaScript 引擎构建的 JavaScript 运行时。我们的工具使用它来编译 JavaScript。Node 版本管理器 (NVM) 是一个简单的 bash 脚本,用于管理多个活跃的 Node.js 版本。

首先,安装 NVM。之后,你可以通过运行以下命令来安装一个 Node 版本

nvm install <version>

查看 Node.js 官网 以了解最新的 LTS 版本是什么。

Yarn

Yarn 是一个用于管理 JavaScript 依赖项的包管理器。要安装 Yarn,请确保您已先安装 Brew,然后运行以下命令在您的机器上全局安装它。请注意,此方法也会安装 Node,但我们不使用那个版本。

brew install yarn

与 Composer 类似,您需要导航到包含特定文件的目录。对于 Yarn,您需要在运行前确保位于 package.json 文件所在的目录:

yarn

Grunt

Grunt 是一款帮助运行重复性任务的工具,例如运行代码检查器、压缩代码或任何其他可以为了开发目的而自动化的任务。一般来说,Grunt 用于管理 JavaScript 代码。

要安装 Grunt 命令行界面 (CLI),请在终端中运行以下命令:

yarn global add grunt-cli

安装 Grunt 后,您可以在项目目录中运行各种命令来处理不同的任务。一个常用的命令是 grunt watch。该命令会监视目录中的更改,并对源代码运行一组预定义的命令,以确保其正确输出。

由于我们的代码库使用了许多类似的任务,我们最终创建了一个代码库来将这些相似的任务集中在一起。

请务必也查看 Grunt 目录,特别是 aliases.yml 文件,以了解有哪些可用的命令。

调试工具栏

为协助调试 WordPress(插件),您可以使用 Debug Bar 插件。 该插件可以记录已运行的查询和已定义的环境参数,还能帮助您发现其他隐藏(但有用)的数据。

Using Composer

什么是 Composer?

Composer 是一个用于 PHP 项目的依赖管理工具(类似于 NPM),可以通过终端运行。

安装 Composer

要在您的系统上安装 Composer,请确保遵循官方安装步骤。

通过 Composer 安装 Yoast SEO

要通过 Composer 安装 Yoast SEO,请在您的 plugins 目录中运行以下命令:

composer require yoast/wordpress-seo

上述命令将下载最新版本的 Yoast SEO 插件并安装所有必要的依赖项。

安装 Yoast SEO 的开发版本

如果您希望帮助开发 Yoast SEO(例如修复错误),我们建议您使用 git clone git@github.com:Yoast/wordpress-seo.git,因为我们不支持通过 Composer 下载 Yoast SEO 的开发版本。

安装后步骤

一旦 Composer 完成其安装过程,或者 git 完成克隆仓库,请确保在 wordpress-seo 目录中运行 yarn,然后运行 grunt:build,以确保所有必要的文件都已正确构建。

更新依赖项

Composer 在 composer.lock 文件中跟踪项目的状态。运行 composer install 将始终重现该状态,确保不同开发者环境的一致性。 当需要更新依赖项时,以下命令将根据 composer.json 中设置的版本限制,将相关依赖项更新到最新版本。

composer update

之后,更新后的 composer.lock 文件需要提交到版本控制中。

:::caution

请注意,更新依赖项是一个需要谨慎处理的过程,这样做可能会导致功能损坏。 在提交更新后的 composer.json 和 composer.lock 版本之前,请务必进行充分的测试。

:::

Coding Guidelines And Principles

PHP 编码规范

Yoast PHP 编码规范 是一个 PHPCS 规则集,用于在所有 Yoast PHP 代码中强制执行 PHP 编码风格。YoastCS 主要基于 WordPress 编码标准。在某些情况下,我们与 WPCS 存在差异。以下是两个示例:

  1. 我们不强制使用尤达风格赋值。
// Yoast 风格
if ( $foo == 'bar' ) {
  echo 'It is Yoast style!';
}

// 尤达风格
if ( 'bar' == $foo ) {
  echo 'Yoda style is how this looks';
}
  1. 我们将 else 放在新行。
// Yoast 风格:
if ( $foo == 'bar' ) {
  echo 'foo bar';
}
else {
  echo 'foo is not bar';
}

// WordPress 编码标准:
if ( $foo == 'bar' ) {
  echo 'foo bar';
} else {
  echo 'foo is not bar';
}

JavaScript 编码规范

我们的 JavaScript 编码规范 记录在 ESLint 规则集中。JavaScript 与 PHP 的规则存在一些差异,例如我们使用驼峰命名法来命名类、函数、方法和变量。目前我们仅编写 ES6 代码,并通过 Webpack 和 Babel 等工具进行编译以确保向后兼容性。

命名规范

作为团队中的开发者,我们希望轻松理解代码的含义。为此,我们需要描述性的类名、函数名和变量名。以下是一些指导原则:

编码原则

保持简单直接(KISS)

阅读代码时,应能轻松理解其逻辑。若陷入抽象、设计模式或其他编码标准的教条,你可能会为了符合标准而修改代码。这有点愚蠢,这也解释了 KISS 中最后一个 S 的含义。始终运用你的判断力,问问自己:这个改动是否让代码逻辑更易理解?如果不确定,随时可以寻求他人建议!

不要重复自己(DRY)

尽量避免在不同地方编写相同的功能。如果代码中存在错误,你不会希望修复多个地方的同一个错误。当出现非DRY代码时,很可能可以将其部分提取为可重用的函数或对象。仅在重复实际发生时进行DRY处理,并确保功能完全相同而非相似。否则,你可能会在之后花费宝贵的时间去解耦那些代码,而这恰恰是你最初试图节省的时间!

SOLID

SOLID 原则 是一组指导代码应如何编写的原则。这些原则旨在帮助你以模块化的方式组织代码,使代码更易于理解,从而降低变更成本。SOLID 中的 S 代表单一职责原则,这可能是最容易理解的。一个类应该只有一个改变的理由,意味着一个类应该只负责一项工作。这些原则中的许多不仅适用于对象,也可以轻松地应用于函数和模块。更多内容请阅读链接文章。

测试

编写代码时,确保其按预期方式运行至关重要。为此,你应该编写测试。在 Yoast,我们使用 PHPUnit 进行 PHP 测试,使用 Jest 进行 JavaScript 测试。

关于测试的一些指导原则:

测试驱动开发 (TDD)

TDD 是一种在编写实际实现代码之前先编写测试的原则。通过这种方式工作,你被迫提前思考要解决的问题。由于测试应该是独立且小巧的,你也已经在思考将要编写的实现功能的代码。虽然我们并不严格强制这种工作方式,但它是养成测试习惯的好方法。

代码侦察原则

重构应主要遵循代码侦察原则。这意味着你离开时留下的代码应比发现时更好。在修复错误或添加新功能时,可以花些时间改进相关功能。当然,仅在确实需要时这样做,并记住保持简洁。

其他

尝试/捕获“随机”功能

某些功能具有随机的成功因素。例如:进行 API 调用时,你永远无法确定 API 是否可用。我们不希望软件因此中断。请将此类功能包装在 try/catch 块中。

压缩 .js 和 .css 文件

请确保你的 JS 和 CSS 文件已被压缩/混淆,并在浏览器中加载压缩版本。我们的大多数仓库都配置了 Grunt 来帮你完成这项工作。只需运行 grunt build 即可!

PHPDocs 与注释

确保所有代码都使用 PHPDoc 进行文档化。必要时也使用行内注释,例如在描述复杂代码段时。

Phpunit Best Practices

除了按照特定的编码指南和原则编写优质代码外,在编写测试时遵循某些准则也同样重要。

理解基础编码原则

在开始之前,充分理解一些基础编码原则非常重要,例如面向对象编程(OOP)、SOLID 原则和 DRY 原则。

请阅读我们的编码指南与原则部分以获取更多信息。

我们如何组织 PhpUnit 测试

Yoast 的大多数 PHP 代码库都采用面向对象的方式编写。这些代码库包含大量类,并且为每个类都创建了一个测试类。 这些测试类通过测试方法来覆盖我们想要测试的类。

每个测试方法对应一种情况

编写测试方法时,一个好的做法是每个测试方法只包含一个断言。此外,添加 @covers 注解并跟上类名和方法名,可以告诉 PhpUnit 正在测试哪个方法,这之后可用于计算测试覆盖率。

示例:

/**
 * @covers ClassName::MethodName
 */
public function test_method_name() {
    $myClass = new MyClass();
    $this->assertTrue( $myClass->MethodName() ) ; 
}

使用数据提供器来减少重复测试

有时,同一个类方法有多个测试方法。这不一定是个问题,但你可以通过使用数据提供器将它们合并到一个测试方法中。

数据提供器是测试对象中的一个方法,你可以在其中为多种情况指定不同的值。 提供器总是返回一个数组,其中包含一个子数组,该子数组存放测试值。这些测试值作为参数传递给测试方法。

/**
 * @dataProvider myDataProvider
 * @covers ClassName::MethodName
 */
public function test_method_name( $expected, $valueToTest, $message ) {
    $actual = $myClass->MethodName( valueToTest )

    $this->assertEquals( $expected, $actual, $message ) ; 
}

/**
 * 测试方法 test_method_name 的数据提供器

 * [0]: 期望值
 * [1]: 传递给方法的值
 * [2]: 传递给断言的消息。
 */
public function myDataProvider( ) {
   return [
       [ true, ‘MyMethod’, ‘Test 1: The string is MyMethod’ ],
       [ false, ‘NotMyMethod’, ‘Test 2: The string is not MyMethod’ ],
   ];
}

[0] 期望值是你可以在数据提供器中添加的内容。这样做可以阐明提供器中数据的结构。

消息是可选的,但使用它们可以帮助使 PHPUnit 中的断言更清晰一些。通过在数据提供器中提供消息,将更容易识别失败的测试。

0051-development-standards-version-control-conventions


title: "IDE 特定文件 #" post_status: publish comment_status: open taxonomy: category: - yoast-developer post_tag: - Standards - Development - Repos

由于我们重度依赖 Git 进行版本控制,因此在管理代码时必须执行一套严格的规则。

分支

我们的仓库包含几个特殊分支:

分支 描述
main 此分支包含最新发布的版本。它不应包含任何未经适当审查和测试的代码。
develop * 此分支包含正在积极开发并将于下次发布中上线的代码。创建新分支时,应基于此分支。
release/*.* 此分支在制作发布候选版本时创建,以确保 trunk 上的开发工作可以继续进行。
hotfix/*.* 此分支用于为最新发布版本创建补丁。此分支始终基于 main,而非 trunk。

* 在某些仓库中,develop 仍被称为 trunk。这是我们未来将要更改的事项。

例外情况

唯一允许直接提交到 main 分支的例外情况是处理高优先级错误修复或安全补丁。一旦计划发布,将使用 develop 分支创建候选发布版(RC),并最终合并到 main 分支。

My Yoast 项目采用不同的设置,功能分支在满足所有要求后直接合并到 main 分支。因此,该代码库中没有 develop 分支。

创建分支

每当开始处理问题时,务必使用独立分支进行提交。这能确保未完成(或可能存在缺陷)的代码不会扩散到所有开发环境。

这些不同的分支分别承载着以下"真相":已发布的内容(main分支)、可发布的内容(develop分支),以及最终将发布的内容(功能分支)。

请避免从未合并的分支创建新分支,这实际上会产生反效果。若需先合并某分支才能继续处理其他问题,请向团队成员申请代码审查和验收测试。

Branch naming conventions

To ensure branches can easily be identified within a repository, we adhere a convention that follows the following structure: {issue number}-{issue title}.

Generally speaking the issue title is a short description of the problem that needs to be solved.

This format implies that all PR's should have an issue on which they are based to ensure there's a clear separation of issue and solution.

In the past we used an alternative convention that also contained your initials, but seeing at multiple people can be working on a single issue, it doesn't make much sense to adhere that structure. You might also run into a structure like stories/{issue title}. This is also part of the 'old' way of naming branches.

树状图示例

我们的 Git 树状结构应如下所示:

main
  |
  |-123-fix-typo
  |-132-xss-in-metabox
  |-...
  |
  |---trunk
        |
        |-164-add-button
        |-166-twitter-integration
        |-...

提交

请尽量使用清晰的提交信息,并保持每次提交尽可能精简。

拉取请求

准备你的分支

当你的分支准备就绪后,请确保将父分支(main、trunk 或某个功能分支)的最新版本合并到你的当前分支中。可以通过执行以下命令完成:

git checkout {parent_branch}
git pull
git checkout {your_branch}
git merge {parent_branch}

你可能会遇到一些合并冲突。解决它们,提交合并,并在提交拉取请求(PR)之前推送代码。

提交 PR 前

三条黄金法则:

注意:不要假设测试人员使用与你相同的设置!撰写 PR 时请务必考虑这一点。

创建 PR

提交 PR 时,请确保将您的分支与正确的父分支进行比较。对于某些仓库,默认父分支是 trunk,但您可以在页面顶部更改此设置:

branch_comparison

通过添加 Fixes #{issuenumber} 来指明修复了哪个问题。这将确保 PR 合并后问题自动关闭。

您的代码现已准备好进行代码审查。

其他

全局 Git 忽略

为确保我们不会将不属于仓库的文件提交到任何仓库,Git 提供了一种全局排除文件使其不被暂存提交的方法。

要为 Git 添加全局忽略文件,请在终端中执行以下命令:

git config --global core.excludesfile ~/.gitignore_global

将以下数据输入到文件 ~/.gitignore_global 中:

# IDE 特定文件 #
.idea

# 编译文件 #
.so

# 操作系统生成的文件 #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

配置 Git 钩子

切换分支时自动运行 composer install

以下命令创建一个 Git 钩子,该钩子会在执行 git checkout 时触发。 这将运行 composer install 命令,以确保您拥有正确的包和生成的自动加载器。

cat <<EOT >> .git/hooks/post-receive
#!/bin/sh
composer install --working-dir=$GIT_DIR/../
EOT
chmod +x .git/hooks/post-receive

您可以将此钩子用于所有使用 Composer 进行依赖管理的仓库。

Writing Issues And Pull Requests

团队协作中一个非常重要的方面是恰当的沟通。这不仅限于团队成员,还包括所有相关人员(例如客户、经理、开发人员等)。 为了尽可能提高效率,我们在 Yoast 遵循一些关于编写问题和拉取请求消息的规则。

问题反馈

撰写问题反馈时需注意几个重要方面,这是我们尽力遵循的原则。通常,在向任一代码库提交问题时,您可以参照这些步骤,但有时问题可能不适用于模板。更多相关内容将在后文说明。

标题

撰写标题时应遵循以下规则:

Content

The content of the issue is based on a standard template that has been added to most repositories. If you notice a repository without this template, please inform a member of the Plugin team.

In case a template is lacking, please make sure to add the following:

For bug reports:

For features / enhancements:

A few general notes regarding writing issues / feature requests:

拉取请求

拉取请求(PR)与议题略有不同。以下是撰写 PR 时需要注意的事项。

标题

议题的标题描述问题,而 PR 的标题描述解决方案。除此之外,其规则与议题标题相同。

内容

对于 PR 的内容,你需要确保添加以下内容:

编写测试说明

每当您提交 PR 时,必须同时包含适当的测试说明。这不仅是因为开发者会审阅,测试人员(如 SEO 和 Beta 测试员)也需要检查问题是否已正确修复。

提交 PR 时请确保描述以下内容:

Filters Actions

Filters

duplicate_post_excludelist_filter

In some cases, you might not want Duplicate Post to copy custom fields when duplicating a post. This filter allows you to filter out such fields.

In either your custom plugin or your theme's functions.php, add the following code and filter out any custom field you don't want to be duplicated.

/** 
 * Filters out custom fields from being duplicated in addition to the defaults.
 *
 * @param array $meta_excludelist The default exclusion list, based on the “Do not copy these fields” setting, plus some other field names.
 *
 * @return array The custom fields to exclude.
 */
function my_custom_fields_filter( $meta_excludelist ) {
    // Merges the defaults array with our own array of custom fields.
    return array_merge( $meta_excludelist, [ 'my_custom_field1', 'my_custom_field2' ] );
}

add_filter( 'duplicate_post_excludelist_filter', 'my_custom_fields_filter' );

duplicate_post_meta_keys_filter

If you want to retrieve or alter custom fields' keys after excluding custom fields you don't want to be duplicated, you can use this filter.

In either your custom plugin or your theme's functions.php, add the following code and filter the meta keys.

/** 
 * Adds an additional meta key to the stack.
 *
 * @param array $meta_keys The keys of the custom fields in the original post, minus those that were previously excluded.
 *
 * @return array The custom fields.
 */
function my_custom_post_meta_keys_filter( $meta_keys ) {
    // Example: Add key my_custom_field3
    $meta_keys[] = 'my_custom_field3';

    return $meta_keys;
}

add_filter( 'duplicate_post_meta_keys_filter', 'my_custom_post_meta_keys_filter' );

duplicate_post_get_clone_post_link

If (for some reason) you need to retrieve or alter the duplicate post link for a post, you can use this filter to do so.

In either your custom plugin or your theme's functions.php, add the following code and retrieve or alter the post link

/** 
 * Add a custom argument to the clone post link.
 *
 * @param string $link The post clone link URL.
 *
 * @return string The post clone link URL with the added argument.
 */
function my_custom_get_clone_post_link( $link ) {
    // Example: Add a query argument to the link.
    return add_query_arg( 'my_arg', 'hello-world', $link );
}

add_filter( 'duplicate_post_get_clone_post_link', 'my_custom_get_clone_post_link' );

duplicate_post_clone_post_link

此过滤器与 duplicate_post_clone_post_link 模板标签结合使用,允许您操作输出链接的各个部分。

在您的自定义插件或主题的 functions.php 文件中,添加以下代码以获取或修改文章链接的 HTML。

/** 
 * 确保文章的克隆链接 HTML 始终包裹在带有自定义 CSS 的 div 中。
 *
 * @param string $link 作为 HTML 的文章克隆链接。
 *
 * @return string 作为 HTML 的文章克隆链接。
 */
function my_custom_clone_post_link( $link ) {
    return '<div class="my_custom_css">' . $link . '</div>';
}

add_filter( 'duplicate_post_clone_post_link', 'my_custom_clone_post_link' );

现在,每当您在主题中调用 duplicate_post_clone_post_link 时,输出的链接将始终被包裹在我们自定义的 div 标签中。

duplicate_post_enabled_post_types

如果(出于某些原因)你需要修改插件启用的文章类型列表,可以使用此过滤器来实现。 该过滤器在从数据库读取选项值后被调用,因此可用于覆盖设置,或启用未在选项页面显示的文章类型。

例如,在你的自定义插件或主题的 functions.php 文件中,添加以下代码以启用插件对 product 文章类型的支持:

/** 
 * 为 WooCommerce 的 "product" 文章类型启用插件,该类型默认不可用。
 *
 * @param array $enabled_post_types 插件已启用的文章类型名称数组。
 *
 * @return array 过滤后的文章类型名称数组。
 */
function my_custom_enabled_post_types( $enabled_post_types ) {
	$enabled_post_types[] = 'product';
	return $enabled_post_types;
}
add_filter('duplicate_post_enabled_post_types', 'my_custom_enabled_post_types');

Actions

duplicate_post_pre_copy

This action is called right before cloning of the post or page starts.

In either your custom plugin or your theme's functions.php, add the following code if you want to hook into this action.

/**
 * Performs some actions before copying the post.
 */
function my_custom_duplicate_post_pre_copy() {
    // Perform some actions before copying the post.
}

add_action( 'duplicate_post_pre_copy', 'my_custom_duplicate_post_pre_copy' );

duplicate_post_post_copy

This action is called right after cloning of the post or page starts.

In either your custom plugin or your theme's functions.php, add the following code if you want to hook into this action.

/**
 * Performs some actions after copying the post.
 */
function my_custom_duplicate_post_post_copy() {
    // Perform some actions after copying the post.
}

add_action( 'duplicate_post_post_copy', 'my_custom_duplicate_post_post_copy' );

dp_duplicate_post

This action is called right after the WordPress standard fields of a post, or a non-hierarchical custom type item, have been copied. Duplicate Post itself hooks some functions to this action to copy custom fields, taxonomies, attachments, comments etc.

This action has the following parameters:

In either your custom plugin or your theme's functions.php, add the following code if you want to hook into this action.

/**
 * Performs some actions after the WordPress standard fields of a post, or a non-hierarchical custom type item, have been copied.
 * 
 * @param int $new_post_id The newly created post's ID.
 * @param WP_Post $post The original post's object.
 * @param string $status The destination status as set by the calling method: e.g. ‘draft’ if the function has been called using the “New Draft” links. Empty otherwise.
 */
function my_custom_dp_duplicate_post( $new_post_id, $post, $status ) {
    // Perform some actions after the post has been duplicated.
}

add_action( 'dp_duplicate_post', 'my_custom_dp_duplicate_post' );

dp_duplicate_page

This action is called right after the WordPress standard fields of a page, or a non-hierarchical custom type item, have been copied. Duplicate Post itself hooks some functions to this action to copy custom fields, taxonomies, attachments, comments etc.

This action has the following parameters:

In either your custom plugin or your theme's functions.php, add the following code if you want to hook into this action.

/**
 * Performs some actions the WordPress standard fields of a page, or a non-hierarchical custom type item, have been copied.
 * 
 * @param int $new_post_id The newly created post's ID.
 * @param WP_Post $post The original post's object.
 * @param string $status The destination status as set by the calling method: e.g. ‘draft’ if the function has been called using the “New Draft” links. Empty otherwise.
 */
function my_custom_dp_duplicate_page( $new_post_id, $post, $status ) {
    // Perform some actions after the post has been duplicated.
}

add_action( 'dp_duplicate_page', 'my_custom_dp_duplicate_page' );

Functions Template Tags

本页面列出了可在插件和模板文件中使用的函数,用于与 Duplicate Posts 进行交互。

duplicate_post_clone_post_link

显示一个克隆当前文章的链接,前提是用户已登录且被允许复制该文章。可在 The Loop 内部或外部使用。如果在循环外部使用,则需要传递文章 ID。 此函数仿照 edit_post_link() 设计。

用法

duplicate_post_clone_post_link( $link, $before, $after, $id );

参数

duplicate_post_get_original

返回原始文章,可以是文章对象、关联数组或数字数组(取决于 $output 参数)。如果找不到原始文章,则返回 null。

它依赖于自定义字段 _dp_original,该字段在插件每次创建副本时都会被设置为原始 ID。 此函数仿照 get_post() 设计。

用法

$original_post = duplicate_post_get_original( $post, $output );

参数

duplicate_post_create_duplicate

根据当前设置,复制作为第一个参数传递的文章对象。这适用于除状态之外的所有内容:除非明确指定了新状态,否则默认复制原始状态。

用法

$new_post_id = duplicate_post_create_duplicate( $post, $status, $parent_id );

参数

Overview

Yoast Duplicate Post 插件包含:

如果您对 Duplicate Post 有功能请求,可以在我们的 GitHub 仓库 中提出。

Ai Errors

import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';

This documentation provides information about the errors displayed by the Yoast AI features. All Yoast AI features connect to the Yoast AI API, which in turn connects to an AI service provider (currently: OpenAI). Below, we list the possible error messages based on the HTTP response status codes received from this API.

400 responses

This section lists the errors that are displayed for 400 Bad Request responses.

"Unethical request" error

The Yoast AI features rely on the Azure OpenAI content filtering system, which has a policy against unethical content. The system flags input that includes hate, violence, self-harm references or sexual content.

This error message is displayed when your content does not pass the content filter system.

:::note[Error message] Usage policy violation
Due to OpenAI's strict ethical guidelines and usage policies, we cannot generate suggestions for the content on this page. If you intend to use AI, kindly avoid the use of explicit, violent, copyrighted, or sexually explicit content. In case you need further help, please contact our support team. Error example The message includes links to: the OpenAI usage policies and the Yoast Support page (for WordPress sites it's the one within the Yoast SEO plugin's Settings; for Shopify sites -- the one on [yoast.com](docs/features/ai/ https:/yoast.com/help/support#support-shopify)). :::

"Not enough content" error

The use of Yoast AI requires enough content in order to generate valuable AI suggestions. This error message is displayed when there is not enough content on the page.

:::note[Error message] Not enough content
Please add more content to ensure a valuable AI suggestion. Learn more on our page about common AI feature problems and errors. In case you need further help, please contact our support team. Error example The message includes links to: an article on common AI feature problems and errors and the Yoast Support page (for WordPress sites it's the one within the Yoast SEO plugin's Settings; for Shopify sites -- the one on [yoast.com](docs/features/ai/ https:/yoast.com/help/support#support-shopify)). :::

"Site unreachable" error

The use of Yoast AI requires your site to be publicly accessible. This error message is displayed when that is not the case.

:::note[Error message] Yoast AI cannot reach your site
To use this feature, your site must be publicly accessible. This applies to both test sites and instances where your REST API is password-protected. Please ensure your site is accessible to the public and try again. Learn more on our page about common AI feature problems and errors. In case you need further help, please contact our support team. Error example The message includes links to: an article on common AI feature problems and errors and the Yoast Support page (for WordPress sites it's the one within the Yoast SEO plugin's Settings; for Shopify sites -- the one on [yoast.com](docs/features/ai/ https:/yoast.com/help/support#support-shopify)). :::

"Bad WordPress request" error

In the Yoast SEO plugins for WordPress, we use the WordPress REST interface to connect to our Yoast AI API. This error message is displayed if something went wrong in that connection.

:::note[Error message] Something went wrong
The request came back with the following error: 'test'. Please try again later. If the issue persists, please contact our support team. Error example The message includes a link to the Yoast Support page (for WordPress sites it's the one within the Yoast SEO plugin's Settings; for Shopify sites -- the one on [yoast.com](docs/features/ai/ https:/yoast.com/help/support#support-shopify)). :::

402 responses

This section lists the errors that are displayed for 402 Payment Required responses.

Subscription error

The use of Yoast AI requires an active subscription for your site. Please refer to this page to understand the subscriptions required to use the Yoast AI features.

This error message is displayed when you do not have the required active subscriptions. The error message and links are adapted depending on the missing subscription(s).

:::note[Error message] Subscription required
For one subscription
To access this feature, you need an active Yoast SEO Premium subscription. Please activate your subscription in MyYoast or get a new Yoast SEO Premium subscription. Afterward, refresh this page. It may take up to 30 seconds for the feature to function correctly.
For two subscriptions
To access this feature, you need active Yoast SEO Premium and Yoast WooCommerce SEO subscriptions. Please activate your subscriptions in MyYoast or get a new Yoast WooCommerce SEO Premium bundle. Afterward, refresh this page. It may take up to 30 seconds for the feature to function correctly. Error exampleError example Depending on the missing subscription(s), the message includes links to: an article on how to activate a Yoast subscription as well as pages on upgrading to Yoast SEO Premium, to Yoast WooCommerce SEO, and to the Yoast WooCommerce Premium bundle. :::

408 responses

This section lists the errors that are displayed for 408 Request Timeout responses.

"Request timeout" error

The Yoast AI features expect a response from the Yoast AI API within 60 seconds. This error message is displayed when the response is not retrieved within 60 seconds.

:::note[Error message] Connection timeout
It seems that a connection timeout has occurred. Please check your internet connection and try again later. Learn more on our page about common AI feature problems and errors. In case you need further help, please contact our support team. Error example The message includes links to: an article on common AI feature problems and errors and the Yoast Support page (for WordPress sites it's the one within the Yoast SEO plugin's Settings; for Shopify sites -- the one on [yoast.com](docs/features/ai/ https:/yoast.com/help/support#support-shopify)). :::

429 responses

This section lists the errors that are displayed for 429 Too Many Requests responses.

Rate limit error

The use of Yoast AI comes with rate limits to ensure fair use. Moreover, we are in the process of introducing a subscription-based limit on the amount of AI generations per month. Please refer to this page to understand the rate limits that are applicable.

This error message is displayed when you surpass your Yoast AI sparks limit or one of the Yoast AI rate limits.

:::note[Error message] You've reached the Yoast AI rate limit
You might have reached your Yoast AI rate limit for a specific time frame or your sparks limit for this month. If you have reached your rate limit, please reduce the frequency of your requests to continue using Yoast AI features. Our help article provides guidance on effectively planning and pacing your requests for an optimized workflow. Error example The message includes a link to an article on the rate limits for the AI features in Yoast SEO Premium. :::

Generic error

For all other error responses, the Generic error is displayed. This includes the following cases: when we are not able to further refine the 400 error response; 403 Forbidden and 503 Service Unavailable responses; other error responses (i.e., which are not 200 OK).

:::note[Error message] Something went wrong
Please try again later. If this issue persists, you can learn more about possible reasons for this error on our page about common AI feature problems and errors. In case you need further help, please contact our support team.> Error example The message includes links to: an article on common AI feature problems and errors and the Yoast Support page (for WordPress sites it's the one within the Yoast SEO plugin's Settings; for Shopify sites -- the one on [yoast.com](docs/features/ai/ https:/yoast.com/help/support#support-shopify)). :::

Other errors

This section lists other errors that can be displayed during the use of Yoast AI features, unconnected to the Yoast AI API.

"SEO analysis required" error

Our Yoast AI features require the SEO analysis to be enabled in the settings. When a user tries to make use of the AI features without having the SEO analysis enabled, this error message is displayed.

:::note[Error message] SEO analysis required
Yoast AI requires the SEO analysis to be enabled. To enable it, please navigate to Site features in Yoast SEO, turn on the SEO analysis, and click 'Save changes'. If it's disabled in your WordPress user profile, access your profile and enable it there. Please contact your administrator if you don't have access to these settings. Error example The message includes a link to the Site features admin page within the Yoast SEO plugin's Settings. :::

模拟这些错误

在 WordPress 中

如果你需要在不实际触发这些错误的情况下显示它们(例如,在不实际达到速率限制的情况下显示速率限制错误),可以通过编辑 Yoast SEO 仓库 中 src/ai-generator/user-interface/get-suggestions-route.php 文件里的 get_suggestions 函数来实现。 为此,请将 try 代码块的内容替换为下面的一个错误。同时,将后续 catch 代码块中的变量替换为错误名称(例如 Bad_Request_Exception),并将该错误添加到导入语句中(例如 use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Bad_Request_Exception;)。


从以下错误中选择一个:

```php
throw new Bad_Request_Exception( "test", 400, "AI_CONTENT_FILTER" );     // 不道德的请求错误
throw new Bad_Request_Exception( "test", 400, "NOT_ENOUGH_CONTENT" );    // 内容不足错误
throw new Bad_Request_Exception( "test", 400, "WP_HTTP_REQUEST_ERROR" ); // WordPress 请求错误
throw new Bad_Request_Exception( "test", 400, "SITE_UNREACHABLE" );      // 站点无法访问错误
throw new Bad_Request_Exception( "test", 400, "other" );                 // 通用错误
throw new Payment_Required_Exception( "test", 402, "test", null, ['Yoast SEO Premium'] ); // 订阅错误,将数组替换为缺失的许可证
throw new Request_Timeout_Exception( "test", 408, "test" );              // 请求超时错误
throw new Too_Many_Requests_Exception( "test", 429, "test" );            // 速率限制错误

Embedded

This documentation provides technical information about how Yoast SEO modifies embedded content formats, such as oEmbed.

Native embedding

Platforms like WordPress often support proprietory content embedding capabilities. These allow users to represent the content of a page as an embedded widget, in the content of another page. These embedded components exist as discreet URLs and 'pages', and must be optimized accordingly.

For sites which suppport native embedding, Yoast SEO should:

oEmbed

For sites which provide oEmbed capabilities, Yoast SEO alters the respective metadata to ensure that it's consistent with the respective 'parent' page. There should be no discrepancy between the title, description, (canonical) url or other properties, as compared to the embedded page.

Rss Feeds

This documentation provides technical information about how Yoast SEO modifies RSS feeds.

For any feed(s) of public posts/pages*, Yoast SEO allows the user to automatically prefix and/or append a dynamic content string (for the purposes of branding, attribution or similar).

* Excluding WordPress' "comment feeds".

Default settings

The default prefix setting ("Content to put before each post in the feed") is blank/empty.

The default suffix setting ("Content to put after each post in the feed") is The post %%POSTLINK%% appeared first on %%BLOGLINK%%..

Replacement variables

The following variables are available, and are automatically replaced in the generated prefix/suffix.

Variable Description
%%AUTHORLINK%% A link to the archive for the post author, with the author's name as anchor text.
%%POSTLINK%% A link to the post, with the title as anchor text.
%%BLOGLINK%% A link to your site, with your site's name as anchor text.
%%BLOGDESCLINK%% A link to your site, with the site's name and description as anchor text.

Indexing & canonical URLs

Depending on the type of feed, additional HTTP headers must be set:

Overview

This documentation provides information about how Yoast SEO analyzes content.

Content analysis & meta controls

Our JavaScript content analysis tool, YoastSEO.js, is able to analyze content for a whole range of SEO, accessibility and readability checks. It contains a 'snippet editor', which generates previews of how the page will (probably) be displayed in Google desktop or mobile search. It also contains an editor in which users can edit the page's title, URL and meta description intuitively.

Example of the SEO & accessibility checks Example of the SEO & accessibility checks

Example of the readability checks Example of the SEO & accessibility checks

Snippet editor (desktop) Snippet editor (desktop)

Snippet editor (mobile) Snippet editor (mobile)

These UI elements should be integrated into the content editing experience and should update in a real-time or semi-realtime manner. For inspiration, implementors are recommended to take a look at how these elements are integrated into Yoast SEO for WordPress.

Advanced page controls

Users should also be able to define the following for any given page:

Setting Behavior
Whether search engines should show the page in search results (yes / no) Sets a meta robots value of noindex
Whether search engines should follow links on the page (yes / no) Sets a meta robots value of nofollow
Advanced meta robots directives Allows users to select additional (valid) meta robots values
Breadcrumbs title Defines the title of the page as/when used in breadcrumbs (markup and structured data)
Canonical URL Allows users to define a custom canonical URL value

Schema controls

Setting Behavior
Page type The type(s) of WebPage that should be used in the schema graph
Article type The type(s) of Article that should be used in the schema graph. Only available on 'post' types.

Breadcrumbs

This documentation provides information about the breadcrumb block that Yoast SEO provides.

:::caution Breadcrumbs in local environments In local environments, or in websites that disable the indexable creation in general, breadcrumbs are not working properly.

Specifically, in those setups the hierarchy of a breadcrumb path might be missing intermediate nodes like the category a post belongs to, or a parent page of a subpage. :::

过滤器

修改输出内容 {#breadcrumb_output}

wpseo_breadcrumb_output 过滤器用于处理面包屑导航的输出。请确保不要清空输出内容,仅进行替换或添加数据。示例如下:

add_filter( 'wpseo_breadcrumb_output', 'override_home_icon' );

/**
 * 将首页文本替换为图标。
 *
 * @param  string $original_breadcrumbs 完整的面包屑导航 HTML。
 * @return string
 */
function override_home_icon( string $original_breadcrumbs ): string {
    $new_home = \str_replace( '>Home<', ' aria-label="Home"><i class="fa fa-homeicon" aria-hidden="true"></i><', $original_breadcrumbs );

    return $new_home;
}

修改单个面包屑链接 {#breadcrumb_single_link}

wpseo_breadcrumb_single_link 过滤器允许修改由 Yoast SEO 面包屑类输出的每个链接。它接收生成的链接和源面包屑信息。示例如下:

add_filter( 'wpseo_breadcrumb_single_link', 'remove_current_page' );

/**
 * 从面包屑中移除当前页面。
 *
 * @param string $link       输出的字符串。
 * @param array  $breadcrumb 面包屑链接数组。
 */
function remove_current_page( $link ): string {
	if ( strpos( $link, 'breadcrumb_last' ) !== false ) {
		$link = '';
	}

	return $link;
}

更改 HTML ID {#breadcrumb_output_id}

wpseo_breadcrumb_output_id 允许更改 Yoast SEO 面包屑导航包装元素的 HTML ID。仅当当前不存在 ID 时才会调用此过滤器。示例:

add_filter( 'wpseo_breadcrumb_output_id', 'add_breadcrumb_id' );

/**
 * 允许更改 Yoast SEO 面包屑导航包装元素的 HTML ID。
 *
 * @param string $id 要添加到包装元素的 ID。
 */
function add_breadcrumb_id( $id ): string {
  return 'my-id';
}

更改 HTML 类 {#breadcrumb_output_class}

wpseo_breadcrumb_output_class 过滤器允许更改 Yoast SEO 面包屑导航包装元素的 HTML 类。仅当当前不存在类时才会调用它。示例:

add_filter( 'wpseo_breadcrumb_output_class', 'add_breadcrumb_class' );

/**
 * 允许更改 Yoast SEO 面包屑导航包装元素的 HTML 类。
 *
 * @param string $class 要添加到包装元素的类。
 */
function add_breadcrumb_class( $class ): string {
  return 'my-class my-other-class';
}

更改输出包装器元素 {#breadcrumb_output_wrapper}

wpseo_breadcrumb_output_wrapper 允许更改 Yoast SEO 面包屑导航的包装器元素。它默认以 span 值被调用。输出应为一个有效的 HTML 元素。示例:

add_filter( 'wpseo_breadcrumb_output_wrapper', 'replace_breadcrumb_wrapper' );

/**
 * 替换面包屑导航的包装器
 *
 * @param  string $output 通常输出的内容。
 * @return string
 */
function replace_breadcrumb_wrapper( string $output ): string {
    $output = 'ol';

    return $output;
}

更改链接包装器 {#breadcrumb_link_wrapper}

wpseo_breadcrumb_single_link_wrapper 过滤器允许更改 Yoast SEO 的链接包装器元素。它默认以 span 值被调用。输出应为一个有效的 HTML 元素。示例如下:

add_filter( 'wpseo_breadcrumb_single_link_wrapper', 'replace_link_wrapper' );

/**
 * 替换链接周围的包装器。
 *
 * @param  string $output 通常输出的内容。
 * @return string
 */
function replace_link_wrapper( string $output ): string {
    $output = 'li';

    return $output;
}

更改分隔符 {#breadcrumb_separator}

wpseo_breadcrumb_separator 过滤器允许更改 Yoast SEO 的分隔符。它仅在调用时带有一个从 Yoast SEO 设置中获取的默认值。输出应为有效的 HTML。以下是一个完全移除分隔符的示例:

add_filter( 'wpseo_breadcrumb_separator', 'replace_breadcrumb_separator' );

/**
 * 替换面包屑导航的分隔符。
 *
 * @param  string $output 通常输出的内容。
 * @return string
 */
function replace_breadcrumb_separator( string $output ): string {
    $output = '';

    return $output;
}

架构输出

Yoast SEO 面包屑区块会输出一个面包屑架构片段,相关文档位于 架构片段 - 面包屑

Link Attributes

This documentation provides technical information about Yoast SEO manages link attributes.

We provide tools which allow users to declare special properties of links within editable content areas, in order to help them adhere to search engine guidelines.

Search engine guidelines

Search engines typically require that links which are sponsored (or otherwise incentivised) - i.e., those bought/sold, placed in exchange for goods or services, or which are otherwise 'non-organic' - to be disclosed. To do this, a rel (relational) attribute with a value of nofollow, sponsored or ugc must be added to the anchor (<a>) tag in question.

Our article, "What are sponsored, nofollow and ugc links, and why use them?", describes the difference between these attributes.

Controls

Yoast SEO provides a UI control for users when placing links, which allows users to select how a link should be treated.

Our help article, "Which link settings should I use?" describes these options and their behaviors.

Overview

This documentation provides information about the types of controls that Yoast SEO provides.

Global site controls

As a site owner, I want to be able to set the following metadata globally.

Default meta options

Option Description Values
og:site_name The name of the site for when shared in social platforms string
twitter:site The X handle of the X profile associated with the site string
Social profiles An array of the URLs of social media properties representing the publisher array
Title separator The character used to delimit a page's 'name' and brand components in the title tag char

Publisher information

Option Description Values
Publisher type Whether the site represents a user or organization user or organization
organization.name The name of the organization which publishes the site. string
organization.logo An image representing the organization which publishes the site. string
person.name The name (and identity, e.g., a user ID/profile) of the person who publishes the site. string
person.logo An image representing the person who publishes the site. string

NB: Only values for one of organization or person fields are required, depending on the Publisher type.

Template controls

For each template (and/or content) type, the user should be able to set:

Option Description Values
Title patterns The default pattern which title values should use string supporting replacement vars
Description patterns The default pattern which meta description values should use string supporting replacement vars
Meta robots values Crawling/indexing controls which deviate from the defaults One or more of index, noindex, follow, nofollow, noodp, noimageindex, noarchive, nosnippet

Page-level controls

For each page, the user should be able to preview and control their SEO settings via our analysis tools

Misc Tools

Tools must be provided to allow users to manage:

Feature controls

It must be possible to enable/disable the following features (when available):

Functional Specification

Yoast SEO 构建并优化多种 HTTP 标头,以管理索引、协助调试并补充其他功能。

x-robots-tag

有时需要防止搜索引擎抓取或索引“非页面”资源(如 PDF 或 XML 文件等不支持 HTML <head> 标记的资源)。 当内容管理系统提供这些资源时,我们可以操作其 HTTP 标头,通过 x-robots-tag HTTP 标头提供抓取和索引指令。

我们在以下情况下执行此操作:

x-redirected-by

当我们的重定向管理器执行重定向时,我们将 'Yoast SEO Premium' 值传递给 WordPress 的 wp_redirect() 函数。这会在响应中添加 x-redirected-by 标头,有助于调试。

Filters

简介

在处理这些过滤器之前,请确保您通过阅读功能规范以及可能还有技术规范来理解什么是 Indexables 及其用途。

完全禁用索引项

默认情况下,根据技术规范,Yoast SEO 会持续为网站上所有可优化的内容创建索引项。这包括文章、页面、自定义文章类型,以及分类法(分类、标签和自定义类型)、用户等的归档页面。

在某些情况下,用户可能希望禁用索引项的创建。当 Yoast\WP\SEO\should_index_indexables 过滤器设置为 false 时,插件将停止存储新创建的索引项。

:::note 在核心函数 wp_get_environment_type() 返回值不是 production 的网站上,我们默认已经禁用了索引项。 :::

:::caution 禁用索引项时的差异 当禁用索引项创建时,以下功能无法完全正常工作:

代码示例

add_filter( 'Yoast\WP\SEO\should_index_indexables', '__return_false' );

排除内容

在 Indexables 的技术规范中,我们概述了何时为文章类型和分类法创建 Indexables。如果出于某种原因,您将文章类型或分类法注册为 public,并且(对于文章类型)它们具有 publish 文章状态,但您不在网站前端使用这些 URL,您应该将它们排除在 Indexables 创建之外。

:::danger 警告 如果您需要这些过滤器,您可能“做错了”。

如果您将文章类型或分类法注册为 public,它们总是会出现在不应该出现的地方。只有当您打算在网站前端使用这些 URL 时,才应该将它们注册为 public。使用这些过滤器当然由您自己承担风险,并且不意味着搜索引擎无法看到这些对象中的内容。 :::

:::caution 设置为 noindex 的文章类型 如果页面出现在前端,我们就需要 Indexables。

因此,即使您已将文章类型设置为 noindex,如果 URL 将显示在前端,我们仍然需要为其创建 Indexable。 :::

排除文章类型 {#post_types}

wpseo_indexable_excluded_post_types 过滤器接收一个被排除的文章类型数组。请确保不要破坏该数组,只需向其添加内容。示例如下:

add_filter( 'wpseo_indexable_excluded_post_types', 'do_not_create_indexables_for_orders' );

/**
 * 将订单从 Yoast SEO 可索引对象创建中排除。
 *
 * @link https://developer.yoast.com/features/indexables/indexables-filters/#post_types
 * 
 * @param string[] $excluded_post_types 按名称排列的被排除文章类型数组。
 * 
 * @return string[]
 */
function do_not_create_indexables_for_orders( $excluded_post_types ) {
    $excluded_post_types[] = 'orders';
    return $excluded_post_types;
}

排除分类法 {#taxonomies}

wpseo_indexable_excluded_taxonomies 过滤器接收一个已排除文章类型的数组。请确保不要破坏该数组,只需向其添加内容。示例如下:

add_filter( 'wpseo_indexable_excluded_taxonomies', 'do_not_create_indexables_for_order_types' );

/**
 * 从 Yoast SEO 可索引对象创建中排除订单类型。
 *
 * @link https://developer.yoast.com/features/indexables/indexables-filters/#taxonomies
 * 
 * @param string[] $excluded_taxonomies 按名称排列的已排除分类法数组。
 * 
 * @return string[]
 */
function do_not_create_indexables_for_order_types( $excluded_taxonomies ) {
    $excluded_taxonomies[] = 'order_type';
    return $excluded_taxonomies;
}

强制包含内容

强制包含文章类型 {#included_post_types}

wpseo_indexable_forced_included_post_types 过滤器接收一个文章类型数组,系统将为这些类型创建索引。请确保不要破坏该数组,只需向其添加内容。示例如下:

add_filter( 'wpseo_indexable_forced_included_post_types', 'force_create_indexables_for_non_public_post_type' );

/**
 * 强制为原本不符合 Yoast SEO 索引创建条件的文章类型创建索引。
 *
 * @link https://developer.yoast.com/features/indexables/indexables-filters/#included_post_types
 * 
 * @param string[] $included_post_types 按名称排列的已包含文章类型数组。
 * 
 * @return string[]
 */
function force_create_indexables_for_non_public_post_type( $included_post_types ) {
    $included_post_types[] = 'not_public_post_type';
    return $included_post_types;
}

强制创建并使用附件索引化对象 {#force_creating_and_using_attachment_indexables}

要使用索引化对象而非内容扫描来查找所有内容图片,请按如下所示启用此过滤器。请参阅自定义 Yoast SEO 处理附件的方式以了解此功能可能适用的场景。

add_filter( 'wpseo_force_creating_and_using_attachment_indexables', '__return_true' );

Functional Specification

Yoast SEO 的 "Indexables" 框架提供了一个抽象层,用于处理与 SEO 相关的文章元数据。

以页面为中心的网页模型

我们软件的大部分功能是存储、管理和评估与 页面 相关的信息。每个页面都有一个唯一的(规范)URL。

这是大多数搜索引擎和系统对网页的'思考'方式。它们基于 URL 为所有已知页面构建一张 地图。我们也采用同样的方法。有了这张地图,我们就能轻松检查、更新和管理特定页面的信息。

表面上看,这似乎是一个简单的概念。但像'页面'这样的词隐藏着复杂性和细微差别——尤其是在 WordPress 的语境中。

例如,在 WordPress 中,存储在数据库中的文章 并不附带 URL。每次系统需要知道页面 URL 时,都必须进行 计算(基于用户为网站定义的 URL 结构设置)。这在计算上是昂贵的。

但处理开销并非唯一的挑战——还存在一些场景,其中'页面'的含义并不明确。

但什么是页面?

除了我们通常理解的网站"页面"概念,我们可能还会遇到 归档 视图(例如某作者发布的所有文章)、替代内容 格式(例如 RSS 源)、分类法(例如标签和分类)、错误模板(例如 404 页面)、分页 结果以及其他特殊类型的内容。在搜索引擎看来,这些都属于"页面"。

从 SEO 的角度来看,每种情况都需要不同的处理方式——各自有其规则和条件。即使是一篇简单的博客文章,也可能有 数十个 需要我们考量和评估的数值。这些包括爬取和索引控制、内容评估分数、关键词、展示设置、媒体资源等等。在确定页面上应输出哪些 SEO 元数据的过程中,我们必须考虑所有这些字段及其相互关系。

例如,仅确定页面的合适 规范 URL 就需要大量的查询和评估。

对于大型网站,所有这些逻辑、存储和处理都可能影响性能——尤其是在 WordPress 中,其数据库结构并非为此类需求设计或优化。

此外,网站包含许多我们 不 希望进行 SEO 评估的"页面"。某些内容类型可能存在于系统中(例如仅用于管理视图),但从未在公开 URL 上暴露。存储和处理这些信息没有意义,因为它们无法被搜索引擎 索引。

明确哪些内容 可索引、哪些 不可索引,是实现高效元数据管理的关键。

什么是可索引项?

可索引项 是指任何能够(理论上)被搜索引擎根据给定 URL 索引 的资源。这包括许多超出“页面”范畴的内容类型——例如分类目录、作者归档、日期归档的分页状态、媒体文件等。

示例:

注意:我们有意_排除_任何非公开页面以及返回错误的页面。

WordPress 中的 Yoast SEO 索引表

Yoast SEO 通过专用数据库表在 WordPress 中创建并管理索引表。该表存储了从 SEO 角度所需的所有信息,涵盖我们已知的每个可索引对象。这意味着当我们需要查询特定页面以确定 SEO 元数据时,可以极其高效地完成此操作。

此过程在后台静默运行,并与 WordPress 原生元数据字段及流程无缝同步。

该表还会自动填充和更新自身。当我们遇到未知的可索引对象时,会创建新记录,以便后续请求可使用该数据。我们还在管理工具中提供了(重新)索引流程,可主动根据网站数据库构建索引表。

通过建立索引表,我们获得了以“SEO 为中心”的网站视图,重点关注页面(及其应输出的元数据)。

索引机制

我们的索引表通过两种方式构建和维护:

这些流程确保索引表始终完整且准确地反映网站状态。

Yoast SEO 存储哪些类型的可索引对象?

我们存储的可索引对象类型包括:

我们还存储了几种“模式”,这些模式代表 模板 和内容类型,为每个可能的排列创建独立可索引对象既无价值也无必要。这些模式包括:

*当文章/分类法类型的 public 属性在 register_post_type/register_taxonomy 中设置为 true 时,我们即认为该页面是“公开”的。

使用场景

当我们全面掌握网站所有公开页面后,即可利用数据库驱动功能与工具。例如:

修改索引对象行为

大多数用户无需直接与索引对象表或逻辑交互。但高级用户可能希望自定义行为以满足特定需求。 为此,我们提供了一系列过滤器来修改默认行为或与数据表交互:

Technical Specification

简介

我们为网站上的所有可索引对象创建了索引项。这些是搜索引擎能够访问的所有 URL。由于 WordPress 存储内容的方式,其中大部分将是“文章”(在 WordPress 数据模型中,页面也被视为“文章”)。因此,大多数网站上的索引项主要是文章。

我们为某些特殊内容创建了专门的索引项,以避免它们轻易地堵塞数据库。这尤其适用于日期归档页面和系统页面(搜索结果页、404 页面)。它们各自拥有一个索引项,用于存储我们的标题模板,以便在前端使用这些模板。

Which "things" get an indexable?

Posts

There are quite a few different types of using posts within WordPress. Only those that are publicly viewable should be considered indexable objects. Of course a post has to exist to be a valid Indexable object.

%%{init: {'theme': 'base', 'themeVariables': { 'edgeLabelBackground':'#ffffff', 'stroke':'#000'}}}%%
 flowchart TD
  Input([Post]) --> PTRegistered{Post <em>type</em> registered?}
  PTRegistered == Yes ==>PTExcluded{Post <em>type</em> excluded<br/><a href='/features/indexables/indexables-filters/#post_types'>through filter</a>?}
  PTRegistered -- No -->NoIndexable([Do <strong>not</strong> create an indexable])
  PTExcluded -- Yes -->PTIncluded{Post <em>type</em> force included<br/><a href='/features/indexables/indexables-filters/#included_post_types'>through filter</a>?}
  PTExcluded == No ==>PTPublic{Is the<br/>post <em>type</em> <code>public</code>?}
  PTPublic == Yes ==>PTAttachment{Is the post<br/><em>type</em> <code>attachment</code>?}
  PTPublic == No ==>PTIncluded
  PTAttachment -- Yes -->AttDisabled{Are <a href='https://yoast.com/features/redirect-attachment-urls/'>attachment URLs<br/> disabled</a> in Yoast SEO?}
  AttDisabled -- Yes -->PTIncluded
  AttDisabled == No ==>PSRegistered{Post <em>status</em> registered?}
  PTAttachment == No ==>PSRegistered
  PSRegistered -- No -->NoIndexable
  PSRegistered == Yes ==>PSPublic{Is the post<br/><em>status</em> <code>public</code>?}
  PSPublic -- No -->IncludedNonPublishPostStatus{Post <em>status</em> one of:<br/><code>draft</code>, <code>pending</code>, <code>future</code>?}
  PTIncluded == No -->NoIndexable
  PTIncluded == Yes -->CreateIndexable
  IncludedNonPublishPostStatus -- No -->NoIndexable
  IncludedNonPublishPostStatus == Yes ==>CreateIndexable
  PSPublic == Yes ==>CreateIndexable([Create an indexable])
  style Input fill:#ddf,stroke:#aaf,stroke-width:2px
  style NoIndexable fill:#ffcccc,stroke:#f00,stroke-width:2px
  style CreateIndexable fill:#ccffcc,stroke:#0f0,stroke-width:2px

Post type archives

A post type archive will only have an indexable when the post type it's registered for is public.

%%{init: {'theme': 'base', 'themeVariables': { 'edgeLabelBackground':'#ffffff', 'stroke':'#000'}}}%%
 flowchart TD
  Input([Post type archive]) --> PTRegistered{Post <em>type</em> registered?}
  PTRegistered == Yes ==>PTPublic{Is the<br/>post <em>type</em> <code>public</code>?}
  PTRegistered -- No -->NoIndexable([Do <strong>not</strong> create an indexable])
  PTPublic == Yes ==>CreateIndexable([Create an indexable])
  PTPublic -- No -->NoIndexable
  style Input fill:#ddf,stroke:#aaf,stroke-width:2px
  style NoIndexable fill:#ffcccc,stroke:#f00,stroke-width:2px
  style CreateIndexable fill:#ccffcc,stroke:#0f0,stroke-width:2px

Terms

A term has to be part of a registered, public taxonomy to be an indexable object.

%%{init: {'theme': 'base', 'themeVariables': { 'edgeLabelBackground':'#ffffff', 'stroke':'#000'}}}%%
 flowchart TD
  Input([Term]) --> TaxRegistered{<em>Taxonomy</em> registered?}
  TaxRegistered == Yes ==> TaxExcluded{Is the <em>taxonomy</em><br>excluded <a href='/features/indexables/indexables-filters/#taxonomies'>through filter</a>?}
  TaxRegistered -- No -->NoIndexable([Do <strong>not</strong> create an indexable])
  TaxExcluded -- Yes -->NoIndexable
  TaxExcluded == No ==>TaxPublic{Is the<br><em>taxonomy</em> public?}
  TaxPublic == Yes ==>TaxForbidden{Is the<br><em>taxonomy</em> one of:<br/><code>post_format</code>?}
  TaxPublic -- No -->NoIndexable
  TaxForbidden -- Yes -->NoIndexable
  TaxForbidden == No ==>CreateIndexable([Create an indexable])
  style Input fill:#ddf,stroke:#aaf,stroke-width:2px
  style NoIndexable fill:#ffcccc,stroke:#f00,stroke-width:2px
  style CreateIndexable fill:#ccffcc,stroke:#0f0,stroke-width:2px

Users

Only when a user has published posts that are public, do they get an author archive. At that point, they become an indexable object.

%%{init: {'theme': 'base', 'themeVariables': { 'edgeLabelBackground':'#ffffff', 'stroke':'#000'}}}%%
 flowchart TD
  Input([User]) --> AuthorArchivesDisabled{Are author archives<br/>disabled in Yoast SEO?}
  AuthorArchivesDisabled -- Yes -->NoIndexable([Do <strong>not</strong> create an indexable])
  AuthorArchivesDisabled == No ==>UserHasPosts{Does the user have<br/><em>published, public</em> posts?}
  UserHasPosts == Yes ==>CreateIndexable([Create an indexable])
  UserHasPosts -- No -->NoIndexable
  style Input fill:#ddf,stroke:#aaf,stroke-width:2px
  style NoIndexable fill:#ffcccc,stroke:#f00,stroke-width:2px
  style CreateIndexable fill:#ccffcc,stroke:#0f0,stroke-width:2px

Indexnow

This documentation provides information about how Yoast SEO integrates with IndexNow.

Ping on update

Similarly to how we handle XML Sitemaps, whenever a URL is updated (as a result of a post, page, or URL being created/updated), then the system should ping an IndexNow endpoint.

Keys & URLs

Pending

Site Connections

This documentation provides information about how Yoast SEO integrates with various search engine's tools and portals.

We provide tools for managing verification codes for Baidu Webmaster Tools, Bing Webmaster Tools, Google Search Console, and Yandex Webmaster Tools.

Managing verification codes

Each of the above vendors expects a distinct format, and has its own validation rules. We sanitize and validate inputs in line with those.

Details pending

Outputting codes

Each validated input should result in a meta tag (with a distinct format in each case) being added to the <head> on the homepage only.

Details pending

Functional Specification

本文档解释了 Yoast SEO 如何生成 llms.txt 文件。

Yoast SEO 的 llms.txt 功能有什么作用?

Yoast SEO 如何选择要包含在 llms.txt 文件中的内容?

文章/页面/自定义文章类型

分类/标签/自定义分类法术语

如何删除 llms.txt 文件?

如何创建自己的 llms.txt 文件?

已知限制

过滤器

llms.txt 功能提供了几个可用的过滤器。

更改文件路径根目录的获取方式,以放置 llms.txt 文件

add_filter( 'wpseo_llmstxt_filesystem_path', 'custom_llmstxt_file_path' );

/**
 * 使用 WP_CONTENT_DIR 常量来获取服务器的 Web 根目录,而不是默认方式。
 *
 * @return string 修改后的 Web 根目录。
 */
function custom_llmstxt_file_path() {
    return dirname( WP_CONTENT_DIR );
}

编辑 llms.txt 文件中添加的 BOM 前缀

add_filter( 'wpseo_llmstxt_encoding_prefix', 'custom_llmstxt_encoding_prefix' );

/**
 * 从 llms.txt 文件中移除 UTF-8 BOM 前缀并添加 UTF-16 BOM 前缀。
 *
 * @return string 编辑后的前缀。
 */
function custom_llmstxt_encoding_prefix() {
    return "\xFF\xFE";
}
add_filter( 'wpseo_llmstxt_encoding_prefix', 'custom_llmstxt_encoding_prefix' );

/**
 * 从 llms.txt 文件中移除 UTF-8 BOM 前缀。
 *
 * @return string 被移除的前缀。
 */
function custom_llmstxt_encoding_prefix() {
    return "";
}

Edit the description in the posts lists

## Posts
- [Post with excerpt](http://example.com/post-with-excerpt/): This is a custom excerpt.
- [Post without excerpt](http://example.com/post-without-excerpt/)
<?php

/**
 * Changes the link description in the llms.txt file.
 *
 * @param string $link_description The description of the link.
 * @param string $post_id          The ID of the post that is being added as a link.
 * @param string $post_type        The post type of the post that is being added as a link.
 *
 * @return string The changed link description in the llms.txt file.
 */
function set_custom_llmstxt_link_description( $link_description, $post_id, $post_type ) {
    // Remove excerpt for specific post types.
    if ( in_array( $post_type, [ 'page' ], true ) ) {
      return '';
    }

    // Keep excerpt for other post types but make it more descriptive.
    return 'Custom description for post ID ' . $post_id . ', ' . $link_description;
}

add_filter( 'wpseo_llmstxt_link_description', 'set_custom_llmstxt_link_description', 10, 3 );
## Pages
- [Page with excerpt](http://example.com/page-with-excerpt/)

## Posts
- [Post with excerpt](http://example.com/post-1): Custom description for post ID 1, this is the custom excerpt.

Extensions And Addons

This documentation provides technical information about which additional meta tags our extensions and addons generate and output.

Yoast WooCommerce SEO plugin

The following tags are added to (or altered on) product pages when this plugin is enabled.

Tag Description
og:type product
og:availability The stock availability status.
product:retailer_item_id The product's retailer ID.
product:condition The product's condition.
product:availability The stock availability status.

On simple products only

Tag Description
product:price:amount The price (omitting a currency symbol, and using . as a delimiter).
product:price:currency The currency code.

Yoast Video SEO plugin

The following tags are added to pages containing an embedded video when this plugin is enabled.

Tag Description
og:video The URL of the video asset.
og:video:width The width of the embedded video.
og:video:height The height of the embedded video.
og:video:duration The duration of the video in seconds.
ya:ovs:adult Whether the video contains adult content.
ya:ovs:upload_date The date that the video was uploaded.
ya:ovs:allow_embed Whether other sites/users may embed the video.

Functional Specification

This documentation provides technical information about which OpenGraph and related tags that Yoast SEO generates and outputs.

We output a variety of OpenGraph and similar tags in the <head> of each page, in order to better inform data consumers about the content of the page in question. OpenGraph data is consumed by Facebook, but also by Pinterest, LinkedIn, WhatsApp and even Google.

OpenGraph metadata

Assuming that the respective admin settings are not disabled, these tags are output on all pages (except for error templates, which are described separately).

Tag Description
og:locale The page's language and target territory, falling back to language only. E.g., en_US, or simply en.
og:type The type of page/resource.

website on the homepage.
profile on user profile pages.
article on all pages/posts and other templates.
og:title The title of the page.

Outputs a value based on the following hierarchy:
  • A user-defined "Facebook title" value for the page.
  • An auto-generated "Social title" from the template in Search Appearance.
  • A user-defined "SEO title" for the page.
  • An auto-generated "SEO title" from the template in Search Appearance.
  • The social title default option (same as the SEO title): %%title%% %%page%% %%sep%% %%sitename%%. For Author & Date archives the default is just the page title.
og:url The canonical URL of the page (or the series root, in the case of paginated content).
og:site_name The name of the site.

Conditional tags

The following tags are only output when their conditions are met:

Tag Description Notes
og:description The description of the page. Outputs a value based on the following hierarchy:
  • A user-defined "Facebook description" value for the page.
  • An auto-generated "Social description" from the template in Search Appearance.
  • A user-defined "Meta description" for the page.
  • An auto-generated "Meta description" from the template in Search Appearance.
  • A user-defined excerpt for the page (only for posts).
  • An auto-generated excerpt from the page's content (for terms: the term description).
article:publisher The Facebook page URL of the publishing organization. Only populated when a value has been provided by the user.
Only populated on post and page types.
article:author The URL of the profile page of the author. Only populated when the user has a valid profile URL.
Only populated on post types.
article:modified_time A timestamp of when the page was last modified. Only populated on post and page types.
article:published_time A timestamp of when the page was published. Only populated on the post type. Use the wpseo_opengraph_show_publish_date filter to add additional posttypes.
og:image The URL of the primary image of the page. Only populated when the page contains a valid image, based on the following hierarchy:
  • A user-defined image "Facebook image" in the Social tab of the Yoast metabox.
  • A user-defined "Featured image" for the page (only for posts).
  • A prominent image from the page's content — when that image is resized in the content, we show its full-sized counterpart (this isn't true when indexables are disabled).
  • Yoast SEO WooCommerce only: Woo SEO adds one more image fallback: the first image in the Woo Product gallery.
  • The "Social default image" from the template in Search Appearance.
  • The site's fallback/default social image (set at SEO > Social > Facebook > Default image).
In case of an author archive, the image is based on the following hierarchy:
  • The "Social default image" from the template in Search Appearance.
  • The user's Gravatar image.
  • The site's fallback/default social image (set at SEO > Social > Facebook > Default image).
og:image:width The width in pixels of the og:image. Only populated when an og:image is set, and its width and height are known.
og:image:height The height in pixels of the og:image. Only populated when an og:image is set, and its width and height are known.

其他条件性标签

虽然这些并非严格意义上的开放图谱标签,但应作为本组要求的一部分进行评估和输出。

标签 描述 备注
<meta name="author" content="%NAME%" /> 文章作者姓名 仅在 post 类型中填充。

Error templates

Only output the following tags are output:

Tag Value
og:locale The page's language and target territory, falling back to language only. E.g., en_US, or simply en.
og:title The site's error page title template value.
og:site_name The name of the site.

Deprecated tags

The following tags used to be output by Yoast SEO, but have been removed in recent updates.

Tag Description Deprecated
fb:admins Legacy Facebook control. No longer appears to be supported Unknown
article:section Legacy Facebook / Open Graph tag. No longer appears to be supported. Yoast SEO v14.0 (Apr 2020)
og:updated_time Legacy Facebook / Open Graph tag. No longer appears to be supported. Yoast SEO v14.0 (Apr 2020)
og:image:secure_url Defines a (separate) URL for the featured image, available over HTTPS. Unnecessarily duplicates the og:image tag when a site is on HTTPS, and, unnecessary/omitted when the site is not. Yoast SEO v14.0 (Apr 2020)
og:video:secure_url Legacy Facebook / Open Graph tag. As per og:image:secure_url, but for the featured video (in our Video SEO for WordPress plugin). Yoast SEO v14.0 (Apr 2020)
og:image:type Defines the image format. Poor value/performance trade-off; especially as we ping Facebook on post publish, at which point they determine and cache this information themselves. Yoast SEO v14.0 (Apr 2020)
fb:app_id The Facebook App ID. Yoast SEO v15.5 (Dec 2020)

Changing Og Locale Output

当您在 Yoast SEO 中启用 OpenGraph 设置时,它会输出一个 og:locale 元标签。这个 og:locale 基于您 WordPress 安装的语言设置。

因此,如果您希望它输出 nl_NL 而非默认的 en_US,只需将 WordPress 安装的语言切换为 nl_NL。您可以在 Make WordPress 多语言团队页面上阅读更多相关信息。

修改 og:locale 标签

如果您不想更改 WordPress 安装的语言,但确实想要更改 Yoast SEO 的区域设置输出,可以通过过滤输出来实现。您应该将如下所示的代码添加到主题的 functions.php 文件中:

/**
 * 更改区域设置输出。
 * 
 * @param string $locale 当前区域设置。
 * 
 * @return string 区域设置。
 */
function yst_wpseo_change_og_locale( $locale ) {
    return 'nl_NL';
}

add_filter( 'wpseo_locale', 'yst_wpseo_change_og_locale' );

移除 og:locale 标签

如果您希望移除 og:locale 标签,需要通过将以下代码添加到您主题的 functions.php 文件中,从 wpseo_opengraph 中解除 WPSEO_OpenGraph->locale() 的挂钩。

此代码片段在大多数情况下有效,但如果您的服务器使用 PHP 5.2,它将无法工作。为了您网站的健康与安全,我们建议在进行任何代码更改之前备份您的网站和数据库,因为我们无法保证在您的特定网站上会发生什么。

function remove_locale_presenter( $presenters ) {
    return array_map( function( $presenter ) {
        if ( ! $presenter instanceof Yoast\WP\SEO\Presenters\Open_Graph\Locale_Presenter ) {
            return $presenter;
        }
    }, $presenters );
}

add_action( 'wpseo_frontend_presenters', 'remove_locale_presenter' );

有效的 og:locale 选项

OpenGraph 有一个有限的受支持 og:locale 指定列表。如果您的区域设置不在此列表中,我们的插件将输出与您语言最匹配的选项。

截至 2018 年 7 月,以下是有效的 og:locale 选项:

en_us, ca_es, cs_cz, cx_ph, cy_gb, da_dk, de_de, eu_es, en_pi, en_ud, ck_us, es_la, es_es, es_mx, gn_py, fi_fi, fr_fr, gl_es, ht_ht, hu_hu, it_it, ja_jp, ko_kr, nb_no, nn_no, nl_nl, fy_nl, pl_pl, pt_br, pt_pt, ro_ro, ru_ru, sk_sk, sl_si, sv_se, th_th, tr_tr, ku_tr, zh_cn, zh_hk, zh_tw, fb_lt, af_za, sq_al, hy_am, az_az, be_by, bn_in, bs_ba, bg_bg, hr_hr, nl_be, en_gb, eo_eo, et_ee, fo_fo, fr_ca, ka_ge, el_gr, gu_in, hi_in, is_is, id_id, ga_ie, jv_id, kn_in, kk_kz, ky_kg, la_va, lv_lv, li_nl, lt_lt, mi_nz, mk_mk, mg_mg, ms_my, mt_mt, mr_in, mn_mn, ne_np, pa_in, rm_ch, sa_in, sr_rs, so_so, sw_ke, tl_ph, ta_in, tt_ru, te_in, ml_in, uk_ua, uz_uz, vi_vn, xh_za, zu_za, km_kh, tg_tj, ar_ar, he_il, ur_pk, fa_ir, sy_sy, yi_de, qc_gt, qu_pe, ay_bo, se_no, ps_af, tl_st, gx_gr, my_mm, qz_mm, or_in, si_lk, rw_rw, ak_gh, nd_zw, sn_zw, cb_iq, ha_ng, yo_ng, ja_ks, lg_ug, br_fr, zz_tr, tz_ma, co_fr, ig_ng, as_in, am_et, lo_la, ny_mw, wo_sn, ff_ng, sc_it, ln_cd, tk_tm, sz_pl, bp_in, ns_za, tn_bw, st_za, ts_za, ss_sz, ks_in, ve_za, nr_za, ik_us, su_id, om_et, em_zm, qr_gr 和 iu_ca

Wpseo Opengraph Images

These filters allow altering an existing OpenGraph image and/or adding images to the output.

Usage

Alter the OpenGraph image for a single post

If an og:image already exists for the page/post/archive, it is replaced by the new one returned by the filter. If there is no og:image, nothing is done by the filter.

<?php
/**
 * Alter the OpenGraph image for a single post
 */
function alter_existing_opengraph_image( $image ) {
    $image_id = $an_image_id;
    $post_id = $a_post_id;
    if ( is_single( $post_id ) ) {
        $image = wp_get_attachment_image_src( $image_id )[0];
    }
    return $image;
}
add_filter( 'wpseo_opengraph_image', 'alter_existing_opengraph_image' );

The same can be done for the og:image:width, og:image:height and og:image:type tags, by using the following three filters:

<?php
/**
 * Alter the OpenGraph image width for a single post type.
 */
function change_opengraph_image_width( $width ) {
    if( is_singular( 'books' ) ) {
        return 150;
    }
    return $width;
}
/**
 * Alter the OpenGraph image height for a single post type.
 */
function change_opengraph_image_height ( $height ) {
	if( is_singular( 'books' ) ) {
        return 250;
    }
    return $height;
}
/**
 * Alter the OpenGraph image type for a single post type.
 */
function change_opengraph_image_type ( $type ) {
	if( is_singular( 'books' ) ) {
        return 'png';
    }
    return $type;
}

add_filter( 'wpseo_opengraph_image_width', 'change_opengraph_image_width' );
add_filter( 'wpseo_opengraph_image_height', 'change_opengraph_image_height' );
add_filter( 'wpseo_opengraph_image_type', 'change_opengraph_image_type' );

Add an extra image to the top of the og:image tags array

The wpseo_add_opengraph_images filter allows to add an extra image to the top of the og:image tags array. Each image added through the filter will have og:image, og:image:width and og:image:height tags.

<?php
/**
 * Add an extra image to the top of the og:image tags array
 */
function add_image_to_opengraph_tags( $image_container ) {
    $image_id = $some_id;
    $image_container->add_image_by_id( $image_id );
}
add_filter( 'wpseo_add_opengraph_images', 'add_image_to_opengraph_tags' );

Add an image to the end of the og:image tags array

The wpseo_add_opengraph_additional_images filter allows to add an image to the end of the og:image tags array. Each image added through the filter will have og:image, og:image:width and og:image:height tags.

<?php
/**
 * Add an image to the end of the og:image tags array
 */
function add_additional_image_to_opengraph_tags( $image_container ) {
    $image_id = $some_id;
    $image_container->add_image_by_id( $image_id );
}
add_filter( 'wpseo_add_opengraph_additional_images', 'add_additional_image_to_opengraph_tags' );

0074-features-robots-txt-functional-specification


title: "START YOAST BLOCK" post_status: publish comment_status: open taxonomy: category: - yoast-developer post_tag: - Robots Txt - Features - Repos

This documentation explains how Yoast SEO modifies robots.txt files.

Default content

When a site does not have a user-defined robots.txt file (at the point of installation/activation), Yoast SEO generates a default output.

# START YOAST BLOCK
# ---------------------------
User-agent: *
Disallow:

Sitemap: https://www.example.com/sitemap_index.xml
# ---------------------------
# END YOAST BLOCK

If a robots.txt file already exists and contains non-empty rules, this block should be prefixed before any existing rules.

Controls

Users must be able to edit the contents of their robots.txt file.

Api

让 Schema 更易于调试

如果您正在处理 Schema,可能会发现其可读性较差。要改变这一点,您应将 yoast_seo_development_mode 过滤器切换为 true。启用后,Yoast SEO 输出的所有 Schema 都将以美观格式呈现。

add_filter( 'yoast_seo_development_mode', '__return_true' );

注意:不建议在生产环境中保持此设置开启。

如果您正在寻找更便捷的 Schema 调试方式,可以尝试使用我们的 Yoast 测试助手插件,它能帮助您更轻松地进行调试。

在前端完全禁用 Schema

如果在 wpseo_json_ld_output 过滤器上返回 false 或空数组,你将禁用 Yoast SEO 的所有 schema 输出。

add_filter( 'wpseo_json_ld_output', '__return_false' );

技术限制:REST API 响应

当使用此过滤器(或通过其他方法,例如第三方插件)禁用 Schema 时,REST API 响应(例如 http://example.com/wp-json/wp/v2/posts/<POST_ID>)中的 yoast_head 属性会正确更新以排除 Schema 输出。然而,yoast_head_json 属性仍将包含 Schema 数据。

这是一个已知的限制,因为 HTML 和 JSON 输出使用不同的代码路径。wpseo_json_ld_output 过滤器旨在禁用前端 HTML <script> 标签的输出,但填充 yoast_head_json(该属性返回用于程序化访问的原始数据)的代码路径并未应用此过滤器。

添加或移除图谱片段

如文档所示,我们在某些页面输出了大量图谱片段。您可能需要移除部分片段,或更理想地添加自定义片段。为此应使用 wpseo_schema_graph_pieces 过滤器。

移除图谱片段

在某些情况下,您可能希望阻止某些图谱片段被输出。以下示例演示了如何排除面包屑片段在输出中显示。

// functions.php
add_filter( 'wpseo_schema_graph_pieces', 'remove_breadcrumbs_from_schema', 11, 2 );
add_filter( 'wpseo_schema_webpage', 'remove_breadcrumbs_property_from_webpage', 11, 1 );

/**
 * 从图谱收集器中移除面包屑图谱片段。
 *
 * @param array  $pieces  当前的图谱片段。
 * @param string $context 当前上下文。
 *
 * @return array 剩余的图谱片段。
 */
function remove_breadcrumbs_from_schema( $pieces, $context ) {
    return \array_filter( $pieces, function( $piece ) {
        return ! $piece instanceof \Yoast\WP\SEO\Generators\Schema\Breadcrumb;
    } );
}

/**
 * 从 WebPage 片段中移除面包屑属性。
 *
 * @param array $data WebPage 的属性。
 *
 * @return array 修改后的 WebPage 属性。
 */
function remove_breadcrumbs_property_from_webpage( $data ) {
    if (array_key_exists('breadcrumb', $data)) {
        unset($data['breadcrumb']);
    }
    return $data;
}

Enable / disabling graph pieces by filter

Sometimes you might want to enable or disable a graph piece based on other variables then the graph piece itself can determine. For instance, you might always want to show a Person on your site. You can do this by hooking into the wpseo_schema_needs_<class-name> filter. In this particular case, the code would look like this:

add_filter( 'wpseo_schema_needs_person', '__return_true' );
add_filter( 'wpseo_schema_person_user_id', function() {
    return 1; // Make sure 1 is a valid User ID.
} );

It really is as simple as returning true on that to always show it, but you can of course also hook a function there with more precise logic. The second filter is needed to provide a user ID that should be used for the Person to show, as otherwise it won't work as the code doesn't know which user to show.

添加图谱片段

我们的每个图谱片段都继承自 Abstract_Schema_Piece 类。我们会向每个片段传递一个包含大量上下文变量的 Meta_Tags_Context 对象。具体示例可参考我们的示例用例,更深入的示例可在Github此处查看。

结合上述要求,添加新的图谱片段相当简单:

// functions.php
add_filter( 'wpseo_schema_graph_pieces', 'add_custom_schema_piece', 11, 2 );

/**
 * 向图谱收集器添加自定义图谱片段。
 *
 * @param array  $pieces  当前图谱片段数组。
 * @param string $context 当前上下文。
 *
 * @return array 图谱片段数组。
 */
function add_custom_schema_piece( $pieces, $context ) {
   $this->context = $context;

   $pieces[] = new MyCustomSchemaPiece( $context );

   return $pieces;
}

引用其他图谱片段

您始终可以使用固定ID引用Yoast SEO核心图谱片段。这些ID可通过Schema_IDs类查找。例如,您可以找到Schema_IDs::ORGANIZATION_HASH、Schema_IDs::PERSON_LOGO_HASH等众多标识符。若您添加的片段需要引用Organization片段,只需如下操作:

$data['organization'] = [ '@id' => Schema_IDs::ORGANIZATION_HASH ];

修改图谱片段数据

如需更改特定片段的输出,请接入我们的 wpseo_schema_<class> 过滤器。例如:

对整个 Schema 执行复杂更改

如果您需要对 Schema 执行复杂操作,例如更改输出中不同部分的值,可以接入我们的 wpseo_schema_graph 过滤器。例如:

add_filter( 'wpseo_schema_graph', 'change_image_urls_to_cdn', 10, 2 );

/**
 * 将所有图片中的主机名替换为 CDN 主机名。
 *
 * @param array             $data    Schema.org 图谱。
 * @param Meta_Tags_Context $context 上下文对象。
 *
 * @return array 修改后的 Schema.org 图谱。
 */
function change_image_urls_to_cdn( $data, $context ) {
    foreach ( $data as $key => $value ) {
        if ( $value['@type'] === 'ImageObject' ) {
            $data[$key]['contentUrl'] = str_replace( 'http://basic.wordpress.test/', 'https://cdn.domain.tld/', $value['contentUrl'] );
        }
    }
    return $data;
}

Gutenberg 区块的 Schema

如果您正在为区块编辑器(有时称为 "Gutenberg")编写区块,您可能也希望添加 Schema 输出。 有两个有用的钩子可以帮助您实现这一点。

了解页面包含哪些区块

首先,您需要知道页面包含哪些区块。您可以通过 wpseo_pre-schema_block-type_<block-type> 操作来判断。如果该操作针对您的区块类型触发,您就知道该区块类型将输出到页面上。

例如,我们使用此过滤器通过以下简单函数来更改常见问题区块中的 @typeof WebPage:

add_action( 'wpseo_pre_schema_block_type_yoast/faq-block', [ $this, 'prepare_faq_schema' ], 10, 1 );

/**
 * 若此操作触发,则表明页面存在常见问题区块,因此过滤页面类型。
 *
 * @param array $blocks 当前页面上此类型的区块数组。
 */
public function prepare_schema( $blocks ) {
    $this->blocks    = $blocks;
    $this->is_needed = true;

    add_filter( 'wpseo_schema_webpage_type', [ $this, 'change_schema_page_type' ] );
}

如您所见,此代码取自某个类内部。wpseo_pre-schema_block_type_<block-type> 操作会接收该类型所有区块的数组作为参数。我们将这些区块保存在类中,后续会使用这些数据来确定其输出。

特定区块的输出

如果您需要为特定区块添加输出,可以使用 wpseo_schema_block_<block-type> 过滤器。该过滤器包含 3 个参数:

例如,我们可以像这样挂载一个 joost-block:

add_filter( 'wpseo_schema_block_yoast/faq-block', [ $this, 'render_joost_block_schema' ], 10, 3 );

然后,当插件遇到 FAQ 区块时,会调用此函数,该函数本身会调用一个新类:

/**
 * 根据区块中的数据渲染 Joost 区块的 Schema。
 *
 * @param array                 $graph   当前页面的 Schema 数据。
 * @param WP_Block_Parser_Block $block   区块数据数组。
 * @param Meta_Tags_Context     $context 包含上下文变量的值对象。
 *
 * @return array 我们的 Schema 图谱。
 */
public function render_joost_block_schema( $graph, $block, $context ) {
	$graph['data'] = $block['data'];
	$graph['id']   = YoastSEO()->meta->for_current_page()->canonical . '#/schema/joost/' . $block['id'];

	return $graph;
}

Background

本页介绍了我们的方法、原理和考量,阐述了核心基本原则、设计决策及方法论。

理解结构化标记的作用

结构化标记帮助搜索引擎、用户和系统理解内容、语境及实体关系。它为网页上的事物属性及其关联添加元信息,这些信息可能无法直接从页面的"人类可读"版本中立即获取或轻松解析。

对我们而言,我们特别关注搜索引擎如何理解网站页面、运营网站的组织、其提供的产品及其他相关概念之间的关系。结构化标记使我们能够描述这些实体的连接方式并定义其属性。

添加此类标记可能导致搜索引擎在搜索结果中提供额外或增强的展示(如"富媒体列表"),并符合新功能/新兴功能(例如"知识图谱面板")的资格要求。

除了直接的营销应用外,结构化标记将使未来的系统、流程和软件能够理解实体间的关系,并利用这种理解来提供新型服务。

Google、Bing 和 Facebook 等平台持续推出依赖结构化标记的新功能和格式支持。

拉里·佩奇的知识图谱面板

为何要创建新标准/方法?

许多内容管理系统、平台、主题和插件已提供某种程度的结构化标记。然而,这些实现往往不完整、不一致、相互冲突或存在错误。

尽管关于实体及其标记的可能结构和属性已有大量文档(来自 schema.org 等),但关于模式标记实现——尤其是我们所需的复杂结构化数据——的建议和文档却有限且不一致。

现有文档主要描述如何表示单个事物,却很少指导如何创建和维护实体及其关系的图谱。

随着结构化标记功能的持续增长,缺乏标准化的实现方法意味着大多数网站越来越可能出错——最坏情况下,可能导致谷歌忽略或将那些网站列入黑名单,使其无法获得富媒体搜索结果展示资格。

集成与关联的挑战

特别是依赖多个移动部件的系统(例如使用第三方主题和/或多个插件的 WordPress 网站)已经面临互操作性难题——缺乏统一的工作方式,开发者难以实现数据的一致性关联、实体交叉引用或代码共享。

例如,一个插件可能添加产品结构化数据,另一个插件添加本地商家结构化数据。这些标记片段无法可靠地通信或集成数据。插件之间没有通用或简便的方式来声明其结构化数据,也无法明确指定示例中的产品由特定本地商家销售,或由运营网站的同一组织生产。

在大多数情况下,现有实现仅限于声明每个独立片段的存在,却无法声明它们之间的关系。它们缺乏连接实体的共享机制。

这种碎片化会导致标记错误,并/或限制网站结构化数据的可扩展性。若缺乏标准化,JSON-LD 中的“关联”特性将难以实现!

基于 schema.org 构建

请注意,本文档并非旨在定义、重建、替换或扩展 schema.org 标记,而是概述在设计和实现解决方案时应遵循的基本原则和概念模型。

本文中的示例代表推荐方法,而非涵盖所有可能场景及其所有变体的示例。

实现应基于这些示例,并充分利用 schema.org 的文档来根据需要定义或添加额外的对象属性。

Functional Specification

import YoastSchemaExample from '@site/src/components/YoastSchemaExample';

我们方法的核心是输出一个“基础脚本”——一个以 JSON-LD 格式渲染的 @graph 对象——它描述了 WebPage、WebSite 和 Organization(对于代表个人的网站,则是 Person)。运行 Yoast SEO 插件 的网站的每个页面都包含此脚本。

在任何特定页面上,该图谱可能会被修改和/或扩展,以反映网页的具体类型及其属性。在任何给定场景中,我们的目标是识别页面的“主要实体”,并发展图谱以表示此实体(参见“主要实体”)。

所有标记、属性和属性均直接取自 schema.org,并且(除以下基础脚本外)提供的所有代码仅用于演示目的。

基础脚本

以下是我们为每个页面构建的图的简化表示。

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://www.example.com/#/schema/Organization/1", "url": "https://www.example.com/", "name": "Example organization name" }, { "@type": "WebSite", "@id": "https://www.example.com/#/schema/WebSite/1", "url": "https://www.example.com/", "name": "Example website", "publisher": { "@id": "https://www.example.com/#/schema/Organization/1" } }, { "@type": "WebPage", "@id": "https://www.example.com/example-page/test/", "url": "https://www.example.com/example-page/test/", "name": "Example page name", "description": "Example page description", "isPartOf": { "@id": "https://www.example.com/#/schema/WebSite/1" } } ] }`}

这种方法与大多数 Schema 标记方法的不同之处在于,我们不是尝试构建一个复杂的“树”(即嵌套属性的数组),而是为每个独立的实体输出一个独立的、顶层的“片段”(技术术语称为“节点”)。这些片段包含在一个或多个 @graph 对象中,这使我们能够通过 ID 交叉引用片段。有关此方法的更多信息,请参阅“探索 ID、关系和嵌套”。

请注意,在 Google 结构化数据测试工具中测试时,结果是一个单一的、连贯的图,其中以主要实体(本例中为 WebPage)为核心焦点。传统方法会显示每个独立的片段,但不会将这些片段“缝合”成一个单一的图。

示例图谱

以下示例展示了如何扩展和修改我们的基础脚本以支持不同场景。无论何种情况,我们都力求呈现网页的"主要实体"(及其属性),以及其与发布该页面的网站和组织(或个人)的关系。

A company homepage

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://www.example.com/#/schema/Organization/1", "name": "Example Organization", "url": "https://www.example.com/", "sameAs": [ "https://www.facebook.com/example/", "https://www.instagram.com/example/", "https://www.linkedin.com/example/", "https://www.youtube.com/example/", "https://www.pinterest.com/example/", "https://en.wikipedia.org/wikiexample/", "https://x.com/example/" ], "logo": { "@type": "ImageObject", "@id": "https://www.example.com/images/example.jpg", "url": "https://www.example.com/images/example.jpg", "contentUrl": "https://www.example.com/images/example.jpg", "width": 120, "height": 120, "caption": "Example Image" }, "image": { "@id": "https://www.example.com/images/example.jp" } }, { "@type": "WebSite", "@id": "https://www.example.com/#/schema/WebSite/1", "url": "https://www.example.com/", "name": "Example Website", "publisher": { "@id": "https://www.example.com/#/schema/Organization/1" }, "potentialAction": { "@type": "SearchAction", "target": "https://www.example.com/?s={search_term_string}", "query-input": "required name=search_term_string" } }, { "@type": "WebPage", "@id": "https://www.example.com/", "url": "https://www.example.com/", "inLanguage": "en-US", "name": "Example Page", "isPartOf": { "@id": "https://www.example.com/#/schema/WebSite/1" }, "about": { "@id": "https://www.example.com/#/schema/Organization/1" }, "datePublished": "2015-09-14T08:13:22+00:00", "dateModified": "2019-03-28T12:46:37+00:00", "description": "Example description" } ] }`}

An article, with an author, on a company website

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://www.example.com/#/schema/Organization/1", "name": "Example Organization", "url": "https://www.example.com/", "sameAs": [ "https://www.facebook.com/example/", "https://www.instagram.com/example/", "https://www.linkedin.com/example/", "https://www.youtube.com/example/", "https://www.pinterest.com/example/", "https://en.wikipedia.org/wikiexample/", "https://x.com/example/" ], "logo": { "@type": "ImageObject", "@id": "https://www.example.com/images/example.jpg", "url": "https://www.example.com/images/example.jpg", "contentUrl": "https://www.example.com/images/example.jpg", "width": 120, "height": 120, "caption": "Example Image" }, "image": { "@id": "https://www.example.com/images/example.jpg" } }, { "@type": "WebSite", "@id": "https://www.example.com/#/schema/WebSite/1", "url": "https://www.example.com/", "name": "Example Website", "publisher": { "@id": "https://www.example.com/#/schema/Organization/1" }, "potentialAction": { "@type": "SearchAction", "target": "https://www.example.com/?s={search_term_string}", "query-input": "required name=search_term_string" } }, { "@type": "WebPage", "@id": "https://www.example.com/blog/example-article/", "url": "https://www.example.com/blog/example-article/", "inLanguage": "en-US", "breadcrumb": { "@id": "https://www.example.com/#/schema/BreadcrumbList/abc123" }, "name": "Example Page", "isPartOf": { "@id": "https://www.example.com/#/schema/WebSite/1" }, "datePublished": "2015-09-14T08:13:22+00:00", "dateModified": "2019-03-28T12:46:37+00:00", "description": "Example description" }, { "@type": "Article", "@id": "https://www.example.com/#/schema/Article/abc123", "headline": "Example article headline", "description": "Example article description", "isPartOf": { "@id": "https://www.example.com/blog/example-article/" }, "mainEntityOfPage": { "@id": "https://www.example.com/blog/example-article/" }, "datePublished": "2019-07-10T08:08:40+00:00", "dateModified": "2019-07-10T08:43:03+00:00", "commentCount": 6, "articleSection": ["Cats","Dogs","Recipes"], "author": { "@id": "https://www.example.com/#/schema/Person/abc123" }, "publisher": { "@id": "https://www.example.com/#/schema/Organization/1" }, "image": [ { "@id": "https://www.example.com/images/example-2.jpg" }, { "@id": "https://www.example.com/images/example-3.jpg" } ], "video": [ { "@id": "https://www.example.com/#/schema/VideoObject/abc123" }, { "@id": "https://www.example.com/#/schema/VideoObject/def456" } ] }, { "@type": "Person", "@id": "https://www.example.com/#/schema/Person/abc123", "name": "Example person name", "image": { "@id": "https://www.example.com/images/example-4.jpg" }, "sameAs": [ "https://www.wikipedia.com/example-organization", "https://www.linkedin.com/company/1234" ] }, { "@type": "BreadcrumbList", "@id": "https://www.example.com/#/schema/BreadcrumbList/abc123", "itemListElement": [ { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://www.example.com/" }, { "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://www.example.com/blog/" }, { "@type": "ListItem", "position": 3, "name": "Example Article" } ] } ] }`}

WooCommerce 商店中的产品

请注意,实际上这是两个独立的图形块拼接而成,但解析结果相同。

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://www.example.com/#/schema/Organization/1", "name": "Example Organization", "url": "https://www.example.com/", "sameAs": [ "https://www.facebook.com/example/", "https://www.instagram.com/example/", "https://www.linkedin.com/example/", "https://www.youtube.com/example/", "https://www.pinterest.com/example/", "https://en.wikipedia.org/wikiexample/", "https://x.com/example/" ], "logo": { "@type": "ImageObject", "@id": "https://www.example.com/images/example.jpg", "url": "https://www.example.com/images/example.jpg", "contentUrl": "https://www.example.com/images/example.jpg", "width": 120, "height": 120, "caption": "Example Image" }, "image": { "@id": "https://www.example.com/images/example.jpg" } }, { "@type": "WebSite", "@id": "https://www.example.com/#/schema/WebSite/1", "url": "https://www.example.com/", "name": "Example Website", "publisher": { "@id": "https://www.example.com/#/schema/Organization/1" }, "potentialAction": { "@type": "SearchAction", "target": "https://www.example.com/?s={search_term_string}", "query-input": "required name=search_term_string" } }, { "@type": [ "WebPage", "ItemPage" ], "@id": "https://www.example.com/products/example-page/", "url": "https://www.example.com/products/example-page/", "inLanguage": "en-US", "breadcrumb": { "@id": "https://www.example.com/#/schema/BreadcrumbList/abc123" }, "name": "Example Product Page", "isPartOf": { "@id": "https://www.example.com/#/schema/WebSite/1" }, "datePublished": "2015-09-14T08:13:22+00:00", "dateModified": "2019-03-28T12:46:37+00:00", "description": "Example description" }, { "@type": "BreadcrumbList", "@id": "https://www.example.com/#/schema/BreadcrumbList/abc123", "itemListElement": [ { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://www.example.com/" }, { "@type": "ListItem", "position": 2, "name": "Products", "item": "https://www.example.com/products/" }, { "@type": "ListItem", "position": 3, "name": "Example Page" } ] }, { "@type": "Product", "@id": "https://www.example.com/#/schema/Product/abc123", "name": "Vneck Tshirt", "image": { "@id": "https://www.example.com/images/example-2.jpg" }, "description": "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.", "sku": 83, "offers": [ { "@type": "Offer", "@id": "https://www.example.com/#/schema/Offer/abc123", "priceSpecification": { "@type": "PriceSpecification", "price": "18.00", "priceCurrency": "EUR", "valueAddedTaxIncluded": "false" }, "availability": "https://schema.org/InStock", "url": "https://www.example.com/products/example-page/", "seller": { "@id": "https://www.example.com/#/schema/Organization/1" } } ], "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.50", "reviewCount": 2 }, "review": [ { "@type": "Review", "@id": "https://www.example.com/#/schema/Review/abc123", "datePublished": "2019-04-09T09:10:12+00:00", "description": "What a nice turtle shirt.", "reviewRating": { "@type": "Rating", "ratingValue": "4" }, "author": { "@type": "Person", "name": "Tim" } }, { "@type": "Review", "@id": "https://www.example.com/#/schema/Review/def456", "datePublished": "2019-04-09T09:10:29+00:00", "description": "Awesome shirt", "reviewRating": { "@type": "Rating", "ratingValue": "5" }, "author": { "@type": "Person", "name": "Patrice" } } ], "brand": { "@type": "Organization", "name": "Turtle" }, "mainEntityOfPage": { "@id": "https://www.example.com/products/example-page/" }, "manufacturer": { "@type": "Organization", "name": "Turtle" } } ] }`}

其他示例

我们的技术文档包含更广泛和多样的示例,以及关于Yoast SEO 软件如何确定在各种场景下输出内容的详细信息。

修改或扩展我们的图表

我们所有的输出都可以通过完整 API进行修改、扩展或禁用(按部分或整体)。

在第三方插件、主题或系统导致图表"解构"、属性重复/合并或 ID 空间共享的场景中,我们建议采用我们的框架并利用我们的 API(或鼓励相关解决方案作者这样做)。

在 Yoast 软件中的使用

本规范构成了自 Yoast SEO 11.0 版本起我们 schema.org / JSON-LD 输出的基础。关于我们的软件如何利用和扩展此规范的具体信息,请参阅此处。

支持与反馈

本规范将持续完善,我们始终乐于协助他人采纳、扩展或改进。若您对本文所述机制存有疑问,或希望将规范应用于您的主题/插件,欢迎在我们的 GitHub 仓库中创建议题。

请别忘了我们还提供技术文档以及用于修改结构化数据输出的 API!

附录

1: JSON-LD 与其他标准的比较

表面上看,与其他方法(例如 RDFa 或 Microdata)提供的内联灵活性相比,JSON-LD 要求在模板中输出单一、静态的代码片段可能显得有限。然而,随着用例复杂性的增加,基于内联的方法很快暴露出缺乏我们所需的灵活性和可扩展性。具体来说:

由于我们试图最大限度地提高灵活性和互操作性,在这种情况下,JSON-LD 显然是赢家。

1a: 通过 CSS/XPath 选择避免内容重复

对 JSON-LD 格式的主要反对意见之一是它可能导致(大块)内容重复。例如,如果我想表示一篇评论,就必须在页面的“人类”可读内容中包含完整的评论内容,同时在 JSON 标记中也包含它(至少根据谷歌的结构化数据要求)。

这通常被视为性能问题,而且确实如此。即使启用并配置了 GZIP(我们不能假设总是如此),这也可能导致服务器响应和浏览器处理产生(尽管较小)开销。

未来,我们希望通过使用 schema.org 的 CssSelectorType 或 XPathType 标记来绕过此问题。这允许我们定位代表相关内容的特定容器(或容器的组合/数组),而不是重复文本。目前,这仅支持 webPageElement 和 Speakable 内容区域。

在这些定位方法获得更广泛支持之前,仅当搜索引擎和外部代理明确要求时才应重复内容。本文档反映了这些场景。

2: 标签管理解决方案与 JavaScript 注入的使用

我们更倾向于在服务器端包含和渲染 JSON-LD 标记,而非通过标签管理解决方案或 JavaScript 文件/函数注入。此外,只要可能,所有属性值都应“硬编码”到图谱中,而非引用或调用来自其他位置的函数或变量。

爬取、处理和利用 JavaScript 注入值的计算成本远高于仅抓取 HTML,且更为复杂,因此许多平台对严重依赖 JavaScript 的方法仅提供部分(甚至不提供)支持。

依赖外部脚本和平台(如标签管理系统)的方法也可能因冲突、竞态条件、延迟和依赖管理而引入脆弱性。

3. 图像处理

在本文档的所有示例中,我们通常对图像做出以下几点假设:

4. 使用规范 URL

url 属性应始终继承 JSON 片段所在页面的“真实规范”值(即,即使规范 URL 标签已手动设置为引用其他页面/URL,仍应使用原始的“真实”规范值)。

例如,如果页面 A 的 URL 为 https://www.example.com/page-a/,但其包含的规范 URL 标签引用了页面 B(位于 https://www.example.com/page-b/),则所有通常与页面 A 相关的 url 参数仍应使用页面 A 的“真实”规范 URL。

5. 验证工具

在测试、评估和调试 schema 时,我们推荐使用以下三种工具。

6. 已知问题

SDTT 在许多场景下与 schema.org 的定义存在偏差。在某些情况下,我们调整或折衷了方法以找到适用于双方的解决方案;在其他情况下,我们根据具体情境偏向其中一方。

例如,SDTT 要求 recipe 必须包含 image 属性。根据 schema.org 食谱规范,这并非强制属性,但 Google 有此要求。类似情况还有很多,SDTT 揭示了 Google 特有的特殊要求和规则,这些要求要么是为满足其需求而对标准进行刻意"调整"的产物,要么是相对随意的决策结果。

以下是我们方法导致冲突和问题的具体场景,我们正在请求 Google 调整其对我们标记的解释和处理方式。

Person 不能作为 Article 的 Publisher

这是一个特别具有挑战性的问题,因为代表 Person 的 WebSite(与代表 Organization 的网站相对,即个人网站)自然会“发布”文章,而该 Person 应被视为“发布者”。这是一个极其常见的用例,但各种 Google 工具和标准却将其标记为无效。

此外,我们图方法的一个关键部分依赖于识别 WebPage(或 Article)与其所在 WebSite 之间的连接。Publisher 是这些实体之间的关键连接,因此,我们选择在这种情况下忽略该错误(但已向 Google 发出警报)。

为了解决这个问题,我们将 Person 与 Organization 合并,创建一个混合类型([Person, Organization]),然后该类型期望/接受 logo 和其他“Publisher”属性。这在 SDTT 中验证通过,但这是一个公认的“变通方案”。

7. 其他消费者

在发布时,Bing 似乎不支持此方法;其“标记验证器”工具(Bing Webmaster Tools 的一部分)无法检测(和/或解析)包含在 @graph 结构中的标记(这是我们方法的核心)。我们正寻求与 Bing 进行对话,以确定他们对支持此方法的立场。

Facebook、X、Pinterest 等社交平台对此标记的支持程度各不相同。大多数依赖 Open Graph 标记('OG 标签')及类似方案,但在 OG 标签缺失或无效时,可能会使用 schema.org 标记的组成部分。

其他搜索引擎(如百度、Yandex 等)的支持情况未知;我们推测其支持通常有限或不存在。我们希望我们方法的广泛采用将鼓励这些及其他消费者扩展其支持。

Integration Guidelines

简介

与 Yoast 结构化数据框架集成既简单,又对用户有益。我们鼓励所有插件/主题/软件作者考虑采用并扩展我们的方法。

您应该首先阅读我们的规范,并确保理解其核心原则。简而言之,这些原则是:

一个使用案例

Yoast SEO 软件已经创建了一个大型结构化图谱,但存在一些我们(目前)不支持的内容类型。您可能希望为特定内容类型添加支持。

例如,您可能提供一个允许网站撰写书评的插件。在这种情况下,您可能希望在自定义文章类型 book_review 上描述一本 Book。为此,您应该有条件地输出自定义的 Book 片段,并将其连接到主图谱中。

实现此功能的示例代码可能如下所示。

注意:这仅在用户运行版本 14.0 或更高的 Yoast SEO 插件 时才有效。插件/主题/第三方开发者应确定 Yoast SEO 插件是否正在运行,并相应地管理其输出。

Create the piece.

First we create the Book object.

/**
 * Class Book
 */
class Book  {

	/**
	 * A value object with context variables.
	 *
	 * @var WPSEO_Schema_Context
	 */
	public $context;

	/**
	 * Book constructor.
	 *
	 * @param WPSEO_Schema_Context $context Value object with context variables.
	 */
	public function __construct( WPSEO_Schema_Context $context ) {
		$this->context = $context;
	}

	/**
	 * Determines whether or not a piece should be added to the graph.
	 *
	 * @return bool Whether or not a piece should be added.
	 */
	public function is_needed() {

		// We only ever want to output this on 'book review' post types.
		if ( is_singular( 'book_review' ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Adds our Book piece of the graph.
	 *
	 * @return array Book Schema markup.
	 */
	public function generate() {

		$canonical = YoastSEO()->meta->for_current_page()->canonical;
		$post_id = YoastSEO()->meta->for_current_page()->post_id;

		// Set the type.
		$data['@type'] = 'Book';

		// Give it a unique ID, based on the URL and the Post ID.
		$data['@id'] = $canonical . '#/book/' . $post_id;

		// Give it a name.
		$data['name'] = the_title_attribute( array( 'echo' => false ) );

		// Make it the main entity of the webpage we're on.
		$data['mainEntityOfPage'] = [ '@id' => $canonical ];

		// Add the book's author, if we know it.
		$author = get_post_meta( $post_id, 'author', true );
		if ( ! empty( $author ) ) {
			// In reality, we'd probably want to add more information than just the name here.
			$data['author'] = $author;
		}	

		return $data;
	}
}

将片段添加到图谱中。

接着,我们将 Book 片段添加到图谱中:

add_filter( 'wpseo_schema_graph_pieces', 'add_book_piece', 11, 2 );

/**
 * 将 Schema 片段添加到我们的输出中。
 *
 * @param array                 $pieces  要输出的图谱片段。
 * @param \WPSEO_Schema_Context $context 包含上下文变量的对象。
 *
 * @return array 要输出的图谱片段。
 */
function add_book_piece( $pieces, $context ) {
	$pieces[] = new Book( $context );
	return $pieces;
}

更新图谱中的其他相关部分

我们知道,在 book_review 类型的文章中,页面内容与该页面讨论的 Book 相关。我们可以通过引用 Book 的 ID 来在知识图谱中体现这一点。

add_filter( 'wpseo_schema_webpage', 'make_webpage_about_book' );

/**
 * 修改网页节点,使其“关于”我们的书籍
 *
 * @param array $data 图谱数据。
 *
 * @return array 修改后的图谱
 */
public static function make_webpage_about_book( array $data ) : array {

	$canonical = YoastSEO()->meta->for_current_page()->canonical;
	$post_id = YoastSEO()->meta->for_current_page()->post_id;

	$data['about'] = array( '@id' => $canonical . '#/book/' . $post_id );
	return $data;
}

常见问题

我的插件/主题已输出 Schema 标记;如何集成?

如果您的插件已输出 Schema.org 元数据,您应该能够相对轻松地将其连接到 Yoast SEO 的图谱。

如果您尚未将独立节点添加到 @graph(并为这些节点分配 @id),可能需要做一些修改以实现有效集成。如果您已采用基于 ID 的方法,可以通过使用我们的 Schema API 并遵循我们的规范,将您的片段“缝合”到我们的图谱中。

如果您采用不同的方法(例如输出结构化 JSON-LD 树或使用微数据),可能存在信息冲突的风险,导致页面表示不准确。

在这种情况下,我们建议您调整代码以遵循上述示例中的方法,并禁用插件/主题的输出(因为我们的逻辑将管理完整图谱的构建和输出)。

注意:如果检测到用户未运行 Yoast SEO,且希望最大化与其他插件的互操作性,您可以按照我们的规范构建自己的图谱。

1. 如何对 Schema 进行去重?

如果你输出的 Schema 与 Yoast SEO 的输出相同,你可能需要对其进行去重。假设你输出了 Organization 数据。如果你的数据没有 Yoast 输出的那么详尽,你可以简单地用 引用 Yoast 的 Organization 数据来替换你的 Organization 数据。如果你的数据更详尽,我们建议 过滤 Yoast SEO 对该特定部分的输出 以包含你的数据。

或者,如果两个部分中没有相互冲突的值,你可以直接使用相同的 @id 属性。消费者会合并这两个部分,并整合信息。

2. How do I connect my Schema to Yoast's?

To connect your Schema output to Yoast SEO's Schema output, you need to determine where in the graph it needs to connect. You have to do this whether you add a Graph piece through Yoast's API, or you just output your Schema via your own methods.

In most cases, you will want to content to the WebPage. The WebPage is unique in our Schema graph, as its @id is always the URL of that page. So, if you have for instance a Recipe, you can simply add this to your Recipe output:

  "isPartOf": { "@id": "<page url>" }

By adding the above, (and of course replacing <page url> with the actual URL of that page), Schema consumers will know to "stitch" your Schema output into the @graph that Yoast SEO creates.

Getting the corect URL to reference can be done easily using the YoastSEO() surface:

YoastSEO()->meta->for_current_page()->canonical

3. 如何知道在哪里连接我的片段?

在上面的例子中,我们使用了 isPartOf。如果你的 Schema "片段" 不是页面上最重要的内容,这没问题。但如果你的页面是一个食谱页面,它不仅仅是"一部分"。它是主要内容。在这种情况下,你不应该使用 isPartOf,而应该使用 mainEntityOfPage,即:

  "mainEntityOfPage": { "@id": "<page url>" }

Technology Approach

import YoastSchemaExample from '@site/src/components/YoastSchemaExample';

JSON-LD 作为首选格式

结构化标记可以通过多种方式以及多种不同的标准来实现。鉴于谷歌严格遵守其规范,我们对 schema.org 定义的标准特别感兴趣。

Schema.org 标记可以通过多种方式添加到网页中。在所有可用的方法中,我们认为在页面的 HTML 源代码中包含 JSON-LD 是(当前)最佳方法。

JSON-LD 提供了我们实现一致且可扩展基础所需的灵活性、可扩展性和标准化。虽然它缺少“内联”(结构化标记直接作为表示这些实体的 HTML 代码的一部分实现)的一些潜在好处,但其优势、灵活性和可扩展性远远超过了这些限制。

ID、关系和嵌套

JSON-LD 允许属性通过 ID 引用其他片段。例如,产品页面 无需包含(或重复)销售该产品的 组织 的所有标记,只需引用代表该 组织 的片段 ID 即可。 理论上,这使我们能够避免重复共享属性,并减少表示页面内容所需的代码/处理/开销。

遗憾的是,这项技术的 跨页面 支持水平有限——Google 的文档表述模糊,且 ID 与 URI 之间的关系存在歧义(据传闻,他们“无法从其他页面提取结构化数据”[原文如此])。鉴于这一限制,我们要求每个页面输出所有相关片段,并通过 hasPart、isPartOf、mainEntityOfPage 及类似的查找机制进行交叉引用。

例如,以下(简化后的)JSON 片段定义了一个 Organization,并将该 Organization 作为 WebSite 的 Publisher 进行引用:

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://www.example.com/#/schema/Organization/1", "url": "https://www.example.com/", "name": "Example organization name" }, { "@type": "WebSite", "@id": "https://www.example.com/#/schema/WebSite/1", "url": "https://www.example.com/", "name": "Example website", "publisher": { "@id": "https://www.example.com/#/schema/Organization/1" } } ] }`}

在此示例中,我们知道网站的 publisher 与 Organization 相同。由于图中已存在代表该 Organization 的顶级片段,我们只需在 WebSite 片段中通过其 @id 引用它。这提供了极大的灵活性,并避免了重复。

基于此能力,我们倾向于尽可能避免属性的深度嵌套,并更愿意拆分出独立的片段,如下文示例所示。这使代码保持可读性、模块化和可扩展性。

构建 ID 参数

ID 参数通常应采用以下结构,下文列出的情况除外:

{{website}}/#/schema/{{type}}/{{ID}}

例如:

请查阅相关片段文档以了解正确的结构(如果已定义)。

异常情况

注意:

{{identifier}} 组合规则

{{identifier}} 参数应始终按以下层级顺序构建:

Exceptions

These are also documented in their various pieces documentation, but bear repeating here.

混合类型与复合 ID

当节点为混合类型(即 @type 为值数组,例如 ['Organization', 'Person'])时,{{type}} 值应按字母顺序连接 @type 值,并用连字符分隔(例如 organization-person)。

主要实体

我们的模型假设每个 URL 都应代表一个主要实体——无论是组织、产品、博客文章(或博客文章集合)、个人还是其他事物。

我们始终致力于让该“主要实体”成为每个页面网络图谱的中心。这种心智模型与我们希望搜索引擎理解我们网络的方式高度契合;它使我们能够以如下方式阐述内容:“此URL代表一个食谱,它是文章的一部分,由个人撰写,位于网页上,而该网页属于网站的一部分,该网站由组织运营”。

本文档中的所有代码示例均体现了这一方法;我们通过使用hasPart、isPartOf、mainEntityOfPage及类似连接来构建实体间的定向关系。

代码碎片化与放置位置

有时无法将代码放置在 <head> 中,例如受架构限制,在构建 <head> 时可能无法访问必要的上下文。考虑到 <body> 内容有时可能包含需要反映在结构化数据中的内容或逻辑,我们也允许在页面末尾、闭合标签 </body> 之前输出代码。

由于我们使用 @id 属性来连接片段,从技术上讲,可以通过多个 <script> 标签和图结构,将代码拆分并分布在整个页面中,并按照本文档概述的方法通过 ID 简单交叉引用实体。还可以通过在页面其他位置创建具有相同 ID 的新引用来扩展现有片段。

我们通常建议系统开发者尽量避免这种碎片化(因为它会给本已复杂的系统引入脆弱性和混淆),但也认识到有时这是必要的。

事实上,当无法在 <head> 中计算和输出所有内容时,我们自己的某些解决方案也采用这种方法。例如,我们的 Yoast WooCommerce SEO 插件 依赖于解析初始化期间不可用的产品信息,因此在页面页脚输出第二个 <script> 块,其中包含对页面图的补充(特别是产品和评论信息)。这个附加图会无缝拼接,形成一个连贯的整体。

混合类型

有时,一个对象可能兼具两种不同事物的特性。例如,一本书既可以是 book 又可以是 product,并同时拥有两者的属性。它可能既有 author 属性,也有 price 属性。

添加多种类型能够提供更大的灵活性,并更精确地描述对象。

然而,在我们的方法中,我们谨慎使用混合类型,因为它们可能会模糊特定页面的“焦点”。如果页面的主要实体(或 URL)是一个复杂的复合类型,我们就有可能偏离“一个 URL,一个事物”的模型。

Aggregateoffer

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一组 Product 的 offers,通常由于属性(颜色、尺寸、价格)的差异而形成。

触发条件

必需属性

一个有效的 AggregateOffer 必须包含以下属性。

失败场景

如果任何必需字段缺失或无效,则不应输出此节点。 如果未输出此节点,任何原本会引用该 AggregateOffer 的实体应移除这些引用(例如,当产品通过 ID 引用一个 AggregateOffer 时)。

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "AggregateOffer", "@id": "https://www.example.com/#/schema/AggregateOffer/abc123", "lowPrice": "22.00", "highPrice": "136.00", "priceCurrency": "GBP", "offerCount": 3, "offers": [ { "@id": "https://www.example.com/#/schema/Offer/abc123" }, { "@id": "https://www.example.com/#/schema/Offer/def456" }, { "@id": "https://www.example.com/#/schema/Offer/ghi789" } ] } ] }`}

Article

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述 WebPage 上的一个 Article。 如果满足所需条件,可转换为更具体的类型(例如 NewsArticle)。

触发器

应在所有支持 作者身份 且拥有作者的文章类型上输出。

必需属性

有效的 Article 必须包含以下属性。

失败场景

如果任何必填字段缺失或无效,则不应输出该节点。

可选属性

以下属性应在可用且有效时添加:

条件属性

仅在满足特定条件时才应输出的可选属性。

若存在版权信息

如果存在评论

如果允许/启用评论功能

Examples

Minimum criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Article", "@id": "https://www.example.com/#/schema/Article/abc123", "headline": "Example article headline", "description": "Example article description", "isPartOf": { "@id": "https://www.example.com/blog/example-article/" }, "mainEntityOfPage": { "@id": "https://www.example.com/blog/example-article/" }, "datePublished": "2019-07-10T08:08:40+00:00", "dateModified": "2019-07-10T08:43:03+00:00", "author": { "@id": "https://www.example.com/#/schema/Person/abc123" }, "publisher": { "@id": "https://www.example.com/#/schema/Organization/1" }, "image": { "@id": "https://www.example.com/uploads/example-image.jpg" } } ] }`}

Extended criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Article", "@id": "https://www.example.com/#/schema/Article/abc123", "headline": "Example article headline", "description": "Example article description", "isPartOf": { "@id": "https://www.example.com/blog/example-article/" }, "mainEntityOfPage": { "@id": "https://www.example.com/blog/example-article/" }, "datePublished": "2019-07-10T08:08:40+00:00", "dateModified": "2019-07-10T08:43:03+00:00", "commentCount": 6, "articleSection": ["cats","dogs","cake"], "inLanguage": "en-US", "author": { "@id": "https://www.example.com/#/schema/Person/abc123" }, "publisher": { "@id": "https://www.example.com/#/schema/Organization/1" }, "image": [ { "@id": "https://www.example.com/uploads/example-image.jpg" }, { "@id": "https://www.example.com/uploads/example-image-2.jpg" } ], "video": [ { "@id": "https://www.example.com/#/schema/VideoObject/abc123" }, { "@id": "https://www.example.com/#/schema/VideoObject/def456" } ], "potentialAction": [ { "@type": "CommentAction", "name": "Comment" "target": [ "https://www.example.com/blog/example-article/#comment" ] } ] } ] }`}

WordPress API:修改文章 Schema 输出 {#api}

如需修改 Yoast SEO 输出的 Article schema,可使用 wpseo_schema_article 过滤器。示例如下:

add_filter( 'wpseo_schema_article', 'change_article_to_social_posting' );

/**
 * 修改文章 Schema 数据的 @type 属性。
 *
 * @param array $data Schema.org 文章数据数组。
 *
 * @return array Schema.org 文章数据数组。
 */
function change_article_to_social_posting( $data ) {
    $data['@type'] = 'SocialMediaPosting';

    return $data;
}

在自定义文章类型上输出文章架构

默认情况下,Yoast SEO 不会在自定义文章类型上输出文章架构。以下代码可改变此行为:

/**
 * 将 'book' 添加到文章类型列表中,使书籍获得文章架构。
 *
 * @param  mixed $post_types 当前文章类型列表。
 *
 * @return array Yoast SEO 为其渲染文章架构的文章类型数组。
 */
function article_schema_for_books( $post_types ) {
	$post_types[] = 'book';
	return $post_types;
}

add_filter( 'wpseo_schema_article_post_types', 'article_schema_for_books' );

请注意,要使文章类型能够输出 Article 架构,该文章类型需要支持作者功能。 对于给定的文章类型 book,您可以通过添加以下代码轻松实现:

add_post_type_support( 'book', 'author' );

目前这不适用于 page 文章类型。我们正在修复此问题。

Add images to your Article Schema

If you want to add an image to an object programmatically, you can do it as follows. Note that we use the YoastSEO surface to get both the canonical from the meta surface as the helpers surface to access the schema->image functionality.

add_filter( 'wpseo_schema_article', 'example_change_article' );

/**
 * Adds images to Article Schema data.
 *
 * @param array $data Schema.org Article data array.
 *
 * @return array Schema.org Article data array.
 */
function example_change_article( $data ) {
    // This is the attachment ID for our image.
    $attachment_id = 12345;

    // We're going to create a graph piece for our image. Every graph piece always needs a Schema ID, so it can
    // be referenced by other graph pieces, best practice is to base that on the canonical adding an ID that's
    // always going to be unique.
    $schema_id     = YoastSEO()->meta->for_current_page()->canonical . '#/schema/image/' . $attachment_id;                 
    $data['image'] = new WPSEO_Schema_Image( $schema_id );

    return $data;
}

Instead of YoastSEO()->helpers->schema->image->generate_from_attachment_id() you can also use YoastSEO()->helpers->schema->image->generate_from_url() which takes, as you've guessed, a URL as input.

To make more changes to our Schema output, see the Yoast SEO Schema API.

Breadcrumb

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述 WebPage 在 WebSite 中的层级位置。

:::caution 本地环境中的面包屑导航 在本地环境中,或在完全禁用可索引对象创建的网站中,面包屑导航可能无法正常工作。

具体来说,在这些设置下,面包屑路径的层级结构可能会缺少中间节点,例如文章所属的分类,或子页面的父页面。 :::

触发器

应作为顶级节点添加到所有具有有效 WebPage 的公开页面的图谱中,但 4xx 和 5xx 范围错误页面/场景除外。

必需属性

一个有效的 BreadcrumbList 必须具有以下属性。

还需注意:

失败场景

如果任何必填字段缺失或无效,则不应输出该节点。

如果未输出该节点,任何原本会声明与 BreadcrumbList 存在关系的实体都应移除这些引用。

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "BreadcrumbList", "@id": "https://www.example.com/#/schema/BreadcrumbList/abc123", "itemListElement": [ { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://www.example.com/" }, { "@type": "ListItem", "position": 2, "name": "Example Section", "item": "https://www.example.com/example-section/" }, { "@type": "ListItem", "position": 3, "name": "Example Page" } ] } ] }`}

WordPress API:更改面包屑架构输出 {#api}

要修改 Yoast SEO 输出的 Breadcrumb 架构,您可以使用我们的 wpseo_schema_breadcrumb 过滤器。示例如下:

替换面包屑架构中的域名


如果您想替换面包屑架构中的域名,可以使用 `wpseo_schema_breadcrumb` 过滤器单独挂钩到面包屑架构片段。
add_filter( 'wpseo_schema_breadcrumb', 'replace_domain_name_to_breadcrumb_schema', 11, 2 );
/**
 * 单独替换面包屑架构片段中的域名。
 * 
 * 
 * @param array $piece Schema.org 面包屑数据数组。
 * @return array 修改后的 Schema.org 面包屑数据数组。
 */
function replace_domain_name_to_breadcrumb_schema( $piece ) {
    $piece['@id'] = str_replace( 'olddomain.tld', 'newdomain.tld', $piece['@id'] );
    foreach ( $piece['itemListElement'] as &$list ) {
            $list['item'] = str_replace( 'olddomain.tld', 'newdomain.tld', $list['item'] );
        if ( $list['item'] ) {
        }
    }
    return $piece;
}

要对我们的架构输出进行更多更改,请参阅 Yoast SEO Schema API。

Comment

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一条评论。通常在 Article 或 WebPage 的上下文中使用。

触发条件

当其他节点需要时(例如,当 Article 包含 comments 时),应作为顶级节点添加到图谱中。

必需属性

一个有效的 Comment 必须包含以下属性。

失败场景

如果任何必需字段缺失或无效,则不应输出该节点。

可选属性

在可用且有效时应添加以下属性:

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Comment", "@id": "https://www.example.com/#/schema/Comment/abc123", "about": { "@id": "https://www.example.com/blog/example-article/" }, "text": "Example comment content", "author": { "@id": "https://www.example.com/#/schema/Person/abc123" } } ] }`}

Event

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个 Event,它在特定时间和地点发生,并可包含额外的可选信息。

触发条件

当页面内容描述一个事件时,应将其作为图谱中的顶级节点添加。

必需属性

一个有效的 Event 必须包含以下属性。

可选属性

以下属性在可用且有效时应添加:

条件属性

当活动在实体地点(或“线下”)举行时

当活动为“虚拟”(或“在线”)时

当活动已取消时

当活动已重新安排时

当事件包含一个或多个“优惠”时(例如,售票活动)

失败场景

如果 Event 的任何必填字段缺失或无效,则不应输出该节点。

如果未输出某个节点,任何原本会声明与其存在关系的实体都应移除这些引用。

Examples

Minimum criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Event", "@id": "https://www.example.com/#/schema/Event/abc123", "name": "My awesome party", "location": { "@type": "Place", "name": "Example Venue", "address": { "@type": "PostalAddress", "@id": "https://www.example.com/#/schema/PostalAddress/abc123", "streetAddress": "123 Example Street", "addressLocality": "Example Town", "addressRegion": "Example Region", "postalCode": "ABC 123", "postOfficeBoxNumber": "123", "addressCountry": "US" } }, "startDate": "2025-07-21T19:00-05:00" } ] }`}

Extended criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Event", "@id": "https://www.example.com/#/schema/Event/abc123", "name": "My birthday party", "description": "We're all getting together in at Example Venue in Fake Town to celebrate my special day!", "location": { "@type": "Place", "name": "Example Venue", "address": { "@type": "PostalAddress", "streetAddress": "123 Fake Street", "addressLocality": "Fake Town", "postalCode": "12345", "addressRegion": "XY", "addressCountry": "US" } }, "startDate": "2025-07-21T19:00-05:00", "endDate": "2025-07-22T19:00-05:00", "eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode", "eventStatus": "https://schema.org/EventRescheduled", "previousStartDate": "2024-07-21T19:00-05:00", "offers": { "@type": "Offer", "@id": "https://www.example.com/#/schema/Offer/abc123", "priceSpecification": { "@type": "PriceSpecification", "price": "22.00", "valueAddedTaxIncluded": "false", "priceCurrency": "GBP" }, "availability": "http://schema.org/InStock", "url": "https://www.example.com/example-product-page/", "seller": { "@id": "https://www.example.com/#/schema/Organization/1" } }, "image": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "organizer": { "@id": "https://www.example.com/#/schema/Organization/1" }, "performer": { "@id": "https://www.example.com/#/schema/Person/abc123" } } ] }`}

Howto

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个包含一系列 steps 的 HowTo 指南。

触发条件

当页面内容包含 HowTo 区块 时,应将其作为顶级节点添加到图谱中。

必需属性

有效的 HowTo 必须包含以下属性。

故障场景

如果 HowTo 或 step 的任何必填字段缺失或无效,则不应输出该节点。

如果未输出某个节点,则任何原本会声明与其存在关系的实体都应移除这些引用。

可选属性

以下属性应在可用且有效时添加:

条件属性

当页面包含有效的 Article 时

当 HowToStep 包含一个或多个图像时

当 HowToStep 具有名称和描述时

当 HowToStep 有 name 但 description 中没有文本时

当 HowToStep 有 description 但没有 name 时

Examples

Minimum criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "HowTo", "@id": "https://www.example.com/#/schema/HowTo/abc123", "mainEntityOfPage": { "@id": "https://www.example.com/example-page/" }, "name": "HowTo example", "step": [ { "@type": "HowToStep", "@id": "https://www.example.com/#/schema/HowToStep/abc123", "name": "Example step 1", "url": "https://www.example.com/example-page/#how-to-step-1", "itemListElement": [ { "@type": "HowToDirection", "text": "The step description." } ] }, { "@type": "HowToStep", "text": "Example step 2 (no description)", "url": "https://www.example.com/example-page/#how-to-step-2" } ] } ] }`}

Extended criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "HowTo", "@id": "https://www.example.com/#/schema/HowTo/abc123", "mainEntityOfPage": { "@id": "https://www.example.com/#/schema/Article/abc123" }, "name": "HowTo example", "description": "HowTo description", "inLanguage": "en-US", "totalTime": "P2DT0H0M", "step": [ { "@type": "HowToStep", "@id": "https://www.example.com/#/schema/HowToStep/abc123", "name": "Example step 1", "url": "https://www.example.com/example-page/#how-to-step-1", "image": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "itemListElement": [ { "@type": "HowToDirection", "text": "The step description." } ] }, { "@type": "HowToStep", "@id": "https://www.example.com/#/schema/HowToStep/def456", "text": "Example step 2 (no description)", "url": "https://www.example.com/example-page/#how-to-step-2", "image": { "@id": "https://www.example.com/uploads/example-image-2.jpg" } }, { "@type": "HowToStep", "@id": "https://www.example.com/#/schema/HowToStep/ghi789", "name": "Example step 3", "url": "https://www.example.com/example-page/#how-to-step-3", "itemListElement": [ { "@type": "HowToDirection", "text": "The first paragrah in the step description." }, { "@type": "HowToDirection", "text": "The second paragrah in the step description." } ] } ] } ] }`}

Image

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述单个图像(通常在嵌入式媒体对象的上下文中)。

注意:虽然 image 属性(给定节点的)可以接受表示图像文件的简单 URL,但我们始终强制使用 ImageObject 以支持通过 ID 引用。

触发器

应在其他节点需要时,作为图的顶级节点添加。例如,当 WebPage 具有特色图像时。

必需属性

一个有效的 ImageObject 必须具有以下属性。

故障场景

如果任何必填字段缺失或无效,则不应输出该节点。

如果未输出该节点,任何原本会声明与 Image 存在关系(例如,作为 Organization 的 logo)的实体都应移除这些引用。

可选属性

以下属性应在可用且有效时添加:

条件属性

仅在满足所需条件时才应输出的可选属性。

当高度和宽度尺寸均已知时

当图像包含标题(或任何其他文本属性)时

备注

contentUrl 和 url 属性被有意重复设置。

contentUrl 属性比 url 属性更准确地描述/引用实际的图像资源(url 属性无法区分图像资源“所在”的 URL 与资源本身的位置)。因此,我们倾向于不设置/使用 url 属性——或者,将其保留用于指代图像位置的场景(例如,指向网页某部分的锚点/片段链接)。

然而,当 url 属性缺失时,Google 的测试工具在某些情况下会报错(针对 SDTT 中的 logo 和 primaryImageOfPage,以及 RRT 中的 primaryImageOfPage)。因此,我们仅添加一个 contentUrl 属性,其值为图像 URL。

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "ImageObject", "@id": "https://www.example.com/uploads/example-image.jpg", "url": "https://www.example.com/uploads/example-image.jpg", "contentUrl": "https://www.example.com/uploads/example-image.jpg" } ] }`}

Extended criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "ImageObject", "@id": "https://www.example.com/uploads/example-image.jpg", "url": "https://www.example.com/uploads/example-image.jpg", "contentUrl": "https://www.example.com/uploads/example-image.jpg", "caption": "Example caption", "inLanguage": "en-US", "width": 120, "height": 120 } ] }`}

Localbusiness

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个允许公众访问的企业。通常用于表示网站“背后”的企业,或关于特定企业的页面。

如果满足所需条件,可以转换为更具体的类型(例如 library),或扩展现有的 Organization。

触发器

必需属性

一个有效的 LocalBusiness 必须包含以下属性。

故障场景

若任何必填字段缺失或无效,节点应回退为常规 Organization(移除所有'本地' @type 值),同时保留下方所有可选属性。

可选属性

以下属性应在可用且有效时添加:

Examples

Minimum criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": [ "Organization", "Place", "Dentist" ], "@id": "https://www.example.com/#/schema/Organization/abc123", "name": "Example business name", "image": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "address": { "@id": "https://www.example.com/#/schema/Address/abc123" } } ] }`}

扩展标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": [ "Organization", "Place", "Dentist" ], "@id": "https://www.example.com/#/schema/Organization/abc123", "url": "https://www.example.com/example-business-page/", "email": "example@example.com", "priceRange": "$$", "areaServed": [ "Bournemouth", "Poole", "Dorset" ], "telephone": "01234 56789", "faxNumber": "02345 67890", "name": "Example business name", "openingHoursSpecification": { "@type": "OpeningHoursSpecification", "dayOfWeek": [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ], "opens": "09:00", "closes": "17:00" }, "geo": { "@type": "GeoCoordinates", "latitude": "51.8066986", "longitude": "5.7284488" }, "vatID": "abc123", "taxID": "def456", "image": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "address": { "@id": "https://www.example.com/#/schema/Address/abc123" } } ] }`}

Offer

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个 Product 的优惠信息(通常包括价格、库存情况等)。

触发条件

Required properties

A valid Offer must have the following properties.

可选属性

以下属性应在可用且有效时添加:

失败场景

如果任何必填字段缺失或无效,则不应输出该节点。

如果节点未输出,任何原本会引用该 Offer 的实体都应移除这些引用(例如,当产品通过 ID 引用 Offer 时)。

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Offer", "@id": "https://www.example.com/#/schema/Offer/abc123", "priceSpecification": { "@type": "PriceSpecification", "price": "22.00", "valueAddedTaxIncluded": "false", "priceCurrency": "GBP" }, "availability": "http://schema.org/InStock", "url": "https://www.example.com/example-product-page/", "seller": { "@id": "https://www.example.com/#/schema/Organization/1" } } ] }`}

Yoast WooCommerce SEO API:修改 Offer Schema 输出 {#api}

要修改 Yoast SEO 输出的 Offer schema,您可以使用我们的 wpseo_schema_offer 过滤器。示例如下:

add_filter( 'wpseo_schema_offer', 'example_change_offer', 10, 3 );

/**
 * 将产品描述添加到 offer schema 中。
 *
 * @param array $offer schema offer 数据。
 * @param WC_Product_Variation $variation 我们正在处理的 WooCommerce 产品变体。
 * @param WC_Product $product 我们正在处理的 WooCommerce 产品。
 *
 * @return array Schema.org Offer 数据数组。
 */
function example_change_offer( $offer, $variation, $product ) {
	$offer['priceSpecification']['description'] = $product->get_description();

	return $offer;
}

我们还提供了一个针对完整产品 schema 的通用性稍弱的过滤器:wpseo_schema_product,更多信息请参阅 产品 Schema 组件。

要对我们的 Schema 输出进行更多修改,请参阅 Yoast SEO Schema API。

Organization

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个组织(公司、企业或机构)。最常用于标识 WebSite 的发布者。

如果满足所需条件,可以转换为更具体的类型(例如 Corporation 或 LocalBusiness)。

触发器

应在所有公开页面上输出。

必需属性

一个有效的 Organization 必须包含以下属性。

失败场景

如果任何必填字段缺失或无效,则不应输出该节点。

如果未输出该节点,任何原本会声明与 Organization 存在关系的实体(例如,作为 WebSite 的 publisher,或 Product 的 manufacturer)应移除这些引用。

可选属性

以下属性应在可用且有效时添加:

转换

Organization 类型可能在以下场景中进行转换。

当业务为“本地”时

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://www.example.com/#/schema/Organization/abc123", "url": "https://www.example.com/", "logo": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "name": "示例组织名称" } ] }`}

Extended criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://www.example.com/#/schema/Organization/abc123", "url": "https://www.example.com/", "name": "Example organization name", "alternateName": "Short org name", "sameAs": [ "https://www.wikipedia.com/example-organization", "https://www.linkedin.com/company/1234" ], "logo": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "image": [ { "@id": "https://www.example.com/uploads/example-image.jpg" }, { "@id": "https://www.example.com/uploads/example-image-2.jpg" } ] } ] }`}

WordPress API:修改组织架构输出 {#api}

要修改 Yoast SEO 输出的 Organization 架构,您可以使用我们的 wpseo_schema_organization 过滤器。示例如下:

add_filter( 'wpseo_schema_organization', 'change_organization_schema', 11, 2 );

/**
 * 向 Yoast SEO 组织架构添加额外属性
 *
 * @param array             $data    架构组织数据。
 * @param Meta_Tags_Context $context 上下文值对象。
 *
 * @return array $data 架构组织数据。
 */
function change_organization_schema( $data, $context ) {
	$data['@type'] = [
		'Organization',
		'Brand',
	];
	$data['founder'] = [
		'@type'  => 'Person',
		'name'   => 'Joost de Valk',
		'sameAs' => 'https://en.wikipedia.org/wiki/Joost_de_Valk',
	];
	$data['foundingDate']       = '2010-05-01';
	$data['numberOfEmployees']  = (int) wp_count_posts( 'yoast_employees' )->publish;
	$data['slogan']             = 'SEO for Everyone';
	$data['description']        = 'Yoast helps you with your website optimization, whether it be through our widely used SEO software or our online SEO courses: we&#039;re here to help.';
	$data['url']                = 'https://yoast.com/';
	$data['legalName']          = 'Yoast BV';
	$data['alternateName']      = 'Yoast';
	$data['parentOrganization'] = [
		'@type'       => 'Organization',
		'name'        => 'Newfold Digital',
		'description' => 'Newfold Digital is a leading web presence solutions provider serving millions of small-to-medium businesses globally.',
		'url'         => 'https://newfold.com/',
		'sameAs'      => [
			'https://newfold.com/',
		],
		'logo'        => 'https://yoast.com/app/uploads/2022/01/newfold-logo.png',
	];
	$data['memberOf']           = [
		'@type'       => 'Organization',
		'name'        => 'World Wide Web Consortium (W3C)',
		'description' => 'The World Wide Web Consortium (W3C) is an international community that develops open standards to ensure the long-term growth of the Web.',
		'url'         => 'https://w3.org/',
		'sameAs'      => [
			'https://w3.org/',
		],
		'logo'        => 'https://www.w3.org/Icons/w3c_main.png',
	];

	return $data;
}

要对我们的架构输出进行更多修改,请参阅 Yoast SEO 架构 API。

Person

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个独立的个人。最常用于标识内容(例如 Article 或 Comment)的 author。

在某些情况下,也可用于标识 WebSite(或其他内容)的 publisher。

触发器

应在其他节点需要时作为图的顶级节点添加。例如,当 Article 有 author 时。

必需属性

一个有效的 Person 必须具有以下属性。

失败场景

如果任何必填字段缺失或无效,则不应输出该节点。

如果未输出该节点,则任何原本会声明与该 Person 存在关系的实体(例如,作为 WebSite 的 publisher,或作为 Article 的 author)都应移除这些引用。

'Admin' 用户名

如果人员的 name 是 'admin' 或类似名称(或其本地化等效名称),则该 Person 应被标记为无效;我们绝不应将内容显示为由 'admin' 创作。

可选属性

以下属性应在可用且有效时添加:

条件属性

仅在满足所需条件时才应输出的可选属性。

示例

Minimum criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Person", "@id": "https://www.example.com/#/schema/Person/abc123", "name": "Example person name" } ] }`}

Extended criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Person", "@id": "https://www.example.com/#/schema/Person/abc123", "name": "Example person name", "image": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "sameAs": [ "https://www.wikipedia.com/example-person", "https://www.facebook.com/example-person" ] } ] }`}

WordPress API: Change Organization Schema output {#api}

To make changes to the Person schema that Yoast SEO outputs, you can use our wpseo_schema_person filter. Here is an example:

Change person output

add_filter( 'wpseo_schema_person', 'schema_change_person', 11, 2 );

/**
 * Changes the Yoast SEO Person schema.
 *
 * @param array             $data    The Schema Person data.
 * @param Meta_Tags_Context $context Context value object.
 *
 * @return array $data The Schema Person data.
 */
function schema_change_person( $data, $context ) {
	if ( isset( $data['worksFor'] ) && $data['worksFor'] === 'Yoast' ) {
		// Make references to "Yoast" actually reference the organization's graph piece.
		$data['worksFor'] = [ '@id' => $context->site_url . Schema_IDs::ORGANIZATION_HASH ];
	}

	return $data;
}

Change person being output

If you want to change the person being output in the schema, you can filter it like this:

add_filter( 'wpseo_schema_person_user_id', 'change_schema_person_id' );

/**
 * Changes the Yoast SEO Person schema.
 *
 * @param int $person_id The Schema Person ID.
 *
 * @return int $person_id The (possibly altered) person ID.
 */
function change_schema_person_id( $person_id ) {
    if ( $person_id === 12 ) {
        return 3; // Make sure this is a valid user ID.
    }
    return $person_id;
}

社交资料

若需调整 Person 输出中 sameAs 数组显示的社交资料,可通过 wpseo_schema_person_social_profiles 过滤器实现。我们在 yoast.com 网站中运用此方法,将成员的 GitHub、WordPress 资料及个人网站添加至 sameAs 输出:

add_filter( 'wpseo_schema_person_social_profiles', 'yoast_add_social_profiles' );

/**
 * 向 sameAs 数组添加社交资料。
 *
 * @param array $profiles 社交资料数组。
 *
 * @return array 更新后的社交资料数组。
 */
function yoast_add_social_profiles( $profiles ) {
    array_push( $profiles, 'github', 'personal', 'wordpress' );

    return $profiles;
}

如需对 Schema 输出进行更多调整,请参阅 Yoast SEO Schema API。

Postaladdress

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个地点的邮政地址;通常在 LocalBusiness 的上下文中使用。

触发条件

应在图中作为顶级节点添加,根据其他节点的需要。例如,当 LocalBusiness 拥有一个地址时。

必需属性

一个有效的 postalAddress 必须包含以下属性。

失败场景

如果任何必需字段缺失或无效,则不应输出该节点。

如果节点未输出,任何原本会声明与 Address 关系的实体(例如,当被 localBusiness 引用时)应移除这些引用。

可选属性

以下属性应在可用且有效时添加:

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "PostalAddress", "@id": "https://www.example.com/#/schema/PostalAddress/abc123", "streetAddress": "123 Fake Street", "postalCode": "ABC 123", "addressCountry": "US" } ] }`}

扩展标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "PostalAddress", "@id": "https://www.example.com/#/schema/PostalAddress/abc123", "streetAddress": "123 Example Street", "addressLocality": "Example Town", "addressRegion": "Example Region", "postalCode": "ABC 123", "postOfficeBoxNumber": "123", "addressCountry": "US" } ] }`}

Product

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述企业销售的产品。

触发条件

必需属性

一个有效的 Product 必须包含以下属性。

失败场景

如果任何必填字段缺失或无效,则不应输出该节点。

如果节点未输出,任何原本声明自己属于 Product 的实体都应移除这些引用(例如,与 Product 相关的 Offer)。

可选属性

以下属性应在可用且有效时添加:

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Product", "@id": "https://www.example.com/#/schema/Product/abc123", "name": "Example Product", "image": { "@id": "https://www.example.com/uploads/example-image.jpg" } } ] }`}

Extended criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Product", "@id": "https://www.example.com/#/schema/Product/abc123", "name": "Example Product", "description": "Example product description", "image": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "offers": { "@id": "https://www.example.com/#/schema/Offer/abc123" }, "brand": { "@id": "https://www.example.com/#/schema/Organization/abc123" }, "manufacturer": { "@id": "https://www.example.com/#/schema/Organization/def456" }, "review": [ { "@id": "https://www.example.com/#/schema/Review/abc123" }, { "@id": "https://www.example.com/#/schema/Review/def456" } ], "sku": "abc123", "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4", "reviewCount": "3077" } } ] }`}

Yoast WooCommerce SEO API:修改产品 Schema 输出 {#api}

要修改 Yoast SEO 输出的 Product schema,你可以使用我们的 wpseo_schema_product 过滤器。示例如下:

add_filter( 'wpseo_schema_product', 'example_change_product' );

/**
 * 向产品 schema 添加颜色属性。
 *
 * @param array $data Schema 产品数据。
 *
 * @return array Schema.org 产品数据数组。
 */
function example_change_product( $data ) {
	$data['color'] = 'red';

	return $data;
}

我们还为产品 schema 的 offer 部分提供了一个更具体的过滤器:wpseo_schema_offer,更多信息请参阅 Offer Schema 片段。

要对我们的 Schema 输出进行更多修改,请参阅 Yoast SEO Schema API。

Productgroup

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个在属性(颜色、尺寸、价格)上存在变体的 Product。 更多信息请参阅 Product Schema 片段。

触发器

必需属性

有效的 ProductGroup 必须包含 offers。Product 的每个 offer 将被转换为 ProductGroup 中 hasVariant 下的一个 Product 条目。

失败场景

如果缺少 offers,我们将不会把 Product 更改为 ProductGroup。

可选属性

以下属性应在可用且有效时添加:

除此之外,ProductGroup 可以拥有 Product 的所有可选属性。

Examples

Minimum criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "ProductGroup", "@id": "https://www.example.com/#/schema/Product/abc123", "name": "Example Product", "image": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "hasVariant": [ { "@id": "https://www.example.com/#/schema/Product/def456" }, { "@id": "https://www.example.com/#/schema/Product/hij789" } ] } ] }`}

Extended criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "ProductGroup", "@id": "https://www.example.com/#/schema/Product/abc123", "name": "Example Product", "description": "Example product description", "image": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "brand": { "@id": "https://www.example.com/#/schema/Organization/abc123" }, "manufacturer": { "@id": "https://www.example.com/#/schema/Organization/def456" }, "review": [ { "@id": "https://www.example.com/#/schema/Review/abc123" }, { "@id": "https://www.example.com/#/schema/Review/def456" } ], "sku": "abc123", "productGroupID": "abc123", "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4", "reviewCount": "3077" }, "hasVariant": [ { "@type": "Product", "@id": "https://www.example.com/#/schema/Product/def456", "name": "Example Product - Red", "image": { "@id": "https://www.example.com/uploads/example-image-red.jpg" }, "color": "red" }, { "@type": "Product", "@id": "https://www.example.com/#/schema/Product/hij789", "name": "Example Product - Blue", "image": { "@id": "https://www.example.com/uploads/example-image-blue.jpg" }, "color": "Blue" } ] } ] }`}

Question

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个 Question。最常用于 FAQPage 或 QAPage 内容中。

触发条件

当页面内容包含 FAQ 块 时,应将其作为顶级节点添加到图谱中。

必需属性

一个有效的 Question 必须包含以下属性。

失败场景

如果 Question 的任何必需字段缺失或无效,则不应输出该节点。

如果某个节点未被输出,则任何原本会声明与其关系的实体都应移除这些引用。

可选属性

以下属性在可用且有效时应添加:

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Question", "@id": "https://www.example.com/#/schema/Question/abc123", "name": "What is the return policy?", "acceptedAnswer": { "@type": "Answer", "text": "Most unopened items in new condition and returned within 90 days will receive a refund or exchange. Some items have a modified return policy noted on the receipt or packing slip. Items that are opened or damaged or do not have a receipt may be denied a refund or exchange. Items purchased online or in-store may be returned to any store. Online purchases may be returned via a major parcel carrier." } } ] }`}

Recipe

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个 Recipe(菜谱),它包含一系列步骤说明、食材和可选字段。

触发条件

当页面内容描述一个菜谱时,应将其作为图谱中的顶级节点添加。

必需属性

一个有效的 Recipe 必须包含以下属性。

故障场景

如果 Recipe 的任何必填字段缺失或无效,则不应输出该节点。

如果某个节点未被输出,则任何原本会声明与其存在关系的实体都应移除这些引用。

可选属性

以下属性在可用且有效时应添加:

条件属性

当菜谱是页面的“主要实体”时

当仅指定 cookTime 或 prepTime 之一时

当同时指定 cookTime 和 prepTime 时

当页面包含有效的 Article 时

当 Article 有 Author 时

Examples

Minimum criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Recipe", "mainEntityOfPage": { "@id": "https://www.example.com/#/schema/Article/abc123" }, "name": "Party Coffee Cake", "image": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "recipeIngredient": [ "2 cups of flour", "3/4 cup white sugar", "2 teaspoons baking powder", "1/2 teaspoon salt", "1/2 cup butter", "2 eggs", "3/4 cup milk" ], "recipeInstructions": [ { "@type": "HowToStep", "text": "Preheat the oven to 350 degrees F. Grease and flour a 9x9 inch pan.", "url": "https://example.com/example-page/#recipe-step-1" }, { "@type": "HowToStep", "text": "In a large bowl, combine flour, sugar, baking powder, and salt.", "url": "https://example.com/example-page/#recipe-step-2" }, { "@type": "HowToStep", "text": "Mix in the butter, eggs, and milk.", "url": "https://example.com/example-page/#recipe-step-3" }, { "@type": "HowToStep", "text": "Spread into the prepared pan.", "url": "https://example.com/example-page/#recipe-step-4" }, { "@type": "HowToStep", "name": "Bake", "url": "https://example.com/example-page/#recipe-step-5" }, { "@type": "HowToStep", "text": "Allow to cool and enjoy.", "url": "https://example.com/example-page/#recipe-step-6" } ] } ] }`}

扩展标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Recipe", "mainEntityOfPage": { "@id": "https://www.example.com/#/schema/Article/abc123" }, "name": "Party Coffee Cake", "image": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "video": { "@id": "https://www.example.com/#/schema/VideoObject/abc123" }, "author": { "@id": "https://www.example.com/#/schema/Person/abc123" }, "inLanguage": "en-GB", "datePublished": "2018-03-10", "description": "This coffee cake is awesome and perfect for parties.", "prepTime": "PT20M", "cookTime": "PT30M", "totalTime": "PT50M", "keywords": "cake for a party, coffee", "recipeYield": "10", "recipeCategory": "Dessert", "recipeCuisine": "American", "suitableForDiet": [ "VegetarianDiet", "LowSaltDiet" ], "tools": [ "A 9x9 inch pan", "A large mixing bowl" ], "nutrition": { "@type": "NutritionInformation", "calories": "270 calories" }, "recipeIngredient": [ "2 cups of flour", "3/4 cup white sugar", "2 teaspoons baking powder", "1/2 teaspoon salt", "1/2 cup butter", "2 eggs", "3/4 cup milk" ], "recipeInstructions": [ { "@type": "HowToStep", "name": "Preheat", "text": "Preheat the oven to 350 degrees F. Grease and flour a 9x9 inch pan.", "url": "https://example.com/example-page/test/#recipe-step-1", "image": { "@id": "https://www.example.com/uploads/example-image-2.jpg" } }, { "@type": "HowToStep", "name": "Mix dry ingredients", "text": "In a large bowl, combine flour, sugar, baking powder, and salt.", "url": "https://example.com/example-page/test/#recipe-step-2", "image": { "@id": "https://www.example.com/uploads/example-image-3.jpg" } }, { "@type": "HowToStep", "name": "Add wet ingredients", "text": "Mix in the butter, eggs, and milk.", "url": "https://example.com/example-page/test/#recipe-step-3", "image": { "@id": "https://www.example.com/uploads/example-image-4.jpg" } }, { "@type": "HowToStep", "name": "Spread into pan", "text": "Spread into the prepared pan.", "url": "https://example.com/example-page/test/#recipe-step-4", "image": { "@id": "https://www.example.com/uploads/example-image-5.jpg" } }, { "@type": "HowToStep", "name": "Bake", "text": "Bake for 30 to 35 minutes, or until firm.", "url": "https://example.com/example-page/test/#recipe-step-5", "image": { "@id": "https://www.example.com/uploads/example-image-6.jpg" } }, { "@type": "HowToStep", "name": "Enjoy", "text": "Allow to cool and enjoy.", "url": "https://example.com/example-page/test/#recipe-step-6", "image": { "@id": "https://www.example.com/uploads/example-image-7.jpg" } } ] } ] }`}

Review

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个 Review(评论)。通常在 Product(产品)或 Organization(组织)的上下文中使用。

触发条件

应在图中作为顶级节点添加,当其他节点需要时。例如,当某个 Product 拥有 review 时。

必需属性

一个有效的 Review 必须包含以下属性。

失败场景

如果任何必需字段缺失或无效,则不应输出该节点。

如果未输出该节点,任何原本会声明与该 Review 有关系的实体(例如,被 Product 引用的 reviews)应移除这些引用。

可选属性

以下属性在可用且有效时应添加:

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Review", "@id": "https://www.example.com/#/schema/Review/abc123", "author": { "@id": "https://www.example.com/#/schema/Person/abc123" }, "reviewRating": { "@type": "Rating", "ratingValue": 4 } } ] }`}

扩展标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Review", "@id": "https://www.example.com/#/schema/Review/abc123", "name": "Example review name", "author": { "@id": "https://www.example.com/#/schema/Person/abc123" }, "reviewRating": { "@type": "Rating", "ratingValue": 8, "bestRating": 10, "worstRating": 1 }, "reviewBody": "Example review body", "datePublished": "2022-07-21" } ] }`}

Searchaction

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述 WebSite 上的一个 SearchAction。

触发条件

当网站拥有/支持内部搜索功能时,应作为 WebSite 节点的 potentialAction 属性添加。

必需属性

一个有效的 SearchAction 必须包含以下属性。

失败场景

如果任何必需字段缺失或无效,则不应输出该节点。

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "SearchAction", "target": "https://www.example.com/?s={search_term_string}", "query-input": { "@type": "PropertyValueSpecification", "valueRequired": true, "valueName": "search_term_string" } } ] }`}

WordPress API:更改 SearchAction 模式输出 {#api}

要更改 Yoast SEO 输出的 SearchAction 模式,可以使用以下过滤器:

禁用 SearchAction 输出

这将完全禁用 SearchAction 输出:

add_filter( 'disable_wpseo_json_ld_search', '__return_true' );

更改 SearchAction URL

要更改搜索 URL,请使用 wpseo_json_ld_search_url 过滤器。确保返回的 URL 包含搜索参数变量 {search_term_string},否则将无法工作。

add_filter( 'wpseo_json_ld_search_url', 'schema_change_search_url' );

function schema_change_search_url() {
    return 'https://example.com/search/?q={search_term_string}';
}

要对我们的模式输出进行更多更改,请参阅 Yoast SEO 模式 API。

Video

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述单个视频(通常在嵌入式媒体对象的上下文中)。

触发条件

应作为图谱中的顶级节点添加,当其他节点需要时。例如,当 WebPage 包含嵌入式视频时。

必需属性

一个有效的 VideoObject 必须包含以下属性。

失败场景

如果任何必填字段缺失或无效,则不应输出该节点。

如果未输出节点,则任何原本会声明与 VideoObject 存在关系的实体(例如,作为 Article 的一部分)都应移除这些引用。

可选属性

以下属性应在可用且有效时添加:

条件属性

仅在满足必要条件时才应输出的可选属性。

在 AMP 模板上

Examples

Minimum criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "VideoObject", "@id": "https://www.example.com/#/schema/VideoObject/abc123", "name": "Example video", "description": "Example video description", "thumbnailUrl": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "uploadDate": "2016-03-31T08:00:00+08:00" } ] }`}

Extended criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@id": "https://www.example.com/#/schema/VideoObject/abc123", "@type": "VideoObject", "contentUrl": "https://www.example.com/videos/video123.mp4", "description": "Example video description", "duration": "PT1M54S", "embedUrl": "https://www.example.com/videos/embed/video123/", "height": 640, "inLanguage": "en-US", "isFamilyFriendly": true, "name": "Example video", "thumbnail": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "uploadDate": "2016-03-31", "width": 480 } ] }`}

Webpage

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述 WebSite 上的单个页面。作为子页面元素(如 Article)的容器。

充当页面内容到父级 WebSite(进而到 Organization)的连接器。

如果满足所需条件,可转换为更具体的类型(如 FAQPage)。

触发器

应在所有公开页面上输出,4xx 和 5xx 系列错误页面/场景除外。

必需属性

有效的 WebPage 必须包含以下属性。

失败场景

如果任何必填字段缺失或无效,则不应输出该节点。

可选属性

以下属性应在可用且有效时添加:

条件属性

仅在满足特定条件时才应输出的可选属性。

当页面为常规页面时(而非文章归档、用户资料等)

当页面被创作时

当页面有评论,且页面不包含有效文章时

当页面为主页时

转换

WebPage 类型可能在以下场景中发生转换。

分类索引

当查询返回文章循环时(例如分类归档、博客首页或其他分类索引),则应将 type 属性更改为 CollectionPage。

个人资料页

当页面涉及特定用户时(例如:成员简介/作者归档):

常见问题页面

当页面包含常见问题时,应将 type 属性转换为包含 FAQPage 和 WebPage 的数组(或按上文所述采用最具体的转换方式)。

搜索结果页面

在搜索结果页面上,应将 type 属性修改为 [CollectionPage, SearchResultsPage] 数组。

Examples

Minimum criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "WebPage", "@id": "https://www.example.com/example-page/", "url": "https://www.example.com/example-page/", "name": "Example page name", "isPartOf": { "@id": "https://www.example.com/#/schema/WebSite/1" } } ] }`}

Extended criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "WebPage", "@id": "https://www.example.com/example-page/", "url": "https://www.example.com/example-page/", "name": "Example page name", "description": "Example page description", "keywords": ["cats","dogs","cake"], "isPartOf": { "@id": "https://www.example.com/#/schema/WebSite/1" }, "inLanguage": "en-US", "datePublished": "2019-07-10T08:08:40+00:00", "dateModified": "2019-07-10T08:43:03+00:00", "breadcrumb": { "@id": "https://www.example.com/#/schema/BreadcrumbList/abc123" }, "primaryImageOfPage": { "@id": "https://www.example.com/uploads/example-image.jpg" }, "image": [ { "@id": "https://www.example.com/uploads/example-image.jpg" }, { "@id": "https://www.example.com/uploads/example-image-2.jpg" } ], "video": [ { "@id": "https://www.example.com/#/schema/VideoObject/abc123" }, { "@id": "https://www.example.com/#/schema/VideoObject/def456" } ], "potentialAction": [ { "@type": "ReadAction", "target": [ "https://www.example.com/example-page/" ] } ] } ] }`}

WordPress API:修改网页 Schema 输出 {#api}

要修改 Yoast SEO 输出的 Webpage Schema,你可以使用我们的 wpseo_schema_webpage 过滤器。以下是一个示例:

add_filter( 'wpseo_schema_webpage', 'example_change_webpage' );

/**
 * 修改 Webpage Schema 数据的 @type。
 *
 * @param array $data Schema.org Webpage 数据数组。
 *
 * @return array Schema.org Webpage 数据数组。
 */
function example_change_webpage( $data ) {
    if ( ! is_page( 'about' ) ) {
        return $data;
    }

    $data['@type'] = 'AboutPage';

    return $data;
}

为了方便起见,我们还有一个更具体的过滤器:wpseo_schema_webpage_type - 用于更改页面类型,因此可以用来使上述示例更加简化。

要对我们的 Schema 输出进行更多更改,请参阅 Yoast SEO Schema API。

Website

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

描述一个 WebSite。是 WebPage 的父级。

触发条件

应在所有公开页面上输出。

必需属性

一个有效的 WebSite 必须包含以下属性。

失败场景

如果任何必填字段缺失或无效,则不应输出该节点。

可选属性

以下属性应在可用且有效时添加:

示例

最低标准

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "WebSite", "@id": "https://www.example.com/#/schema/WebSite/1", "url": "https://www.example.com", "name": "Example website" } ] }`}

Extended criteria

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "WebSite", "@id": "https://www.example.com/#/schema/WebSite/1", "url": "https://www.example.com", "name": "Example website", "alternateName": "Example.com", "inLanguage": "en-US", "potentialAction": { "@type": "SearchAction", "target": "https://www.example.com/?s={search_term_string}", "query-input": "required name=search_term_string" }, "publisher": { "@id": "https://www.example.com/#/schema/Organization/1" } } ] }`}

WordPress API:修改网站 Schema 输出 {#api}

要修改 Yoast SEO 输出的 Website schema,你可以使用我们的 wpseo_schema_website 过滤器。示例如下:

add_filter( 'wpseo_schema_website', 'example_change_website' );

/**
 * 修改网站 Schema 数据输出,覆盖名称和备用名称。
 *
 * @param array $data Schema.org 网站数据数组。
 *
 * @return array Schema.org 网站数据数组。
 */
function example_change_website( $data ) {
    $data['name']          = 'Yoast';
    $data['alternateName'] = 'Yoast.com';

    return $data;
}

要修改 SearchAction 输出,请参阅其 API 文档。

要对我们的 Schema 输出进行更多修改,请参阅 Yoast SEO Schema API。

Local Seo

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

Local SEO for WordPress 的架构输出基于 Yoast SEO for WordPress 的基础架构输出,增加了额外的本地商家详细信息(例如地址和营业时间)。

核心逻辑

当定义主要地点时

当未定义主要地点时

其他应用场景

在单个地点页面

当页面不代表主要地点时

当网站代表拥有多个地点的单一企业时

所有情况下

示例

单地点商业网站上的任意文章

此示例假设:

{`{ "@context": "https://schema.org", "@graph": [ { "@type": [ "Organization", "Place", "Dentist" ], "@id": "https://www.example.com/#/schema/organization/1", "url": "https://www.example.com/", "email": "example@example.com", "logo": { "@id": "https://www.example.com/#/schema/image/abc123" }, "priceRange": "$$", "areaServed": "Bournemouth, Poole, Dorset", "telephone": ["01234 56789", "02345 67890"], "currenciesAccepted": "EUR, GBP", "paymentAccepted": "Cash, cheque, most major credit cards", "faxNumber": "02345 67890", "name": "Example business name", "openingHoursSpecification": { "@type": "OpeningHoursSpecification", "dayOfWeek": [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ], "opens": "09:00", "closes": "17:00" }, "geo": { "@type": "GeoCoordinates", "latitude": "51.8066986", "longitude": "5.7284488" }, "vatID": "abc123", "taxID": "def456", "image": { "@id": "https://www.example.com/#/schema/image/abc123" }, "address": { "@id": "https://www.example.com/#/schema/address/abc123" } }, { "@type": "PostalAddress", "@id": "https://www.example.com/#/schema/address/abc123", "streetAddress": "123 Example Street", "addressLocality": "Example Town", "addressRegion": "Example Region", "postalCode": "ABC 123", "postOfficeBoxNumber": "123", "addressCountry": "US" }, { "@type": "WebSite", "@id": "https://www.example.com/#/schema/website/1", "url": "https://www.example.com/", "name": "Example website", "potentialAction": { "@type": "SearchAction", "target": "https://www.example.com/?s={search_term_string}", "query-input": "required name=search_term_string" }, "publisher": { "@id": "https://www.example.com/#/schema/organization/1" } }, { "@type": "WebPage", "@id": "https://www.example.com/example-post/", "url": "https://www.example.com/example-post/", "name": "Example post name", "description": "Example post description", "keywords": "cats,dogs,cake", "isPartOf": { "@id": "https://www.example.com/#/schema/website/1" }, "inLanguage": "en-US", "datePublished": "2019-07-10T08:08:40+00:00", "dateModified": "2019-07-10T08:43:03+00:00", "image": [ { "@id": "https://www.example.com/#/schema/image/abc123" }, { "@id": "https://www.example.com/#/schema/image/def456" } ] }, { "@type": "ImageObject", "@id": "https://www.example.com/#/schema/image/abc123", "url": "https://www.example.com/uploads/example-image.jpg", "caption": "Example caption", "width": 120, "height": 120 }, { "@type": "ImageObject", "@id": "https://www.example.com/#/schema/image/def456", "url": "https://www.example.com/uploads/example-image.jpg", "caption": "Example caption", "width": 120, "height": 120 }, { "@type": "Article", "@id": "https://www.example.com/example-post/#/schema/article/abc123", "headline": "Example article headline", "description": "Example article description", "isPartOf": { "@id": "https://www.example.com/example-post/" }, "mainEntityOfPage": { "@id": "https://www.example.com/example-post/" }, "datePublished": "2019-07-10T08:08:40+00:00", "dateModified": "2019-07-10T08:43:03+00:00", "commentCount": 6, "articleSection": "Cats,Dogs,Recipes", "author": { "@id": "https://www.example.com/#/schema/person/abc123" }, "publisher": { "@id": "https://www.example.com/#/schema/organization/1" }, "image": [ { "@id": "https://www.example.com/#/schema/image/abc123" }, { "@id": "https://www.example.com/#/schema/image/def456" } ], "video": [ { "@id": "https://www.example.com/#/schema/video/abc123" }, { "@id": "https://www.example.com/#/schema/video/def456" } ] }, { "@type": "Person", "@id": "https://www.example.com/#/schema/person/abc123", "name": "Example person name", "image": { "@id": "https://www.example.com/#/schema/image/abc123" }, "sameAs": [ "https://www.wikipedia.com/example-person", "https://www.facebook.com/example-person" ] } ] }`}

多地点企业的位置存档

此示例假设:

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://www.example.com/#/schema/organization/1", "url": "https://www.example.com/", "email": "example@example.com", "logo": { "@id": "https://www.example.com/#/schema/image/abc123" }, "telephone": "01234 56789", "faxNumber": "02345 67890", "vatID": "abc123", "taxID": "def456", "currenciesAccepted": "USD", "paymentAccepted": "Credit card only", "name": "Example business name", "image": { "@id": "https://www.example.com/#/schema/image/abc123" } }, { "@type": "WebSite", "@id": "https://www.example.com/#/schema/website/1", "url": "https://www.example.com/", "name": "Example website", "potentialAction": { "@type": "SearchAction", "target": "https://www.example.com/?s={search_term_string}", "query-input": "required name=search_term_string" }, "publisher": { "@id": "https://www.example.com/#/schema/organization/1" } }, { "@type": "WebPage", "@id": "https://www.example.com/example-local-archive/", "url": "https://www.example.com/example-local-archive/", "name": "Example local archive name", "description": "Example local archive description", "isPartOf": { "@id": "https://www.example.com/#/schema/website/1" }, "inLanguage": "en-US", "datePublished": "2019-07-10T08:08:40+00:00", "dateModified": "2019-07-10T08:43:03+00:00", "image": [ { "@id": "https://www.example.com/#/schema/image/abc123" }, { "@id": "https://www.example.com/#/schema/image/def456" } ] }, { "@type": "ImageObject", "@id": "https://www.example.com/#/schema/image/abc123", "url": "https://www.example.com/uploads/example-image.jpg", "caption": "Example caption", "width": 120, "height": 120 }, { "@type": "ImageObject", "@id": "https://www.example.com/#/schema/image/def456", "url": "https://www.example.com/uploads/example-image.jpg", "caption": "Example caption", "width": 120, "height": 120 }, { "@type": "ItemList", "@id": "http://www.example.com/page/#postlist", "mainEntityOfPage": { "@id": "https://www.example.com/example-local-archive/" }, "numberOfItems": 81, "itemListElement": [ { "@type": "ListItem", "position": 1, "name": "Location One", "url": "https://www.example.com/location-one/" }, { "@type": "ListItem", "position": 2, "name": "Location Two", "url": "https://www.example.com/location-two/" }, { "@type": "ListItem", "position": 3, "name": "Location Three", "url": "https://www.example.com/location-three/" } ] } ] }`}

请注意,itemList 仅出于演示目的被截断为三个项目。

单个地点页面

此示例假设:

{`{ "@context": "https://schema.org", "@graph": [ { "@type": [ "Organization", "Place", "Dentist" ], "@id": "https://www.example.com/#/schema/organization/1", "url": "https://www.example.com/", "email": "example@example.com", "logo": { "@id": "https://www.example.com/#/schema/image/abc123" }, "telephone": ["01234 56789", "02345 67890"], "currenciesAccepted": "EUR, GBP, USD", "paymentAccepted": "Cash only", "priceRange": "$$", "areaServed": [ "Bournemouth", "Poole", "Dorset" ], "faxNumber": "02345 67890", "name": "Example business name", "openingHoursSpecification": { "@type": "OpeningHoursSpecification", "dayOfWeek": [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ], "opens": "09:00", "closes": "17:00" }, "geo": { "@type": "GeoCoordinates", "latitude": "51.8066986", "longitude": "5.7284488" }, "vatID": "abc123", "taxID": "def456", "image": { "@id": "https://www.example.com/#/schema/image/abc123" }, "address": { "@id": "https://www.example.com/#/schema/address/abc123" } }, { "@type": "PostalAddress", "@id": "https://www.example.com/#/schema/address/abc123", "streetAddress": "123 Example Street", "addressLocality": "Example Town", "addressRegion": "Example Region", "postalCode": "ABC 123", "postOfficeBoxNumber": "123", "addressCountry": "US" }, { "@type": "PostalAddress", "@id": "https://www.example.com/#/schema/address/def456", "streetAddress": "456 Example Street", "addressLocality": "Example City", "addressRegion": "Example County", "postalCode": "DEF 456", "postOfficeBoxNumber": "456", "addressCountry": "UK" }, { "@type": "WebSite", "@id": "https://www.example.com/#/schema/website/abc123", "url": "https://www.example.com/", "name": "Example website", "potentialAction": { "@type": "SearchAction", "target": "https://www.example.com/?s={search_term_string}", "query-input": "required name=search_term_string" }, "publisher": { "@id": "https://www.example.com/#/schema/organization/1" } }, { "@type": "WebPage", "@id": "https://www.example.com/single-location-page/", "url": "https://www.example.com/single-location-page/", "name": "Example single location page name", "description": "Example single location page description", "isPartOf": { "@id": "https://www.example.com/#/schema/website/abc123" }, "inLanguage": "en-US", "datePublished": "2019-07-10T08:08:40+00:00", "dateModified": "2019-07-10T08:43:03+00:00", "image": [ { "@id": "https://www.example.com/#/schema/image/abc123" }, { "@id": "https://www.example.com/#/schema/image/def456" } ] }, { "@type": "ImageObject", "@id": "https://www.example.com/#/schema/image/abc123", "url": "https://www.example.com/uploads/example-image.jpg", "caption": "Example caption", "width": 120, "height": 120 }, { "@type": "ImageObject", "@id": "https://www.example.com/#/schema/image/def456", "url": "https://www.example.com/uploads/example-image.jpg", "caption": "Example caption", "width": 120, "height": 120 }, { "@type": [ "Organization", "Place", "Dentist" ], "@id": "https://www.example.com/#/schema/organization/def456", "mainEntityOfPage": { "@id": "https://www.example.com/single-location-page/" }, "branchOf": { "@id": "https://www.example.com/#/schema/organization/1" }, "url": "https://www.example.com/single-location-page/", "email": "example2@example.com", "priceRange": "$$$", "areaServed": [ "Manchester", "London", "Scotland" ], "telephone": "01234 56789", "faxNumber": "02345 67890", "paymentAccepted": "Cash, cheque", "name": "Example business name", "openingHoursSpecification": { "@type": "OpeningHoursSpecification", "dayOfWeek": [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ], "opens": "09:00", "closes": "17:00" }, "geo": { "@type": "GeoCoordinates", "latitude": "1.2345678", "longitude": "2.3456789" }, "vatID": "abc123", "taxID": "def456", "image": [ { "@id": "https://www.example.com/#/schema/image/abc123" }, { "@id": "https://www.example.com/#/schema/image/def456" } ], "address": { "@id": "https://www.example.com/#/schema/address/def456" } } ] }`}

更多信息

News Seo

News SEO for WordPress 的 schema 输出建立在 Yoast SEO for WordPress 的基础 schema 输出之上,以提供额外的新闻上下文和细节。

核心逻辑

当某个文章类型被选定为新闻文章格式时,则:

其他场景

NewsArticle 子类型

当用户将文章类型或单篇文章指定为 NewsArticle 的特定子类型(例如 OpinionNewsArticle)时,将 @type 属性设置为该值。

在不包含 Article 的“新闻页面”上

在少数情况下,非文章格式(例如页面、自定义文章类型、首页、产品页面等)可能被选为新闻类型。通常,这些页面格式不包含 Article。

在这些情况下,应将一个 Article(及其依赖部分)添加到图谱中,并将其多类型化为 Article 和 NewsArticle,就像该页面是一个常规的新闻文章一样。

在某些场景下,这可能导致多个实体声明与 WebPage 存在 mainEntityOfPage 关系(例如,当页面已包含 Product 时)。发生这种情况时,应更改冲突节点(例如 Product)上的 mainEntityOfPage 属性,以引用 NewsArticle 的 ID。

更多信息

Video Seo

我们的 Video SEO 插件 的 Schema 输出基于 Yoast SEO 的 Schema 输出,以添加关于嵌入式视频的额外细节。

对于您已启用 Video SEO 的所有文章类型,当它在页面上遇到其识别的视频嵌入时,会自动添加一个 videoObject 片段。

核心逻辑

其他场景

在包含 Article 的页面上

更多信息

Woocommerce Seo

我们的 WooCommerce SEO 插件 的 Schema 输出基于 Yoast SEO 的 Schema 输出,增加了关于产品和电子商务功能的额外细节。

核心功能

在所有页面上

在产品页面上

过滤器

在结账页面上

在所有页面上可选。

更多信息

Yoast Seo Premium

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

我们 Yoast SEO Premium 的 schema 输出建立在 Yoast SEO schema 输出 的基础上,旨在为组织添加更多详细信息。

核心功能

额外的可选属性

以下属性应在可用且有效时添加:

网站政策:

附加组织信息:

通过提供更深入的信息来丰富贵组织的资料。分享的细节越多,Google 就越能理解您的网站。

组织标识符:

Yoast Seo Shopify

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

本页面记录了 Yoast SEO for Shopify 应用输出的 schema.org 标记。

输出总体上与我们的通用方法保持一致,但由于平台机制差异存在一些细微变化。

结构化数据和微数据移除

许多 Shopify 网站和主题包含模式"片段";例如,孤立的微数据片段、断开的 JSON-LD 节点以及单薄或重复的实体描述。 为防止潜在的冲突和错误,我们尝试移除主题模板中所有现有的结构化数据(包括 JSON-LD 和微数据格式),当我们的应用被安装时。

大多数添加结构化数据功能(例如,评论、食谱等)的应用不应受到影响,前提是它们没有将其脚本直接放置在主题代码中。

核心逻辑

每个页面都应(尝试)输出以下部分:

请注意,这取决于应用架构设置中的以下条件:

当满足这些条件时,我们会生成基础脚本。在 Shopify 商店中,这通常看起来类似于以下内容(上述各部分的组合):

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://www.example.com/#/schema/organization/1", "url": "https://www.example.com", "name": "Example organization name", "logo": { "@id": "https://www.example.com/#/schema/ImageObject/1234" }, "image": [ { "@id": "https://www.example.com/#/schema/ImageObject/1234" } ], "sameAs": [ "https://www.wikipedia.com/example-organization", "https://facebook.com/example-organization", "https://x.com/example-organization", "https://www.instagram.com/example-organization" ] }, { "@type": "ImageObject", "@id": "https://www.example.com/#/schema/ImageObject/1234", "url": "https://www.example.com/content/images/image.jpg", "contentUrl": "https://www.example.com/content/images/image.jpg", "width": 520, "height": 240 }, { "@type": "WebSite", "@id": "https://www.example.com/#/schema/website/1", "url": "https://www.example.com", "name": "Example store", "potentialAction": { "@type": "SearchAction", "target": "https://www.example.com/search?q={search_term_string}", "query-input": "required name=search_term_string" }, "inLanguage": "en", "publisher": { "@id": "https://www.example.com/#/schema/organization/1" } }, { "@type": [ "WebPage", "CollectionPage" ], "@id": "https://www.example.com/", "url": "https://www.example.com/", "name": "Example store", "about": { "@id": "https://www.example.com/#/schema/organization/1" }, "breadcrumb": { "@id": "https://www.example.com/#/schema/breadcrumb" }, "isPartOf": { "@id": "https://www.example.com/#/schema/website/1" } }, { "@type": "BreadcrumbList", "@id": "https://www.example.com/#/schema/breadcrumb", "itemListElement": [ { "@type": "ListItem", "name": "Example store", "position": 1 } ] } ] }`}

在产品页面

示例

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Product", "@id": "https://www.example.com/products/example-product/#/schema/Product", "mainEntityOfPage": { "@id": "https://www.example.com/products/example-product" }, "name": "Example product", "image": { "@id": "https://www.example.com/#/schema/image/abc123" }, "description": "Product description", "sku": "abc123", "url": "https://www.example.com/products/example-product", "offers": { "@type": "AggregateOffer", "@id": "https://www.example.com/products/example-product/#/schema/AggregateOffer", "availability": "https://schema.org/InStock", "offerCount": 1, "lowPrice": 10.0, "highPrice": 10.0, "priceCurrency": "EUR", "url": "https://www.example.com/products/example-product", "priceSpecification": { "@type": "PriceSpecification", "valueAddedTaxIncluded": true } } } ] }`}

关于博客文章

Yoast Seo

import YoastSchemaExample from '../../../../src/components/YoastSchemaExample';

本页面记录了 Yoast SEO 插件输出的 schema.org 标记。更多关于我们的 API、集成机制和方法论的信息,请参阅概述。

核心逻辑

每个页面都应(尝试)输出以下片段:

这将生成一个“基础脚本”,通常类似于以下内容(上述片段的组合):

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://www.example.com/#/schema/organization/1", "url": "https://www.example.com/#/schema/organization/", "name": "Example organization name", "sameAs": [ "https://www.wikipedia.com/example-organization", "https://www.linkedin.com/company/1234" ], "logo": { "@id": "https://www.example.com/#/schema/image/abc123" }, "image": [ { "@id": "https://www.example.com/#/schema/image/abc123" }, { "@id": "https://www.example.com/#/schema/image/def456" } ] }, { "@type": "WebSite", "@id": "https://www.example.com/#/schema/website/1", "url": "https://www.example.com/", "name": "Example website", "potentialAction": { "@type": "SearchAction", "target": "https://www.example.com/?s={search_term_string}", "query-input": "required name=search_term_string" }, "publisher": { "@id": "https://www.example.com/#/schema/organization/1" } }, { "@type": "WebPage", "@id": "https://www.example.com/example-page/test/", "url": "https://www.example.com/example-page/test/", "name": "Example page name", "description": "Example page description", "keywords": "cats,dogs,cake", "isPartOf": { "@id": "https://www.example.com/#/schema/website/1" }, "inLanguage": "en-US", "datePublished": "2019-07-10T08:08:40+00:00", "dateModified": "2019-07-10T08:43:03+00:00", "breadcrumb": { "@id": "https://www.example.com/example-page/#/schema/breadcrumb/abc123" }, "image": [ { "@id": "https://www.example.com/#/schema/image/abc123" }, { "@id": "https://www.example.com/#/schema/image/def456" } ] }, { "@type": "BreadcrumbList", "@id": "https://www.example.com/example-page/#/schema/breadcrumb/abc123", "itemListElement": [ { "@type": "ListItem", "position": 1, "item": { "@type": "WebPage", "@id": "https://www.example.com/", "url": "https://www.example.com/", "name": "Home" } }, { "@type": "ListItem", "position": 2, "item": { "@id": "https://www.example.com/example-page/test/" } } ] }, { "@type": "ImageObject", "@id": "https://www.example.com/#/schema/image/abc123", "url": "https://www.example.com/uploads/example-image.jpg", "caption": "Example caption", "width": 120, "height": 120 }, { "@type": "ImageObject", "@id": "https://www.example.com/#/schema/image/def456", "url": "https://www.example.com/uploads/example-image.jpg", "caption": "Example caption", "width": 120, "height": 120 } ] }`}

其他场景

在以下场景中,上述基础脚本应进行修改或扩展。

当网站代表个人时

针对单篇文章

当页面包含 HowTo 块时

当页面包含常见问题解答区块时

更多信息

REST API 端点

Get Schema for Post Type (JSON-L)

Retrieve aggregated schema pieces for a specific post type in JSON-L format.

Endpoint:

GET /wp-json/yoast/v1/schema-aggregator/get-schema/{post_type}[/{page}]

Parameters:

Parameter Type Required Default Description
post_type string Yes - The post type to aggregate (e.g., post, page, product)
page integer No 1 Page number for pagination

Pagination:

You can customize pagination using filters:

Response Format:

{
   "@context": "https://schema.org",
   "@type": "Article",
   "@id": "https://example.com/hello-world/#article",
   "headline": "Hello World",
   "description": "A brief description of the article",
   "articleBody": "The full article content...",
   "keywords": ["hello", "world"],
   "datePublished": "2024-01-15T10:30:00+00:00",
   "dateModified": "2024-01-20T14:45:00+00:00",
   "author": {
       "@id": "https://example.com/#/schema/person/1"
   },
   "publisher": {
       "@id": "https://example.com/#organization"
   }
    
}

Cache Headers:

Responses include cache control headers with a 5-minute (300 seconds) cache duration:

Cache-Control: max-age=300

Example Request (cURL):

# Get first page of posts
curl https://example.com/wp-json/yoast/v1/schema-aggregator/get-schema/post

# Get second page
curl https://example.com/wp-json/yoast/v1/schema-aggregator/get-schema/post/2

Get XML Schemamap

Retrieve an XML sitemap of all available schema endpoints.

Endpoint:

GET /wp-json/yoast/v1/schema-aggregator/get-xml

Parameters:

None.

Response Format:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
   <url contentType="structuredData/schema.org">
      <loc>https://example.com/wp-json/yoast/v1/schema-aggregator/get-schema/page</loc>
      <lastmod>2026-01-01T14:03:56Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>0.8</priority>
   </url>
   <url contentType="structuredData/schema.org">
      <loc>https://example.com/wp-json/yoast/v1/schema-aggregator/get-schema/post</loc>
      <lastmod>2026-01-01T14:03:56Z</lastmod>
      <changefreq>daily</changefreq>
      <priority>0.8</priority>
   </url>
</urlset>

Cache Headers:

Responses include cache control headers with a 5-minute (300 seconds) cache duration:

Cache-Control: max-age=300

robots.txt Integration:

The schemamap is automatically referenced in your site's robots.txt:

Sitemap: https://example.com/wp-json/yoast/v1/schema-aggregator/get-xml

Example Request:

curl https://example.com/wp-json/yoast/v1/schema-aggregator/get-xml

Customizing Post Types:

By default, the schemamap includes all indexable post types. You can customize this using the wpseo_schema_aggregator_post_types filter:

add_filter( 'wpseo_schema_aggregator_post_types', 'customize_schema_post_types' );

/**
 * Customize which post types appear in schema aggregator.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/api-reference/#get-schema-map-xml
 *
 * @param array $post_types Array of post type names.
 *
 * @return array Modified array of post type names.
 */
function customize_schema_post_types( $post_types ) {
    // Only include posts and pages.
    return [ 'post', 'page' ];
}

CLI 命令

聚合站点架构

通过 WP-CLI 检索文章类型的聚合架构。

命令:

wp yoast aggregate_site_schema <post_type> [--page=<page>]

参数:

参数 类型 必需 默认值 描述
<post_type> 字符串 是 - 要聚合的文章类型
--page 整数 否 1 分页的页码

输出格式:

JSON-LD 输出将打印到标准输出。

使用示例:

# 获取文章的第一页
wp yoast aggregate_site_schema post

# 获取产品的第二页
wp yoast aggregate_site_schema product --page=2

清除模式缓存

使所有或特定文章类型的缓存模式数据失效。

命令:

wp yoast clear_schema_aggregator_cache [<post_type>]

参数:

参数 类型 必需 默认值 描述
<post_type> 字符串 否 - 可选,指定要清除缓存的文章类型

使用示例:

# 清除所有模式缓存
wp yoast clear_schema_aggregator_cache

# 仅清除文章缓存
wp yoast clear_schema_aggregator_cache post

# 仅清除产品缓存
wp yoast clear_schema_aggregator_cache product

筛选与增强

被过滤的内容

默认情况下,Schema 聚合器会移除以下类别的模式片段:

此筛选可减少干扰,专注于有意义的内容实体。

What Gets Enhanced

The Schema Aggregator enhances certain schema types with additional data:

Article Enhancement

Articles (and subtypes like BlogPosting, NewsArticle) are enhanced with:

  1. articleBody: Full post content (configurable max length, default: 500 characters)
  2. description: Post excerpt (configurable max length, default: no limit)
  3. keywords: Post tags as keyword array (optionally includes categories)

Configuration:

// Adjust article body length.
add_filter( 'wpseo_article_enhance_config_article_body_max_length', function() {
    return 2000; // Increase to 2,000 characters.
} );

// Adjust excerpt length.
add_filter( 'wpseo_article_enhance_config_excerpt_max_length', function() {
    return 500; // Limit to 500 characters.
} );

// Include categories as keywords.
add_filter( 'wpseo_article_enhance_config_categories_as_keywords', '__return_true' );

// Disable article body enhancement.
add_filter( 'wpseo_article_enhance_article_body', '__return_false' );

// Disable excerpt enhancement.
add_filter( 'wpseo_article_enhance_use_excerpt', '__return_false' );

// Disable keywords enhancement.
add_filter( 'wpseo_article_enhance_keywords', '__return_false' );

人员信息增强

人员模式 (@type: Person) 已通过以下方式增强:

  1. 职位头衔:从 WordPress 用户元数据中获取 (job_title 字段)

配置方法:

// 禁用职位头衔增强功能。
add_filter( 'wpseo_person_enhance_person_job_title', '__return_false' );

自定义过滤

Schema Aggregator 采用策略模式进行过滤。默认策略使用元素到上下文的映射对模式片段进行分类,并过滤掉整个类别(Actions、Enumerations、Meta、Website)。您可以在三个级别自定义过滤:

替换整个筛选策略

实现 Filtering_Strategy_Interface 并通过 wpseo_schema_aggregator_filtering_strategy 钩子注册:

add_filter( 'wpseo_schema_aggregator_filtering_strategy', function( $default_filter ) {
    return new My_Custom_Filter();
} );

你的自定义类必须实现 filter 方法,该方法接收一个 Schema_Piece_Collection 并返回一个筛选后的 Schema_Piece_Collection。

修改元素到上下文的映射

默认过滤策略将模式类型映射到类别(例如 website、actions、meta)。您可以使用两个过滤器修改此映射:

// 替换整个映射。
add_filter( 'wpseo_schema_aggregator_elements_context_map', function( $map ) {
    $map['my_category'] = [ 'WebPage', 'SomeOtherType' ];
    return $map;
} );

// 修改单个类别(例如,将 Table 添加到 website 类别)。
add_filter( 'wpseo_schema_aggregator_elements_context_map_website', function( $types ) {
    $types[] = 'Table';
    return $types;
} );

条件与属性级过滤

默认策略还支持通过节点级过滤器实现细粒度控制:

See Also

Filters

Schema Aggregator 提供 WordPress 过滤器,允许您自定义其行为,从缓存配置到内容增强。本文档为每个过滤器提供完整参考和实用示例。

快速参考

Filter Category Purpose
wpseo_schema_aggregator_post_types Post Types Control which post types to aggregate
wpseo_schema_aggregator_per_page Pagination Set default items per page (1000)
wpseo_schema_aggregator_per_page_big Pagination Set items per page for big schema post types (100)
wpseo_schema_aggregator_big_schema_post_types Pagination Define which post types have large schema
wpseo_schema_aggregator_cache_enabled Cache Enable/disable caching
wpseo_schema_aggregator_cache_ttl Cache Set cache duration (dynamic by default)
wpseo_schema_aggregator_schemamap_changefreq schemamap Set XML schemamap update frequency
wpseo_schema_aggregator_schemamap_priority schemamap Set XML schemamap priority
wpseo_schema_aggregator_filtering_strategy Filtering Implement custom filtering logic
wpseo_schema_aggregator_elements_context_map Context Map Override complete context map
wpseo_schema_aggregator_elements_context_map_{context} Context Map Customize elements for specific context
wpseo_article_enhance_config_{key} Article Enhancement Configure article enhancement settings
wpseo_article_enhance_{enhancement} Article Enhancement Toggle article enhancements (article_body, use_excerpt, keywords)
wpseo_article_enhance_body_when_excerpt_exists Article Enhancement Include body when excerpt exists
wpseo_article_enhance_article_body_fallback Article Enhancement Include body when no excerpt
wpseo_person_enhance_config_{key} Person Enhancement Configure person enhancement settings
wpseo_person_enhance_{enhancement} Person Enhancement Toggle person enhancements (person_job_title)
wpseo_disable_robots_schemamap Robots.txt Disable schemamap in robots.txt

Post Type Configuration

wpseo_schema_aggregator_post_types

Control which post types are included in the schema aggregation.

Parameters:

Parameter Type Description
$post_types array Array of post type names to aggregate

Default Value: All indexable post types

Example: Include Only Specific Post Types

add_filter( 'wpseo_schema_aggregator_post_types', 'limit_aggregated_post_types' );

/**
 * Limit schema aggregation to posts and pages only.
 *
 * This is useful when you have custom post types that don't need
 * structured data or when you want to improve performance.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_post_types
 *
 * @param array $post_types Array of post type names.
 *
 * @return array Modified array of post type names.
 */
function limit_aggregated_post_types( $post_types ) {
	// Only aggregate posts and pages.
	return [ 'post', 'page' ];
}

Example: Exclude Specific Post Types

add_filter( 'wpseo_schema_aggregator_post_types', 'exclude_custom_post_types' );

/**
 * Exclude internal post types from schema aggregation.
 *
 * Remove post types that are used for internal purposes and
 * should not appear in structured data output.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_post_types
 *
 * @param array $post_types Array of post type names.
 *
 * @return array Modified array of post type names.
 */
function exclude_custom_post_types( $post_types ) {
	// Remove internal post types.
	$excluded = [ 'acf-field', 'acf-field-group', 'custom_css' ];

	return array_diff( $post_types, $excluded );
}

分页配置

Schema Aggregator 根据架构片段的大小使用不同的分页限制。某些文章类型(如 WooCommerce 产品)会生成大型架构对象,需要较小的页面尺寸。

wpseo_schema_aggregator_per_page

Set the default number of items per page for schema aggregation.

Parameters:

Parameter Type Description
$per_page int Number of items per page

Default Value: 1000

Example:

add_filter( 'wpseo_schema_aggregator_per_page', 'custom_default_per_page' );

/**
 * Reduce default items per page for better performance.
 *
 * Lower pagination can help with memory limits on shared hosting
 * or when dealing with post types that have moderate schema sizes.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_per_page
 *
 * @param int $per_page Number of items per page.
 *
 * @return int Modified number of items per page.
 */
function custom_default_per_page( $per_page ) {
	return 500; // Reduce from 1000 to 500.
}

wpseo_schema_aggregator_per_page_big

Set the number of items per page for post types with large schema pieces.

Parameters:

Parameter Type Description
$per_page_big int Number of items per page for big schema post types

Default Value: 100

Example:

add_filter( 'wpseo_schema_aggregator_per_page_big', 'custom_big_per_page' );

/**
 * Adjust pagination for post types with large schema.
 *
 * Increase or decrease based on your server's memory limits
 * and the actual size of your schema pieces.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_per_page_big
 *
 * @param int $per_page_big Number of items per page for big schema.
 *
 * @return int Modified number of items per page.
 */
function custom_big_per_page( $per_page_big ) {
	return 50; // Reduce from 100 to 50 for very large schemas.
}

wpseo_schema_aggregator_big_schema_post_types

Define which post types should use the "big schema" pagination limit.

Parameters:

Parameter Type Description
$big_schema_post_types array Array of post type names with large schema pieces

Default Value: [ 'product' ]

Example:

add_filter( 'wpseo_schema_aggregator_big_schema_post_types', 'add_big_schema_post_types' );

/**
 * Mark custom post types as having large schema pieces.
 *
 * Post types with extensive custom fields, galleries, or complex
 * relationships should use the lower pagination limit.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_big_schema_post_types
 *
 * @param array $big_schema_post_types Array of post type names.
 *
 * @return array Modified array of post type names.
 */
function add_big_schema_post_types( $big_schema_post_types ) {
	// Add custom post types with large schemas.
	$big_schema_post_types[] = 'property'; // Real estate listings.
	$big_schema_post_types[] = 'event'; // Events with extensive details.

	return $big_schema_post_types;
}

Cache Configuration

The Schema Aggregator implements intelligent caching to balance performance and freshness. Cache automatically clears when posts are updated.

wpseo_schema_aggregator_cache_enabled

Enable or disable the cache system entirely.

Parameters:

Parameter Type Description
$enabled bool Whether caching is enabled

Default Value: true

Example:

add_filter( 'wpseo_schema_aggregator_cache_enabled', 'disable_schema_cache_in_dev' );

/**
 * Disable caching in development environment.
 *
 * This ensures you always see fresh data during development
 * without needing to manually clear the cache.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_cache_enabled
 *
 * @param bool $enabled Whether caching is enabled.
 *
 * @return bool Modified cache enabled status.
 */
function disable_schema_cache_in_dev( $enabled ) {
	// Disable cache in local/staging environments.
	if ( defined( 'WP_ENV' ) && in_array( WP_ENV, [ 'development', 'staging' ], true ) ) {
		return false;
	}

	return $enabled;
}

wpseo_schema_aggregator_cache_ttl

Set the cache time-to-live (TTL) in seconds.

Parameters:

Parameter Type Description
$ttl int Cache duration in seconds

Default Value: Dynamic based on serialized payload size:

Example: Fixed TTL

add_filter( 'wpseo_schema_aggregator_cache_ttl', 'custom_cache_ttl' );

/**
 * Set a fixed cache duration.
 *
 * Use this when you want consistent cache behavior regardless
 * of payload size.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_cache_ttl
 *
 * @param int $ttl Cache duration in seconds.
 *
 * @return int Modified cache duration.
 */
function custom_cache_ttl( $ttl ) {
	// Cache for 2 hours regardless of payload size.
	return 7200;
}

Schemamap Configuration

The schemamap is an XML sitemap that lists all available schema endpoints. It's automatically referenced in your site's robots.txt.

wpseo_schema_aggregator_schemamap_changefreq

Set the change frequency for schemamap entries.

Parameters:

Parameter Type Description
$changefreq string Change frequency (always, hourly, daily, weekly, monthly, yearly, never)

Default Value: 'daily'

Example:

add_filter( 'wpseo_schema_aggregator_schemamap_changefreq', 'custom_schemamap_changefreq' );

/**
 * Set schemamap change frequency to weekly.
 *
 * Use this to indicate how often search engines should check
 * for updated schema data.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_schemamap_changefreq
 *
 * @param string $changefreq Change frequency value.
 *
 * @return string Modified change frequency.
 */
function custom_schemamap_changefreq( $changefreq ) {
	return 'weekly'; // Change from daily to weekly.
}

wpseo_schema_aggregator_schemamap_priority

Set the priority value for schemamap entries.

Parameters:

Parameter Type Description
$priority float Priority value between 0.0 and 1.0

Default Value: 0.8

Example:

add_filter( 'wpseo_schema_aggregator_schemamap_priority', 'custom_schemamap_priority' );

/**
 * Increase schemamap priority to indicate importance.
 *
 * Higher priority suggests to search engines that this content
 * is important relative to other URLs on your site.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_schemamap_priority
 *
 * @param float $priority Priority value (0.0 to 1.0).
 *
 * @return float Modified priority value.
 */
function custom_schemamap_priority( $priority ) {
	return 1.0; // Maximum priority.
}

筛选策略

筛选策略决定哪些模式片段被包含或排除在聚合之外。默认情况下,聚合器会过滤掉 Actions、Enumerations、Meta 类型和 Website 元素。

wpseo_schema_aggregator_filtering_strategy

Implement a custom filtering strategy by providing your own strategy class.

Parameters:

Parameter Type Description
$strategy Filtering_Strategy_Interface The filtering strategy instance

Default Value: Default_Filter instance (filters Actions, Enumerations, Meta, and Website categories)

Example:

add_filter( 'wpseo_schema_aggregator_filtering_strategy', 'custom_filtering_strategy' );

/**
 * Implement custom schema filtering logic.
 *
 * Use this to create advanced filtering rules beyond the default
 * context-based filtering. Your strategy class should implement
 * the filtering logic in its filter() method.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_filtering_strategy
 *
 * @param object|null $strategy Custom filtering strategy instance.
 *
 * @return object Custom filtering strategy.
 */
function custom_filtering_strategy( $strategy ) {
	// Return custom strategy that only includes Article types.
	return new class {
		/**
		 * Filter schema pieces to only include Articles.
		 *
		 * @param array $schema_piece Schema piece to filter.
		 *
		 * @return bool Whether to include this schema piece.
		 */
		public function filter( $schema_piece ) {
			// Only include Article types and their subtypes.
			$article_types = [ 'Article', 'BlogPosting', 'NewsArticle', 'ScholarlyArticle' ];

			if ( isset( $schema_piece['@type'] ) ) {
				$type = $schema_piece['@type'];

				// Handle array of types.
				if ( is_array( $type ) ) {
					return ! empty( array_intersect( $type, $article_types ) );
				}

				return in_array( $type, $article_types, true );
			}

			return false;
		}
	};
}

Elements Context Map

The context map categorizes schema.org types into contexts like Content, Commerce, Entity, etc. This is used by the default filtering strategy.

wpseo_schema_aggregator_elements_context_map

Override the complete elements context map.

Parameters:

Parameter Type Description
$context_map array Complete mapping of schema types to contexts

Default Value: Comprehensive map of 1000+ schema.org types

Example:

add_filter( 'wpseo_schema_aggregator_elements_context_map', 'custom_context_map' );

/**
 * Provide a completely custom context map.
 *
 * This replaces the entire default mapping. Use this when you need
 * complete control over which types belong to which contexts.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_elements_context_map
 *
 * @param array $context_map Complete context map.
 *
 * @return array Custom context map.
 */
function custom_context_map( $context_map ) {
	// Define a simplified context map.
	return [
		'content' => [
			'Article',
			'BlogPosting',
			'NewsArticle',
		],
		'commerce' => [
			'Product',
			'Offer',
		],
		'entity' => [
			'Person',
			'Organization',
		],
	];
}

wpseo_schema_aggregator_elements_context_map_{context}

Modify elements for a specific context. Available contexts: content, commerce, entity, event, data, medical, action, enumeration, meta, website.

Parameters:

Parameter Type Description
$elements array Array of schema types in this context

Example: Add Custom Types to Content Context

add_filter( 'wpseo_schema_aggregator_elements_context_map_content', 'add_custom_content_types' );

/**
 * Add custom schema types to the content context.
 *
 * This is useful when you have custom schema types that should
 * be treated as content entities.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_elements_context_map_context
 *
 * @param array $elements Schema types in the content context.
 *
 * @return array Modified array of schema types.
 */
function add_custom_content_types( $elements ) {
	// Add custom types to content context.
	$elements[] = 'Recipe';
	$elements[] = 'HowTo';
	$elements[] = 'FAQPage';

	return $elements;
}

Example: Remove Types from Action Context

add_filter( 'wpseo_schema_aggregator_elements_context_map_action', 'remove_action_types' );

/**
 * Remove specific action types from the action context.
 *
 * This can help further refine which actions are filtered out.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#wpseo_schema_aggregator_elements_context_map_context
 *
 * @param array $elements Schema types in the action context.
 *
 * @return array Modified array of schema types.
 */
function remove_action_types( $elements ) {
	// Remove SearchAction from action context.
	// This means it won't be filtered out anymore.
	return array_diff( $elements, [ 'SearchAction' ] );
}

Article Enhancement

Articles and their subtypes (BlogPosting, NewsArticle, etc.) can be enhanced with additional properties like articleBody, description, and keywords.

Article Enhancement Configuration

Use the wpseo_article_enhance_config_{key} filter pattern to configure enhancement behavior. Available keys:

Example: Adjust Article Body Length

add_filter( 'wpseo_article_enhance_config_article_body_max_length', 'custom_article_body_length' );

/**
 * Increase maximum article body length.
 *
 * Useful for long-form content where you want to include
 * more of the article text in the schema.
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#article-enhancement-configuration
 *
 * @param int $max_length Maximum length in characters.
 *
 * @return int Modified maximum length.
 */
function custom_article_body_length( $max_length ) {
	return 2000; // Increase from 500 to 2000 characters.
}

Example: Include Categories as Keywords

add_filter( 'wpseo_article_enhance_config_categories_as_keywords', '__return_true' );

Example: Only Use Manual Excerpts

add_filter( 'wpseo_article_enhance_config_excerpt_prefer_manual', '__return_true' );

文章增强功能开关

使用 wpseo_article_enhance_{enhancement} 过滤器模式来动态启用或禁用增强功能。可用的增强功能包括:article_body、use_excerpt、keywords(默认值均为 true)。

示例:禁用文章正文增强

add_filter( 'wpseo_article_enhance_article_body', '__return_false' );

示例:禁用摘要增强

add_filter( 'wpseo_article_enhance_use_excerpt', '__return_false' );

文章正文与摘要共存设置

控制当文章已存在摘要时,是否仍包含正文内容。

过滤器: wpseo_article_enhance_body_when_excerpt_exists

参数说明:

参数名 类型 描述
$include_body 布尔值 当摘要存在时是否包含正文

默认值: false

使用示例:

add_filter( 'wpseo_article_enhance_body_when_excerpt_exists', '__return_true');

文章正文回退

控制当摘要不存在时是否包含文章正文。

过滤器: wpseo_article_enhance_article_body_fallback

参数:

参数 类型 描述
$use_fallback bool 是否使用正文作为回退

默认值: true

示例:

add_filter( 'wpseo_article_enhance_article_body_fallback', '__return_false' );

人员信息增强

人员架构片段可根据 WordPress 用户数据进行属性增强。

人员增强配置

使用 wpseo_person_enhance_config_{key} 过滤器模式来配置人员增强设置。这与文章增强配置遵循相同的通用模式——过滤器接收默认值并返回自定义值。

示例:

add_filter( 'wpseo_person_enhance_config_my_custom_key', 'custom_person_config' );

/**
 * 自定义人员增强配置值。
 *
 * @link https://developer.yoast.com/features/schema/schema-aggregator/filters/#person-enhancement-configuration
 *
 * @param string|int|bool $value 默认配置值。
 *
 * @return string|int|bool 修改后的配置值。
 */
function custom_person_config( $value ) {
	return 'custom_value';
}

人员信息增强开关

使用 wpseo_person_enhance_{enhancement} 过滤器模式来启用或禁用特定的人员信息增强功能。可用的增强项:person_job_title(默认值:true)。

示例:禁用职位头衔增强功能

add_filter( 'wpseo_person_enhance_person_job_title', '__return_false' );

Robots.txt 集成

默认情况下,schemamap 会自动在您网站的 robots.txt 文件中被引用。如果需要,您可以禁用此集成。

wpseo_disable_robots_schemamap

禁用 robots.txt 中的 schemamap 引用。

参数:

参数 类型 描述
$disable bool 是否禁用 robots.txt 集成

默认值: false

示例:

add_filter( 'wpseo_disable_robots_schemamap', '__return_true' );

开发环境配置

禁用缓存并调整设置以便于开发:

/**
 * 开发环境下的 Schema 聚合器配置。
 */

// 在开发环境中禁用缓存。
add_filter( 'wpseo_schema_aggregator_cache_enabled', function( $enabled ) {
	return defined( 'WP_DEBUG' ) && WP_DEBUG ? false : $enabled;
} );

// 使用较小的分页以便更快地测试。
add_filter( 'wpseo_schema_aggregator_per_page', function() {
	return defined( 'WP_DEBUG' ) && WP_DEBUG ? 10 : 1000;
} );

// 在开发环境中不向 robots.txt 添加 schemamap。
add_filter( 'wpseo_disable_robots_schemamap', function() {
	return defined( 'WP_DEBUG' ) && WP_DEBUG;
} );

Overview

Schema Aggregator 会收集您 WordPress 站点上所有内容的结构化数据(schema.org 标记),并通过统一的 REST API 提供这些数据。 这使得搜索引擎和其他使用者无需爬取单个页面,即可轻松访问全面的结构化数据。

哪些内容会被聚合

默认情况下,模式聚合器会处理所有公开文章类型(文章、页面、自定义文章类型)。

您可以使用 wpseo_schema_aggregator_post_types 过滤器来自定义要包含的文章类型。

模式类型

聚合器处理 schema.org 类型,分为 10 个上下文类别:

具体被过滤的模式类型在源代码中指定。

主要特性

REST API 端点

聚合后的架构可通过以下 REST 端点访问:

GET /wp-json/yoast/v1/schema-aggregator/get-schema/{post_type}[/{page}]
GET /wp-json/yoast/v1/schema-aggregator/get-xml

CLI commands

Automate schema aggregation with WP-CLI:

wp yoast aggregate_site_schema <post_type> [--page=<page>]
wp yoast clear_schema_aggregator_cache [<post_type>]

Caching

The Schema Aggregator implements a dynamic caching strategy:

Schemamap(结构化数据的 XML 映射)

schemamap 提供了所有可用 schema 端点的 XML 索引:

<?xml version="1.0" encoding="UTF-8"?>
<schemalist>
    <schema>
        <loc>https://example.com/wp-json/yoast/v1/schema-aggregator/get-schema/post</loc>
    </schema>
    <schema>
        <loc>https://example.com/wp-json/yoast/v1/schema-aggregator/get-schema/page</loc>
    </schema>
</schemalist>

schemamap 会自动在您网站的 robots.txt 中被引用:

Schemamap: https://example.com/wp-json/yoast/v1/schema-aggregator/get-xml

外部数据源支持

Schema Aggregator 可与以下系统无缝集成:

开始使用

启用功能

在 Yoast SEO 中,模式聚合器默认是禁用的。如果你需要启用它,可以通过编程方式实现:

WPSEO_Options::set( 'enable_schema_aggregation_endpoint', true );

或者,你也可以通过 WordPress 管理后台的 Yoast SEO 设置来启用它。

JSON-L 格式的聚合模式示例

获取 post 文章类型的模式:

curl https://example.com/wp-json/yoast/v1/schema-aggregator/get-schema/post

响应:

{
    "@context": "https://schema.org",
    "@type": "Article",
    "@id": "https://example.com/hello-world/#article",
    "headline": "Hello World",
    "datePublished": "2026-01-15T10:30:00+00:00",
    "author": {
        "@id": "https://example.com/#/schema/person/1"
    }
}
{
  "@context": "https://schema.org",

  "@type": "Article",
  "@id": "https://example.com/your-second-post/#article",
  "headline": "The second post",
  "datePublished": "2026-01-15T10:30:00+00:00",
  "author": {
    "@id": "https://example.com/#/schema/person/1"
  }
}

清除缓存

清除所有缓存的架构:

wp yoast clear_schema_aggregator_cache

清除特定文章类型的缓存:

wp yoast clear_schema_aggregator_cache post

了解更多

Api

要以编程方式更改 Yoast SEO 为 URL 生成的规范链接,你可以使用 wpseo_canonical 过滤器。

更改规范链接

例如,以下代码将更改 ID 为 12345 的页面的规范链接:

/**
 * 过滤规范链接。
 *
 * @param string $canonical 当前页面生成的规范链接。
 *
 * @return string 过滤后的规范链接。
 */
function prefix_filter_canonical_example( $canonical ) {
  if ( is_page( 12345 ) ) {
    $canonical = 'https://example.com/canonical-url/';
  }

  return $canonical;
}

add_filter( 'wpseo_canonical', 'prefix_filter_canonical_example' );

移除规范链接

如果你想完全移除某个页面的规范链接,只需为该页面返回 false,如下所示:

/**
 * 过滤规范链接。
 *
 * @param string $canonical 当前页面生成的规范链接。
 *
 * @return bool|string 过滤后的规范链接,或 false 表示要移除它。
 */
function prefix_filter_canonical_example( $canonical ) {
  if ( is_page( 12345 ) ) {
    return false;
  }

  return $canonical;
}

add_filter( 'wpseo_canonical', 'prefix_filter_canonical_example' );

或者要从所有页面移除,只需这样做:

add_filter( 'wpseo_canonical', '__return_false' );

Functional Specification

任何有效的、可索引的页面(即返回 200 HTTP 状态码且不包含 noindex 指令的请求)都应在文档的 <head> 部分包含一个规范 URL 标签。

有效的规范 URL 标签采用以下格式:<link rel="canonical" href="{{URL}}" />

以下部分将说明如何构建标签中 {{URL}} 组件的值。

按请求类型划分的逻辑

下表描述了我们会考虑的所有请求类型以及每种情况下规范 URL 值的结构。请注意,这并未涵盖所有可能的情况或边缘情况,因为我们只期望本规范通常在内容管理系统或网站的约束范围内应用。

另请注意,全文使用的“页面”一词指代响应请求 URL 而提供的任何有效、可索引的结果。这包括一些通常被视为“非页面”的结果,例如 RSS 订阅源。

请求类型 备注 规范 URL 结构 示例
首页 假设首页位于网站根目录。否则,请参考单页。 %%protocol%%//%%hostname%%/ https://www.example.com/
单页 例如“关于我们”页面或博客文章。 %%protocol%%//%%hostname%%/%path% https://www.example.com/about-us
分类索引页 任何基于分类法或类似方式列出选定文章或页面的模板。例如,分类页面、标签页面,或列出特定作者所有文章的页面。 %%protocol%%//%%hostname%%/%path% https://www.example.com/widgets, https://www.example.com/author/john-smith, https://www.example.com/color/blue, https://www.example.com/category/food, https://www.example.com/tag/size/small
日期索引页 任何按日期筛选列出选定文章或页面的模板。例如,在特定年份发布的所有文章,或在特定日期/某天添加的所有页面。 %%protocol%%//%%hostname%%/%date% https://www.example.com/2006, https://www.example.com/2012-08-22, https://www.example.com/2019/01/30, https://www.example.com/date/01-20-2018, https://www.example.com/time/1562149536
“数据”页面 由系统生成的 RSS/Atom/XML 视图。例如,文章或分类的评论订阅源,或 XML 站点地图。 %%protocol%%//%%hostname%%/%path% https://www.example.com/category/post/feed, https://www.example.com/sitemap.xml, https://www.example.com/blog/feed/atom

构建 %%protocol%% 变量

如果应用程序知晓网站的首选协议(例如,开发者或用户已为站点地址定义了包含 http 或 https 的选项),则应获取协议值,并在变量后附加 : 字符(以生成如 https: 的结果)。

如果应用程序无法确定正在使用的协议,则应在固定链接中省略此参数(从而生成协议无关的 URL)。

构建 %%hostname%% 变量

如果应用程序知晓其运行网站的首选地址(主机名)(例如,开发者或用户已定义网站地址选项,如 www.example.com),则应返回该值。 若没有首选域名的证据,则应返回请求主机名。

构建 %%path%% 变量

决定 %%path%% 变量包含哪些组件的逻辑可能因内容类型、网站、平台和用户偏好/配置而异。

此外,资源通常可能通过多种不同的路径查询和结构被请求,且几乎没有证据表明哪种方式最“正确”。即使使用看似可靠的平台方法,例如 WordPress 的 get_permalink() 函数(以及分类索引等的类似方法),返回的 URL 是否应作为规范 URL 值使用,仍可能存在一些模糊性。

鉴于这一挑战,以下章节将探讨基于多种场景返回最优 %%path%% 组件所需的具体逻辑。

包含上级目录

某些页面的 URL 可能包含父级目录。如果这些目录的包含对于返回目标页面是必需的,那么 %%path%% 变量也应包含这些目录。例如:

注意:Yoast SEO 插件包含移除 WordPress 路径中 category/ “基础”组件的功能。如果启用此功能,category/ 组件也应从 %%path%% 中移除。

祖先类型偏好设定

在以下各种情况中,均假设用户已明确指定应使用哪些祖先元素来构建 URL。例如,在 WordPress 中,用户可以定义“固定链接结构”,并决定所有文章链接前都应包含其标签(而非分类),或统一添加“widgets”字样。

若已设定此类偏好,则规范的 %%path%% 应始终遵循此设定。

若某个页面可通过多种路径访问(例如 category/widgets/example-post 与 tag/cats/example-post 均指向同一页面)且未设定祖先类型偏好,则 %%path%% 应在条件允许时按以下优先级顺序选择规范祖先(若无精确匹配则采用适配/可类比概念):

处理多个祖先页面

在上述每种场景中,请求的页面都可能存在多个有效祖先页面(例如,一篇博文属于多个分类,或某个分类本身又属于多个上级分类)。

如果用户已声明偏好某个特定祖先页面(例如“主要分类”),则应在 %%path%% 中使用该祖先页面。

当没有明确偏好时,默认行为是按字母顺序使用首个有效选项。例如:

构建 %%date%% 变量

日期结构用于日期索引(例如“2016年发布的所有文章”),以及当用户希望所有文章/页面都带有日期根目录时。

在这两种情况下,我们假设用户已定义首选日期结构/格式,该格式可以是任何形式(例如 Y/m/d、U、d-M-y、date/d-m-Y 等)。如果是这样,则 %%path%% 应以前述格式作为前缀(例如 date/22-05-2018/post-name)。

若未发现首选格式(且页面可能通过多种格式访问,例如原始时间戳和“美观”结构),%%path%% 应在有效时按以下优先级顺序选择规范日期格式(若无精确匹配则采用/使用可比概念):

其他注意事项

当未使用“美观固定链接”时

某些系统配置(或偏好设置)会导致有效的 URL 格式和请求仅限于“原始”对象查询。例如:

在此情况下,规范的 %%path%% 组件应匹配上述原始查询 ID(但应省略任何不改变查询结果的参数)。

请注意,如果“美观固定链接”可用,这些“原始”查询应执行 301 重定向到其对应的规范链接。

尾部斜杠

本文档中的所有示例均假设请求不包含或不强制要求尾部斜杠。实际上,这因系统和用户偏好而异。在大多数情况下,有效页面(除“数据”页面外)确实包含尾部斜杠(并通过 301 重定向强制要求)。

因此,所有 %%path%% 变量应根据以下场景有条件地包含或排除尾部斜杠:

此外,位于域根目录的主页应始终包含尾部斜杠。

分页

某些请求可能接受额外参数以返回分页状态。例如,category/page/3(或 ?category=cats&page=3)。 当返回有效的分页响应时,分页组件应附加到 %%path%% 变量。在添加分页组件不会产生分页结果的情况下(例如,请求超出范围),则不应附加。

路径净化

路径值在输出前应始终经过以下处理:

复合类型

某些请求类型可能需要组合多个 %%path%% 组件才能返回有效结果。例如,按日期筛选的分类(category/cats/2012 或 category/cats/2012/06/02),或基于日期的作者索引的分页状态(writers/george/2006/page/2)。这些组件应添加到 %%path%% 中。

如果这些组件可以按多种顺序应用(并返回相同且有效的响应),则 %%path%% 应按以下顺序构建(当组件存在时):%%taxonomy%%/%%date%%/%%pagination%%。

无索引页面

具有 noindex 指令的元机器人标签(或 x-robots-tag HTTP 标头)的页面应完全省略规范 URL 标签。这包括模板逻辑指定 noindex 属性的情况(如搜索结果页),以及用户将特定页面设置为 noindex 的情况。

错误场景

导致错误的请求(所有产生 4xx 或 5xx 范围 HTTP 响应的场景)应完全省略规范 URL 标签。

用户指定的规范 URL

当用户为页面手动指定了规范 URL 时,此设置应覆盖所有其他逻辑。我们应原样输出该值,绕过上述定义的清理和处理规则。

此规则同样适用于用户指定的跨域规范 URL。

任何冲突、错误或中断的风险都应在发布工作流程中通过内联警告或验证错误来解决。

Api

To change the meta description tag that Yoast SEO generated programmatically, you can use the wpseo_metadesc filter.

Change the meta description tag

For example, the following code would change the meta description on a page with ID 12345:

/**
 * Filters the description.
 *
 * @param string $description The current page's generated meta description.
 *
 * @return string The filtered meta description.
 */
function prefix_filter_description_example( $description ) {
  if ( is_page( 12345 ) ) {
    $description = 'My custom custom meta description';
  }
  return $description;
}
add_filter( 'wpseo_metadesc', 'prefix_filter_description_example' );

Functional Specification

Every public page on a website should contain a unique, useful editorial summary of the content/purpose of that page, output in a meta description tag.

A valid meta description tag takes the following format: <meta name="description" content="{{value}}" />.

Constructing {{value}}

User-specified patterns

Whenever the user has defined a pattern/template for meta description values (e.g., using Yoast SEO's replacement variables), that pattern should be used.

User-specified values

Whenever the user has defined a specific value for a page, that value should be used. User-specified values override all patterns and defaults.

Omitted by default

In all scenarios, Yoast prefers to output no meta description by default, rather than an auto-generated value. It's our opinion that there are very few scenarios where auto-generated meta descriptions are a good description of the page (e.g., they often simply extract the first paragraph, which is rarely a suitable candidate). Additionally, most search engines which rely on the meta descriptions are also sophisticated enough to evaluate the relationship between the search query and page content, and to extract or generate their own meta description.

Providing an admin comment

When a page lacks a meta description value, the following comment should be output in the HTML code in place of the tag:

<!-- Admin only notice: this page does not show a meta description because it does not have one, either write it for this page specifically or go into the [SEO - Search Appearance] menu and set up a template. -->

Api

请参阅 https://developer.yoast.com/customization/apis/metadata-api

Functional Specification

Yoast SEO 始终在每个页面的 <head> 中输出一个 meta robots 标签。当用户希望通过搜索引擎主动限制页面的抓取、索引或展示时(通过我们的控制功能),我们会更改该标签的内容。

该标签采用以下格式:<meta name="robots" content="{{values}}" />。

构建 {{values}}

鉴于一个 meta robots 标签可能包含多个(可能相互冲突的)值,并且可能适用多个重叠条件,那么:

标准结构

默认情况下,Yoast SEO 在每个(公开)页面上输出以下结构的 meta robots 标签:

<meta name="robots" content="{{values}}, max-snippet:-1, max-image-preview:large, max-video-preview:-1" />

这为所有机器人提供了通用指令,并让用户选择加入 Google 和 Bing 对摘要/媒体限制的特定处理方式。

除非用户(或通过页面/模板/过滤逻辑)另有定义,否则 {{values}} 输出 index, follow。

场景

在非公开页面上

任何“非公开”页面——即用户确定不应出现在搜索结果中的页面、文章或归档类型(例如,通过我们的“搜索外观”设置)——应返回 noindex 和 follow 属性。例如: <meta name="robots" content="noindex, follow" />

每篇文章/页面的自定义控制

用户为特定文章或页面指定的任何高级/自定义属性,应按定义在 {{values}} 属性中返回。

如果设置了 noindex 值,则该页面应被视为非公开页面(见上文)。

错误模板

在 4xx 和 5xx 模板上不应输出 robots 标签。

解决冲突

在指令冲突的情况下,应始终遵循限制性最强的设置,使用以下规则:

例如,index、noindex、noimageindex 和 follow 值的组合应导致输出 noindex, follow(因为 noindex 比 index 限制性更强,并且 noimageindex 与 noindex 结合时是冗余的;有关值关系的完整概述,请参阅文档)。

Overview

This documentation provides technical information about how Yoast SEO generates and/or manages meta robots tags.

Resources

Further reading

Api

To change the title tag that Yoast SEO generated programmatically, you can use the wpseo_title filter.

Change the title tag

For example, the following code would change the title on a page with ID 12345:

/**
 * Filters the title.
 *
 * @param string $title The current page's generated title.
 *
 * @return string The filtered title.
 */
function prefix_filter_title_example( $title ) {
  if ( is_page( 12345 ) ) {
    $title = 'My custom title';
  }
  return $title;
}
add_filter( 'wpseo_title', 'prefix_filter_title_example' );

Functional Specification

网站上的每个页面都应在文档的 <head> 部分包含一个独特且描述性的标题标签。

有效的标题标签采用以下格式:<title>{{VALUE}}</title>。

以下部分将说明如何构建标签中 {{VALUE}} 组件的值。

各请求类型的默认逻辑

下表描述了我们认为的所有请求类型以及每种情况下标题标签的结构。请注意,这并未涵盖所有可想象的场景或边缘情况,因为我们只期望此规范通常在内容管理系统或网站的约束范围内应用。

另请注意,全文使用的“页面”一词指为请求的 URL 提供的任何“非系统”响应。这包括归档、分类法和错误状态。它不包括“非页面”结果(通常不支持 <title> 标签),如 RSS 订阅和 XML 站点地图。

请求类型 备注 标题结构 示例
首页 假设首页位于网站根目录。否则,请参见单页。 %%sitename%% %%page%% %%sep%% %%tagline%% Acme Inc - The best widgets in the world
单页 在 WordPress 中,也考虑单篇文章。例如,“关于我们”页面或博客文章。 %%postname%% %%page%% %%sep%% %sitename% Top 10 widgets - Acme Inc
分类索引页 任何基于分类法或类似方式列出选定文章或页面的模板。例如,分类页面或标签页面,或列出特定作者所有文章的页面。 %%term_title%% archive %%page%% %%sep%% %sitename% Blue widgets archive - Acme Inc
日期索引页 任何按日期筛选列出选定文章或页面的模板。例如,在特定年份发布的所有文章,或在特定日期/某天添加的所有页面。 %%date%% %%page%% %%sep%% %sitename% 2018 - Acme Inc
搜索页面 任何按用户查询筛选列出选定文章或页面的模板。例如,与“widget”一词相关的所有文章。 You searched for %%search_phrase%% %%page%% %%sep%% %sitename% You searched for red widgets - Acme Inc
404 页面 当用户请求返回 404 错误的 URL 时提供的任何模板。 Page not found %%sep%% %sitename% Page not found - Acme Inc

这些变量大多应具有相当的自描述性,不需要大量文档。然而,为避免歧义:

清理处理

任何用户输入值(即 URL 的任何组成部分,特别是任意字符串和细化条件,如搜索词)都应始终进行适当的清理处理。

用户指定模式

每当用户为标题定义了替代模式/模板时(例如,使用 Yoast SEO 的替换变量),应使用该模式替代默认设置。

用户指定值

当用户为页面定义了特定值时,应使用该值替代默认(或特定类型)模式。

Functional Specification

This documentation provides technical information about which X tags Yoast SEO generates and outputs. These tags are only consumed by X.

We output a variety of <meta> tags for X in the <head> of each page, in order to better inform X about the content of the page in question.

Note that for a lot of tags, X falls back on OpenGraph metadata. Where it falls back, we provide the OpenGraph metadata and don't provide the X tag specifically unless it has been customized.

X 元数据

以下标签为社交网络和平台(如 Facebook、X、Pinterest)提供关于页面及其内容的额外信息。

假设相应的管理员设置未被禁用,这些标签将在所有页面上输出(单独描述的 error 模板除外)。

标签 描述
twitter:card 预览页面时使用的卡片类型(默认为 summary_large_image)。
twitter:site 网站所有者/运营者的 X 账号。

Conditional tags

The following tags are only output when their conditions are met:

Tag Description Notes
twitter:creator The X handle of the content author. Only populated when the content author has a valid X handle.
Only populated on post types.
twitter:title The title of the page, specifically for sharing on X. Only populated when a specific value (which is different from the og:title value) is defined for X.
twitter:description The description of the page, specifically for sharing on X. Only populated when a specific value (different from the og:description value) is defined for X.

The hierarchy for this is as follows:
  • A user-defined "X description" value for the page.
  • An auto-generated "Social description" from the template in Search Appearance.
  • No output, i.e. falls back on og:description.
twitter:image The URL of the primary image of the page, specifically for sharing on X. Only populated when a specific image (different from the og:image) is defined for X, or, when Facebook / og:image tags are disabled.

  • A user-defined “X image” in the Social tab of the Yoast metabox in a twitter:image tag
  • A user-defined “Facebook image” in the Social tab of the Yoast metabox in an og:image tag
  • A user-defined “Featured image” for the page (in an og:image tag, or in a twitter:image tag if Open Graph is disabled)
  • A prominent image from the page’s content. (in an og:image tag, or in a twitter:image tag if Open Graph is disabled) — when that image is resized in the content, we show its full-sized counterpart (this isn't true when indexables are disabled)
  • Yoast SEO WooCommerce only: Woo SEO adds one more image fallback: the first image in the Woo Product gallery. (in an og:image tag, or in a twitter:image tag if Open Graph is disabled)
  • The “Social default image” from the template in Search Appearance in an og:image tag
  • The site-wide Social default image from SEO > Social in an og:image tag
  • No output.
In case of an author archive, the image is based on the following hierarchy:
  • The “Social default image” from the template in Search Appearance in an og:image tag
  • The user’s Gravatar image (in an og:image tag, or in a twitter:image tag if Open Graph is disabled)
  • No output, i.e. falls back to og:image.

"Enhanced Slack sharing" tags

We utilize X's "label/data" meta tag format to add additional data, which is shown when sharing URLs on Slack. Note that this should only be output if the "Enhanced Slack sharing" setting is enabled for the site.

Tag Description Notes
<meta name="twitter:label1" content="Est. reading time" /> Only output on post and page types.
<meta name="twitter:data1" content="%x% minutes" /> Where %x% is the estimated reading time, rounded down to the closest minute.

Reindex Indexables

随着 Yoast SEO 14.0 的发布,我们引入了新的数据库表,用于整合站点中所有可索引对象的元数据。 然而,这些数据库表需要先填充数据才能正常使用。为了在大型站点上高效完成此操作,我们引入了 WP CLI 命令。 本页将介绍该命令及其所有选项。

:::tip 如果您不了解 WP CLI,那您就错过了一个好工具!它是 WordPress 的命令行界面,能大大简化许多任务。在 wp-cli.org 上了解更多信息。 :::

如果您计划使用我们的 REST API 或 界面 API,提前在您的网站上运行 CLI 命令非常重要。

语法

运行索引化过程的基本命令如下:

wp yoast index
索引文章  100% [==============================] 0:00 / 0:00
索引分类项  100% [==============================] 0:00 / 0:00
索引文章类型归档页  100% [=================] 0:00 / 0:00
索引通用对象  100% [====================] 0:00 / 0:00

注意事项

选项

运行索引命令时还有一些可选参数:

故障排除

Index 不是 'yoast' 的已注册子命令

这意味着您使用的 Yoast SEO 版本不正确。请注意,Yoast SEO WP CLI 索引命令需要 Yoast SEO 14.0 或更高版本才能正常工作。

Api

虽然 Yoast SEO 为 XML 站点地图提供了合理的默认行为(以及包含/排除的 UI 控件),但自定义主题或插件有时需要修改我们的标记或逻辑。
在这些情况下,您可以使用以下示例来修改站点地图的生成和输出方式。

排除内容类型

排除特定文章

/**
 * 从 XML 站点地图中排除文章。
 *
 * @return array 要排除的文章 ID。
 */
function exclude_posts_from_xml_sitemaps() {
    return [ 1, 2, 3 ];
}

add_filter( 'wpseo_exclude_from_sitemap_by_post_ids', 'exclude_posts_from_xml_sitemaps' );

Exclude a post type

/**
 * Exclude a post type from XML sitemaps.
 *
 * @param boolean $excluded  Whether the post type is excluded by default.
 * @param string  $post_type The post type to exclude.
 *
 * @return bool Whether a given post type should be excluded.
 */
function sitemap_exclude_post_type( $excluded, $post_type ) {
    return $post_type === 'recipes';
}

add_filter( 'wpseo_sitemap_exclude_post_type', 'sitemap_exclude_post_type', 10, 2 );

Exclude a taxonomy

/**
 * Exclude a taxonomy from XML sitemaps.
 *
 * @param boolean $excluded Whether the taxonomy is excluded by default.
 * @param string  $taxonomy The taxonomy to exclude.
 *
 * @return bool Whether a given taxonomy should be excluded.
 */
function sitemap_exclude_taxonomy( $excluded, $taxonomy ) {
    return $taxonomy === 'ingredients';
}

add_filter( 'wpseo_sitemap_exclude_taxonomy', 'sitemap_exclude_taxonomy', 10, 2 );

Exclude an author

/**
 * Excludes author with ID of 5 from author sitemaps.
 *
 * @param array $users Array of User objects to filter through.
 *
 * @return array The remaining authors.
 */
function sitemap_exclude_authors( $users ) {
   return array_filter( $users, function( $user ) {
        if ( $user->ID === 5 ) {
            return false;
        }

        return true;
    } );
}

add_filter( 'wpseo_sitemap_exclude_author', 'sitemap_exclude_authors' );

Exclude a taxonomy term

/**
 * Excludes terms with ID of 3 and 11 from terms sitemaps.
 *
 * @param array $terms Array of term IDs already excluded.
 *
 * @return array The terms to exclude.
 */
function sitemap_exclude_terms( $terms ) {
    return [ 3, 11 ];
}

add_filter( 'wpseo_exclude_from_sitemap_by_term_ids', 'sitemap_exclude_terms' );

添加内容

添加自定义文章类型

/**
 * 为自定义文章类型添加 XML 站点地图。
 * 
 * @return void
 */
function enable_custom_sitemap() {
    global $wpseo_sitemaps;
    if ( isset( $wpseo_sitemaps ) && ! empty ( $wpseo_sitemaps ) ) {
        $wpseo_sitemaps->register_sitemap( 'recipe', 'create_recipe_sitemap' );
    }
}

add_action( 'init', 'enable_custom_sitemap' );

向 XML 站点地图索引添加额外/外部 XML 站点地图

/**
 * 将额外的/自定义的 XML 站点地图字符串写入 XML 站点地图索引。
 *
 * @param string $sitemap_custom_items 描述一个或多个自定义站点地图的 XML。
 *
 * @return string 包含额外 XML 的站点地图索引。
 */
function add_sitemap_custom_items( $sitemap_custom_items ) {
    $sitemap_custom_items .= '
<sitemap>
<loc>https://www.example.com/external-sitemap-1.xml</loc>
<lastmod>2017-05-22T23:12:27+00:00</lastmod>
</sitemap>';
    return $sitemap_custom_items;
}

add_filter( 'wpseo_sitemap_index', 'add_sitemap_custom_items' );

为文章添加图片

某些主题或页面构建器模块可能不会在站点地图中显示图片。您可能需要通过过滤器 wpseo_sitemap_urlimages 来添加它们。此过滤器将注册图片以使其出现在站点地图上。

/**
 * 一个为文章添加图片的示例。
 *
 * @param array $images 与文章相关的图片 URL 数组。
 * @param int   $post_id  文章 ID。
 * 
 * @return array 要添加的图片 URL 数组。
 */
function filter_wpseo_sitemap_urlimages( $images, $post_id ) {
  array_push( $images, [ 'src' => 'https://www.example.com/wp-content/uploads/extra-image.jpg' ]);
  return $images;
};
add_filter( 'wpseo_sitemap_urlimages', 'filter_wpseo_sitemap_urlimages' );

为分类项添加图片

某些主题、页面构建器模块或插件可能不会在站点地图中显示图片。您可能需要通过过滤器 wpseo_sitemap_urlimages_term 来添加它们。此过滤器将注册图片以显示在站点地图上。

/**
 * 为分类项添加图片的示例。
 *
 * @param array $images 与分类项相关的图片 URL 数组。
 * @param int   $term_id  分类项 ID。
 * 
 * @return array 要添加的图片 URL 数组。
 */
function filter_wpseo_sitemap_urlimages_term( $images, $term_id ) {
  array_push( $images, [ 'src' => 'https://www.example.com/wp-content/uploads/extra-image.jpg' ]);
  return $images;
};
add_filter( 'wpseo_sitemap_urlimages_term', 'filter_wpseo_sitemap_urlimages_term' );

为首页添加图片

当首页不是静态页面,而是最新文章列表时,你可以通过过滤器 wpseo_sitemap_urlimages_front_page 向站点地图添加图片。该过滤器将注册图片,使其出现在站点地图中。

/**
 * 一个向首页添加图片的示例。
 *
 * @param array $images 与首页相关的图片 URL 数组。
 * @return array 要添加的图片 URL 数组。
 */
function filter_wpseo_sitemap_urlimages_front_page( $images ) {
  array_push( $images, [ 'src' => 'https://www.example.com/wp-content/uploads/extra-image.jpg' ]);
  return $images;
};
add_filter( 'wpseo_sitemap_urlimages_front_page', 'filter_wpseo_sitemap_urlimages_front_page' );

Miscellaneous

Alter the URL of an entry

/**
 * Alters the URL structure for an example custom post type.
 *
 * @param string  $url  The URL to modify.
 * @param WP_Post $post The post object.
 *
 * @return string The modified URL.
 */
function sitemap_post_url( $url, $post ) {
	if ( $post->post_type === 'guest_authors' ) {
		return \str_replace( 'guest-authors', 'guests', $url );
	}

	return $url;
}

add_filter( 'wpseo_xml_sitemap_post_url', 'sitemap_post_url', 10, 2 );

调整站点地图条目数量

/**
 * 修改每个 XML 站点地图中的条目数量。
 *
 * @return integer 每个站点地图的最大条目数。
 */
 function max_entries_per_sitemap() {
    return 100;
}

add_filter( 'wpseo_sitemap_entries_per_page', 'max_entries_per_sitemap' );

Add extra properties to the <video:video> container

/**
 * Adds an example <video:live> property to a <video:video> container for a particular post
 *
 * @param string $property A placeholder for a custom property.
 * @param int    $post_id  The post ID.
 * 
 * @return string The property to add.
 */
 function add_video_live_property( $property = '', $post_id ) {

    // Bail if this isn't the example post we want to modify.
    if ( $post_id !== 12345 ) {
        return $property;
    }

    // Set our custom property.
    $property = '<video:live>yes</video:live>';

    return $property;
}

add_filter( 'wpseo_video_item', 'add_video_live_property', 10, 2 );

过滤 urlset 元素

例如,如果你想在 XML 站点地图中使用 xhtml:link 元素来添加 hreflang 标记,可以这样做:

/**
 * 过滤所有站点地图的 `urlset`。
 *
 * @param string $urlset 站点地图 `urlset` 的输出。
 */
add_filter( 'wpseo_sitemap_urlset', function( $urlset ) { 
  return str_replace( '>', ' xmlns:xhtml="http://www.w3.org/1999/xhtml">', $urlset );
}, 1, 10 );

Filter the sitemap URL

/**
 * Allows filtering of the XSL URL used in the current environment.
 *
 * @param string $current_url The current XSL URL.
 */
add_filter( 'wpseo_sitemap_public_url', function ( $current_url ) {
  return 'https://another-url.com/main-sitemap.xml';
} );

Functional Specification

用户希望被搜索引擎发现和索引的任何页面都应列在 XML 站点地图中。

应为每种内容类型构建单独的 XML 站点地图。大型站点地图可以拆分为独立的、分页的文件。

每个 XML 站点地图都应列在XML 站点地图索引中。

内容类型

应为以下每种内容类型创建 XML 站点地图,前提是该内容类型为公开且包含有效成员。

除了单独的 XML 站点地图外,还应创建一个XML 站点地图索引,其中引用所有这些站点地图。

排除项与空站点地图

以下情况应导致页面被排除在(相应的)XML 站点地图之外:

其中一些情况可能导致 XML 站点地图(或 XML 站点地图索引)为空。这是预期且允许的行为。

对不存在的站点地图 URL 的请求应返回正常的 404 行为。

内容

XML 站点地图索引

索引文件为每个独立的 XML 站点地图包含一个 <sitemap> 条目,每个条目都有 loc 和 lastmod 属性。例如:

<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
  <loc>https://www.example.com/author-sitemap.xml</loc>
  <lastmod>2019-10-21T12:37:01+00:00</lastmod>
</sitemap>
{...}
</sitemapindex>

如果不可用,可选属性可以省略。

URL 与重定向

该文件应可通过 /sitemap_index.xml 访问。对 /sitemap.xml 的请求应重定向至此地址。

XML 站点地图

每个站点地图为每个页面包含一个 <url> 条目,由 loc、lastmod 和 image:image 属性组成。例如:

<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1 http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
  <loc>https://www.example.com/blog/example-post/</loc>
  <lastmod>2018-04-21T10:00:12+00:00</lastmod>
  <image:image>		 
    <image:loc>https://www.example.com/example-image.jpg</image:loc>
  </image:image>
  <image:image>		 
    <image:loc>https://www.example.com/another-example-image.jpg</image:loc>
  </image:image>
</url>
{...}
</urlset>

如果不可用,可选属性可以省略。

限制与分页

默认情况下,每个站点地图最多可包含 1,000 个项目。如果某个内容类型的项目超过 1,000 个,则会生成额外的站点地图(最多 50,000 个)来容纳溢出的项目。

网址与重定向

每个站点地图的 URL 应按以下格式构建:{{type}}-sitemap{{n}}.xml,其中:

例如:post-sitemap.xml、post-sitemap2.xml、custom_taxonomy_name-sitemap.xml。

当 {{n}} 为 1 或 0 时,对站点地图的请求应触发 301 重定向以移除 {{n}}(例如:对 post-sitemap1.xml 和 post-sitemap0.xml 的请求应重定向至 post-sitemap.xml)。

XSL 样式表

站点地图索引和单个站点地图引用 XSL 文件,该文件为页面提供布局和样式配置。

索引控制

XML 站点地图无需被搜索引擎索引即可被读取或解析。因此,所有 XML 站点地图响应都应返回一个值为 noindex, follow 的 x-robots-tag HTTP 标头。

其他场景

视频站点地图

我们的 WordPress 视频 SEO 插件 会添加一个额外的 视频站点地图(位于 video-sitemap.xml,并包含在站点地图索引中),其中包含网站内容中托管/引用的每个视频的信息。

对于每个包含视频的页面,站点地图包含一个 <url> 包装器,其中包含 <loc> 值(页面的规范 URL / 永久链接),以及页面中每个视频的 <video:video> 容器。例如:

<url>
  <loc>https://www.example.com/example-page/</loc>
  <video:video>
    <video:title><![CDATA[Example Video]]></video:title>
    <video:publication_date>2015-12-15T15:36:03+00:00</video:publication_date>
    <video:description><![CDATA[Example video description]]></video:description>
    <video:player_loc>https://www.youtube-nocookie.com/v/abc123</video:player_loc>			 
    <video:thumbnail_loc>https://www.example.com/video-thumbnail.jpg</video:thumbnail_loc>
    <video:duration>98</video:duration>
    <video:view_count>367</video:view_count>
    <video:width>640</video:width>
    <video:height>390</video:height>
    <video:tag>Example tag</video:tag>
    <video:tag>Another example tag</video:tag>
    <video:family_friendly>yes</video:family_friendly>
    <video:uploader info="https://www.example.com/example-profile-page">Example Person</video:uploader>
  </video:video>
</url>
{…}
</urlset>

视频 XML 站点地图使用专用的 XSL 文件。

新闻站点地图

我们的 WordPress 新闻 SEO 插件 会添加一个额外的 新闻站点地图(位于 news-sitemap.xml,并包含在站点地图索引中),其中包含网站上发布的新闻文章信息。

新闻站点地图应仅引用过去 48 小时内发布或修改的文章。

该站点地图包含一个 <url> 包装器,其中包含 <loc> 值(页面的规范 URL / 永久链接),以及每个新闻文章的 <news:news> 容器。例如:

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
<url>
  <loc>http://www.example.com/example-page/</loc>
  <news:news>
    <news:publication>
      <news:name>Example site name</news:name>
      <news:language>en</news:language>
    </news:publication>
    <news:publication_date>2019-11-15T09:59:38+00:00</news:publication_date>
    <news:title><![CDATA[Example post name]]></news:title>
  </news:news>
</url>
{…}
</urlset>

新闻 XML 站点地图使用专用的 XSL 文件。

:::info

一些延伸阅读:

:::

Overview

Yoast SEO for Shopify 应用会对商店的主题文件进行修改和添加。这样做是为了优化技术 SEO 的各个方面,并将我们的内容编辑工具与网站输出连接起来。 本文档概述了我们所做的更改类型,并提供了如何还原这些更改的详细信息。

主题变更

Yoast SEO 会尝试用我们自己的输出来替换主题中所有与 SEO 相关的元数据。这包括:

其实现方式是通过“注释掉”主题代码中的相关行,并渲染我们自己的输出。这一过程发生在安装时。

示例

此示例展示了商店 theme.liquid 文件的一部分在 Yoast SEO 修改前后的可能样子。

修改前

<!doctype html>
<html class="no-js" lang="{{ request.locale.iso_code }}">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="theme-color" content="">
    <link rel="canonical" href="{{ canonical_url }}">

After

<!doctype html>
<html class="no-js" lang="{{ request.locale.iso_code }}">
  <head>{% render 'yoast-seo' %}
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="theme-color" content="">
    {%- comment -%}Auto-disabled. Yoast plugin is taking care of Meta Tags output.{%- endcomment -%}
    {%- if disabled_by_yoast_seo -%}
      <link rel="canonical" href="{{ canonical_url }}">
      <!-- disabled_by_yoast_seo -->
    {%- endif -%}

方法

在上面的示例中,您可以看到我们添加了 {% render 'yoast-seo' %} 和一个 {%- if disabled_by_yoast_seo -%} 条件。

这种方法使我们能够应用更复杂的逻辑,并集成我们应用的内容设置。例如,我们不是简单地在模板中输出一个规范 URL值,而是使用以下代码片段来控制整个标签的行为:

{%- capture yoast_seo_page_type -%}{%- render 'yoast-seo-page-type' -%}{%- endcapture -%}
{%- if yoast_seo_page_type != "404" %}
	{%- capture yoast_canonical_url -%}{%- render "yoast-seo-variable", variable: "canonical_url" -%}{%- endcapture -%}
	<link rel="canonical" href="{{- yoast_canonical_url | replace: '"', '&#34;' -}}"/>
{%- endif -%}

我们创建并使用了许多这样的代码片段来控制 SEO 设置的优化和渲染方式。

其他变更

除了优化所有模板 <head> 部分的输出外,我们还对 模板、布局 和 区块 文件进行了大量细微调整。

这些变更旨在移除所有内联的 结构化数据(采用 JSON-LD 和 微数据 格式)。我们移除此代码是为了实施 我们自己的 schema.org 解决方案,同时降低冲突和重复的风险。

请注意,我们的应用提供了控制选项,允许您针对特定 类型 的 schema.org 属性切换此行为。当您希望保留自定义的 schema 输出时(例如,当主题进行了定制化修改,或其他应用添加了补充性的 schema.org 内容时),这会很有帮助。

恢复或移除主题更改

恢复主题更改最简单的方法是使用应用内的专用控件。该控件可在应用的设置菜单中找到。 更多相关信息请查阅我们的应用卸载帮助页面。

此控件将移除所有主题自定义设置,将您的主题恢复到未修改状态,同时保留安装后进行的任何自定义配置。

移除应用后的更改清理

遗憾的是,Shopify 未提供在应用移除时自动清理主题更改的机制。这意味着部分用户可能无法访问我们的清理工具,必须手动移除相关更改。

若您已卸载应用且无法重新安装,建议访问应用卸载帮助页面。该页面提供了最新的主题修改清理指南。

Internationalization

import YoastSchemaExample from '../../../src/components/YoastSchemaExample';

Shopify 允许商家在其在线商店中支持多种语言。通过 Yoast SEO for Shopify 应用添加的内容,也可以通过这些系统进行翻译。本文档提供了关于多语言应用如何与 Yoast SEO for Shopify 集成以翻译该内容的信息。

Translating texts specific to your content

Yoast SEO for Shopify allows you to enrich the meta information about your content. You can add meta information for search engines and social platforms. All information for a piece of content (e.g. a product, or a blog article) is stored in a single metafield: yoast_seo.indexable. This metafield contains at least the following JSON data in the structure:

{
  "title": "", // The content for the <title> tag.
  "description": "", // The content for the <meta name="description" /> tag.
  "open_graph_title": "", // The content for the <meta name="og:title" /> tag.
  "open_graph_description": "", // The content for the <meta name="og:description" /> tag.
  "twitter_title": "", // The content for the <meta name="twitter:title" /> tag.
  "twitter_description": "", // The content for the <meta name="twitter:description" /> tag.
}

片段变量

我们允许在此内容中使用变量,这些变量在渲染输出时会被替换为最新数据。这些变量遵循 %%variable_name%% 格式,不应翻译。请查看完整的 Shopify 中可用片段变量列表。

Translating fallback texts

When no specific texts have been given for content, Yoast SEO for Shopify uses configurable fallbacks. These can be configured for different types of content and can potentially contain translatable text too. To store these fallbacks, a single metafield is created on the Shop object directly: yoast_seo.settings. This metafield also contains JSON data, which at least has the following structure:

{
  "contentTypes": {
    "<content_type>": {
      "templates": {
        "seo": {
          "single": {
            "title": "", // The fallback for <title> tags for this type of content.
            "description": "" // The fallback for <meta name="description"> tags for this type of content.
          }
        }
      }
    }
  }
}

The fallbacks also have support for the same snippet variables.

Translating special pages

The homepage is a special page that is also stored as a content type homepage in the yoast_seo.settings metafield.

The overview pages with all products and all collections can also be optimized. When the <content_type> is products or collections, the "single" property has an adjacent "archive" property with the same structure. For products this represents the page at /collections/all: a page listing all products. For collections this represents the page /collections: a page listing all collections.

There are 2 other content types, not directly related to content from Shopify: productTags and blogPostTags. These content types are applied when filtering a collection or a blog with one or more tags. Filtered collections can be found at /collections/<collection_slug>/<tag_name>. Filtered blogs can be found at /blogs/<blog_slug>/tagged/<tag_name>.

Yoast SEO for Shopify also allows merchants to optimize the <title> tag for 404 and search pages. These are also stored in the yoast_seo.settings metafield on the shop. Their data is not in the contentTypes property. Instead, this text is available in:

{
  "advancedSettings": {
    "notFoundPages": {
      "title": "", // The content for the <title> tag for 404 pages.
      }
    },
    "searchPages": {
      "title": "", // The content for the <title> tag for search pages.
      }
    },
  }
}

All special pages have support for the same snippet variables.

Review Apps

import YoastSchemaExample from '../../../src/components/YoastSchemaExample';

为了与 Shopify 平台上的评论应用集成,我们依赖 Shopify 提供的标准元字段定义。 这些定义在整个平台内具有互操作性,允许不同的应用相互连接,并无缝集成到主题中。

Yoast SEO for Shopify 架构中的变更

基于标准元字段定义,产品页面的架构中增加了一个额外的 aggregateRating 属性。 为了生成这个 AggregateRating,我们分别使用产品元字段数据 reviews.rating_count 和 reviews.rating 来填充 reviewCount 和 ratingValue 属性。

以下是新架构属性的示例:

{`{ "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4", "reviewCount": "3077" } }`}

支持的集成

除了与标准元字段定义的集成外,目前 Yoast SEO for Shopify 还与以下服务集成:

Judge.me 在页面上添加了一个单独的架构属性,通过 @id 引用连接到 Yoast SEO for Shopify 产品架构。 这个单独的架构属性在 Yoast SEO for Shopify 生成的架构图中无法找到。

Schema

import YoastSchemaExample from '@site/src/components/YoastSchemaExample';

Yoast SEO for Shopify 会输出 schema.org 标记。这是基于 Shopify 中可用的数据以及通过应用界面填写的额外数据来完成的。其他添加结构化数据功能(例如,评论、食谱等)的应用可以将这些数据整合到我们的 schema.org 数据中。关于我们的方法、技术和规范的更多信息可以在这里找到。

修改模式图

图中的所有片段都包含一个 @id 属性。此 ID 可用于属性以 引用 现有片段,或通过附加数据 扩展 现有片段。网页上可以放置多个 <script> 标签,所有 <script> 标签中所有片段的组合应形成一个连接的模式数据图。请使用我们首选的验证工具验证所有更改。

有关特定 @id 值及其创建方式的更多信息,请参见此处。

添加片段

通常,我们的图谱会包含一个 Organization(组织),并将该 Organization 作为 WebSite(网站)的发布者进行引用。 我们还会添加当前 WebPage(网页)是 WebSite 的一部分。如果你想向此图谱中添加引用特定片段的其他数据,可以通过引用 @id 属性来实现。例如,你可以添加一个 AggregateRating(综合评分),其中被评级的项目是 Organization:

{`{ "@context": "https://schema.org", "@graph": [ { "@type": "AggregateRating", "@id": "https://www.example.com/#/schema/AggregateRating/1", "ratingValue": "4", "ratingCount": "250" "itemReviewed": { "@id": "https://www.example.com/#/schema/Organization/1" } } ] }`}

编辑片段

架构图中已有的片段也可以进行编辑。这只能通过向片段添加额外数据来实现。 例如,当前的 WebPage 可以通过添加 FAQPage 类型进行增强,并添加 Question 片段。此示例 依赖于现有 WebPage 的 @id 属性,这将导致数据合并为一个大的 WebPage / FAQPage 片段。

{`{ "@context": "https://schema.org", "@type": "FAQPage", "@id": "https://www.example.com/pages/frequently-asked-questions", "mainEntity": [{ "@type": "Question", "name": "Is there a local shop we can visit?", "acceptedAnswer": { "@type": "Answer", "text": "No. The shop is only online." } }] }`}

各模块的可用性

并非所有模块都适用于网店的每个页面。我们描述了核心逻辑,说明这些模块何时会被添加到页面中。在修改图谱时,也应考虑相同的逻辑。

检查组件是否已启用

通过 Yoast SEO for Shopify 应用,可以禁用多个组件。在这些情况下,组件将永远不会被输出。 我们将此数据存储在 shop 对象上的元字段 yoast_seo.settings 中。这是一个 JSON 类型的元字段,其中这些组件的布尔启用状态存储在 schema.outputControls 中。

Schema 组件 元字段
整个图谱 shop.metafields.yoast_seo.value.schema.outputControls.schema
Organization shop.metafields.yoast_seo.value.schema.outputControls.organization
WebSite shop.metafields.yoast_seo.value.schema.outputControls.website
WebPage shop.metafields.yoast_seo.value.schema.outputControls.webpage
Article shop.metafields.yoast_seo.value.schema.outputControls.article
Product shop.metafields.yoast_seo.value.schema.outputControls.product
BreadcrumbList shop.metafields.yoast_seo.value.schema.outputControls.breadcrumb

检查已定义的组织名称

仅当在 Yoast SEO for Shopify 应用的设置中定义了名称时,才会添加 Organization 片段。 在 shop 对象的同一 yoast_seo.settings 元字段中,也存在用于表示组织的数据。 组织名称不应为空。该名称存储在 shop.metafields.yoast_seo.value.schema.siteRepresentation.organizationName 中。

检查有效文章

虽然 WebPage 始终可以放置在页面上,但 Article 依赖于 Organization,因此也依赖于已定义的组织名称。

Article 还需要 description 属性的数据。默认情况下,我们为此重用元描述。如果没有元描述,文章将不会输出。

Article 的第三个要求是拥有要显示的图像。此图像可以是 page_image,也可以是存储在 shop.metafields.yoast_seo.value.siteSettings.siteDefaults.siteImage.url 中并存在于全局 Liquid 变量 images 中的已定义站点图像。图像也可以取自 article.image 对象,或使用存储在 article.metafields.yoast_seo.indexable.value.open_graph_image_url 中的 OpenGraph 设置。

构建 ID 参数

我们架构输出中的所有片段都可以通过 @id 属性引用。要复制 @id 值,我们应用以下逻辑。