
在开发 WordPress 插件时,一个关键步骤是预装重要数据,确保插件从一开始就能顺利运行。以活动管理器插件为例。安装时,如果插件能自动生成一个名为 “Upcoming Events” 的页面,显示未来活动的列表,这将大有裨益。
这个预先配置好的页面嵌入了类似 [event_list number="10" scope="future" status="publish"]
这样的简码,让用户无需阅读文档就能立即利用插件的功能。
安装数据不仅在首次安装插件时有帮助,在随后更新插件时也有帮助。例如,如果更新引入了日历视图功能,插件可以自动创建一个新页面 “Events Calendar“,通过 [event_calendar status="publish"]
这样的简码来展示这一新增功能。
一般来说,数据安装的范围涵盖各种需求:
- 生成具有特定标题和内容的新页面。
- 为插件创建的自定义帖子类型(CPT)添加条目。
- 在
wp_options
表中插入默认设置
- 为用户角色分配新功能
- 为插件提供的新功能或更新功能向用户分配元数据(例如,用户可更改事件日期格式,并首先为所有用户添加默认值)
- 创建插件上下文中常用的类别,如 “conferences” 或 “sports”。
安装数据必须是一个渐进的过程,否则我们可能会创建重复的条目。
例如,如果插件 1.1 版引入了 “Upcoming Events” 页面,而用户从 1.0 版进行了更新,则只应安装与 1.1 版相关的新数据。这种增量式更新可确保在 1.2 版推出日历功能时,只添加新的 “Events Calendar” 页面,避免与 “Upcoming Events” 页面重复。
因此,在更新时,插件必须检索之前安装的版本,并只安装与新版本相对应的数据。
本文将解释如何在 WordPress 插件中安装初始数据,并在进一步更新时不断添加新数据。
提供当前版本
为了处理增量过程,插件必须跟踪其当前版本,通常在插件主文件的头文件中声明。当然,我们不能直接引用它,因为它在 PHP 注释中。因此,我们还需要在变量中定义该值,并将其提供给负责初始化和配置的 Plugin
类:
// Same version as in the header
new Plugin($pluginVersion)->setup();
<?php
/*
Plugin Name: My plugin
Version: 1.6
*/
// Same version as in the header
$pluginVersion = '1.6';
new Plugin($pluginVersion)->setup();
<?php
/*
Plugin Name: My plugin
Version: 1.6
*/
// Same version as in the header
$pluginVersion = '1.6';
new Plugin($pluginVersion)->setup();
Plugin
类利用 PHP 8.0 的构造函数属性推广功能,存储了这个版本,以便我们以后引用:
public function __construct(
protected string $pluginVersion,
public function setup(): void
// Initialization logic here...
<?php
class Plugin {
public function __construct(
protected string $pluginVersion,
) {}
public function setup(): void
{
// Initialization logic here...
}
// ...
}
<?php
class Plugin {
public function __construct(
protected string $pluginVersion,
) {}
public function setup(): void
{
// Initialization logic here...
}
// ...
}
请注意,初始化和配置插件的逻辑被添加到了 setup
方法中,而不是构造函数中。这是因为构造函数必须避免产生副作用;否则,我们在扩展或组合 Plugin
类时可能会产生错误。
让我们来看看这种情况是如何发生的。假设我们最终添加了一个 BetterPlugin
类,该类由 Plugin
类的功能组成:
public function printSomething(): string
$plugin = new Plugin($pluginVersion);
return '<div class="wrapper">' . $plugin->printSomething() . '</div>';
class BetterPlugin {
public function printSomething(): string
{
$pluginVersion = '1.0';
$plugin = new Plugin($pluginVersion);
return '<div class="wrapper">' . $plugin->printSomething() . '</div>';
}
}
class BetterPlugin {
public function printSomething(): string
{
$pluginVersion = '1.0';
$plugin = new Plugin($pluginVersion);
return '<div class="wrapper">' . $plugin->printSomething() . '</div>';
}
}
每当在 printSomething
内执行 new Plugin()
时,都会创建一个新的 Plugin
实例。如果将配置逻辑添加到构造函数中,那么每次创建新的 Plugin
对象时都会执行配置逻辑。在我们的例子中,我们只想创建一次 “Upcoming Events” 页面,而不是多次。通过在设置方法中添加逻辑,我们可以避免这个问题。
跟踪以前的版本
WordPress 没有提供方便的方法来检索被替换插件的版本。因此,我们必须自己在数据库的 wp_options
表中存储这个值。
将版本存储在 ” myplugin_version
” 条目下,其中 myplugin_
是插件的名称(例如 eventsmanager_version
)。重要的是,在所有设置前都要加上 myplugin_
,以避免潜在冲突,因为我们无法确保其他插件不会添加 version
选项。
在每次请求中加载插件时, Plugin
已经知道当前的版本(来自前面的属性 $pluginVersion
),并将从数据库中检索上次存储的版本。这种比较决定了插件的状态:
- 新安装:检测数据库中是否缺少插件的版本条目,表示首次安装(即
$storedPluginVersion
为null
)
- 更新:当当前版本超过数据库存储的版本时识别,表明需要升级。
- 否则,没有变化
无论何时发生变化,我们都会调用 prepareAndInstallPluginSetupData
来安装相应的数据,无论是新安装(这种情况下必须安装所有版本的所有数据)还是更新(只安装所有新版本的数据)。可为空的 $previousVersion
变量会指示是哪种情况( $previousVersion
为 null
=> 新安装)。
调用此方法后,我们还必须在数据库中存储当前插件版本,使其成为新的 “最后存储” 版本。这项工作必须在调用 prepareAndInstallPluginSetupData
之后完成,这样,如果该方法出错(如抛出 RuntimeException
)且数据未安装,数据库中仍会存储上一版本,并在下一次请求时尝试新一轮安装数据。
public function setup(): void
$this->managePluginDataVersioning();
* If the plugin has just been newly-installed + activated
* or updated, install the appropriate data.
protected function managePluginDataVersioning(): void
$myPluginVersionOptionName = 'myplugin_version';
$storedPluginVersion = get_option($myPluginVersionOptionName, null);
// Check if the main plugin has been activated or updated
$isPluginJustFirstTimeActivated = $storedPluginVersion === null;
$isPluginJustUpdated = !$isPluginJustFirstTimeActivated && $storedPluginVersion !== $this->pluginVersion;
// If there were no changes, nothing to do
if (!$isPluginJustFirstTimeActivated && !$isPluginJustUpdated) {
function () use ($myPluginVersionOptionName, $storedPluginVersion): void {
$this->prepareAndInstallPluginSetupData($storedPluginVersion);
update_option($myPluginVersionOptionName, $this->pluginVersion);
protected function prepareAndInstallPluginSetupData(?string $previousVersion): void
<?php
class Plugin {
// ...
public function setup(): void
{
if (!is_admin()) {
return;
}
$this->managePluginDataVersioning();
}
/**
* If the plugin has just been newly-installed + activated
* or updated, install the appropriate data.
*/
protected function managePluginDataVersioning(): void
{
$myPluginVersionOptionName = 'myplugin_version';
$storedPluginVersion = get_option($myPluginVersionOptionName, null);
// Check if the main plugin has been activated or updated
$isPluginJustFirstTimeActivated = $storedPluginVersion === null;
$isPluginJustUpdated = !$isPluginJustFirstTimeActivated && $storedPluginVersion !== $this->pluginVersion;
// If there were no changes, nothing to do
if (!$isPluginJustFirstTimeActivated && !$isPluginJustUpdated) {
return;
}
\add_action(
'init',
function () use ($myPluginVersionOptionName, $storedPluginVersion): void {
$this->prepareAndInstallPluginSetupData($storedPluginVersion);
// Update on the DB
update_option($myPluginVersionOptionName, $this->pluginVersion);
}
);
}
protected function prepareAndInstallPluginSetupData(?string $previousVersion): void
{
// Installation logic...
}
}
<?php
class Plugin {
// ...
public function setup(): void
{
if (!is_admin()) {
return;
}
$this->managePluginDataVersioning();
}
/**
* If the plugin has just been newly-installed + activated
* or updated, install the appropriate data.
*/
protected function managePluginDataVersioning(): void
{
$myPluginVersionOptionName = 'myplugin_version';
$storedPluginVersion = get_option($myPluginVersionOptionName, null);
// Check if the main plugin has been activated or updated
$isPluginJustFirstTimeActivated = $storedPluginVersion === null;
$isPluginJustUpdated = !$isPluginJustFirstTimeActivated && $storedPluginVersion !== $this->pluginVersion;
// If there were no changes, nothing to do
if (!$isPluginJustFirstTimeActivated && !$isPluginJustUpdated) {
return;
}
\add_action(
'init',
function () use ($myPluginVersionOptionName, $storedPluginVersion): void {
$this->prepareAndInstallPluginSetupData($storedPluginVersion);
// Update on the DB
update_option($myPluginVersionOptionName, $this->pluginVersion);
}
);
}
protected function prepareAndInstallPluginSetupData(?string $previousVersion): void
{
// Installation logic...
}
}
请注意, prepareAndInstallPluginSetupData
(以及随后的数据库更新)是在 init
操作钩子上执行的。这是为了确保内容管理系统中的所有数据都已准备好供检索和操作。
特别是,在 init
钩子之前,不能访问分类标准(标签和类别)。如果插件的安装过程需要创建 CPT 条目并为其分配自定义类别,则该过程只能从 init
钩子开始运行。
从性能角度看,每次请求都从数据库访问上次存储的版本并不理想。为了改善这种情况,可以将插件所需的所有选项合并到一个数组中,将它们存储在一个条目中,然后通过对数据库的一次调用访问所有选项。
例如,如果插件还需要存储一个 myplugin_date_format
选项来显示事件日期,我们可以创建一个具有 version
和 date_format
属性的单一条目 myplugin_options
。
要访问上次存储的版本,PHP 代码必须进行如下调整:
protected function managePluginDataVersioning(): void
$myPluginOptionsOptionName = 'myplugin_options';
$myPluginOptions = get_option($myPluginOptionsOptionName, []);
$storedPluginVersion = $myPluginOptions['version'] ?? null;
function () use ($myPluginOptionsOptionName, $myPluginOptions): void {
$myPluginOptions['version'] = $this->pluginVersion;
update_option($myPluginOptionsOptionName, $myPluginOptions);
<?php
class Plugin {
// ...
protected function managePluginDataVersioning(): void
{
$myPluginOptionsOptionName = 'myplugin_options';
$myPluginOptions = get_option($myPluginOptionsOptionName, []);
$storedPluginVersion = $myPluginOptions['version'] ?? null;
// ...
\add_action(
'init',
function () use ($myPluginOptionsOptionName, $myPluginOptions): void {
// ...
// Update on the DB
$myPluginOptions['version'] = $this->pluginVersion;
update_option($myPluginOptionsOptionName, $myPluginOptions);
}
);
}
}
<?php
class Plugin {
// ...
protected function managePluginDataVersioning(): void
{
$myPluginOptionsOptionName = 'myplugin_options';
$myPluginOptions = get_option($myPluginOptionsOptionName, []);
$storedPluginVersion = $myPluginOptions['version'] ?? null;
// ...
\add_action(
'init',
function () use ($myPluginOptionsOptionName, $myPluginOptions): void {
// ...
// Update on the DB
$myPluginOptions['version'] = $this->pluginVersion;
update_option($myPluginOptionsOptionName, $myPluginOptions);
}
);
}
}
避免同时请求安装重复数据
如果两个或两个以上的用户在同一时间访问 wp-admin,安装过程可能会被触发一次以上。为避免同一数据被安装两次或更多次,我们使用暂态作为标记,只允许第一个请求安装数据:
- 检查暂存器
myplugin_installing_plugin_setup_data
是否存在(再次强调,该名称必须以 myplugin_
作为前缀);如果存在,则什么也不做(因为其他进程正在安装数据)。
- 否则,在数据库中存储瞬时数据,以安装数据所需的合理最长时间(例如 30 秒)
- 安装数据
- 删除暂存数据
代码如下:
* Use a transient to make sure that only one instance
* will install the data. Otherwise, two requests
* happening simultaneously might execute the logic
protected function prepareAndInstallPluginSetupData(?string $previousVersion): void
$transientName = 'myplugin_installing_plugin_setup_data';
$transient = \get_transient($transientName);
if ($transient !== false) {
// Another instance is executing this code right now
\set_transient($transientName, true, 30);
$this->installPluginSetupData($previousVersion);
\delete_transient($transientName);
protected function installPluginSetupData(?string $previousVersion): void
<?php
class Plugin {
// ...
/**
* Use a transient to make sure that only one instance
* will install the data. Otherwise, two requests
* happening simultaneously might execute the logic
*/
protected function prepareAndInstallPluginSetupData(?string $previousVersion): void
{
$transientName = 'myplugin_installing_plugin_setup_data';
$transient = \get_transient($transientName);
if ($transient !== false) {
// Another instance is executing this code right now
return;
}
\set_transient($transientName, true, 30);
$this->installPluginSetupData($previousVersion);
\delete_transient($transientName);
}
protected function installPluginSetupData(?string $previousVersion): void
{
// Do something...
}
}
<?php
class Plugin {
// ...
/**
* Use a transient to make sure that only one instance
* will install the data. Otherwise, two requests
* happening simultaneously might execute the logic
*/
protected function prepareAndInstallPluginSetupData(?string $previousVersion): void
{
$transientName = 'myplugin_installing_plugin_setup_data';
$transient = \get_transient($transientName);
if ($transient !== false) {
// Another instance is executing this code right now
return;
}
\set_transient($transientName, true, 30);
$this->installPluginSetupData($previousVersion);
\delete_transient($transientName);
}
protected function installPluginSetupData(?string $previousVersion): void
{
// Do something...
}
}
安装所有版本的数据
如前所述,如果更新插件,我们必须只安装新版本的数据,而不是所有版本的数据。这意味着我们需要按版本管理要安装的数据。
在下面的代码中,数组 $versionCallbacks
表示每个版本要执行的函数,该函数执行安装数据的逻辑。我们遍历所有版本的列表,使用 version_compare
将每个版本与上一个版本进行比较,如果比较结果大于上一个版本,则执行相应的函数安装相应的数据。
请注意,如果 $previousVersion
为null
(即新安装的版本),则会执行所有函数。
* Provide the installation in stages, version by version, to
* be able to execute it both when installing/activating the plugin,
* or updating it to a new version with setup data.
* The plugin's setup data will be installed if:
* - $previousVersion = null => Activating the plugin for first time
* - $previousVersion < someVersion => Updating to a new version that has data to install
protected function installPluginSetupData(?string $previousVersion): void
'1.1' => $this->installPluginSetupDataForVersion1Dot1(...),
'1.2' => $this->installPluginSetupDataForVersion1Dot2(...),
foreach ($versionCallbacks as $version => $callback) {
* If the previous version is provided, check if the corresponding update
* has already been performed, then skip
if ($previousVersion !== null && version_compare($previousVersion, $version, '>=')) {
protected function installPluginSetupDataForVersion1Dot1(): void
protected function installPluginSetupDataForVersion1Dot2(): void
class Plugin {
/**
* Provide the installation in stages, version by version, to
* be able to execute it both when installing/activating the plugin,
* or updating it to a new version with setup data.
*
* The plugin's setup data will be installed if:
*
* - $previousVersion = null => Activating the plugin for first time
* - $previousVersion < someVersion => Updating to a new version that has data to install
*/
protected function installPluginSetupData(?string $previousVersion): void
{
$versionCallbacks = [
'1.1' => $this->installPluginSetupDataForVersion1Dot1(...),
'1.2' => $this->installPluginSetupDataForVersion1Dot2(...),
// ... Add more versions
];
foreach ($versionCallbacks as $version => $callback) {
/**
* If the previous version is provided, check if the corresponding update
* has already been performed, then skip
*/
if ($previousVersion !== null && version_compare($previousVersion, $version, '>=')) {
continue;
}
$callback();
}
}
protected function installPluginSetupDataForVersion1Dot1(): void
{
// Do something...
}
protected function installPluginSetupDataForVersion1Dot2(): void
{
// Do something...
}
}
class Plugin {
/**
* Provide the installation in stages, version by version, to
* be able to execute it both when installing/activating the plugin,
* or updating it to a new version with setup data.
*
* The plugin's setup data will be installed if:
*
* - $previousVersion = null => Activating the plugin for first time
* - $previousVersion < someVersion => Updating to a new version that has data to install
*/
protected function installPluginSetupData(?string $previousVersion): void
{
$versionCallbacks = [
'1.1' => $this->installPluginSetupDataForVersion1Dot1(...),
'1.2' => $this->installPluginSetupDataForVersion1Dot2(...),
// ... Add more versions
];
foreach ($versionCallbacks as $version => $callback) {
/**
* If the previous version is provided, check if the corresponding update
* has already been performed, then skip
*/
if ($previousVersion !== null && version_compare($previousVersion, $version, '>=')) {
continue;
}
$callback();
}
}
protected function installPluginSetupDataForVersion1Dot1(): void
{
// Do something...
}
protected function installPluginSetupDataForVersion1Dot2(): void
{
// Do something...
}
}
为每个特定版本安装数据
最后,我们必须为每个版本安装实际数据(创建页面、CPT 条目、添加选项等)。
在这段代码中,我们为事件管理器插件添加了适用于 v1.1
版本的 “Upcoming Events” 页面:
protected function installPluginSetupDataForVersion1Dot1(): void
'post_status' => 'publish',
'post_title' => \__('Upcoming Events', 'myplugin'),
'post_content' => '[event_list number="10" scope="future"]',
class Plugin {
// ...
protected function installPluginSetupDataForVersion1Dot1(): void
{
\wp_insert_post([
'post_status' => 'publish',
'post_type' => 'page',
'post_title' => \__('Upcoming Events', 'myplugin'),
'post_content' => '[event_list number="10" scope="future"]',
]);
}
// ...
}
class Plugin {
// ...
protected function installPluginSetupDataForVersion1Dot1(): void
{
\wp_insert_post([
'post_status' => 'publish',
'post_type' => 'page',
'post_title' => \__('Upcoming Events', 'myplugin'),
'post_content' => '[event_list number="10" scope="future"]',
]);
}
// ...
}
然后,我们为 v1.2
版创建 “Events Calendar” 页面(在本例中,使用页面上的 Gutenberg 区块,添加名为 event-calendar
的自定义块):
protected function installPluginSetupDataForVersion1Dot2(): void
'post_status' => 'publish',
'post_title' => \__('Events Calendar', 'myplugin'),
'post_content' => serialize_blocks([
'blockName' => 'myplugin/event-calendar',
class Plugin {
// ...
protected function installPluginSetupDataForVersion1Dot2(): void
{
\wp_insert_post([
'post_status' => 'publish',
'post_type' => 'page',
'post_title' => \__('Events Calendar', 'myplugin'),
'post_content' => serialize_blocks([
[
'blockName' => 'myplugin/event-calendar',
'attrs' => [
'status' => 'publish',
],
'innerContent' => [],
],
]),
]);
}
}
class Plugin {
// ...
protected function installPluginSetupDataForVersion1Dot2(): void
{
\wp_insert_post([
'post_status' => 'publish',
'post_type' => 'page',
'post_title' => \__('Events Calendar', 'myplugin'),
'post_content' => serialize_blocks([
[
'blockName' => 'myplugin/event-calendar',
'attrs' => [
'status' => 'publish',
],
'innerContent' => [],
],
]),
]);
}
}
将所有代码整合在一起
我们完成了!Plugin
类的全部 PHP 代码如下,其中包含跟踪插件版本和安装相应数据的逻辑:
public function __construct(
protected string $pluginVersion,
public function setup(): void
$this->managePluginDataVersioning();
* If the plugin has just been newly-installed + activated
* or updated, install the appropriate data.
protected function managePluginDataVersioning(): void
$myPluginVersionOptionName = 'myplugin_version';
$storedPluginVersion = get_option($myPluginVersionOptionName, null);
// Check if the main plugin has been activated or updated
$isPluginJustFirstTimeActivated = $storedPluginVersion === null;
$isPluginJustUpdated = !$isPluginJustFirstTimeActivated && $storedPluginVersion !== $this->pluginVersion;
// If there were no changes, nothing to do
if (!$isPluginJustFirstTimeActivated && !$isPluginJustUpdated) {
function () use ($myPluginVersionOptionName, $storedPluginVersion): void {
$this->prepareAndInstallPluginSetupData($storedPluginVersion);
update_option($myPluginVersionOptionName, $this->pluginVersion);
* Use a transient to make sure that only one instance
* will install the data. Otherwise, two requests
* happening simultaneously might both execute
protected function prepareAndInstallPluginSetupData(?string $previousVersion): void
$transientName = 'myplugin_installing_plugin_setup_data';
$transient = \get_transient($transientName);
if ($transient !== false) {
// Another instance is executing this code right now
\set_transient($transientName, true, 30);
$this->installPluginSetupData($previousVersion);
\delete_transient($transientName);
* Provide the installation in stages, version by version, to
* be able to execute it both when installing/activating the plugin,
* or updating it to a new version with setup data.
* The plugin's setup data will be installed if:
* - $previousVersion = null => Activating the plugin for first time
* - $previousVersion < someVersion => Updating to a new version that has data to install
protected function installPluginSetupData(?string $previousVersion): void
'1.1' => $this->installPluginSetupDataForVersion1Dot1(...),
'1.2' => $this->installPluginSetupDataForVersion1Dot2(...),
foreach ($versionCallbacks as $version => $callback) {
* If the previous version is provided, check if the corresponding update
* has already been performed, then skip
if ($previousVersion !== null && version_compare($previousVersion, $version, '>=')) {
protected function installPluginSetupDataForVersion1Dot1(): void
'post_status' => 'publish',
'post_title' => \__('Upcoming Events', 'myplugin'),
'post_content' => '[event_list number="10" scope="future" status="publish"]',
protected function installPluginSetupDataForVersion1Dot2(): void
'post_status' => 'publish',
'post_title' => \__('Events Calendar', 'myplugin'),
'post_content' => serialize_blocks([
'blockName' => 'myplugin/event-calendar',
<?php
class Plugin {
public function __construct(
protected string $pluginVersion,
) {
}
public function setup(): void
{
if (!is_admin()) {
return;
}
$this->managePluginDataVersioning();
}
/**
* If the plugin has just been newly-installed + activated
* or updated, install the appropriate data.
*/
protected function managePluginDataVersioning(): void
{
$myPluginVersionOptionName = 'myplugin_version';
$storedPluginVersion = get_option($myPluginVersionOptionName, null);
// Check if the main plugin has been activated or updated
$isPluginJustFirstTimeActivated = $storedPluginVersion === null;
$isPluginJustUpdated = !$isPluginJustFirstTimeActivated && $storedPluginVersion !== $this->pluginVersion;
// If there were no changes, nothing to do
if (!$isPluginJustFirstTimeActivated && !$isPluginJustUpdated) {
return;
}
\add_action(
'init',
function () use ($myPluginVersionOptionName, $storedPluginVersion): void {
$this->prepareAndInstallPluginSetupData($storedPluginVersion);
// Update on the DB
update_option($myPluginVersionOptionName, $this->pluginVersion);
}
);
}
/**
* Use a transient to make sure that only one instance
* will install the data. Otherwise, two requests
* happening simultaneously might both execute
* this logic
*/
protected function prepareAndInstallPluginSetupData(?string $previousVersion): void
{
$transientName = 'myplugin_installing_plugin_setup_data';
$transient = \get_transient($transientName);
if ($transient !== false) {
// Another instance is executing this code right now
return;
}
\set_transient($transientName, true, 30);
$this->installPluginSetupData($previousVersion);
\delete_transient($transientName);
}
/**
* Provide the installation in stages, version by version, to
* be able to execute it both when installing/activating the plugin,
* or updating it to a new version with setup data.
*
* The plugin's setup data will be installed if:
*
* - $previousVersion = null => Activating the plugin for first time
* - $previousVersion < someVersion => Updating to a new version that has data to install
*/
protected function installPluginSetupData(?string $previousVersion): void
{
$versionCallbacks = [
'1.1' => $this->installPluginSetupDataForVersion1Dot1(...),
'1.2' => $this->installPluginSetupDataForVersion1Dot2(...),
// ... Add more versions
];
foreach ($versionCallbacks as $version => $callback) {
/**
* If the previous version is provided, check if the corresponding update
* has already been performed, then skip
*/
if ($previousVersion !== null && version_compare($previousVersion, $version, '>=')) {
continue;
}
$callback();
}
}
protected function installPluginSetupDataForVersion1Dot1(): void
{
\wp_insert_post([
'post_status' => 'publish',
'post_type' => 'page',
'post_title' => \__('Upcoming Events', 'myplugin'),
'post_content' => '[event_list number="10" scope="future" status="publish"]',
]);
}
protected function installPluginSetupDataForVersion1Dot2(): void
{
\wp_insert_post([
'post_status' => 'publish',
'post_type' => 'page',
'post_title' => \__('Events Calendar', 'myplugin'),
'post_content' => serialize_blocks([
[
'blockName' => 'myplugin/event-calendar',
'attrs' => [
'status' => 'publish',
],
'innerContent' => [],
],
]),
]);
}
}
<?php
class Plugin {
public function __construct(
protected string $pluginVersion,
) {
}
public function setup(): void
{
if (!is_admin()) {
return;
}
$this->managePluginDataVersioning();
}
/**
* If the plugin has just been newly-installed + activated
* or updated, install the appropriate data.
*/
protected function managePluginDataVersioning(): void
{
$myPluginVersionOptionName = 'myplugin_version';
$storedPluginVersion = get_option($myPluginVersionOptionName, null);
// Check if the main plugin has been activated or updated
$isPluginJustFirstTimeActivated = $storedPluginVersion === null;
$isPluginJustUpdated = !$isPluginJustFirstTimeActivated && $storedPluginVersion !== $this->pluginVersion;
// If there were no changes, nothing to do
if (!$isPluginJustFirstTimeActivated && !$isPluginJustUpdated) {
return;
}
\add_action(
'init',
function () use ($myPluginVersionOptionName, $storedPluginVersion): void {
$this->prepareAndInstallPluginSetupData($storedPluginVersion);
// Update on the DB
update_option($myPluginVersionOptionName, $this->pluginVersion);
}
);
}
/**
* Use a transient to make sure that only one instance
* will install the data. Otherwise, two requests
* happening simultaneously might both execute
* this logic
*/
protected function prepareAndInstallPluginSetupData(?string $previousVersion): void
{
$transientName = 'myplugin_installing_plugin_setup_data';
$transient = \get_transient($transientName);
if ($transient !== false) {
// Another instance is executing this code right now
return;
}
\set_transient($transientName, true, 30);
$this->installPluginSetupData($previousVersion);
\delete_transient($transientName);
}
/**
* Provide the installation in stages, version by version, to
* be able to execute it both when installing/activating the plugin,
* or updating it to a new version with setup data.
*
* The plugin's setup data will be installed if:
*
* - $previousVersion = null => Activating the plugin for first time
* - $previousVersion < someVersion => Updating to a new version that has data to install
*/
protected function installPluginSetupData(?string $previousVersion): void
{
$versionCallbacks = [
'1.1' => $this->installPluginSetupDataForVersion1Dot1(...),
'1.2' => $this->installPluginSetupDataForVersion1Dot2(...),
// ... Add more versions
];
foreach ($versionCallbacks as $version => $callback) {
/**
* If the previous version is provided, check if the corresponding update
* has already been performed, then skip
*/
if ($previousVersion !== null && version_compare($previousVersion, $version, '>=')) {
continue;
}
$callback();
}
}
protected function installPluginSetupDataForVersion1Dot1(): void
{
\wp_insert_post([
'post_status' => 'publish',
'post_type' => 'page',
'post_title' => \__('Upcoming Events', 'myplugin'),
'post_content' => '[event_list number="10" scope="future" status="publish"]',
]);
}
protected function installPluginSetupDataForVersion1Dot2(): void
{
\wp_insert_post([
'post_status' => 'publish',
'post_type' => 'page',
'post_title' => \__('Events Calendar', 'myplugin'),
'post_content' => serialize_blocks([
[
'blockName' => 'myplugin/event-calendar',
'attrs' => [
'status' => 'publish',
],
'innerContent' => [],
],
]),
]);
}
}
小结
WordPress 插件在安装时通常需要安装数据。此外,当新版本的插件提供新功能时,插件也可能需要在更新时安装数据。
在本文中,我们学习了如何跟踪版本并为插件安装相应的数据。
您的 WordPress 插件可以从安装数据中受益吗?请在评论中告诉我们。
评论留言