相比模板和模板组件这类功能,模式的最大优势之一在于可以在其中使用 PHP,这为实现各种功能带来了无限可能。不过,模式所能具备的功能仍存在一定的限制。

在本文中,您将了解模式系统的局限性、何时以及如何使用 PHP,以及一些实现动态功能的常见用例。

模式在初始化时注册

在模式中使用 PHP 时,重要的是要明白模式的块标记实际上是在何时被编译,又是在何时被渲染的。

模式的注册发生在 init 钩子中。此时,WordPress 会编译模式的内容,并将其保存为基于 HTML 的块标记副本,这样它就可以在编辑器和前端使用。正是在这个注册过程中,模式才能执行 PHP 代码。

模式的块标记实际上要等到在编辑器或前端被使用时才会被渲染。不过,在渲染时这些块标记仍是普通的 HTML。

从实际应用角度来看,这意味着在 init 阶段的注册过程中,很多数据是不可用的。您的模式无法访问全局查询或文章之类的内容,也无法使用与这些数据相关的函数。因此,像 is_home()、is_single()、get_post() 这样的 WordPress 函数在当时还不可用。

例如,以下 PHP 代码在模式中无法正确执行:

<?php if ( is_page() ) : ?>
	<!-- wp:paragraph -->
	<p><?php the_title(); ?></p>
	<!-- /wp:paragraph -->
<?php endif; ?>

在模式注册时,is_page() 和 the_title() 函数都无法获取它们所需的全局数据,因此无法正常工作。

如果您习惯于构建传统主题,这种方式可能会让您觉得很不直观,尤其是当您将基于 PHP 的模式与传统的基于 PHP 的模板组件相混淆时。

在渲染时执行 PHP 还有其他方法,比如使用 块绑定 API。但这些方法不在模式文档的讨论范围内。

实际上,您仍然可以使用大量丰富的 PHP 函数以及许多 WordPress 函数。在本文的后续章节中,您将了解到更多关于您可以使用哪些功能的内容。

一个示例模式

在本文的剩余部分,我们将使用一个主要以 HTML 形式构成的模式,然后您将了解如何添加 PHP 代码来动态处理其中的部分内容。

在您的主题中创建一个名为 patterns/hero.php 的新文件,并将以下代码放入其中:

<?php
/**
 * Title: Hero
 * Slug: themeslug/hero
 * Categories: featured
 */
?>
<!-- wp:cover {"overlayColor":"contrast","isUserOverlayColor":true,"align":"full"} -->
<div class="wp-block-cover alignfull">
	<span aria-hidden="true" class="wp-block-cover__background has-contrast-background-color has-background-dim-100 has-background-dim"></span>
	<div class="wp-block-cover__inner-container">
		
		<!-- wp:heading {"textAlign":"center"} -->
		<h2 class="wp-block-heading has-text-align-center">欢迎来到我的网站</h2>
		<!-- /wp:heading -->

		<!-- wp:paragraph {"align":"center"} -->
		<p class="has-text-align-center">这是我的小小家园。</p>
		<!-- /wp:paragraph -->

		<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->
		<div class="wp-block-buttons">
			<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
			<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button">按钮 A</a></div>
			<!-- /wp:button -->
			<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
			<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button">按钮 B</a></div>
			<!-- /wp:button -->
		</div>
		<!-- /wp:buttons -->

	</div>
</div>
<!-- /wp:cover -->

如果将此模式插入编辑器,它应该看起来像这样:

WordPress 文章编辑器,背景为黑色,上面有白色文字“欢迎来到我的网站”。该内容被包裹在 Cover 块中。

在模式中实现文本国际化

块模式的主要用途之一就是实现文本国际化(即为翻译做好准备)。例如,如果模板或模板组件中包含文本,要实现文本国际化,唯一的方法就是将其移到模式中,因为在模式中您可以完全使用 PHP。

由于模式文件本身就是 PHP 代码,因此您可以使用 WordPress 中的任何国际化函数。在下面的示例中,您将使用 esc_html_e() 来同时确保文本的安全性并使其适合翻译人员处理。

让我们来看看您刚才的 Hero 模式中的 Heading 块:

<!-- wp:heading {"textAlign":"center"} -->
<h2 class="wp-block-heading has-text-align-center"&gt>欢迎来到我的网站</h2>
<!-- /wp:heading -->

如您所见,Heading 块中的“欢迎来到我的网站”文本并未实现国际化。当您将主题发布给使用多种语言的全世界用户时,确保这段文本可以被翻译非常重要。

要实现 Heading 块文本的国际化,必须将其包裹在国际化函数中(本例中为 esc_html_e())。因此,您需要将块标记修改为如下形式:

<!-- wp:heading {"textAlign":"center"} -->
<h2 class="wp-block-heading has-text-align-center"><?php esc_html_e( 'Welcome to My Site', 'themeslug' ); ?></h2>
<!-- /wp:heading -->

还有另外三个地方也需要进行同样的修改:首先是 Paragraph 块的文本,然后是两个 Button 块。修改后的 patterns/hero.php 文件应该看起来像这样:

<?php
/**
 * Title: Hero
 * Slug: themeslug/hero
 * Categories: featured
 */
?>
<!-- wp:cover {"overlayColor":"contrast","isUserOverlayColor":true,"align":"full"} -->
<div class="wp-block-cover alignfull">
	<span aria-hidden="true" class="wp-block-cover__background has-contrast-background-color has-background-dim-100 has-background-dim"></span>
	<div class="wp-block-cover__inner-container">
		
		<!-- wp:heading {"textAlign":"center"} -->
		<h2 class="wp-block-heading has-text-align-center"><?php esc_html_e( 'Welcome to My Site', 'themeslug' ); ?></h2>
		<!-- /wp:heading -->

		<!-- wp:paragraph {"align":"center"} -->
		<p class="has-text-align-center"><?php esc_html_e( 'This is my little home away from home.', 'themeslug' ); ?></p>
		<!-- /wp:paragraph -->

		<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->
		<div class="wp-block-buttons">
			<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
			<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php esc_html_e( 'Button A', 'themeslug' ); ?></a></div>
			<!-- /wp:button -->
			<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
			<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php esc_html_e( 'Button B', 'themeslug' ); ?></a></div>
			<!-- /wp:button -->
		</div>
		<!-- /wp:buttons -->

	</div>
</div>
<!-- /wp:cover -->

使用图片及其他资源

为图片及其他资源添加动态 URL 是模式的另一项重要功能。由于您可以使用 PHP,因此可以运用诸如 get_theme_file_uri() 这样的函数来输出与您的主题一同打包的图片 URL。

试着在“hero”模式的 Cover 块中添加图片背景。如果您需要图片,可以尝试从 WordPress 的 图片库 中获取图片(下面的示例使用了 尼泊尔夜景图片)。

下载一张图片并将其保存到主题的 /assets/images 文件夹中(或您选择的任意文件夹),并将其命名为 hero-background.jpg。

然后在编辑器中打开该模式,并将这张图片上传作为 Hero 模式中 Cover 块的背景,它应该看起来像这样:

WordPress 文章编辑器,Cover 块的背景为夜间城市图片,背景上覆盖有欢迎文字和按钮。

如果您复制构成该模式的各个块,会发现模式代码中出现了两个图片 URL。它们会是类似如下的硬编码 URL:

http://localhost/wp-content/uploads/2023/10/hero-background.jpg

您需要修改这两个 URL,使其指向位于主题 /assets/images 文件夹中的图片。为此,需要做两件事:

  • 使用如 get_theme_file_uri() 这样的函数获取正确的 URL。
  • 使用 esc_url() 对 URL 进行转义,以确保其能够安全地被输出。

将硬编码的图片 URL 更改为如下形式:

<?php echo esc_url( get_theme_file_uri( 'assets/images/hero-background.jpg' ) ); ?>

最终的模式代码应该看起来像这样(请注意,此示例包含了前文提到的国际化文本及动态图片的代码):

<?php
/**
 * Title: Hero
 * Slug: themeslug/hero
 * Categories: featured
 */
?>
<!-- wp:cover {"url":"<?php echo esc_url( get_theme_file_uri( 'assets/images/hero-background.jpg' ) ); ?>","id":3838,"dimRatio":50,"overlayColor":"contrast","align":"full"} -->
<div class="wp-block-cover alignfull">
	<span aria-hidden="true" class="wp-block-cover__background has-contrast-background-color has-background-dim"></span>
	<img class="wp-block-cover__image-background wp-image-3838" alt="" src="<?php echo esc_url( get_theme_file_uri( 'assets/images/hero-background.jpg' ) ); ?>" data-object-fit="cover"/>
	<div class="wp-block-cover__inner-container">
		
		<!-- wp:heading {"textAlign":"center"} -->
		<h2 class="wp-block-heading has-text-align-center"><?php esc_html_e( 'Welcome to My Site', 'themeslug' ); ?></h2>
		<!-- /wp:heading -->

		<!-- wp:paragraph {"align":"center"} -->
		<p class="has-text-align-center"><?php esc_html_e( 'This is my little home away from home.', 'themeslug' ); ?></p>
		<!-- /wp:paragraph -->

		<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->
		<div class="wp-block-buttons">
			<?php foreach ( $buttons as $button ) : ?>
				<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
				<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php echo esc_html( $button ); ?></a></div>
				<!-- /wp:button -->
			<?php endforeach; ?>
		</div>
		<!-- /wp:buttons -->

	</div>
</div>
<!-- /wp:cover -->

foreach() 循环

您几乎可以使用所有的 PHP 函数和功能,而在构建主题的过程中,这些功能无疑会派上用场。不过,foreach() 循环很可能是您会经常使用的功能之一。

foreach() 循环在包含重复块的模式中尤为有用。例如,在您的 patterns/hero.php 文件中,就有两组 Button 块:

<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php esc_html_e( 'Button A', 'themeslug' ); ?></a></div>
<!-- /wp:button -->

<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php esc_html_e( 'Button B', 'themeslug' ); ?></a></div>
<!-- /wp:button -->

开发领域的核心原则之一是 DRY(不要重复自己)。而这段代码违背了这一原则。通过使用一个 foreach() 循环,您只需创建一次块标记,然后通过循环来重复使用它。

首先,您需要一个用于循环的数组,也就是按钮的内容。在 patterns/hero.php 文件的顶端、闭合的 ?> 标签之前,添加一个包含标签的数组:

$buttons = array(
	__( 'Button A', 'themeslug' ),
	__( 'Button B', 'themeslug' )
);

然后用以下代码替换两个 Button 块的标记:

<?php foreach ( $buttons as $button ) : ?>
	<!-- wp:button {"className":"wp-block-button is-style-outline"} -->
	<div class="wp-block-button is-style-outline"><a class="wp-block-button__link wp-element-button"><?php echo esc_html( $button ); ?></a></div>
	<!-- /wp:button -->
<?php endforeach; ?>

此时,您的模式就完全具备了动态功能。随着您对模式开发的深入,将会发现更多使用 PHP 的可能性。可以把这篇文章视为您后续开发的基础。