WordPress-开发快读启动指南-全-

WordPress 开发快读启动指南(全)

原文:zh.annas-archive.org/md5/dcac104d9ef430570842be35a3e8d8c3

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

WordPress 是世界上使用最广泛的 CMS。作为一名开发者,WordPress 是向广大社区分享你的知识以及建立盈利性业务的理想方式。与其他框架从头开始构建相比,对于新手开发者来说,开始 WordPress 开发一直是一个挑战。通过连接零散的部分来构建 WordPress 站点非常容易,但实际上并不知道底层发生了什么。许多开发者倾向于走这条路,最终面临维护和安全噩梦。本书旨在解释 WordPress 开发中使用的组件,何时何地使用它们,以及为什么你应该在特定场景中使用每个组件。

你将从学习 WordPress 的基本开发设置和编码标准开始这本书。掌握内置数据库是成为 WordPress 开发专家的关键之一。在本节中,你将了解数据库结构,并检查内置功能的用法及其局限性,以便规划应用程序的数据需求。然后,你将进入一个重要主题:主题和插件开发过程。在本节中,你将了解主题和插件如何融入应用程序,并了解扩展主题和插件的基本到高级技术。

在掌握基础知识后,我们将探讨 WordPress 提供的广泛 API 的使用,并了解如何利用它们快速构建解决方案。接下来,我们将进入另一个重要部分,探讨捕获、处理和显示用户数据的技术,同时将第三方组件集成到网站设计中。最后,你将学习如何使用安全且易于维护的代码测试和部署应用程序,同时为最终用户提供最佳性能。

到这本书的结尾,你将能够为任何开发任务选择正确的组件,并构建与现有插件和主题兼容的灵活解决方案。

本书面向对象

本书旨在面向计划使用 WordPress 构建自定义网站的 Web 开发者和网站所有者。此外,Web 开发者可以将本书作为进入插件和主题开发的起点。

本书涵盖内容

第一章,WordPress 开发入门,涵盖了 WordPress 的基本安装和配置过程。设置开发环境、WordPress 编码标准的介绍以及理解 WordPress 文件/文件夹结构是本章的亮点。

第二章,管理数据库结构、存储和检索,介绍了现有的 WordPress 数据库表结构及其在自定义开发中的作用。本章节的重点包括执行基本查询、管理自定义表和跟踪各种功能的数据库使用情况。

第三章,使用主题开发设计灵活的前端,探讨了在自定义开发中主题的角色和功能。本章节的重点包括深入覆盖模板层次结构、自定义模板设计和主题中的自定义开发技术。

第四章,使用插件开发构建自定义模块,探讨了插件的作用并介绍了简单插件开发的基础知识。本章节的重点包括理解插件生命周期事件、识别构建插件的优点以及开发覆盖生命周期事件的插件。

第五章,使用附加组件、过滤器和动作扩展插件,专注于使用附加组件和 WordPress 钩子扩展现有插件和核心功能。本章节的重点包括自定义第三方插件、集成多个插件以及识别现有插件的可扩展功能。

第六章,WordPress API 的实用应用,探讨了 WordPress API 及其在自定义开发中的功能。本章节的重点包括掌握短代码的使用、使用 Rewrite API 构建自定义路由以及通过 REST API 允许远程连接。

第七章,管理自定义文章类型和处理表单,解释了使用自定义文章类型和自定义表单的主要数据捕获和管理过程。本章节的重点包括为处理具有自定义文章类型的属性管理网站构建面向对象的插件以及在前端构建自定义表单。

第八章,在开发中探索关键模块,深入探讨了通过交互式设计和定制后端功能来改善用户体验的过程。本章节的重点包括集成 UI 组件、使用页面构建器和简化后端管理功能。

第九章,增强安全性、性能和维护,涵盖了构建高质量和可靠网站的非功能性开发方面。本章节的重点包括测试过程、确保网站安全步骤、使用插件迁移网站以及探索维护的主要任务。

为了充分利用这本书

需要具备 PHP、JavaScript、HTML 和 CSS 的基本知识。您还需要一台计算机、一个浏览器以及以下工作环境的互联网连接:

  • Apache 网络服务器

  • PHP 版本 5.4 或更高

  • WordPress 版本 4.9.8

  • MySQL 版本 5.6+ 或 MariaDB 10.0+

下载示例代码文件

您可以从www.packt.com的账户下载本书的示例代码文件。如果您在其他地方购买了此书,您可以访问www.packt.com/support并注册,以便将文件直接通过电子邮件发送给您。

您可以通过以下步骤下载代码文件:

  1. www.packt.com登录或注册。

  2. 选择支持选项卡。

  3. 点击代码下载与勘误。

  4. 在搜索框中输入书籍名称,并遵循屏幕上的说明。

文件下载后,请确保使用最新版本的以下软件解压缩或提取文件夹:

  • WinRAR/7-Zip(适用于 Windows)

  • Zipeg/iZip/UnRarX(适用于 Mac)

  • 7-Zip/PeaZip(适用于 Linux)

本书代码包也托管在 GitHub 上,地址为github.com/PacktPublishing/WordPressDevelopmentQuickStartGuide。如果代码有更新,它将在现有的 GitHub 仓库中更新。

我们还有其他来自我们丰富图书和视频目录的代码包。您可以在github.com/PacktPublishing/上找到它们!查看它们!

下载彩色图像

我们还提供了一份包含本书中使用的截图/图表彩色图像的 PDF 文件。您可以从这里下载:www.packtpub.com/sites/default/files/downloads/9781789342871_ColorImages.pdf

代码应用实例

访问以下链接查看代码运行的视频:

bit.ly/2AzlbTj

使用的约定

本书使用了多种文本约定。

CodeInText:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“我们需要在static函数内部使用self::$instance对象。”

代码块设置如下:

add_action( 'plugins_loaded', 'wqcpt_plugin_init' );
function wqcpt_plugin_init(){
  global $wqcpt;
  $wqcpt = WPQuick_CPT::instance();
}

当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:

add_action( 'plugins_loaded', 'wqcpt_plugin_init' );
function wqcpt_plugin_init(){
  global $wqcpt;
  $wqcpt = WPQuick_CPT::instance();
}

粗体:表示新术语、重要单词或您在屏幕上看到的单词。例如,菜单或对话框中的单词在文本中显示如下。以下是一个示例:“从管理面板中选择系统信息。”

警告或重要提示看起来像这样。

技巧和窍门看起来像这样。

联系我们

欢迎读者反馈。

一般反馈:如果您对这本书的任何方面有疑问,请在邮件主题中提及书名,并给我们发送邮件至customercare@packtpub.com

勘误:尽管我们已经尽最大努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们将非常感激您能向我们报告。请访问www.packt.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入详细信息。

盗版:如果您在互联网上发现我们作品的任何非法副本,如果您能提供位置地址或网站名称,我们将不胜感激。请通过copyright@packt.com与我们联系,并附上材料的链接。

如果您有兴趣成为作者:如果您在某个领域有专业知识,并且您有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com

评价

请留下您的评价。一旦您阅读并使用过这本书,为何不在购买它的网站上留下评价呢?潜在读者可以查看并使用您的客观意见来做出购买决定,我们 Packt 公司可以了解您对我们产品的看法,并且我们的作者可以查看他们对书籍的反馈。谢谢!

想了解更多关于 Packt 的信息,请访问packt.com

第一章:WordPress 开发简介

WordPress 是世界上使用最广泛的 CMS,对开发者的需求正在上升。初学者 WordPress 开发者通常局限于使用现有插件和通过主题功能文件进行微调。作为一名开发者,你需要对 WordPress 核心模块和组件有深入的了解,以及一个适当的开发环境来应对复杂的应用需求。

本章的重点是向您介绍 WordPress 开发的先决条件,同时澄清一些关于开发的误解。与其他框架不同,WordPress 没有 MVC 架构或类似架构。因此,开发者有自由去寻找自己的技术。我们将从开发者如何使用现有功能进行开发,以及将我们的代码与 WordPress 组件集成的技术进行概述开始。然后,我们将继续讨论 WordPress 的安装以及默认和高级配置下配置文件的使用。开发工具和编码标准是生产高质量代码的关键,因此我们还将讨论 WordPress 特定编码标准的使用以及流行开源开发工具的使用。

在本章中,我们将涵盖以下主题:

  • 安装和配置 WordPress 开发环境

  • 如何使用 WordPress 配置文件

  • WordPress 文件/目录结构的高级概述及其在开发中的使用

  • WordPress 编码标准和其使用的简要介绍

  • 使用流行的工具进行代码调试

到本章结束时,你将具备理解 WordPress 关键开发领域和为流畅的工作流程准备开发环境所需的知识。

WordPress 开发简介

本书旨在为初学者 WordPress 开发者和希望使用 WordPress 构建自定义网站的网站所有者编写。我们深入探讨使用 WordPress 开发解决方案,了解开发的确切含义和涉及到的领域非常重要。许多人自称是 WordPress 开发者,但实际上他们所做的只是改变一些主题设计和使用主题功能文件。WordPress 开发可以从简单的主题设计更改到构建复杂的网络应用程序。

下面是 WordPress 开发中涉及到的组件:

  • 主题功能文件:开始开发时遇到的第一项技术。通常,我们可以使用过滤器和动作添加小的代码片段来改变主题或插件的功能。由于此文件在主题更新时会被更新,因此不建议添加自己的自定义代码。然而,在使用自己的主题或没有版本更新的主题时,有一些例外。这是一个添加一些代码并学习如何通过动作和过滤器自定义 WordPress 的好起点。

  • 子主题:这是开发过程中的下一步,在深入使用插件进行复杂实现之前。子主题是基于现有主题创建的主题。我们可以选择覆盖子主题中的所有文件,或者只添加必要的文件以覆盖。如果你刚开始 WordPress 开发并想添加一些快速代码片段,创建子主题并使用函数文件是理想的解决方案。在开发过程中,子主题应用于较小的实现,如设计更改或在前端屏幕上显示更多/更少的数据。由于我们只使用functions.php文件进行代码更改,因此不建议使用此技术来实现高级功能。然而,现代主题功能丰富,因此在子主题中进行高级实现可能会有例外。

  • 插件:作为开发者,开发插件是构建自定义网站最重要的方面。插件允许你构建独立和可扩展的解决方案。通常,将任何定制添加到单独的插件或基于其范围的特定站点通用插件中是一个好习惯。你可以创建插件来自定义现有的 WordPress 后端和前端功能、主题功能,以及开发超出 WordPress 内置模块的完全新功能。

  • 插件扩展:尽管我们称之为插件扩展,但它们是扩展另一个插件功能的插件。在 WordPress 开发中,我们不会从头开始构建一切,因为有一个庞大的免费插件库和数千个付费插件可以完成各种任务。大多数这些现有插件可能无法完美地满足我们的需求。因此,我们需要一种方法来定制这些插件的功能,而不会在插件更新时丢失更改。插件扩展通过使用插件提供的动作、过滤器和 API 来实现。

这些是开发自己的 WordPress 解决方案的主要组件和文件。现在,我们需要将重点转移到开发中涉及的主要领域以及它们如何适应 WordPress。让我们回顾一下在开发中现有功能的用法以及自定义构建功能的用法。

帖子、页面和自定义帖子类型管理

自 WordPress 诞生以来,它一直被用作以博客为主的 CMS。经过这么多年,帖子页面仍然是 WordPress 的主要方面,加上自定义帖子类型。许多网站的主要功能是帖子管理。因此,你需要掌握帖子创建、编辑、显示和管理附加数据的各个方面。随着自定义帖子类型的出现,开发者可以满足许多用户数据捕获应用的需求。由于基本功能如创建、编辑、显示和列表都内置在自定义帖子类型中,开发者可以快速扩展这些功能。

许多大型应用程序,如活动管理系统、在线商店、房地产网站和职位管理网站,都是使用自定义帖子类型建模的,因此它是 WordPress 开发中的一个主要功能。

用户管理

这又是一个具有全面功能,用于用户数据、访问级别和权限的内置模块。在许多现代应用程序和网站上,你可能需要注册成员。因此,你可以使用现有用户模块的功能来处理基本用户数据和权限,同时通过扩展核心功能来开发前端登录、注册和资料管理的高级功能。

与自定义帖子类型类似,你会发现许多应用程序的主要功能之一是用户管理。论坛、在线学生管理系统和 CRM 是 WordPress 中一些高级用户管理应用程序的例子。

表单数据管理

捕获、处理和显示用户数据的过程是许多网站的主要特征。我们之前已经讨论了使用自定义帖子类型来简化数据建模过程。然而,内置的自定义帖子类型功能仅限于后端。从前端捕获用户数据到自定义帖子类型,表单管理是必不可少的。在某些情况下,由于某些限制,我们不得不从头开始使用表单管理,而不使用自定义帖子类型。这些限制包括使用现有的数据库表、钩子和自定义帖子类型中的内置模板。

我们可以使用主题模板、短代码或现有的钩子来添加表单。在处理这些表单时,将使用现代 JavaScript 库,同时使用自定义数据库表来增强灵活性。因此,这是 WordPress 开发中另一项基本技能。

构建自定义布局和扩展主题布局

每个 WordPress 网站或应用程序都是围绕一个主题和一系列插件构建的。一般来说,大多数人更喜欢使用高质量的现有主题,而有些人则需要从头开始为他们构建一个完整的主题。无论这些偏好如何,你需要具备主题开发技能,以适应现代趋势。基于小部件的动态屏幕、响应式布局以及基于用户偏好的动态内容显示是主题中使用的现代趋势之一。最常见的发展形式是自定义现有主题布局和构建新的帖子/页面模板。

现代应用程序可能还需要你在主题中添加复杂功能,而不是将它们分离成插件。因此,你需要具备处理主题文件以及常见主题钩子的实际经验,以构建高级用户界面。

这些是 WordPress 开发中经常涉及的一些领域,因此你需要熟悉这些领域,以便能够快速提供解决方案。除了这些领域之外,你还可以使用现有的钩子扩展大多数 WordPress 功能,以及构建自定义功能,如 API、集成第三方 UI 组件等。

设置开发环境

为了开始 WordPress 开发,你需要一个支持 WordPress 的工作开发环境。因此,我们需要关注构建开发环境的必要步骤,如下所示:

  • 安装支持 PHP、Apache 和 MySQL 或 Maria DB 的开发栈

  • 安装和配置 WordPress

  • 设置网络浏览器和代码编辑器

让我们快速查看每个选项,以构建理想的 WordPress 开发环境。

安装开发栈

运行 WordPress 的最基本要求包括 PHP、Apache 服务器和 MySQL 数据库。因此,我们需要从头开始安装这些组件,或者使用现有的工具,如 WAMP、XAMPP 或 MAMP。由于本书是关于 WordPress 开发的快速入门,我们将使用现有的工具,而不浪费时间去手动安装。你可以根据你的操作系统选择工具。对于本书,我们将在 Windows 平台上使用 WAMP 服务器。

WAMP 服务器提供了一个内置的安装过程,你只需要在通过几个定义良好的安装屏幕时提供所需输入即可。由于这已经在多个在线资源中讨论过,我们在这里不会解释安装过程。你可以参考 www.wampserver.com/en/ 了解设置 WAMP 的说明。

安装和配置 WordPress

一旦安装了开发栈,我们就可以进入 WordPress 安装过程。WordPress 提供了安装软件框架的最简单自动化过程之一。完整的安装过程不超过 5-6 步,只需几分钟。由于它已经在 WordPress 代码库中得到了全面记录,我们不会浪费时间从头开始解释。你可以参考 codex.wordpress.org/Installing_WordPress#Detailed_Instructions 了解完整的安装过程。

尽管安装过程很简单,但配置可能会根据你的要求和不同的环境而有所不同。因此,我们将在接下来的章节中详细讨论配置。

设置网络浏览器和代码编辑器

作为开发者,您应该在您的系统上安装至少两个浏览器。WordPress 经常更新到现代库和趋势的现代版本,如响应式设计。因此,您需要确保可用的浏览器已更新到最新版本。此外,您可能还需要安装所有流行的浏览器,如 Chrome、Firefox、Opera、Safari 和 Internet Explorer,以符合浏览器兼容性。

代码编辑器是开发的重要方面之一,开发者需要选择一个符合他们个人兴趣的工具。一些流行的代码编辑器包括 Sublime Text、Brackets、Textmate 和 Notepad++。在选择代码编辑器时,您需要寻找以下基本功能:

  • 支持的编程语言

  • 语法高亮和代码可读性

  • 自动完成

  • 简化的缩进和格式化

  • 版本管理

  • 文件/文件夹搜索和替换

许多这些编辑器都是功能齐全的解决方案,因此所有这些功能都是默认可用的。在这种情况下,您可以根据个人偏好做出决定。到此,我们的开发环境已经准备好,配备了启动所需的常见工具。

准备开发工具

尽管我们可以立即开始开发,但拥有使我们的任务更容易作为开发者的必要工具,同时节省宝贵的时间,这一点很重要。您可以在创建用于测试和调试代码以及帮助您定位实现中问题的数百个开发者工具中找到。在这里,我们将探讨一些最必要的工具来支持您的开发。

使用浏览器开发工具进行客户端监控

曾经我们不得不安装不同的扩展来在不同的浏览器中使用开发工具。现在,开发工具已成为所有现代浏览器最新版本的标准。这些工具提供了广泛的调试代码的便利设施。以下截图预览了 Chrome 浏览器的开发者工具部分:

图片

上一张截图预览了控制台标签页,在这里您将看到与用户请求相关的错误、通知和信息。此标签页简化了跟踪和修复客户端编码中错误的过程。我们还可以使用其他标签页,如“资源”、“网络”、“性能”、“安全”来跟踪 AJAX 请求、文件加载优先级、内存和带宽使用,以及可能的安全风险。

“AJAX 是异步 JavaScript 和 XML 的缩写。AJAX 是一套使用许多客户端 Web 技术来创建异步 Web 应用的 Web 开发技术。使用 Ajax,Web 应用可以在不干扰现有页面显示和行为的情况下,异步(在后台)从服务器发送和检索数据。”

– 维基百科

其他浏览器提供了具有类似功能的内置开发工具。Firefox 浏览器包括 Inspector、Console、Debugger、Style Editor、Performance、Memory、Network 和 Storage 标签页。Internet Explorer 包括 DOM Explorer、Console、Debugger、Network、UI Responsiveness、Profiler 和 Memory 标签页。主要功能集在所有浏览器中都是一致的。作为开发者,您至少应该使用浏览器工具来完成一些简单任务,例如监控错误、检查 HTML 标签、跟踪 AJAX 请求和测试 CSS 样式。

使用 WordPress 插件进行服务器端监控

WordPress 插件库提供了相当多的插件来帮助开发者跟踪错误、性能问题、请求中使用的样式、执行的钩子、加载的脚本以及各种其他有用的功能。选择使用多少插件以及使用哪些插件取决于您。在本节中,我们将探讨一些最受欢迎的调试和测试插件。

查询监控

这是调试类别中最近更新插件中最广泛使用的插件。此插件的主要功能是监控用户请求中的数据库查询,并帮助您优化它们。我们使用许多插件来构建 WordPress 网站,因此每个用户请求可能执行大量查询。在大多数情况下,相当一部分查询与用户请求无关,是由于插件开发者低质量的编码而执行。此插件允许您按插件跟踪查询,这使得识别负责执行大量查询以及不必要的插件的插件变得更容易。

以下截图预览了使用Query Monitor跟踪 WordPress 核心和单个插件执行的查询:

Query Monitor 使用预览

除了监控查询外,您还可以使用此插件来检查当前用户请求中加载的钩子、脚本和样式。Query Monitor 是 WordPress 的必备插件。您可以在wordpress.org/plugins/query-monitor/找到有关 Query Monitor 插件的更多详细信息。

调试此插件

这是最近创建的用于支持开发者的插件之一。与Query Monitor不同,此插件没有主要功能。相反,它为跟踪 WordPress 中所有类型的特性提供了广泛的功能。您可以跟踪短代码、文章类型、PHP 类/函数使用、媒体附件以及许多其他内容,包括我们在 Query Monitor 中已经讨论过的特性。

以下截图预览了使用 Debug This 的可用功能列表:

调试和测试插件预览

如您可能注意到的,这是一个对开发者极其有用的工具。唯一的缺点是信息以纯数组的形式输出,这使得阅读变得困难。尽管它缺乏使用用户友好的屏幕对信息进行适当组织,但您可以在不使用代码中的 var_dump 的情况下解决复杂问题。您可以在 wordpress.org/plugins/debug-this/ 找到有关“调试此”插件的更多详细信息。

除了这两个插件之外,还有更多有用的插件可以帮助开发者编写高质量的代码,并通过错误处理和优化节省时间。以下是在开发中使用的此类插件列表:

我们查看了 WordPress 插件库中最受欢迎的测试和调试插件。有付费插件和在线服务可以进一步改进开发过程。您可以测试这些插件,看看哪些适合您的开发环境。

配置文件的使用

WordPress 使用一个名为 wp-config.php 的配置文件,位于您 WordPress 安装根目录内。在前面的部分,我们介绍了安装过程,此文件根据指定信息自动生成。为了使用 WordPress 的高级功能,以及根据系统更改进行手动修改,我们将详细解释此文件。让我们快速查看此文件的初始内容,如下所示:

define('DB_NAME', 'wpquick'); 
define('DB_USER', 'wpdadmin'); 
define('DB_PASSWORD', ' GBm+Hq1T1Clyq '); 
define('DB_HOST', 'localhost'); 
define('DB_CHARSET', 'utf8mb4'); 
define('DB_COLLATE', ''); 

define('AUTH_KEY',         '}ezywl=_z-&_r-Ter]^)GafZQ!;T}sG{`RI?y.!BDgKKtW6WLqk>FnH<p1@ZsHZ`'); 
define('SECURE_AUTH_KEY',  'm1+,]G-])dt)%T:9ziw;|,]s&k^ ^Z0Vp.7DaSC0U)GT>*GY:jW@'); 
define('LOGGED_IN_KEY',    '&W_%WB%jjS0+_oBN:-cz5]qK<Jv*1{oM vji[~}k(uN*`g>wczC}<6?8t!BX{Z;G'); 
define('NONCE_KEY',       'vL9gM*pJPP3BC>I29+8*f[[)%kI$>)^clg%T;`9ONsl7RXAkzm]oX18Y~1c.;n%6'); 
define('AUTH_SALT',        'y>:&&$GBm+Hq1T1Clyq=Vp{Mk>f;nRofa/f!i}ex(m&-rs&dkT!ja0ilwJD~Lk4qQ'); 
define('SECURE_AUTH_SALT', 'PQMwuucwM`=0z7AwEJO@## }kQ]+o,bl2CZ()!d|*_FEl)>iI'); 
define('LOGGED_IN_SALT',   'XDlT]5pBg4jTg=e#XA2u{CTrdP!SU|aD o&Rq4/}: !Gu_2;)u-nW}0(/EEu4Ysb'); 
define('NONCE_SALT',       'CCX2?iAcCJ{Se5!ViEUO(/E0~/`ez_TZ=oAFrZ?DMru/RzLz(iPv(LwV%L0#a5px'); 

$table_prefix  = 'wp_'; 
define('WP_DEBUG', false); 

if ( !defined('ABSPATH') ) 
   define('ABSPATH', dirname(__FILE__) . '/'); 
require_once(ABSPATH . 'wp-settings.php'); 

文件的第一部分处理您网站的数据库配置。如您所见,数据库详细信息已根据您在安装过程中提供的输入自动添加。当您将网站从本地环境迁移到生产环境,或者更改数据库详细信息(如用户名和密码)时,您需要手动修改此部分。一旦数据库详细信息被修改并保存,它将立即影响您网站的加载。

下一个部分定义了 WordPress 的安全密钥。这些密钥在 WordPress 安装时自动生成。在没有安全密钥的情况下,您可以访问api.wordpress.org/secret-key/1.1/salt/并生成一组新的密钥。本节包含四个密钥和四个盐值,用于您的网站。这些密钥用于保护用户的 cookies。您不需要了解每个密钥的含义或记住这些密钥。WordPress 将使用这些密钥生成 cookie 名称以及 cookie 值的哈希值,这使得黑客攻击您的认证信息变得困难。尽管我们采取了多少安全预防措施,但由于 WordPress 的开源性质和第三方插件的使用,您的网站仍然有可能被黑客攻击。在这种情况下,您可以重新生成并添加这些密钥到配置文件中。这将使所有现有的 cookies 失效,因此所有用户都必须重新登录。

下两行定义了表前缀和调试模式。前缀是通过安装过程中提供的输入配置的。建议将前缀更改为除wp之外的其他任何内容,以提高安全性。默认情况下,调试模式设置为FALSE,因此您不会在网站上看到任何 PHP 错误。在开发环境中,您应该将此值更改为TRUE,以便识别代码中的错误。一旦开发完成,您应该在将文件上传到实时服务器之前将其改回FALSE。最后几行定义了文件路径,并加载设置文件。

这是wp-config.php文件的最基本版本,它足以处理基本的 WordPress 网站。

高级配置概述

作为一名开发者,您将需要从简单的 WordPress 博客构建到完全功能性的网络应用程序。因此,高级配置对于安全性、高级功能和应用程序的性能变得非常重要。让我们快速了解一下您如何使用其他配置选项修改配置文件。

保护插件和上传目录

由于 WordPress 的开源性质,每个人都知道插件和文件在您的网站中的存储位置。因此,黑客或垃圾邮件攻击者对您的网站进行攻击的过程变得更容易。作为解决方案,我们可以使用配置文件将插件和上传目录移动到不同的路径,以增加攻击者找到攻击路径的难度。

让我们添加更多配置来更改这些目录的默认位置:

define( 'WP_PLUGIN_DIR', $_SERVER['DOCUMENT_ROOT'] . '/example.com/modules/' ); 
define( 'WP_PLUGIN_URL', 'http://localhost/ example.com /modules/'); 
define( 'UPLOADS', 'modules/media' ); 

前两行将插件目录从 wp-content/plugins 移动到您根目录中的 modules 目录。由于路径已更改为根目录,攻击者将难以识别路径。这些配置仅更改目录。如果您已经在 wp-content/plugins 目录中安装了插件,您将不得不手动将它们移动到新目录。下一行将媒体上传路径从 wp-content/uploads 更改为主安装内的 modules/media 目录。

将主题目录移动的过程并不简单,因为路径是相对于 wp-content 目录配置的。因此,在您想要更改主题目录的情况下,您将不得不更改 wp-content 目录的主路径。您可以手动使用这些配置,或者使用现有的插件来帮助保护您的网站。

配置高级调试设置

之前,我们通过将 WP_Debug 设置为 TRUE 在开发环境中启用了调试。还有更多设置可以控制调试过程并提供有关我们代码中问题的更多信息。让我们了解一下开发者的一些高级选项。

脚本调试

通常,由于不同插件和主题中使用不同的脚本版本,JavaScript 文件中的冲突是一个常见问题。当使用压缩版本和合并的脚本文件时,识别这些错误是一个繁琐的过程。让我们看看用于解决这些问题的规则:

define( 'SCRIPT_DEBUG', true ); 
define( 'CONCATENATE_SCRIPTS', false ); 

第一行通过加载未压缩的文件而不是压缩版本来启用脚本调试。第二行防止多个脚本的合并,允许您跟踪错误的行。当您在 WordPress 核心文件中遇到脚本问题时,应使用这些配置规则。

记录错误和自定义 php.ini 规则

我们可以在浏览器中显示编码错误或将它们记录到单独的日志文件中。这取决于开发者的偏好,但我觉得记录错误是未来参考的理想解决方案。此外,有时我们需要更改 PHP 配置以使我们的代码工作。许多托管服务器不允许您直接编辑 php.ini 文件,因此我们可以使用 wp-config.php 文件来覆盖 php.ini 文件的默认设置。

一些托管提供商不提供更改 php.ini 文件的访问权限,您将不得不通过服务器支持请求修改。

考虑以下配置规则,通过修改 php.ini 进行错误日志记录:

@ini_set( 'log_errors', 'On' ); 
@ini_set( 'display_errors', 'Off' ); 
@ini_set( 'error_log', '/home/example.com/logs/php_error.log' ); 

首先,我们通过使用 display_errors 设置启用 PHP 错误记录并禁用在浏览器中显示错误。然后,我们定义错误将被记录的文件路径。您需要首先创建此目录,并在此规则生效之前提供必要的写入权限。

在本节中,我们简要概述了 WordPress 开发中最基本和高级的配置。还有很多其他设置可以帮助开发者,以及控制功能。您可以在 WordPress 手册中查看可用的配置及其用法,网址为codex.wordpress.org/Editing_wp-config.php。现在,我们已经有了完整的设置,可以开始基于 WordPress 的开发任务。

理解 WordPress 文件和目录结构的作用

开始 WordPress 开发并不是一个复杂任务。它涉及对主题进行细微的修改、安装和定制现有插件,以及使用可用的钩子。随着您的进步,您会遇到更复杂的需求,并期望得到未来兼容的解决方案。在不考虑未来 WordPress、插件和主题版本的情况下尝试构建这样的解决方案可能会导致噩梦。熟悉现有的 WordPress 文件/目录及其作用对于开发可维护的解决方案至关重要。在本节中,我们将查看默认 WordPress 安装中现有的文件和目录。

以下是你 WordPress 安装中典型文件和目录的截图:

图片

让我们来看看 WordPress 内部一些最重要的文件和目录:

  • wp-admin:这是 WordPress 存储与后台功能相关的文件的地方。这个目录中的文件基于admin.php文件。这些文件的主要功能包括检查管理员权限、连接到数据库以及加载管理员仪表板功能。这个目录会随着 WordPress 版本更新而升级,因此这些文件的内容会被替换。

  • wp-content:这是 WordPress 存储用户上传的文件的地方,如插件、主题和媒体文件。我们可以在不受到 WordPress 版本更新影响的情况下在这里添加额外的文件/目录。因此,这个目录中的内容不会被替换。

  • wp-includes:这是 WordPress 中最大的目录,包含超过 800 个文件,用于提供管理员仪表板以及前端功能。这个目录中的主要文件是functions.php,被认为是主要的 WordPress API。这个目录会随着 WordPress 版本更新而升级,因此这些文件的内容会被替换。

  • .htaccess:这个文件是您描述 Apache 服务器配置规则的地方。默认情况下,它将包含最少的规则。您可以根据需求手动添加配置规则。有一些插件会自动将必要的配置规则添加到这个文件中。这个文件用于 WordPress 永久链接的配置。从 WordPress 设置部分更改永久链接结构是跟踪此文件中规则更改的最简单方法。

  • index.php:此文件负责根据用户请求初始化 WordPress,并提供服务响应。

  • wp-config.php:此文件用于网站的配置,包括数据库、密钥、插件和主题目录路径。因此,尽可能保持此文件的安全性非常重要。在 WordPress 版本升级时,此文件不会被替换,因此你可以使用自己的配置。

在这里,我们查看涉及开发的最重要文件和目录。你还可以查看其他核心文件的注释,以了解其功能。

如何选择开发所需的文件和目录

WordPress 开发的流程与其他 PHP 框架相比稍微更具挑战性。在任何一个其他框架中,我们选择一个特定的框架版本,并为该特定版本开发功能。升级我们解决方案框架的未来版本完全是可选的。然而,与 WordPress 相反。我们无法长时间坚持使用特定的 WordPress 版本,尤其是由于安全问题的考虑。你们大多数人都会希望 WordPress 发布新版本后立即升级版本。因此,开发不因版本升级而中断的解决方案非常重要。基本上,我们应该能够在不触及任何我们的代码的情况下升级 WordPress。

因此,你需要首先理解的是你可以将代码文件放在哪里,以及你如何在开发中使用 WordPress 的核心文件/目录。我们在上一节讨论了 WordPress 目录内的主要文件和目录。让我们缩小范围,专注于用于自定义开发的文件和文件夹。基本上,我们必须选择不会被 WordPress 更新所替换的文件和文件夹。在我们讨论的文件和文件夹中,wp-contentwp-config.phphtaccess不受升级的影响。因此,我们的开发应该专注于这些文件和文件夹。

正如我们提到的,wp-content是存储所有用户上传文件的目录。因此,我们需要在这个目录内添加我们的代码文件。让我们探索这个目录的内容,以了解可以创建在哪里以及什么类型的文件。

以下是你wp-content文件夹内文件和文件夹的截图:

图片

上述截图包含了默认目录和根据自定义需求创建的自定义目录。让我们简要概述每个文件夹的作用:

  • plugins:这是 WordPress 存储运行网站所需所有插件的地方。在初始安装时,你会获得 Akismet 反垃圾邮件插件和 Hello Dolly 插件。之后,你可以使用这个目录添加自己的插件,以及从其他开发者那里安装现有的插件。这个目录可以被认为是 WordPress 开发的中心。

  • 主题:这是 WordPress 存储所有主题的地方,这些主题为您的网站提供设计和初始功能。初始安装包含一些默认主题,例如二十十七二十十六等。之后,您可以使用此目录添加自己的主题,以及从其他开发者那里安装现有主题。与插件不同,您在任何时候都只能从此目录中使用一个主题。

  • 上传:这是存储所有用户上传的帖子、页面和插件文件的地方。此目录在您上传第一个文件之前不会创建。通常,帖子和页面的所有媒体文件都将存储在以年/月格式的子目录结构中。您可以为处理插件和主题的文件创建无限多的子目录。

  • 语言:此目录在初始安装时不可用,当您在 WordPress 中更改语言时会创建它。因此,它对开发任务来说并不重要。

  • mu-plugins:此目录存储了网站必须使用的插件,初始安装时不可用。必须使用的插件是运行网站所必需的插件,因此无法停用。这些插件在您的 WordPress 安装的所有网站上自动启用。尽管在某些情况下这些插件很有用,但由于执行某些钩子时的某些限制,使用它们并不理想。因此,我们不会考虑将它们纳入本书的开发任务中。

  • 升级:此目录默认不可用,用于存储 WordPress 版本升级的临时文件。因此,对我们开发任务来说非常重要。

  • 缓存和日志:这些是一些由插件或主题创建的用于处理某些功能的自定义目录。我在此截图中使用它们来解释自定义文件夹的使用。您可以创建这样的自定义文件夹来处理与主题或插件相关的文件。

在快速浏览了文件和目录之后,我们可以得出结论,我们的主要关注点应该是wp-content目录内的插件主题上传目录。在这些目录中使用代码和媒体文件确保我们的解决方案不会被版本升级所清除。

WordPress 编码标准的简要概述

在编程中,编写可工作的代码并不是开发者唯一的职责。他们还需要负责编写高质量的代码,这些代码可以扩展以供未来的增强,并确保代码对其他开发者易于理解。这就是编码标准成为开发重要方面的地方。编码标准是一套针对特定编程语言的规则和约定,所有开发者都应遵循这些标准以使代码保持一致性。

我假设你们熟悉 PHP 编码标准,并准备好利用这些经验来开发 WordPress。然而,WordPress 本身有一套编码标准,其中一些规则与 PHP 标准略有不同。编码标准目前分为四个部分,称为 PHP、HTML、CSS 和 JavaScript。作为一名开发者,你需要熟悉 WordPress 编码标准,尤其是在你为 WordPress 仓库开发主题和插件时。

让我们来看看 WordPress 中使用的一些主要 PHP 编码标准:

  • 单引号和双引号:PHP 处理单引号比双引号快,因为解析器不需要检测和执行任何变量。这就是为什么对于没有评估的字符串,最好使用单引号。

  • 缩进:你应该使用制表符而不是空格来缩进代码。

  • 括号的开闭:即使对于单行评估不是必需的,你也应该始终使用开闭括号。

  • 使用空格:WordPress 建议在逗号后面,以及在逻辑、比较、字符串和赋值运算符的两侧放置空格。

  • 命名约定:你应该始终使用小写字母和下划线来命名变量、函数、动作、过滤器等。WordPress 编码标准中不推荐使用camelCase方法。

  • 动态钩子:钩子是 WordPress 特有的,因此指南建议通过将变量括在花括号中来使用插值,而不是使用连接。

这些是 WordPress 中的一些常见编码标准,你可以在codex.wordpress.org/WordPress_Coding_Standards找到完整的标准集,包括 CSS、JavaScript 和 HTML。熟悉纯 PHP 编码的开发者会注意到 WordPress 标准中的一些细微变化,例如使用大量空格,并且函数不使用驼峰命名法。因此,即使是经验丰富的开发者,要想跳出舒适区,以 WordPress 的方式编写代码,也是一个挑战。

前一节介绍了主要的 PHP 编码标准。你们大多数人应该对这些标准有一个大致的了解,因为你们已经使用过 PHP。然而,由于基于钩子的架构,WordPress 有一些特定的编码风格/约定。你们中许多人可能没有意识到这些约定,但这些约定对于构建不与其他冲突的主题和插件至关重要。

让我们快速概述一下这些约定,这些将在本书的后续章节中详细讨论:

  • 加载脚本和样式:通常,我们可以通过使用<script><link>标签在任何 HTML 页面中加载脚本和样式。WordPress 使用内置的 PHP 函数来加载脚本和样式。因此,建议您只在这些被称为wp_enqueue_scripts的操作中加载这些文件。在这些操作之外加载这些文件可能会导致与 WordPress 核心功能以及其他插件主题的冲突。

  • 加载第三方库:如jQueryUnderscore以及 Backbone.js这样的库在现代 Web 开发中经常被使用。通常,开发者有选择在他们的代码文件中包含这些库或从 CDN 加载它们以获得更快性能的选项。在 WordPress 中,由于涉及许多不同开发者的插件,这种方法是不可行的。因此,我们需要加载库的一个通用版本以避免代码冲突。所以,我们必须使用与 WordPress 内置的库版本。

  • 使用适当的执行事件:在纯 PHP 编程中,我们可以根据我们的喜好在任何地方和任何时候执行代码。WordPress 使用基于钩子的事件驱动架构,因此代码以预定义的方式通过事件执行。除非我们使用正确的事件,否则我们的代码可能会与其他代码冲突。例如,我们需要在init钩子内或之后访问$_POST变量。在必要的事件之前尝试访问这些数据可能会导致代码中出错。

我们简要介绍了开始 WordPress 开发所需的编码标准和约定。通常,对于特定客户的开发项目来说,这已经足够了。然而,有时您可能需要进一步的知识,尤其是在插件和主题开发的事件驱动约定方面。我们将在接下来的章节中介绍必要的编码约定。

摘要

本章的目标是准备好环境并了解复杂开发任务的前提条件。我们探讨了将我们的代码集成到 WordPress 中的技术,以及确定开发中最常用的模块。

我们简要概述了开发环境的安装和配置,同时确定了简化我们任务的必要工具。最后,我们了解了 WordPress 文件和目录结构,以便我们的解决方案能够以未来兼容的方式提供。

在第二章“管理数据库结构、存储和检索”中,我们将探讨现有的 WordPress 数据库结构及其使用,同时讨论自定义数据库表的实际应用。

第二章:管理数据库结构、存储和检索

数据库是存储应用程序数据的主要位置。WordPress 提供了一个内置的数据库,其中包含预定义的表,与传统从零开始设计的 Web 应用程序相比,WordPress 的数据库设计更为成熟。熟悉现有数据库表的优势和劣势是开发高质量主题和插件的关键。

在本章中,我们将学习现有数据库表在 WordPress 中的功能和限制。您将了解现有数据库表及其作用,同时了解如何在常见功能(如文章创建、用户创建、媒体上传等)中管理数据。您还将了解在开发中自定义表的使用以及使用查询函数检索数据。

在本章中,我们将涵盖以下主题:

  • 理解默认数据库表的作用

  • 跟踪和理解主要功能数据库使用情况

  • 创建和管理自定义表

  • 实施 CRUD 操作

  • 多站点数据库结构的简要概述

到本章结束时,您将具备最大化使用现有数据库表进行开发的知识,同时为高级用例使用自定义表。

WordPress 数据库简介

规划和设计数据库的过程是任何项目最关键的阶段之一。一个良好的数据库设计可以简化未来增长的过程,同时提高数据存储和检索的性能。通常,识别实体及其之间关系是开发团队的职责。另一方面,WordPress 是一个 CMS,因此提供现有的数据库来处理核心功能。所以,在大多数情况下,规划数据库的过程并不是作为开发者的责任。相反,开发者面临着一个更艰巨的任务,即理解核心表并管理数据需求及其限制。当然,你可以为任何需求创建和使用自己的数据库表。但这样做就消除了在 WordPress 之上开发的目的。

WordPress 数据库目前支持 MySQL 和 MariaDB,其中 MariaDB 是最受欢迎的选择。然而,大多数现有的 WordPress 站点仍在使用 MySQL。在成功安装后,WordPress 将创建十一个数据库表来满足核心功能。自其诞生以来,向后兼容性一直是 WordPress 团队的首要任务之一。因此,在不久的将来,您可能不会看到这十一个表有重大变化。

理解内置数据库表的作用

WordPress 数据库结构旨在存储核心功能所需的最小数据量。因此,开发者需要彻底了解现有表,以便有效地使用它们来构建灵活的网站。让我们看一下最新 WordPress 版本的实体关系图:

如您所注意到的,所有现有的表与其他表的关系不超过一两个,因此理解和操作数据库变得是一个更简单的任务。现在,我们将查看每个数据库表及其重要列、其角色以及它是如何与其他表协同工作的。您需要参考前面的图表来可视化我们讨论中的要点。

与帖子相关的数据库表

直接涉及帖子的数据库表有三个,而处理与帖子相关数据的表有六个。许多现有的 WordPress 网站要么是只有几个页面的基本网站,要么是拥有数百篇帖子的博客。因此,大多数网站的数据都由这六个数据库表管理。让我们来看看与帖子直接相关的两个表。

wp_posts

这是许多 WordPress 网站和应用程序的主要数据存储位置。此表包含帖子、页面和自定义帖子类型的主要数据。此表包含二十三列。让我们确定这个表中一些最重要的列:

post_author 在这里您可以找到创建帖子或页面的用户的 ID。此列用于与wp_users表建立关系。
post_content 帖子/页面的完整内容存储在这个列中。因此,这个列中的数据可以从几个词到数千个词和图片不等。
post_title 帖子/页面的标题存储在这个列中。它用于在后台识别页面以及在前端突出显示标题。
post_excerpt 此列中的数据是可选的,用于保存帖子/页面的摘要。通常,这用于帖子列表(存档)页面,以展示帖子的概要,并鼓励用户阅读整个帖子。
post_status WordPress 特定的帖子状态存储在这个列中。这些状态定义了帖子生命周期中的一个步骤。默认状态包括发布、待审、私有、未来、继承、垃圾箱、草稿和自动草稿。大多数这些状态都是不言自明的。然而,您可能需要关于继承和自动草稿状态的解释。WordPress 提供了在数据库中存储帖子修订版本的能力。当我们更新帖子时,数据库中的先前帖子数据自动成为标记为继承状态的修订版本。此外,WordPress 会在特定的时间间隔自动保存帖子,这些修订版本被标记为自动草稿
post_password 此列存储受密码保护的帖子或页面的密码。
post_parent 此列存储父帖子的 ID。WordPress 支持附件、页面和帖子修订版本的子帖子。
guid 帖子/页面的完整 URL 存储在这个列中。
post_type 此处存储具有唯一键的类型文章。默认文章类型包括文章、页面、修订、附件和nav_menu_item。除了这些类型之外,您可以使用自定义文章类型创建任何新类型。

通常,此表旨在用于内置类型,如文章、页面、菜单和附件。随着自定义文章类型的引入,此表被广泛用于存储数据,如产品、论坛主题、属性以及您能想到的任何东西。

wp_postmeta

这是文章的二级存储,其中存储与文章和页面相关的可选数据。此表包含四个列。让我们确定此表中一些最重要的列:

post_id 这是您找到包含这些附加数据的文章 ID 的地方。此列用于与wp_posts 表形成关系。
meta_key 所有附加文章数据都保存为键值对。此列定义了文章数据的标识。应使用字母数字字符、下划线和破折号为此列使用唯一键。默认情况下,WordPress 存储文章和页面的编辑锁定信息。随着您向文章或创建菜单添加数据,您将看到更多带有下划线前缀的键存储。这与我们作为开发者所使用的不同,我们使用不同的表存储这些数据,并将这些键作为列。
meta_value 此列包含指定元键的相关文章数据。通常,这些值以纯文本或序列化值的形式存储。

通常,此表用于存储从文章编辑屏幕添加的管理级别值和自定义字段。在具有自定义文章类型的大型网站上,此表变得至关重要。在这样的网站上,我们可以存储数据,如在线商店中的产品选项、房地产网站中的房产详情以及工作管理网站中的工作详情。

术语相关表格

在本节中,我们可以找到四个表格,旨在处理特定文章的分类。在 WordPress 中,我们使用术语taxonomoy来表示这些分类。默认情况下,我们有两个分类法,即分类和标签。这些表格能够处理这两种分类法类型以及您自己的分类法类型。让我们详细查看这四个表格。

wp_terms

这用于存储分类法的选项。您应该了解 WordPress 文章中的分类和标签。我们称那些类型为分类法。分类法内的选择称为术语。分类是一个分类法,我们创建的诸如健康、体育和旅行之类的选择被认为是术语。此表包含四个列。让我们确定此表中一些最重要的列:

name 存储在网站前端显示的术语标题
slug 存储术语的唯一标识符

WordPress 初始安装生成了两个分类法,称为分类和帖子标签。然而,默认情况下没有创建任何术语,只包含未分类作为术语。我们可以使用这个表来创建帖子标签或分类选择。我们还可以在自定义场景中使用它来创建术语,如 WordPress、PHP 和 jQuery,用于工作管理网站中的工作技能。

wp_termmeta

这是存储术语数据的二级位置。这个表由四个列组成,并且使用键值对的方式与其他元数据表类似工作。

默认情况下,这个表不常使用。你可以使用这个表来存储你自己的术语的附加细节。假设你有关于文章的术语,如体育、健康、旅行。你想要使用不同的设计来显示每个类别的存档页面。在这种情况下,你可以在术语元表中存储 CSS、图片、模板,并使用这些信息来加载设计。

wp_term_relationships

这曾经用于在帖子与术语之间建立关系。这个表由三个列组成,并且只使用 ID 作为数据。让我们来识别这个表中一些最重要的列:

object_id 存储与分类相关联的帖子或自定义帖子类型的 ID
term_taxonomy_id 存储分类法的 ID

这是一个纯粹关联表,通过 ID 连接两个表。作为开发者,你只需要分配关系,这个表不根据站点类型有不同的用途。

wp_term_taxonomy

这个表存储了称为分类法的分类类型。这个表由六个列组成。让我们来识别这个表中一些最重要的列:

term_id 这是术语的 ID,用于与wp_terms表建立连接。
taxonomy 这是分类法的唯一 slug。默认分类法是分类和post_tag
parent 存储在网站中使用子术语时的父术语。
count 存储属于此分类法的帖子数量和自定义帖子类型。

这个表包含默认的分类和post_tag分类法,用于正常 WordPress 帖子。我们可以在工作管理网站上创建如工作技能这样的自定义分类法,在线商店中的产品类型,房地产网站中的属性特征等等。

用户相关表

用户数据通过现有数据库中的两个表进行管理。默认情况下,我们在这些表中存储了关于用户的基本详细信息。现代网站引入了个性化内容的概念,因此用户管理变得越来越重要。所以,你会在许多网站上看到这些表的大量使用。让我们详细看看这两个表中的每一个。

wp_users

这是用户的主要存储位置,由十个列组成。让我们来识别这个表中一些最重要的列:

user_login 用作网站的登录名。您可以使用自己的实现允许用户更改它。然而,此值需要是唯一的。
user_pass 存储用户的加密密码。
user_email 用作用户的电子邮件。由于 WordPress 允许用户使用电子邮件登录,因此此值对于每个用户都必须是唯一的。
user_registered 存储注册日期,常用于对用户列表进行排序或在基于会员的网站上分配会员期限。

此表中的数据不会根据您正在开发网站的类型而改变,因此您只需要了解在此表中插入、更新和删除用户的知识。

wp_usermeta

用作用户数据的次要位置。默认情况下,它跟踪名称、描述、能力等。此表由四列组成。以下表格中我们将识别一些最重要的列:

user_id 这是您找到此数据所属用户 ID 的地方。此列用于与wp_users表建立关系。
meta_key 所有附加用户数据都保存为键值对。此列定义了用户数据的标识。应使用字母数字字符、下划线和短横线为该列使用唯一的键。这与我们作为开发者所使用的不同,我们使用不同的表来存储这些数据,并将这些键作为列。
meta_value 此列包含指定元键的用户相关数据。通常,这些值以纯文本或序列化值的形式存储。

除了默认选项之外,我们还可以使用此表来存储简单的自定义用户数据,如出生日期、国家、城市,以及高级用户数据,如兴趣和简介图片。

选项和评论表

本节由三个用于配置和评论的表组成。让我们详细查看这些表。

wp_options

此表存储 WordPress 的所有内置和自定义设置。这是另一个类似于wp_postmetawp_usermeta的表,其中使用键值对来保存数据。此表中的默认选项包括网站 URL、网站描述、日期格式等。

开发者可以使用此表来存储他们主题或插件设置。通常,插件或主题设置以序列化值的形式存储在此表的单行中。然而,你可以根据你的偏好存储尽可能多的选项。

wp_comments

此表存储文章、页面和自定义文章类型的评论。此表由十五列组成。以下表格中我们将识别一些最重要的列:

comment_post_ID 存储评论所属的文章、页面或自定义文章类型的 ID。
comment_author 保存创建此评论的用户名称。
comment_content 用于保存用户添加的评论。此列可以包含纯文本值,也可以根据网站允许的选项包含 HTML。
comment_approved 在大多数网站上,评论在公开显示之前需要经过审核。此列存储关于评论是否已批准或待批准的布尔状态。
comment_type 存储评论类型。默认评论类型包括评论、pingbacks 和 trackbacks。我们还可以使用自定义评论类型。
user_id 创建评论的用户 ID。此列用于与wp_users表建立连接。

通常,此表用于存储帖子、页面和自定义帖子类型的基本评论。然而,我们可以通过创新地使用评论来超越常规。我们可以使用评论作为基于问题的网站(如 stackoverflow)的答案、论坛主题中的用户消息或在线产品市场中的评论。

wp_commentmeta

这用于存储额外评论数据的辅助位置。此表由四个列组成,其工作方式类似于 WordPress 中的其他元数据表。

此表没有默认用途。我们可以使用此表来存储额外的数据,例如产品评论网站中的评分、问答网站中的验证状态等。

在本节中,我们探讨了 WordPress 中的所有十一个默认数据库表及其在开发中的作用。您必须了解这些表中的每一列及其可能值,因为大多数 WordPress 开发都是在核心功能之上进行的。

理解数据库使用的重要性

在当今世界,许多开发者对 WordPress 核心功能缺乏深入了解,但仍然能够将您的需求转化为可行的解决方案。然而,这些解决方案通常基于假设,而不是了解底层的具体过程和数据使用情况。

跟踪数据库使用情况并了解任何给定过程的精确数据需求对于构建灵活且可扩展的解决方案,这些解决方案能够完美地与其他网站部分集成至关重要。让我们确定一些需要完美了解 WordPress 功能中数据使用情况的用例:

在前端构建现有功能

WordPress 为管理员级用户提供了一个功能强大的后端,其中包含许多功能。帖子创建和用户管理是后端的一些顶级功能。相比之下,现有的 WordPress 前端功能相对较弱,主要专注于显示数据。因此,将后端功能转移到前端是一个常见的需求,允许权限较低的用户从前端创建内容,而不必提供对后端的访问权限。

在这种情况下,我们需要了解后端过程中使用的确切数据,以便我们可以在前端过程中模拟它。通常,开发者对这些过程中存储的所有数据的必要知识不足。因此,此功能的客户端方面工作得很好,但由于数据不足,后端出现了问题。

让我们考虑一个简单的场景,即你希望让用户通过你网站上的表单上传文件,并将文件存储在 WordPress 媒体库中。乍一看,你会发现媒体文件存储在wp_posts表中,并包含必要的数据。因此,你将数据保存到wp_posts表中。然而,你可能没有注意到文件的其他元数据存储在wp_postmeta表中。因此,你的前端实现可能会破坏依赖于这些数据的核心功能或其他插件。

定制现有插件

在 WordPress 网站开发中使用其他现有插件几乎是肯定的。通常,这些插件并不直接满足我们的需求,因此定制成为明显的选择。了解数据的使用变得尤为重要,因为这些定制插件与核心功能相比,缺乏文档和代码质量。在不了解其数据使用的情况下定制插件,与核心功能相比可能会导致重大冲突。

在这种情况下,我们需要对文档进行彻底检查,以手动跟踪数据库使用情况,并在可能的情况下获得开发者的支持来构建定制解决方案。让我从一个我们自己的插件中举一个例子。我们有一个用户资料管理插件,其中我们在wp_usermeta表中存储额外的用户详细信息。由于在wp_usermeta表中搜索每个用户的数百行是一个繁琐的任务,所以我们也将所有附加数据存储在单行中作为搜索缓存。我们见过许多开发者定制插件以更新用户数据,却不知道需要搜索缓存值。尽管用户数据已更新,但由于缺少搜索缓存值,用户不会出现在搜索结果中。这种场景在插件定制中很常见,必须使用数据跟踪方法来识别和避免它们。

我们已经研究了跟踪和了解数据使用成为关键的最常见情况。你可能会发现其他不太常见的情况,其中数据的一致性变得至关重要。

如何跟踪 WordPress 中的数据库使用情况

我们确定了跟踪和了解核心功能以及定制功能的数据需求的重要性。接下来,问题是我们如何跟踪和理解数据以避免潜在的冲突。因此,我们需要一种记录/显示用户请求中运行的所有查询的方法。有无数种跟踪数据库查询的方法,包括付费的企业级工具和命令行。由于你刚开始 WordPress 开发,我们将探讨一些更简单且有用的方法,这些方法可以扩展你的知识。

使用 MySQL/MariaDB 日志文件

这是一种作为开发者必须知道的一个简单且重要的方法。MySQL 和 MariaDB 都允许我们创建一个日志文件,我们可以跟踪数据库中连接、初始化和执行查询的所有步骤。首先,我们需要向 MySQL/MariaDB 的ini文件添加一些配置。由于我们正在使用 WAMP 来完成本书的开发任务,你可以点击 WAMP 图标,转到 MySQL 部分,并打开my.ini文件。你可以使用相同的过程来处理 MAMP 或其他提供 MySQL/MariaDB 配置的工具。

在 Linux 服务器上,MySQL/MariaDB 的配置文件通常是/etc/mysql/my.cnf;如果你找不到它,尝试使用sudo updatedb ; locate my.cnf来定位它。

接下来,你可以在文件末尾添加以下行来配置日志文件输出:

[mysqld]
log-output = FILE
general-log = 1
general_log_file=C:/wamp/logs/general-query.log

在 Linux 服务器上,你可以使用/var/log/mysql/mysql-debug.log而不是C:/wamp/logs/general-query.log作为日志文件路径。修改后,像这样重新启动 MySQL/MariaDB 服务器:/etc/init.d/mysql restart

一旦添加了规则,你应该保存并重新启动 WAMP 服务器。现在,你可以刷新你的 WordPress 站点来测试用户请求。

请记住,许多托管提供商不允许访问 MySQL/MariaDB 日志。

然后,在指定位置打开日志文件,你将看到在该请求中执行了许多查询。因此,你可以使用这种方法通过清除文件、执行请求和查看更新的日志文件来跟踪数据库更改。这种方法的一个限制是,它显示了包括大量SELECT查询在内的所有查询。由于我们只想跟踪INSERTUPDATEDELETE查询,这并不是一个完美的方法。

比较数据库备份

这是一种比较更改的简单方法之一,而不是通过审查执行的查询来识别它们。在这个方法中,我们使用phpMyAdmin进行数据库备份,执行用户请求,然后进行另一个数据库备份。然后,我们可以使用像MELDmeldmerge.org/)这样的文件比较工具来检查请求期间数据库是如何变化的。这个过程的一个限制是,你需要手动进行数据库备份,对于大型数据库,比较可能会变得耗时。

创建手动查询记录器

这是我们创建自己的查询记录器并指定需要跟踪哪些查询的理想方法。让我们看看以下代码的实现:

function wpc_filter_query( $query ) {
  if (strpos($query, 'INSERT') !== false || strpos($query, 'UPDATE') !== false || strpos($query, 'DELETE') !== false)
    error_log('#### $query', 0);

  return $query;
};
add_filter( ‘query', ‘wpc_filter_query', 10, 1 );

上一段代码使用了 WordPress 查询过滤器来过滤用户请求中执行的所有查询。一个完整的sql查询被解析为一个参数,我们过滤INSERTUPDATEDELETE查询。然后,我们可以将查询记录到指定的文件中。在这里,我们为了说明目的,将查询添加到 PHP 错误日志中。你可以使用wamp/logs/php_error.log中的日志文件来检查预期的查询。你可以将此代码包含在主题的functions.php文件中,直到我们开始插件开发。

你可以选择一种首选方法或外部数据库工具来跟踪任何用户请求的数据库使用情况,并了解不同过程中必须使用的数据。

跟踪常见 WordPress 功能的数据库使用情况

到目前为止,你应该能够配置和跟踪后端和前端用户请求的数据库使用情况。在本节中,我们将探讨 WordPress 最常用功能中的数据库使用情况,并了解存储指定数据的需求。

帖子创建

由于这是 WordPress 网站中最常用的功能,你需要深入了解数据库的使用。首先,你需要进入帖子 | 新建部分,清除现有的查询日志。然后,你可以添加数据并发布帖子以跟踪数据库使用情况。帖子创建屏幕提供了广泛的功能,因此数据库使用可能根据你使用的选项数量而变化。让我们通过使用帖子创建中最常见的选项来跟踪查询:

INSERT INTO `wp_posts` (`post_author`, `post_date`...........) VALUES (1, '2018-07-13 08:43:22'.....)
UPDATE `wp_posts` SET `post_author` = 1, `post_date` = '2018-07-13 08:43:30' ..... WHERE `ID` = 48
INSERT INTO `wp_postmeta` (`post_id`, `meta_key`, `meta_value`) VALUES (48, 'test field', '1')
INSERT INTO `wp_term_relationships` (`object_id`, `term_taxonomy_id`) VALUES (48, 23)
UPDATE `wp_term_taxonomy` SET `count` = 3 WHERE `term_taxonomy_id` = 23
INSERT INTO `wp_term_relationships` (`object_id`, `term_taxonomy_id`) VALUES (48, 24)
UPDATE `wp_term_taxonomy` SET `count` = 4 WHERE `term_taxonomy_id` = 24

日志文件将包含大量查询。其中许多是由于 WordPress 自动保存过程和用于 transients、缓存和管理员级别选项的临时数据存储而生成的。我们已经省略了这些查询,并包括了理解帖子创建所必需的基本查询。

在这个场景中,我们分配了现有的分类、标签和自定义字段,以及主要帖子内容。正如你所见,这个过程从向wp_posts表插入查询开始,该表存储所有主要帖子特定数据。当你加载帖子创建屏幕时,此查询立即执行。然后,当你点击发布按钮时,WordPress 更新已创建的帖子。下一个查询将自定义字段数据添加到wp_postmeta表。如果你有多个自定义字段,你会看到多个这样的查询,每个字段一个。

接下来,两个查询将帖子与分类相关联,并更新该特定分类的帖子计数。剩下的两个查询使用与分类相同的流程来处理标签。在这里,我们看到了在帖子创建中使用四个数据库表。我们不仅可以使用现有的分类和标签,还可以创建新的来分析帖子创建中所有六个帖子相关表的使用情况。我建议你进一步通过更改帖子状态、添加特色图片、更改帖子格式和更改帖子可见性来跟踪数据库使用情况。

页面创建

这与帖子创建过程类似,我们使用页面 | 新建部分来加载页面创建屏幕。与帖子创建过程相比,页面创建的选项更少。因此,我们应该注意到在此过程中执行的查询数量有所减少。让我们看看执行的查询:

INSERT INTO `wp_posts` (`post_author`, `post_date`, ........) VALUES (1, '2018-07-13 09:09:15',.......)
UPDATE `wp_posts` SET `post_author` = 1, `post_date` = '2018-07-13 09:09:31', ..... WHERE `ID` = 49

正如我们预期的,这个过程中只执行了两个查询。第一个查询在页面加载时创建页面,下一个查询在用户点击发布按钮时更新内容。因此,与帖子相比,作为开发者管理页面更容易。

用户创建

许多网站只由一个或两个管理员组成,公共注册功能被阻止。然而,由于需要个性化内容而不是所有用户都使用相同的内容,现代网站对用户注册的需求正在增加。因此,前端注册成为一种常见的实现方式。让我们通过从后端用户 | 添加新用户部分创建用户来跟踪数据库使用情况:

INSERT INTO `wp_users` (`user_pass`, `user_email`, `user_url`, `user_nicename`, `display_name`, `user_registered`, `user_login`)
VALUES ('$P$B.NS/td6lI7ug01eNW64p.', 'testuseremail@gmail.com', '', 'testuser', 'john doe', '2018-07-13 09:14:21', 'testuser')
INSERT INTO `wp_usermeta` (`user_id`, `meta_key`, `meta_value`) VALUES (4, 'nickname', 'testuser')
INSERT INTO `wp_usermeta` (`user_id`, `meta_key`, `meta_value`) VALUES (4, 'first_name', 'john')
INSERT INTO `wp_usermeta` (`user_id`, `meta_key`, `meta_value`) VALUES (4, 'locale', '')
INSERT INTO `wp_usermeta` (`user_id`, `meta_key`, `meta_value`) VALUES (4, 'wp_capabilities', 'a:1:{s:10:\"subscriber\";b:1;}')

该过程从向wp_users表插入一个新用户开始,包括如用户名、电子邮件、密码和注册日期等主要细节。用户的附加细节以键值对的形式存储在wp_usermeta表中。这又不同于我们作为开发者所使用的,我们将这些数据存储在不同的表中,这些键作为列。我们可以看到许多用于存储如名称、描述、语言等细节的 INSERT 查询。在这些值中,localewp_capabilities是关键值,其中我们定义了用户的语言并维护用户角色和能力。开发者可以使用wp_usermeta表来存储他们网站所需的定制数据。

媒体上传

在现代网站上,我们很少只看到基于全文的内容。大多数帖子页面都是设计用来使用图片和视频的。因此,媒体管理器成为许多网站所有者必须使用的功能。使用和开发 WordPress 媒体上传器可能与从头开始构建文件上传表单有所不同。让我们回顾一下使用媒体 | 添加新内容部分进行媒体上传的数据库使用情况:

INSERT INTO `wp_posts` (`post_author`, `post_date`, ...........) VALUES (1, '2018-07-13 09:16:26',....)
INSERT INTO `wp_postmeta` (`post_id`, `meta_key`, `meta_value`) VALUES (50, '_wp_attached_file', '2018/07/test.png')
INSERT INTO `wp_postmeta` (`post_id`, `meta_key`, `meta_value`) VALUES (50, '_wp_attachment_metadata', 'a:5:{s:5:\"width\";i:512;s:6:\"height\";i:512.....')

在传统开发中,我们使用一个单独的表来存储上传文件的相关细节。然而,WordPress 通过将媒体视为一种帖子类型采取了不同的方法。这可能不是理想的实现方式,因为大量的媒体文件可能会影响加载重要帖子页面的性能。但我们需要习惯这个过程。因此,主要媒体文件细节存储在wp_posts表中。除此之外,在wp_postmeta表中创建了两个元字段来处理路径和元数据,如标题、宽度、高度。作为开发者,我们可以使用 postmeta 表来存储项目中媒体文件所需的大量附加数据。

我们使用了四种最常用的场景来跟踪数据库使用情况。你可能已经理解了保持数据一致性和使用数据库跟踪以实现这一目标的重要性。在这里,我们跟踪了默认 WordPress 安装中的查询。随着我们使用越来越多的插件,这些场景变得极其复杂。因此,我建议你使用这种技术来跟踪流行 WordPress 插件的功能数据,并了解我们如何在不破坏其他部分的情况下自定义功能。

多站点数据库结构概述

多站点网络是一个允许在同一 WordPress 安装中创建多个站点的功能,同时共享可用资源。从纯 PHP 开发转向 WordPress 的开发者可能不熟悉这个概念。在多站点中,整个数据库结构会发生变化,这是我们不能预料的。因此,如果你计划开发兼容多站点的插件和主题,了解多站点数据库的工作方式是很重要的。以下是将多站点与单站点安装相比的简单说明:

图片

如你所见,数据库中已添加了六个新表,现有表的数量也有所增加。这张图仅说明了在多站点环境中表之间的连接方式。然而,这并不是表之间的精确关系图。让我们在先前的图中识别六个新表的作用:

  • wp_site: 它是主要数据库表,其中存储了所有网络详情。WordPress 允许你在同一安装中创建多个网络。然而,我们很少看到同一安装中有超过一个网络的实际应用。这意味着这个表将只有一个记录来存储网络详情,例如网络域名和路径。

  • wp_sitemeta: 它存储网络选项和设置,其工作方式类似于wp_options表。

  • wp_blogs: 它是网络中站点的首要存储位置。由于你正在运行多站点网络,这个表将包含两个额外的记录,用于存储站点详情,包括域名、路径、创建日期和特定站点的状态。

  • wp_blog_versions: 它存储网络中站点的当前数据库版本。WordPress 在版本升级时需要特定的数据库版本。在这种情况下,这个表将用于跟踪数据库版本。

  • wp_registration_log: 它存储网络中每个站点的管理员用户详情。

  • wp_signups: 它存储通过 WordPress 注册过程添加的新用户以及相应站点的元数据。这些数据也存储在相应站点的wp_users表中。

我们简要介绍了多站点特定的表。让我们看看在其他多站点环境中其他表是如何连接的。习惯于使用传统数据库概念(如规范化)的开发者可能会发现理解并适应 WordPress 处理多站点的方式比较困难。通常,我们设计数据库表时,会以必要表之间的关系连接一切。然而,在这种情况下,我们看到一些表没有与其他表连接,而一些表则没有表关系地与其他表连接。因此,了解这些表在多站点中的工作方式是很重要的。

多站点网络的目的在于共享 WordPress 安装、主题和插件。然而,我们仍然需要不同的数据库表来处理网络中每个站点的数据。WordPress 在多站点网络中创建了同一数据库表的多个版本。因此,默认表被分配给多站点网络中的第一个站点。网络中新创建的站点将拥有与wp_2wp_3等前缀相同的表集。插图显示这些核心表与wp_blogs表相连。然而,wp_blogs与其他表之间没有关系。相反,当站点加载时,WordPress 会识别加载的博客 ID,然后使用具有博客 ID 前缀的表。网络中的每个站点在共享 WordPress 安装、插件和主题的同时,使用具有博客 ID 前缀的独立数据库表。

在开发过程中,多站点结构可能不会成为主要问题,除非你不遵循推荐的数据库访问方式。然而,了解每个站点的数据存储位置对于测试和解决冲突非常重要。

创建和管理自定义表格

默认的 WordPress 数据库可以通过任意数量的自定义表来扩展,以满足我们项目的需求。我们唯一需要考虑的是在现有表上创建自定义表。创建自定义表有两个主要原因:

  • 匹配数据到现有表的难度:现有的数据库表足够灵活,可以处理许多常见需求。然而,在某些情况下,我们需要足够明智地选择自定义表。假设我们有一个需要存储用户以前工作历史的要求。如果我们考虑现有表,我们只能将这个要求匹配到wp_usermeta表作为键值对。然而,使用具有多个值的相同元键来实施这些数据的适当搜索是非常困难的。在这种情况下,我们需要一个自定义表来创建预定义的列,并简化用户工作搜索的过程。

  • 数据量增加:文章表在 WordPress 站点中扮演着重要角色。当涉及到大型网站或具有大量数据的网站和应用时,不建议将所有数据都保存在文章表中。假设我们正在构建一个创建数百万订单的产品目录。将订单详情作为自定义文章类型存储在文章表中并不是理想的实现方式。在这种情况下,由于数据集很大,文章表将失去控制。同样的理论也适用于现有的元数据表。在这些情况下,将不同的数据集分离到它们自己的表中,以提高性能并保持事物可管理,是明智之举。

现在,我们可以看看如何与自定义数据库表一起工作的过程。

创建自定义表格

在纯 PHP 开发中,我们在进入实现阶段之前会手动创建数据库表。在基于 WordPress 插件的架构中,我们肯定在项目的后期阶段可能需要使用插件来创建自定义表格。通过插件创建自定义表格涉及 WordPress 推荐的一定预定义的程序。由于表创建是一个一次性任务,我们应该在插件激活或安装时实现此过程。让我们看看创建自定义数据库表的代码:

function create_custom_tables() {
  global $wpdb;
  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
  $custom_table = $wpdb->prefix.'user_job_history'; 
  if($wpdb->get_var("show tables like '$custom_table'") != $custom_table) {
    $sql = "CREATE TABLE $custom_table (
              id mediumint(9) NOT NULL AUTO_INCREMENT,
              time datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
              user_id mediumint(9) NOT NULL,
              job_title mediumint(9) NOT NULL,
              job_description longtext NOT NULL,
              job_period var varchar(255) NOT NULL,
              UNIQUE KEY id (id
                );";
    dbDelta( $sql );
  } 
}

首先,我们必须包含upgrade.php文件以使用dbDelta函数。接下来最重要的事情是使用数据库表的名称前缀。默认情况下,WordPress 为所有表创建一个名为wp_的前缀。我们应该在第一章,WordPress 开发简介中提到的安装过程中更改前缀。使用现有前缀以保持一致性并避免在多站点场景中出现问题是很重要的。接下来,我们必须使用show tables查询检查数据库表的存在。最后,你可以定义你的表创建查询,并使用dbDelta函数在数据库中实现它。

查阅创建表查询的指南codex.wordpress.org/Creating_Tables_with_Plugins,因为dbDelta函数在某些场景中可能很棘手。

我们应该仅使用插件安装/卸载来创建和删除自定义表格,以避免不必要的查询。由于我们尚未开始插件开发,你需要等待本书的后续章节来了解如何以及在哪里使用此代码。

我们探讨了 WordPress 网站上自定义表格的必要性。尽管自定义表格在 WordPress 中为你提供了更多的灵活性,但正如以下所列出的,将会有相当多的限制:

  • WordPress 默认备份将不包括自定义表格。

  • 没有内置的函数用于访问数据库。所有查询、过滤和验证都需要从头开始使用现有的 $wpdb 变量来完成。

  • 显示这些表格数据的用户界面需要从头开始创建。

因此,在所有可能的情况下,你应该避免创建自定义表格,除非它们在你的应用程序的上下文中具有优势。

在现有表上执行基本的 CRUD 操作

WordPress 提供了用于访问现有数据库表的优化内置方法。因此,我们不需要编写自己的查询。让我们看看基本的创建、读取、更新、删除CRUD)操作是如何在现有表上执行的。

插入记录

所有现有表都包含用于创建新记录的预构建插入函数。其中一些函数使用预定义的参数集,而其他函数接受信息数组以插入记录。让我们看看一些常用的插入函数:

wp_insert_post wp_posts 表中创建新帖子或页面。如果用于现有帖子,它将更新现有记录。接受一个包含所有帖子详细信息的数组类型参数。
add_option 如果不存在,则在 wp_options 表中创建新选项。接受键和值作为参数。
wp_insert_user wp_users 表中创建新用户。接受一个包含所有帖子详细信息的数组类型参数。

更新记录

所有现有表都包含用于更新现有记录的预构建更新方法。以下列表说明了几个内置的更新函数:

update_post_meta 这将在 wp_postmeta 表中创建或更新关于帖子的额外详细信息。接受帖子 ID、键和值作为参数。
wp_update_term 这将在 wp_terms 表中更新现有术语。接受术语 ID 和分类法作为参数。
update_user_meta 根据用户 ID 在 wp_usermeta 表中更新用户元详细信息。接受用户 ID、键和值作为参数。

删除记录

我们在现有表的删除记录方面有类似的方法,就像更新记录一样。以下列表说明了几个内置的删除函数:

delete_post_meta 这使用指定的键在 wp_postmeta 表中删除自定义字段。接受帖子 ID、键和值作为参数。
wp_delete_post 这从 wp_posts 表中删除现有帖子、页面或附件。接受帖子 ID 作为参数。
delete_user_meta 这从 wp_usermeta 表中删除匹配用户元数据的记录。接受用户 ID、键和值作为参数。

选择记录

如同往常,有一组内置函数用于从现有表中选择记录。以下列表包含了一些数据选择函数:

get_posts 根据传递的参数从 wp_posts 表中检索帖子作为数组。此外,我们还可以使用带有必要参数的 WP_Query 类以面向对象的方法从 OOP 获取帖子列表。接受一个包含所有帖子详细信息的数组类型参数。
get_option 这从 wp_options 表中检索给定键的选项值。接受一个键参数。
get_users 这从 wp_user 表中检索用户列表作为数组。接受一个包含所有帖子详细信息的数组类型参数。

大多数对现有表的数据库操作都可以使用这些内置函数执行,并且很少需要创建自己的查询。

在自定义表上执行基本的 CRUD 操作

WordPress 提供了一个名为wpdb的内置类来处理数据库查询。这个类位于wp-includes目录中。wpdb类将在你的插件和主题中作为全局变量可用,并提供访问 WordPress 数据库中所有表的方法,包括自定义表。使用这个类进行查询增加了额外的安全层,同时也优化了数据库查询。

使用wpdb类进行 CRUD 操作非常简单,它提供了内置的方法。关于如何使用wpdb类的完整指南可以在codex.wordpress.org/Class_Reference/wpdb找到。

基本上,没有内置的方法可以直接使用函数访问自定义表,因此必须使用wpdb类来处理自定义表。

插入记录

wpdb类提供了一个内置的插入函数来将记录插入到自定义数据库表中。因此,我们需要使用它以提高性能,而不是从头开始编写INSERT查询。让我们看看插入函数的语法:

$wpdb->insert(
  'table',
  array( 'column1' => 'value1', 'column2' => 'value2' ),
  array( '%s', '%d' )
);

第一个参数接受表名。你需要指定带有 WordPress 数据库前缀的自定义表名。下一个参数使用列名和相应值的数组。最后一个参数定义了之前数组中定义的字段的正确格式。在成功时,我们可以获取受影响的行数,在失败时返回 false。

更新和删除记录

这些操作与插入类似,通过使用wpdb类的更新和删除函数来实现。让我们看看语法:

$wpdb->update(
  'table',
  array( 'column1' => 'value1' ),
  array( 'ID' => 1 ),
  array( '%s' ),
  array( '%d' )
);

在这个方法中,将条件作为数组传递给第三个参数,将 where 子句中列的格式作为第五个参数。其他参数与插入函数完全相同:

$wpdb->delete( 'table', array( 'ID' => 1 ), array( '%d' ) );

在这种方法中,我们只需要将删除条件作为一个数组以及相应列的格式传递。前面的查询将删除 ID 等于1的记录。

选择记录

我们可以将查询传递给各种wpdb类函数,如get_resultsqueryget_var,并获取结果数据集。以下部分说明了这些函数的使用:

$wpdb->get_results( 'select query' ) 这可以用来从任何数据库表中选择一组记录。
$wpdb->query('query') 这可以用来执行任何自定义查询。这通常用于更新和删除语句,而不是选择语句,因为它只提供受影响的行数作为结果。
$wpdb->get_row('query') 这可以用来从数据库中检索一行,作为一个对象、关联数组或数字索引数组。

在本节中,我们对查询数据库所使用的技巧以及简化数据库操作的内置函数进行了简要概述。在接下来的章节中,我们将在开发主题和插件时使用这些函数以及高级查询技巧。

摘要

本章的目标是了解默认数据库表的作用以及我们如何根据需求调整它们。我们首先分析了数据库关系图,并确定了表之间的连接。我们还研究了每个数据库表在常用数据中的作用。

我们确定了了解核心功能正确数据库使用方法的需求,并探讨了跟踪每个功能数据库使用的各种方法。然后,我们对多站点数据库的结构进行了简要概述,以及它与单站点数据库结构的差异。最后,我们查看了一些用于查询默认表和自定义表的函数。

在第三章,使用主题开发设计灵活的前端,我们将通过使用子主题、主题模板和动态主题组件来开始开发过程。

第三章:使用主题开发设计灵活的前端

网站的设计在吸引访客方面起着至关重要的作用。WordPress 主题被用作设计网站前端的标准技术。数千种现有主题的可用性使得设计过程既简单又灵活。这意味着开发者能够以快速的过程构建自定义设计。通过修改现有主题文件构建高级设计是开发中的常见问题,通常在网站开发的后期阶段实现。开发者应该能够将默认的 WordPress 主题转变为惊人的前端,并构建自定义主题,同时与 WordPress 核心和主题升级兼容。

在本章中,你将了解主题中应该包含什么,以及如何使用主题文件层级结构通过自定义模板构建灵活的设计。你还将学习通过使用子主题、动作、过滤器和页面模板来扩展主题。从那里,你将学习理解并修改现有网站中各种主题模板的过程。理解不同的主题组件如何融入你的设计,并在不破坏主题的情况下修改这些组件是本章的亮点。

在本章中,我们将涵盖以下主题:

  • 理解主题在开发中的作用

  • 跟踪和理解现有网站中的主题组件

  • 使用子主题扩展现有主题

  • 对主题模板层级的深入概述

  • 使用动作和过滤器扩展主题功能

  • 条件更改主题设计的技巧

到本章结束时,你将能够理解主题的作用,并拥有构建高度可定制主题模板以设计现代网站的知识。

技术要求

遵循此程序需要安装 WordPress 4.9.8。即使你有 WordPress 的更高版本,描述的示例也应该没有重大问题地工作。

本章的代码文件可以在 GitHub 上找到:

github.com/PacktPublishing/WordPress-Development-Quick-Start-Guide/tree/master/Chapter03

查看以下视频以查看代码的实际应用:

bit.ly/2EPpNIR

WordPress 主题简介

WordPress 主题是一组文件,使用预定义的结构和功能创建,作为网站的展示层。简单来说,展示层应包含生成布局和所有由模型传递的数据所需的 HTML。WordPress 旨在创建内容管理系统,因此它不关注将展示层与其业务逻辑分离。

主题包含模板文件,这些文件是 HTML 代码和 PHP 逻辑的混合。作为一个开发者,你需要了解设计和应用逻辑以与主题一起工作。

你 WordPress 网站中的主题位于wp-content/themes目录中,每个主题使用自己的文件夹。一个主题通过在style.css文件中使用的预定义注释集来识别。如果此文件不可用或注释损坏,即使主题文件已放置在wp-content/themes目录中,WordPress 也不会将其列为主题。

安装和配置主题

安装 WordPress 主题的过程非常简单,即使对于一个没有 CMS 先前经验的人也是如此。WordPress 在初始安装时包含了一些基本主题,并激活了网站上最新的主题。Twenty Seventeen 主题是最新版 WordPress 默认激活的主题。

让我们来看看安装主题的过程:

  1. 首先,你必须以管理员身份登录并转到“外观”|“主题”部分。你将看到可用主题的列表,第一个是活动主题。你可以通过点击列表中其他可用主题的“激活”按钮来更改主题。

  2. 一旦新主题被激活,旧的主题将被自动停用。通常,除非你的网站需要基本功能,否则我们不会使用现有的主题。因此,我们需要一种安装新主题的方法。

  3. 我们可以通过在“外观”|“主题”部分使用“添加新主题”按钮来安装主题。你将获得来自 WordPress 主题目录的免费主题列表,如下面的截图所示:

图片

  1. 你有两种选择:从免费主题目录安装主题或上传目录中不可用的主题。第一种选项只需要你点击列表中主题的“激活”按钮。另一种选项需要你点击“上传主题”按钮并上传一个有效的 WordPress 主题的.zip 文件。安装后,你可以从单独的屏幕激活主题。

新主题的文件将位于wp-content/themes目录中,以主题名称创建一个新的目录。一旦安装了新主题,你将在“外观”菜单的“自定义”、“小工具”和“菜单”部分看到变化。主题特定的设置和组件将被添加到这些菜单项中。

我们将在本章中始终使用默认的 Twenty Seventeen 主题。由于它是一个免费的内建主题,因此你不会找到一个单独的选项面板。在高级付费主题中,你会在一个单独的菜单项中找到一个选项面板,在那里你可以配置主题的功能和布局。

这种基本的安装方式已经足够好,你现在可以深入到主题的开发方面。我们将从了解如何在使用开发中利用主题开始。

理解主题在开发中的作用

在成千上万的免费和付费主题中选择现有主题的能力是 WordPress 作为 CMS 成功的原因之一。使用现有主题可以显著减少开发时间和成本,相比之下,从头开始设计网站。

主题的主要目的是作为网站的展示层。然而,WordPress 已经发展到这样一个状态,即仅作为展示层设计的简单主题不再适用。因此,开发者们在他们的主题中包含越来越多的高级功能和逻辑,以与其他主题竞争。在现代网站上,开发者的角色可能包括自定义主题设计、从头开始构建主题,甚至创建特定应用的主题。因此,我们需要了解主题中涉及的开发类型。让我们看看作为开发者最常见的任务:

  • 自定义文章和页面设计:这是开发者在其开发者角色初期面临的最常见任务。已有数百万个网站使用 WordPress 创建,其中很大一部分是由使用现有主题和插件的非技术性网站所有者创建的。因此,当需要更改主题时,所有者除了寻找第三方开发者外别无选择。在这种情况下,你将被要求修改现有主题设计以符合客户的要求。因此,开发者需要熟悉各种类型的现代主题和设计技术。

  • 创建页面模板:这是开发者的下一步,客户会要求你创建不同类型的着陆页来推广他们的产品、服务和其他业务。

  • 集成 UI 组件:现代网站充满了各种交互式组件,如滑块、画廊、手风琴、图表等。其中大部分都是从开源库中生成的。许多付费主题内置了对这些类型组件的支持。但在你的网站使用基本主题的情况下,你可能会被要求集成这些组件,使它们更具吸引力,并在有限的空间内提供大量信息。因此,你需要了解将这些库集成到主题中的技术。

  • 将设计转换为主题:这需要你从头开始构建一个主题。在以前,我们从设计师那里获得 PSD 文件,开发者的角色是将它转换为 HTML。使用 WordPress,你需要额外的一步,将 HTML 转换为 WordPress 主题。除非你了解 WordPress 模板层次结构、主题函数和必要的 WordPress 约定,否则这是一个非常困难的过程。

上述列表概述了在旨在作为基本 CMS 运行的网站上与主题一起工作的任务。然而,现代 WordPress 网站通过创建完整的 Web 应用超越了常规。在这些应用中,我们可以看到应用主题的使用。

应用特定主题是什么?

通用 WordPress 主题主要使用一组标准主题文件,主要目的是提供出色的博客文章和页面功能。另一方面,应用特定主题使用更多应用特定文件,旨在构建应用特定功能。这些文件包括应用特定功能、业务逻辑和模板。这类主题的优势在于可以直接在网站上使用,而不是在通用主题之上开发这些功能。然而,应用主题的局限性在于无法切换主题,因为大多数功能都绑定在主题上。让我们看看一些常见的应用主题类型,以了解应用主题的概念:

  • Buddypress: 这是一个允许您将社交网络功能添加到 WordPress 的插件。如今,专为 BuddyPress 设计的主题旨在扩展基本社交网络功能。BuddyPress 主题的功能包括用户管理、用户群组管理、用户活动、用户消息等。您可以在themeforest.net/category/wordpress/buddypress查看 BuddyPress 特定主题的惊人功能。

  • 房地产: 这类主题是为创建房产列表和销售而设计的。除了基本的帖子模板和页面模板外,这类主题还包含各种高级模板和功能,如房产地图、经纪人管理、房产比较和搜索。您可以在themeforest.net/category/wordpress/real-estate查看房地产特定主题的功能。

  • LMS: 这类主题是为学习管理系统而设计的,其中包含教师、学生和课程。除了基本主题功能外,这些主题还包括管理课程、讲师、学生、考试和成绩的模板和功能,以及所有上述数据的先进搜索功能。您可以在themeforest.net/category/wordpress/education?term=lms查看 LMS 特定主题的功能。

在查看特定应用主题的使用后,您将了解到这些主题包含整个插件或可能整个应用的功能。这可能不符合开发最佳实践的理想实现。然而,我们必须习惯于构建或使用这样的主题,以便提供成本效益的解决方案。

如您可能已经注意到的,作为开发者,您在主题方面的角色非常广泛。大多数情况下,您将不得不承担设计部分的责任,以及主题的功能性。了解主题组件如何协同工作以构建网站的设计和功能非常重要。

理解内置主题组件

任何 WordPress 前端屏幕的设计都由许多组件组成,这些组件位于模板文件之上。这些组件充当网站数据的占位符位置。通过了解这些组件,您可以允许管理员根据各种需求动态更改内容和位置。

让我们来看看这些组件及其在主题中的角色:

  • 菜单:这是一个包含帖子、页面、自定义帖子类型和自定义链接的链接集合。导航菜单是任何网站中必须使用的功能,可以在不丢失路径的情况下浏览可用的页面。在以前,我们只有位于网站页眉部分的单个菜单。现代网站包含多个菜单,并将相同的菜单放置在多个位置以简化导航。您网站上的菜单数量取决于主题支持的菜单位置。您可以选择使用一个或多个主题支持的菜单位置。可以通过访问www.example.com/wp-admin/nav-menus.php?action=locations来识别主题中支持的菜单。

  • 小工具:这是一个允许您显示静态内容、从数据库显示动态内容、捕获用户数据或为用户提供特定功能的块。小工具可以放置在您网站的侧边栏中。默认的小工具集合包括存档、日历、分类、元信息、最近的文章和搜索。除了 WordPress 提供的这些默认小工具之外,您还将获得相当数量的特定于插件的主题。可以通过停用所有插件并访问www.example.com/wp-admin/widgets.php来识别主题中可用的小工具。

  • 侧边栏:这原本是为了在网站的任何页面/帖子左侧或右侧作为一列来工作的。侧边栏的内容是通过从 WordPress 的后端拖动小工具来添加的。然而,现代网站需要更高级的布局,因此侧边栏被用作小工具区域,而不仅仅是作为一列。在现代主题中,您可以在网站的任何地方都有侧边栏,以便重复使用小工具以及动态更改内容。可以通过访问www.example.com/wp-admin/widgets.php并检查屏幕的右侧来识别主题中可用的侧边栏。

在本节中,我们简要概述了内置主题组件的功能以及我们可以在 WordPress 后端找到它们的位置。这些细节将帮助您在从头创建网站的过程中。然而,当与现有网站一起工作时,您可能需要通过在前端识别它们然后移动到后端来反向追踪它们。下一节将涵盖这些组件的识别。

识别现有网站中使用的主题组件

正如我们已经讨论过的,从头开始开发主题不是使用 WordPress 构建的小型到中型网站的主要要求。在大多数情况下,开发者将不得不与现有网站和现有主题一起工作,这些主题是由所有者选择的。因此,您应该具备查看网站中任何给定帖子/页面的技能,然后识别用于帖子或页面的模板和主题组件,以便快速将需求转化为解决方案。让我们看看识别在帖子或页面上显示的重要主题组件的过程:

  • 菜单:在现有网站上,我们需要能够识别某些屏幕中的菜单以便进行自定义。我们没有内置或外部的跟踪菜单的功能。

    通常,菜单位于网站的页眉和页脚部分。首先,您需要寻找一组看起来像菜单并指向您网站文章和页面的链接。然后,您可以使用浏览器检查工具查找菜单容器。通常,您会发现菜单容器将在元素 ID 或类中包含菜单名称。让我们检查 Twenty Seventeen 主题顶部菜单的元素,如下面的截图所示:

图片

如您所见,<ul>元素使用了一个名为top-menu的 ID,而<nav>元素使用了一个名为区域标签Top Menu。因此,现在我们可以访问www.example.com/wp-admin/nav-menus.php?action=locations并找到Top Menu。接下来,我们可以更改该位置的菜单或菜单项。如果您需要自定义菜单位置的代码,您可以在主题中搜索以menu_idtop-menuwp_nav_menu函数。这些菜单名称在元素中的使用因主题而异。然而,您应该能够通过检查元素找到有关菜单位置的提示。

  • 使用的模板:当我们被要求为现有网站屏幕进行定制时,最难的部分是识别用于生成屏幕的主题模板。然而,我们有使用What The File (wordpress.org/plugins/what-the-file/)插件的选项,我们在第一章,WordPress 开发简介中讨论过。一旦安装了这个插件,你将在每个前端屏幕上获得一个名为 What The File 的菜单项。然后,你可以查看一个屏幕,并将鼠标悬停在 What The File 菜单项上,以查看用于该屏幕的模板,如下面的截图所示:

图片

上一张截图显示了一个付费主题的单个帖子。你可以很容易地注意到用于给定屏幕的主要模板和子模板。你可以在主题中找到这些模板以进行必要的修改。

  • 侧边栏和小工具:通常,侧边栏位于任何屏幕的左侧或右侧,小工具被放置在其中。然而,现代主题使用许多动态小工具区域,因此侧边栏可以放置在网站的任何位置。这使得在没有工具的情况下跟踪侧边栏变得困难。幸运的是,我们可以使用调试栏(wordpress.org/plugins/debug-bar/)插件,我们在第一章,WordPress 开发简介中讨论过,以及一个名为调试栏 – 侧边栏与小工具(wordpress.org/plugins/debug-bar-sidebars-widgets/)的附加插件。这个附加插件已经四年没有更新了。然而,它似乎运行良好,并且是我找到的完成这项任务的最佳工具。

    一旦安装了这两个插件,你可以在屏幕上查看并从顶部菜单点击调试 | 侧边栏。你将获得一个类似于以下屏幕:

图片

这个工具清楚地显示了在任何给定屏幕中加载的侧边栏及其细节,以及每个侧边栏中加载的小工具及其细节。现在,我们可以使用这些信息访问后端小工具部分,并对当前屏幕进行必要的修改。

你应该使用这些技术来快速跟踪任何现有网站中的组件,并在快速过程中构建解决方案。

开始开发主题

我们在前一节中讨论了主题中涉及的开发任务。从头开始构建主题是一个范围非常广泛的过程,因此在这种类型的书中讨论是不可行的。因此,我们将专注于定制现有主题,同时提供构建主题所需的知识。在现有主题中添加或修改功能有两种方法:

  • 通过修改模板文件来构建和定制设计功能

  • 通过可用的过滤器和动作扩展主题功能

这两种技术都需要我们修改现有主题的文件。在大多数网站上,我们将使用由第三方开发者开发的现有、免费或付费主题。因此,我们显然会收到带有更多功能、错误修复和改进代码的主题更新版本。主题更新过程会清除对核心主题文件所做的所有更改。这意味着我们需要一种方法来定制主题,而不会在主题更新时丢失更改。这就是子主题概念被使用的地方。

什么是子主题?

子主题是主题的一个子版本,继承了其功能、设计和样式。我们将主主题称为父主题。根据其名称和目的,你可能认为子主题是父主题的一部分。然而,子主题在wp-content/themes目录内有自己的目录,并通过几行注释与父主题相关联。子主题不会随着父主题更新而更新,因此我们可以用它来进行定制开发。

创建子主题

对于 WordPress 新手开发者来说,可能会认为这是一个相当复杂的过程,涉及许多步骤和配置。然而,这是一个简单的过程,只需不到一分钟。让我们为现有的Twenty Seventeen主题创建一个名为WPQuickStart的子主题。

第一步是在wp-content/themes目录中创建一个名为wpquickstart的目录。接下来,我们需要在wpquickstart目录中创建一个style.css文件,并添加以下注释:

/*
Theme Name: WPQuickStart
Theme URI:
Description: Twenty Seventeen Child Theme
Author: Rakhitha Nimesh
Author URI: http://example.com
Template: twentyseventeen Version: 1.0.0 */

代码中高亮显示的行定义了目录为子主题,并定义了父主题。你应该使用父主题的目录名称作为模板设置。此过程的最后一步是在wpquickstart目录中创建一个functions.php文件,并添加以下代码以加载父主题样式:

add_action( 'wp_enqueue_scripts', 'wpquickstart_enqueue_styles' ); 
function wpquickstart_enqueue_styles() { 
    wp_enqueue_style( 'twenty-seventeen-style', get_template_directory_uri() . '/style.css' ); 
    wp_enqueue_style( 'wpquickstart-style',    get_stylesheet_directory_uri() . '/style.css', 
        array('twenty-seventeen-style')    ); 
}  

上述代码包含了父主题和子主题的style.css文件。我们通过将twenty-seventeen-style传递到依赖数组中,为子主题样式文件添加了一个依赖。由于子主题的style.css文件依赖于父主题,它将在Twenty Seventeen主题的style.css文件之后加载。因此,我们使用WPQuickStart style.css文件覆盖必要的父主题样式。

只需三个步骤,我们的子主题就可以具备父主题的所有功能。其他文件遵循相反的过程,除了functions.php文件。如果子主题中有模板文件,它将加载而不是父主题的模板文件。另一方面,子主题中缺失的模板文件将从父主题加载。

使用子主题修改父主题

现在,最重要的了解是如何使用子主题来实现前面章节中讨论的两个技术。让我们从使用模板文件开始。

通过修改模板文件来构建和定制设计

我们创建了一个子主题来定制父主题的设计,而不会在主题更新时丢失更改。这可以通过修改父主题的整个模板或部分模板来实现。让我们通过使用一个示例模板设计更改来学习这一点。考虑以下截图,以满足我们的需求:

左侧的屏幕显示了Twenty Seventeen主题的默认单篇帖子设计。假设我们想要更改帖子模板以移除侧边栏,并显示评论表单,以便用户在阅读帖子时可以添加评论。因此,我们必须将左侧的屏幕更改为与右侧的屏幕相匹配。

为了满足我们的要求,我们必须覆盖父文件模板。因此,我们需要将 Twenty Seventeen 主题的single.php文件复制到 WPQuickStart 子主题的根文件夹中。对于新的设计,我们不需要侧边栏,因此以下行需要从我们在子主题中创建的single.php文件中删除:

<?php get_sidebar(); ?>

相反,我们需要将评论表单添加到相同的位置。评论模板已经通过以下代码加载到single.php中,在帖子内容之后:

if ( comments_open() || get_comments_number() ) : 
  comments_template(); 
endif; 

我们需要从single.php文件的现有位置删除此代码,并将其添加到我们之前放置侧边栏的位置。考虑以下代码,其中我们将comments代码放置在一个名为wpquickstart-comments的容器元素中:

<div id="wpquickstart-comments" >             
  <?php if ( comments_open() || get_comments_number() ) :       
          comments_template();                      
        endif; ?>     
</div> 

最后,我们需要将具有primary ID 的元素的 CSS 类从content-area更改为wpquickstart-content-area。现在,我们可以应用 CSS 来减小帖子内容元素的大小,并将评论移至帖子内容右侧。将以下 CSS 添加到子主题的style.css文件中:

.wpquickstart-content-area{ 
    width:50% !important; float:left; 
} 
#wpquickstart-comments{ 
    width: 45%;    float: left;    padding: 0 3%; 
} 
#wpquickstart-comments #comments{ 
    padding-top: 0 !important; 
} 

现在,您可以刷新浏览器以检查带有右侧评论表单的新设计。这是一个如何根据我们的需求更改父主题设计的非常基础的例子。通过使用几行样式和移动现有元素,我们能够在几分钟内创建一个新的设计。在实际网站开发中,您可以通过使用相同的过程将简单的设计转变为高级、功能丰富的设计。

通过可用的过滤器和动作来扩展主题功能

我们已经讨论了使用子主题来定制和扩展主题模板的重要性。使用 WordPress 过滤器和动作钩子是另一种定制主题功能的方法。我们将主要使用这些钩子来扩展主题提供的功能。然而,我们可以使用一些过滤器和动作钩子来更改模板的部分,而无需完全替换它们。

在 WordPress 中,一个动作是一个在过程特定点执行的 PHP 函数。一个过滤器是一个用于修改过程中的现有数据或功能的函数。尽管动作旨在在事件前后执行某些操作,但它们经常用于向模板添加内容。关于动作和过滤器的更多内容将在后续章节中介绍。

一旦我们选择了一个主题,我们就可以探索主题内的动作和过滤器,以了解其可扩展功能和位置。识别这些动作和过滤器的最简单方法是使用代码编辑器的目录或项目搜索功能。大多数高质量的代码编辑器默认提供此功能。你应该使用术语apply_filters来搜索主题中的过滤器,并使用do_action来查找其中定义的动作。你可能会在免费主题中找到少量这些钩子,在付费主题中找到数百个。这些钩子包括内置的 WordPress 钩子以及主题特定的钩子。作为一个开发者,你需要对主题中常见的内置 WordPress 钩子有深入的了解。让我们看看在主题中常用的某些过滤器和动作:

  • the_content: 这是在主题中最常用的过滤器,旨在修改帖子内容。我们可以修改整个帖子内容,或者在一个帖子前后添加动态内容。通常,这用于显示有关帖子(如相关帖子、广告和社交分享按钮)的附加数据。以下代码用于在每个帖子后添加自定义内容。此代码应放置在子主题的functions.php文件中:
add_filter( 'the_content', 'wpquickstart_add_custom_text_single_posts' );  
function wpquickstart_add_custom_text_single_posts( $content ) { 
    if ( is_single() && in_the_loop() && is_main_query() ) { 
        return $content . "Custom Text After Post"; 
    }  
    return $content; 
} 
  • 帖子内容作为参数传递给这个函数。在这里,我们使用is_single模板标签来确保自定义内容仅应用于单个帖子,而不是存档。其他两个条件限制此文本不应用于主查询和循环之外的帖子。这是the_content过滤器功能的一个非常基本的示例。你可以添加更多内容并设置不同的条件来处理各种需求。

  • the_permalink: 这个过滤器会过滤当前帖子的永久链接显示。你可以使用与前面示例类似的代码来修改帖子链接。

  • the_title: 这个过滤器会过滤帖子的标题。用于根据某些条件修改帖子标题。

  • comment_form_default_fields: 这个过滤器会过滤评论表单的字段。默认情况下,评论表单包含评论、姓名、电子邮件和网站字段。字段作为数组类型参数传递给此过滤器。你可以使用此过滤器添加新字段或从评论表单中删除现有字段。以下代码使用此过滤器从评论表单中删除website字段:

function wpquickstart_remove_website_field($fields) { 
    unset($fields['url']); 
    return $fields; 
}  
add_filter('comment_form_default_fields', 'wpquickstart_remove_website_field'); 
  • wp_nav_menu_item_custom_fields:名称表明这是一个过滤器,但实际上它是在 WordPress 中执行的动作。将自定义字段添加到菜单项的过程是菜单项特定 CSS 和菜单项限制等功能的常见任务之一。在这种情况下,我们可以使用此动作来添加自定义字段,如下面的代码所示:
add_action( 'wp_nav_menu_item_custom_fields', 'wpquickstart_menu_button_fields', 10, 4 ); 
function wpquickstart_menu_button_fields( $item_id, $item, $depth, $args ) {     ?> 
  <div class="fusion-menu-options-container"> 
   <div class="option-details"> 
     <h3><?php _e( 'Menu Class', 'wpquickstart' ); ?></h3> 
     <p class="description"><?php _e( 'Add custom css class.', 'wpquickstart' ); ?></p> 
   </div> 
   <div class=""> 
     <input type="text" id="wpquick-menu-class" name="wpquick-menu-class" value="" /> 
   </div> 
  </div> 
<?php } ?> 
  • 上述代码将在后端菜单中的菜单项中添加一个名为菜单类的新文本字段。

这些是数百个可能的 WordPress 动作和过滤器中常用的一些。您应该通过使用各种 WordPress 主题来探索这些钩子,以确定您实现所需的钩子。

在上一节中讨论的过滤器和动作用于常见的 WordPress 用途,因此可以在任何主题中使用。另一方面,特定于主题的动作和过滤器是为高级主题功能设计的。大多数这些动作和过滤器不能在其他主题中使用。我们可以通过查看钩点名使用的前缀来轻松识别特定于主题的钩子。Twenty Seventeen主题使用twentyseventeen_作为特定于主题钩子的前缀。让我们考虑在 Twenty Seventeen 主题中可用的一个过滤器,以了解特定于主题钩子的使用:

apply_filters( 'twentyseventeen_content_width', $content_width ); 

此过滤器用于根据页面布局自定义页面宽度。让我们看看我们如何实现此过滤器来修改宽度:

add_filter( 'twentyseventeen_content_width', 'wpquickstart_content_width' ); 
function wpquickstart_content_width($content_width){ 
  if(is_page()) { 
    $content_width = "800"; 
  } 
  return $content_width; 
} 

在上述代码中,我们通过使用特定于主题的过滤器更改了Twenty Seventeen主题中页面的宽度。我们可以添加更多条件来更改宽度和显示不同类型的布局。

扩展特定于主题钩子的步骤

现在,您应该对主题中内置的钩子和特定于主题的钩子有一个简要的了解。让我们看看使用这些钩子自定义主题的基本步骤:

  1. 在主题上执行目录搜索,通过跟踪apply_filtersdo_action函数来识别钩子。

  2. 对于过滤器,检查是否已向apply_filters函数传递了超过两个参数。第二个参数将是您将要过滤的变量,额外的参数将用于支持过滤过程。

  3. 如果apply_filters函数包含两个参数,您可以使用以下代码实现它:

    add_filter( 'filter_hook_name', 'filter_implementation_function_name' ); 
    function filter_implementation_function_name ($param1){ 
         // Modify $param1 based on your preferences 
         return $param1; 
    }
  1. 如果apply_filters函数包含超过两个参数,您可以使用以下代码:
    add_filter( 'filter_hook_name', 'filter_implementation_function_name',10,3 ); 
    function filter_implementation_function_name ($param1,$param2,$param3){ 
         // Modify $param1 based on your preferences and help of $param2 and $param3 
         return $param1; 
    }
  1. 在上述代码中,10 将是优先级。数字 3 将是apply_filters函数中在钩子名称之后的参数数量。

  2. 对于动作,检查是否已向do_action函数传递了多个参数。这些参数将用于支持我们自定义动作代码的执行。

  3. 使用以下代码实现动作:

    add_action( 'filter_hook_name', 'action_implementation_function_name',10,2 ); 
    function action_implementation_function_name ($param1,$param2){ 
         // execute some code related to action - Ex: send emails, save data to database 
         echo 'Custom Content' ; // you can add content to location where action hook is executed 
    }    
  1. 您应根据参数的数量更改 2。如果钩子只有一个参数,则可以省略 10 和 2。

在本节中,我们探讨了两种修改或扩展主题功能的技术。您应该检查网站的要求,并决定您是否可以使用现有的钩子进行某些自定义,或者是否需要在子主题中完全替换模板文件。

使用 WordPress 主题模板层次结构

一个 WordPress 主题可以用仅三个文件来构建:style.cssindex.phpcomments.php。然而,完整的模板层次结构可以支持相当多的子模板,足够灵活以满足许多网站的先进需求。通常情况下,开发者将在层次结构中工作少于十个文件。然而,对于具有高级需求的项目,了解完整的模板层次结构可能会很有用。让我们看看以下图表,它由 WordPress 用于处理初始请求的页面类型组成:

图片

根据加载的内容,有六种路径来处理用户请求。归档页面单页是其中最复杂且最灵活的,结构较高。在深入了解完整的模板层次结构之前,让我们确定这些页面的主要用途:

  • 归档页面:通常用于显示文章列表、分类文章、标签文章、作者文章和自定义文章类型,并附带关于文章的简要摘要

  • 单页:用于显示单个文章、页面或自定义文章类型

  • 网站首页:用于显示您网站的首页

  • 博客文章索引页面:此页面用于显示您网站上的文章列表

  • 404 错误页面:当 WordPress 找不到请求的匹配模板时使用

  • 搜索结果页面:显示包含帖子列表和页面的搜索结果

您可以在developer.wordpress.org/themes/basics/template-hierarchy/找到更多关于模板层次结构的详细信息。现在,我们需要遍历每种页面类型的完整层次结构,以获取处理高级应用需求的知识。让我们从归档页面开始。

归档页面层次结构

此路径分为六个子类型,WordPress 将为任何给定请求的帖子数据集合使用这些子类型之一。让我们考虑以下图表,用于归档页面层次结构:

图片

在此路径中,我们使用archive.php文件作为主要模板。对于主题而言,这是一个可选文件,当此模板在主题中缺失时,index.php文件将处理归档。然而,几乎所有的这些主题都会包含archive.php文件来处理数据集合。让我们看看各种类型的归档页面,从作者归档开始。

作者归档页面

在 WordPress 中,帖子是由管理员或具有帖子创建能力的用户创建的。在大多数主题中,您会在帖子标题旁边看到一个链接的作者名称。一旦您点击链接,您将被重定向到一个包含指定用户创建的所有帖子的页面。这是作者存档页面,通常由archive.php文件处理。让我们看看 Twenty Seventeen 主题的作者存档页面。

基本上,我们拥有作者的显示名称和描述。这可能对于拥有博客和一到两个作者的简单 WordPress 站点来说已经足够好了。在拥有大量作者且主要站点功能是发布文章的情况下,这可能看起来有些朴素。相反,这类网站需要您拥有一个精心设计的个人资料,包括个人照片、网站详情和社交媒体账户详情。以下截图是来自一个流行主题修改后的作者模板预览:

图片

如您所见,这个网站使用了一个完整的用户个人资料以及一个修改后的两列帖子列表。我们无法在不使模板变得过于复杂的情况下使用archive.php来构建这种设计。相反,我们可以利用作者存档路径中的额外模板,如前图所示。作者存档路径中有三个模板,如下所示:

  • author.php:这是自定义作者存档的主要模板。许多主题,包括付费主题,使用archive.php来生成作者存档,而不是单独的模板。因此,我们可以在子主题中添加自己的author.php文件,并设计一个单独的作者存档。这个模板在archive.phpindex.php之前执行。

  • author-$id.php:这是在author.php模板之前执行的两种动态模板之一。用户的 ID 作为模板名称的一部分,因此这个模板只为单个用户加载。由于这是一个动态模板,您在任何主题中都不会找到它。这是开发者应该创建以适应网站特定需求的东西。为每个作者创建主题模板并不实际,除非您有数量有限且固定的作者。因此,在开发中很少需要使用这种模板。

  • author-$nicename.php:这与author-$id.php类似,并且在之前的模板之前执行。这个模板使用 WordPress 的昵称而不是 ID。

在需要自定义作者存档页面的情况下,您应将archive.php页面的内容复制到author.php文件中,并根据需要更改设计。有关作者模板和作者数据的实现,更多信息请参阅codex.wordpress.org/Author_Templates

分类、标签和自定义分类法存档

分类是 WordPress 用来将事物分组到不同部分的一个术语。如果你已经使用过 WordPress 文章,你应该熟悉在文章创建屏幕中的分类文章标签。这两个是默认分配给普通文章的内置分类。另一方面,自定义分类是根据不同网站的需求为特定文章类型创建的分组类型。我们可以将一个或多个分类值分配给文章或自定义文章类型项。一旦文章被分配,我们就会得到一个包含特定分类文章列表的存档页面。

分类、标签和自定义分类以完全相同的方式工作,具有类似的模板层次结构。archive.php文件默认使用分类标题和描述来加载这三种类型。在作者存档中,我们需要一种方式来以吸引人的设计显示完整的作者详情。在这种情况下,我们不需要关注这些细节,因为分类很少使用附加详情。相反,我们可能需要更改分类存档页面中文章的设计和显示。在这种情况下,我们可以使用category.phptag.phptaxonomy.php模板,类似于我们使用author.php的方式。这些模板需要根据你的需求添加到子主题中,因为大多数主题只使用archive.php文件来处理这三种类型。

与作者存档类似,这三种类型各自也有两个使用分类 ID 或短语的动态子模板。然而,我们很少使用它们,因为模板需要根据我们在开发中使用的分类动态创建。

日期存档页面

这是一种另一种存档页面,我们可以看到按年、月或日排列的文章列表。我们正在使用Twenty Seventeen主题,因此你会在侧边栏看到一个名为存档的部件,其中包含一系列值,如 2018 年 8 月、2018 年 7 月等。一旦你点击这些链接之一,它将加载该月创建的文章。网站的 URL 可能看起来类似于http://www.example.com/2018/07。我们可以通过从 URL 中删除 07 来检查年度存档,通过在 07 之后添加日期来检查每日存档。再次强调,默认情况下使用archive.php来处理这三种存档页面。

现在,如果你考虑我们的存档页面图,你会注意到使用date.php文件将日期存档与archive.php文件分开。这个模板与我们已经讨论过的作者和分类模板类似工作。尽管它不常被使用,但我们可以在子主题中添加date.php文件,并显示关于每年、每月或每天的任何附加信息。

自定义文章类型存档

在现代 WordPress 网站上,自定义文章类型的使用很常见。与普通博客文章不同,自定义文章类型可以包含相当多的数据和设计要求。因此,在大多数情况下,使用archive.php来加载自定义文章列表可能不可行。

相反,我们可以观察前面的图表来识别额外模板文件的使用。与之前的路径不同,我们只有一个用于自定义文章存档的动态模板,称为archive-$post_type.php。文章类型变量应该被替换成你网站上的文章类型,如产品、主题和论坛。尽管这是一个动态模板类型,但我们可以创建并添加这个模板到子主题中,因为大多数情况下,一个网站中的自定义文章类型是固定的。在这种情况下,当 WordPress 找不到带有archive-$post_type.php的模板时,它将只加载archive.php。如今,许多高级主题都内置了自定义文章类型支持,因此你将在主题文件中找到这样的模板。让我们看看以下截图,用作存档页面:

图片

左侧的屏幕显示了一个正常的博客文章存档,它使用archive.php文件进行显示。右侧的屏幕显示了一个自定义文章类型,以用户友好的方式展示了一个完全不同的设计和各种数据。我们需要archive-$post_type.php来构建这样的设计,因为使用同一个archive.php文件无法生成这两个屏幕。

单页层次结构

这个路径分为两个子类型,用于显示单个文章或页面的数据。让我们考虑以下图表,用于单页层次结构。

图片

在存档中,我们展示了带有摘要详情的文章集合。在这个路径中,我们需要显示单个文章或页面的完整数据。我们使用single.php文件作为这个路径的主要模板。这是一个主题的可选文件,当这个模板在主题中缺失时,index.php文件将处理文章和页面。然而,几乎所有的主题都会包含single.php文件以用于单个文章数据。让我们看看以下各种单页类型,从静态页面开始。

静态页面层次结构

在 WordPress 中,页面用于管理网站的架构并包含永恒的内容。通常,我们称页面为静态的,尽管它们与我们过去拥有的纯 HTML 网站的页面不完全相同。然而,内容不经常改变,因此与文章相比被认为是静态的。在一个基本网站上,你会找到 WordPress 页面用于联系、关于、服务等等。这个路径分为两种类型,称为自定义模板默认模板。让我们从默认模板开始,这样我们可以理解自定义模板的需求。

默认情况下,WordPress 页面由page.phpsingular.phpindex.php处理。大多数主题包含一个单独的page.php文件来处理页面,因此singular.phpindex.php可能不会被使用。

我们可以用page.php文件构建我们的大部分页面设计。然而,在某些情况下,如果你想用不同的设计处理不同的页面,page.php之前还有两个可选模板:

  • page-$slug.php:这个模板使用别名来识别页面并提供单独的设计。这个模板只能用来设计一个页面。

  • page-$id.php:这个模板使用页面 ID 为选定的页面提供单独的设计。这个模板只能用来设计一个页面。

许多网站包含有限数量的页面,这些页面的设计通常各不相同。因此,可以通过指定 ID 或别名来使用不同的页面模板。然而,在现代网站上,这两个模板可能不会被使用,因为可以通过自定义页面模板实现相同的功能,这将在下一节中讨论。默认模板适用于大多数页面设计简单且仅旨在向用户提供内容的网站。

与此相反,自定义页面模板是不同于默认设计的模板,用于更改网站中一个或多个页面的外观和感觉。这个模板在page.php模板之前加载。与其他层次结构中的模板不同,页面模板没有预定义的命名模式。相反,我们可以将其命名为我们想要的任何名称,并在模板文件中使用 PHP 注释将其指定为模板。

创建和使用自定义页面模板

创建新的页面模板的过程相对简单。首先,我们需要在主题的根目录下创建一个文件。让我们将其命名为fullwidth.php。接下来,我们需要在fullwidth.php文件的顶部添加以下注释,如下所示:

/* Template Name: WPQuickStart Full Width */

上述注释将注册此为页面模板,WordPress 将在页面创建屏幕中将其作为选项加载。在Twenty Seventeen主题中,页面设计包含两列,其中标题显示在左侧,内容显示在右侧。左侧列几乎占据页面的一半,因此对于网站上的某些页面来说并不理想。因此,我们的全宽模板将删除左侧列并将内容区域的宽度增加到 100%。

首先,我们必须从page.php文件中复制内容并将其添加到我们的fullwidth.php模板中。接下来,将以下行替换为来自twentyseventeen/template-parts/post/content.php文件的内容:

get_template_part( 'template-parts/page/content', 'page' );

接下来,我们必须删除完整的标题标签以省略左侧列。然后,我们需要将包含entry-content类的<div>元素的宽度增加到 100%。最后,在entry-content div 元素内添加页面标题。现在,我们已经准备好了一个全宽页面模板。

现在,我们可以从管理部分转到“页面 | 添加新页面”来创建一个新的页面。你的主题提供的可用页面模板将列出,如下面的截图所示:

图片

模板设置允许我们为任何给定页面分配一个页面模板。因此,当多个页面需要相同的设计时,我们可以使用此类模板。在默认模板部分,我们讨论了page-$slug.phppage-$id.php文件的可用性。使用这种技术,我们必须为每个需要的页面创建模板。当我们删除或更改现有页面时,我们还需要手动修改模板。自定义页面模板没有这些限制,我们可以使用一个模板来设计多个页面。我们还可以从页面创建部分快速更改模板。因此,我们应该优先考虑自定义页面模板,而不是默认路径中可用的动态页面类型。

在其基本形式中,我们使用页面模板来改变页面的结构。我们已经通过引入全宽模板来改变了页面的结构。最基本的模板类型包括两列、三列、全宽、右侧边栏和左侧边栏模板。与我们在现代高级主题中获得的模板相比,这些都是非常基本的模板类型。这些现代主题包括具有列表、网格和相册的精选集模板、联系表单、带有各种滑块的首页模板等等。

这只是一个模板类型的列表,你需要查看这些模板设计,以便了解页面模板的实际功能。你可以在许多高级主题中找到这些类型的模板。我建议你访问themeforest.net/category/wordpress,并查看现代主题提供的页面类型。你会对页面模板的创新使用感到惊讶。

了解如何从头开始构建页面模板非常重要。基本上,你可以在模板名称注释之后添加任何内容。由于你刚开始 WordPress 开发,你可以在模板中使用page.php文件中的各种函数,并在其基础上构建。一旦你有了经验,你应该从头开始创建高级模板,而不需要从现有模板中获取任何代码。

单个帖子页面层次结构

在上一节中,我们讨论了页面作为静态元素。另一方面,帖子是高度动态的,并提供了时间敏感的信息。通常,帖子列表页面会频繁更改,新帖子会显示在列表的顶部。在本路径中,我们考虑的是显示单个帖子或自定义帖子类型的完整详情。

默认情况下,WordPress 帖子由single.phpsingular.phpindex.php处理,具体取决于层次结构中先前模板文件的可用性。大多数主题包含一个单独的single.php文件来处理帖子,因此singular.phpindex.php可能不会被使用。single.php模板与page.php非常相似。然而,帖子有诸如分类、标签、特色图片和其他帖子的分页等选项。因此,包括这些细节将使single.phppage.php中改变。

此路径分为三种类型,称为附件帖子、自定义帖子博客帖子。让我们从附件帖子特定的模板开始。

附件帖子层次结构

在 WordPress 中,上传到媒体库或分配给帖子的文件被视为附件。图中显示了附件模板的上至七级。然而,除非网站的主要功能是管理附件,我们很少使用这些模板。因此,single.php模板将负责处理大多数主题中的附件。由于这些可选模板使用较少,我们不会讨论它们。

您可以转到媒体 | 库,点击其中一个附件。然后,您可以点击查看附件页面链接,使用single.php文件查看附件。如果您的网站需要您更改默认模板,您应该在子主题中添加attachment.php文件并尝试自定义设计。

博客帖子层次结构

WordPress 中的默认帖子通常被称为博客帖子,因为它旨在为博客提供内容。默认情况下,single.php文件负责处理博客帖子的模板。在存档部分,我们了解了正常帖子列表与自定义帖子类型列表在高级功能方面的区别。同样,我们需要更多特定于自定义帖子类型的模板,因此使用single.php处理所有三种类型可能不可行。在这种情况下,我们可以使用single-post.php模板,该模板正是为正常博客帖子设计的。single-post.php文件的内容将与single.php类似,我们可以添加更多元素和样式来改变设计。

我们还可以在博客帖子路径中找到可选的$custom.php模板。这是页面模板的一种变体,我们可以为特定帖子类型指定模板。此功能是在 WordPress 4.7 版本中引入的。

创建和分配帖子页面模板

创建特定帖子模板的过程与我们之前使用的过程类似。我们只需在主题的根目录中创建一个带有首选名称的模板文件,并将以下注释添加到文件的顶部:

/* Template Name: Featured Post
Template Post Type: post, product */

在这个模板中,我们必须添加一个额外的注释,称为模板帖子类型,列出支持的帖子类型。根据我们的注释,此模板将适用于正常博客文章和产品自定义帖子类型。将模板分配给帖子的过程与我们用于页面的过程相同。

自定义帖子层级

这是网站中使用的自定义帖子类型的路径,其中single.php是默认模板。通常,自定义帖子类型的设计与博客文章的设计有很大差异,因此几乎可以保证使用动态的single-$post_type.php模板。我们必须在主题的根目录中创建一个新的模板,并以帖子类型的 slug 命名。这些模板在高级主题中经常被使用,因此您可以寻找带有single-偏好的模板。

除了主模板外,我们还可以通过使用帖子的 slug 和帖子模板,如前节所述,获得两个前置模板。使用single-$post_type-$slug.php将不再是必需的,因为可以通过使用带有额外优点的$custom.php模板来实现相同的功能。让我们看一下下面的截图,以便我们了解正常single.php模板和自定义设计的single-$post_type.php模板之间的区别:

图片

左侧的屏幕显示了一个使用single.php设计的正常博客文章。右侧的屏幕包含使用single-vehicle.php模板的Vehicle自定义帖子类型的先进设计。您可以清楚地看到设计上的差异以及自定义帖子类型中single-$post_type.php模板的需求。在 WordPress 网站开发中,您将需要构建自定义帖子类型的先进模板的技能,因此了解这些额外的模板及其代码非常重要。

模板层级中的其他页面

到目前为止,我们已经讨论了 WordPress 两个主要起始模板类型的层级。还有四种其他页面类型,深度有限,其作用不如存档和单页类型重要。让我们看一下下面的图表,以了解这些四种类型的层级:

图片

网站首页模板路径

在基本主题中,网站的首页由index.php处理,其中所有博客文章都显示为列表。除非您的网站主要功能是博客,否则将需要一个单独的模板来设计首页的设计。根据模板层级,home.php可以用来设计网站的首页。这个模板在大多数高级主题中都是可用的,并包含非常先进的设计。在这个路径中还有一个可选的模板,称为front-page.php。然而,它在大多数主题中不可用,因此在常规开发任务中可能不需要使用它。

除了使用home.phpfront-page.php模板之外,我们还可以使用任何默认页面作为我们网站的首页。这由 WordPress 后端的一个设置处理。我们可以使用 WordPress 的“设置 | 阅读设置 | 您的首页显示设置”来更改首页,如下面的截图所示:

默认情况下,此设置将使用“您最新的文章”选项。将此设置更改为“静态页面”并使用自定义页面是一种常见的技术。

博客文章索引页面模板路径

此路径定义了用于主博客页面的模板,其中显示一系列常规文章。默认情况下,这由index.php文件处理,并作为网站的首页加载。我们可以创建任何页面并将其用作博客文章索引,通过使用前述章节中解释的设置。此模板路径简单直接,通常不会根据不同场景而改变。

错误 404 页面模板路径

当 WordPress 无法在模板层次结构或任何模板修改钩子中找到合适的模板时,将使用此路径。大多数主题都内置了一个包含“内容未找到”信息的 404 模板。在主题中拥有此模板对于帮助用户以有意义的信息和提供到网站上其他重要内容的链接至关重要。

搜索结果页面模板路径

WordPress 具有内置的搜索功能,搜索结果通常包含文章和页面。这些结果类似于存档页面,顶部有搜索文本。默认情况下,此路径由index.php文件处理。然而,可能存在搜索结果页面需要与常规存档页面不同的场景。在内容中突出显示搜索项的需求,是我们需要单独设计的实例之一。在这种情况下,我们可以使用包含与index.php文件相同内容的search.php文件,并根据客户的定制要求进行修改。

在本节中,我们对完整的 WordPress 主题模板层次结构进行了详细的介绍。您可能已经对模板的所有理论方面感到疲惫。然而,深入了解这些模板对于构建高级网站设计和定制现有网站至关重要,在这些网站中,您将需要不断与新的主题合作。

条件性更改模板设计和内容

主题的主要目的是为您的网站提供一致的布局。我们只能使用一个主题,因为混合来自不同主题的不同布局会创建对比鲜明的设计,使最终用户感到困惑。相反,我们可以在主题内部条件性地更改设计,而不会失去一致性。

现在,你可能想知道,什么是统一的布局?基本上,设计中的元素应该在所有屏幕上保持相似。假设我们在页面上使用 UI 组件库构建了一个表单。那么,应该声明的是,网站上其他部分的表单应该使用相同的 UI 组件库。如果你在其他部分使用不同的 UI 元素,用户可能会感到困惑。同样,设计中的每个其他元素都需要保持一致性。然而,这并不意味着我们不能在不同部分的网站上改变设计。更多关于这一点将在接下来的章节中讨论。

什么是条件模板设计?

在网站开发中,我们可能需要改变不同部分的设计和内容,以便进行以下操作:

  • 匹配用户偏好:许多现代网站允许用户选择内容偏好。因此,用户有多种选择,例如选择在任何给定屏幕上显示哪些组件,从某些类别过滤内容,甚至布局和配色方案。

  • 仅显示帖子/页面的相关内容:通常,我们有一个侧边栏,里面充满了许多小工具。在某些情况下,这些内容可能与主要内容没有直接关系。假设我们有普通文章和另一种称为书籍的帖子类型。在书籍中,我们可能有一个侧边栏小工具来显示诸如评分、ISBN、页数和纸张类型等信息。这与普通文章不相关,因此需要有条件地移除。

  • 突出显示不同内容类型的各个部分:不同的内容类型需要特定的布局。假设我们网站上既有普通文章也有电子产品。对于普通文章,我们显示标题、图片和内容。这种风格对于产品来说是不适用的,因为我们需要一种吸引顾客的方式。因此,我们需要通过使用图片画廊、图片滑块或可展开的标签来突出产品特性。总的来说,我们需要根据内容类型有条件地改变布局以突出内容。

我们已经讨论了 WordPress 模板层次结构。内置的模板层次结构提供了许多模板类型,以便进行有条件的变化。了解模板和内容变化的可能,而不需要作为开发者付出太多努力,这是很重要的。我们可以从基本样式到页眉、页脚、侧边栏、菜单,甚至整个模板应用有条件的变化。

根据变化类型,有几种方式可以有条件地改变设计。在本节中,我们将讨论两种简单的技术。

在主题文件中添加条件

这是最简单的作为开发者开始的方式。在这种技术中,我们修改现有的主题文件,添加条件,并在这些条件内加载不同的元素和内容。我们已经讨论了更改核心主题文件的缺点。在这里,我们需要在子主题中实现这项技术。你可以在本章前面创建的 Twenty Seventeen 子主题中实现这些技术。

让我们通过为我们的网站创建有条件的页眉来理解这项技术。通常,WordPress 网站上的页面是静态的,提供有关网站的信息。关于我们、服务和联系是一些常见的页面用途。另一方面,帖子或自定义帖子类型页面高度动态,包含各种内容,如促销、广告、社交分享选项等。因此,存在需要为帖子页面使用不同页眉的场景。现在让我们保持页眉不变,并修改带有顶部广告的帖子页眉。以下是对Twenty Seventeen主题默认页眉部分的截图:

图片

首先,你需要将Twenty Seventeenheader.php文件复制到子主题的根目录。接下来,你可以定义条件来更改帖子页眉部分。以下代码为正常帖子模板的页眉部分添加了一个广告栏:

<?php if(is_single()) { ?> 
  <div id="wpquick-article-ads" style="padding:20px;text-align:center;font-size:20px;background:red;color:#FFF;"> GET MEMBERSHIP WITH 30% DISCOUNT </div> 
<?php } ?> 

上述代码应在子主题中的header.php文件的<header>标签之后添加。现在,你可以查看单个帖子,页眉将包含动态广告栏,如下面的截图所示:

图片

这种技术可以有效地用于在只有有限不同条件的情况下有条件地更改模板。然而,由于它会复杂化模板文件,因此不适合处理复杂条件。

我们可以使用相同的过程有条件地更改网站的其他重要部分。让我们快速看一下更改这些部分所涉及的步骤:

  • 页脚:我们需要将父主题中的footer.php文件复制到子主题中。然后,我们根据必要条件遵循相同的流程来添加/删除内容。

  • 侧边栏:我们需要将父主题中的sidebar.php文件复制到子主题中。然后,我们根据必要条件遵循相同的流程来加载不同的侧边栏或向现有的侧边栏模板中添加静态内容。

  • 菜单:通常,菜单是通过 wp_nav_menu 函数生成的,该函数位于主题的 header.php 文件中。在这种情况下,我们使用的是 Twenty Seventeen 主题,因此你可以在 template-parts/navigation/navigation-top.php 文件中找到这个函数。你可以复制这个模板并修改它,以便根据必要的条件加载不同的菜单。

在本节中,我们探讨了根据条件更改网站标题的过程。你可以扩展这项技术,在标题中进行重大设计更改,以及使用其他条件,如主页、分类页面、特定页面等。你可以在 codex.wordpress.org/Template:Conditional_Tags 找到用于添加各种类型条件的可用模板标签列表。

条件加载模板文件

在这项技术中,我们使用现有的 WordPress 过滤器根据自定义条件动态更改模板。WordPress 在加载默认模板之前执行一个名为 template_include 的过滤器,基于我们之前讨论的层次结构。在模板层次结构部分,我们通过使用给定路径中的前一个模板来更改模板。使用此过滤器允许我们在任何给定路径中更改任何模板。让我们看一下以下代码,以了解这项技术的实现:

add_filter( 'template_include', 'wpquickstart_conditional_template', 99 ); 
function wpquickstart_conditional_template( $template ) { 
   if ( is_page( 'portfolio' ) ) { 
         $new_template = locate_template( array( 'portfolio-page-template.php' ) ); 
         if ( !empty( $new_template ) ) {  
               return $new_template; 
         } 
   } 
   return $template; 
} 

默认模板的位置作为参数传递给此过滤器。在这种情况下,我们检查名为 portfolio 的页面是否被加载。如果条件匹配,我们将返回一个动态的新模板而不是默认模板。前面的代码将在主题的根目录中查找模板。这种方法的优势在于能够同时在不同模板路径上使用不同的条件并加载必要的模板。我们还可以使用这项技术根据条件如 URL 参数、登录用户或用户角色以及请求的引用来加载动态模板。

作为一名开发者,你必须了解这项技术。可能有网站在主题的 functions.php 文件中以及使用自定义插件时使用这项技术。如果你不了解这项技术,你将不得不浪费大量时间浏览模板层次结构以找出加载的模板。

摘要

应用程序的前端是用户的主要交互点,因此请求前端更改的可能性相对较高。因此,使用主题的现有功能来使模板尽可能灵活是非常重要的。在本章中,我们探讨了主题及其组件在开发中的重要性。然后,我们开始使用子主题、动作和过滤器扩展现有主题。接下来,我们深入概述了主题模板层次结构,以便以灵活的方式构建新主题以及自定义现有主题。最后,我们探讨了根据高级站点需求有条件地更改设计的技巧。

在第四章《使用插件开发构建自定义模块》中,我们将探讨构建不同类型 WordPress 插件的过程,以及添加超出主题功能之外的功能。

第四章:使用插件开发构建自定义模块

丰富的免费插件库和基于插件的架构是 WordPress 作为 CMS 成功的关键。插件允许开发者构建独立的功能,以及与其他开发者开发的模块连接。我们可以使用插件来构建由全球顶级 WordPress 开发者编写的优质网站,而无需花费一分钱。从开发者的角度来看,插件允许您帮助成千上万的用户,同时还能推广您作为开发者的技能。任何具有基本编程知识的人都可以创建插件以满足特定应用的需求。然而,开发出可跨多个项目复用的优质插件需要相当大的努力。

在本章中,您将了解插件的概念以及它们与您的主题有何不同。您还将通过创建一个帖子附件插件来了解适当的插件的生存周期事件,以说明这些事件的使用。插件开发中的主要概念,如数据验证、清理和 nonce,将被介绍。了解如何利用现有的 WordPress 功能来构建可以添加或删除而不会影响其他部分的插件是本章的亮点。

在本章中,我们将涵盖以下主题:

  • 理解插件在开发中的作用

  • 从零开始创建基本插件

  • 探索插件的生存周期

  • 开发帖子附件插件

  • 识别自定义插件开发的优点

  • 构建高质量插件的指南

到本章结束时,您应该能够理解插件的作用,并具备使用推荐实践从头开始构建基本插件所必需的知识。

技术要求

遵循此流程需要您已安装 WordPress 4.9.8。即使您拥有更晚版本的 WordPress,描述的示例也应能无重大问题地运行。

即使您拥有更晚版本的 WordPress,描述的示例也应能无重大问题地运行。

本章的代码文件可以在 GitHub 上找到:

github.com/PacktPublishing/WordPress-Development-Quick-Start-Guide/tree/master/Chapter04

查看以下视频以查看代码的实际应用:

bit.ly/2Py2szO

理解插件的作用

插件是一组专门用于作为独立解决方案工作的函数,同时扩展或添加 WordPress 的新功能。在 WordPress 中,插件类似于我们在 iOS 和 Android 中使用的应用。我们有权使用或删除任何插件,而不会影响 WordPress 的核心功能。此外,插件允许我们将独立功能分离成各自的插件,使它们更容易维护。wordpress.org的插件库拥有超过 50,000 个免费插件,有时你甚至不需要为 WordPress 网站开发任何东西。你只需使用多个插件并正确集成它们,就可以构建高级功能。

插件在网站开发中起着至关重要的作用。在第三章中,使用主题开发设计灵活的前端,我们讨论了主题的作用以及主题范围之外的功能。基本上,任何超出主题范围的功能都应该使用插件来开发。然而,也有一些场景,我们也会使用插件来开发特定于主题的事物,如模板和样式,以便它们与多个主题兼容。在大多数情况下,插件应该要么使用它们自己的样式和设计,要么默认使用主题的样式。专注于特定主题的插件开发并不理想,因为在后期阶段几乎不可能切换到另一个主题。

在开发过程中,构建插件的范围可以从只有几行代码的过滤器实现到数千行代码的高级模块。让我们看看一些使用插件的常见开发任务:

  • 构建可重用块:WordPress 网站大多由非技术性的网站所有者构建,因此构建简单功能的块是常见任务。这些块允许客户在网站的任何位置添加功能,而无需开发者。我们可以使用短代码或小工具提供作为可重用块的特性。在现代网站上,页面构建器经常被用来构建页面设计。因此,我们也可以开发用于页面构建器的组件,这些组件将被用作可重用块。

  • 修改内置功能:这对于许多网站来说是必须的,尤其是对于扩展后端功能和自定义后端显示。由于这些是内置功能,我们不能修改 WordPress 文件来定制它们。因此,我们需要选择内置功能的动作和过滤器,并使用插件来实现它们。

  • 数据捕获和显示:这是另一种常见的需求类型,尤其是对于超出基本博客功能的网站。在这些任务中,我们需要在前端创建自定义表单,并使用后端功能存储数据或创建自己的数据存储。内置的自定义帖子类型和自定义构建表单用于处理此类需求。

  • 集成 UI 组件:现代网站充满了旨在在有限空间内显示大量数据的交互式用户界面组件。这些组件大多由开源 JavaScript 库提供支持。因此,集成这些库并允许管理员向这些组件添加数据是另一个常见需求。

在处理特定 WordPress 网站时,你将面临一些挑战。构建插件以处理这些类型的需求对于 WordPress 开发者来说是必不可少的,因此我们将在接下来的章节中介绍它们。然而,如果你计划开发自己的插件,挑战和可能性是巨大的。我们可以探索 WordPress 插件目录,了解插件能做什么以及涉及的范畴。

基于功能的 WordPress 插件类型

随着你继续探索,你会明白插件的作用可能各不相同。因此,识别这些不同类型的插件以面对扩展不同 WordPress 模块的挑战是很重要的。在这里,我们将选择一些不同插件类型的流行插件,并讨论它们如何与 WordPress 的前端和后端交互。这纯粹是个人偏好,因此在你探索插件目录后,你可能希望以不同的方式对它们进行分类:

  • Yoast SEO (wordpress.org/plugins/wordpress-seo/): 此插件通过在页面中添加必要的内容来提高网站的 SEO。此类插件在后台有界面,我们可以添加内容或设置。然而,这些插件在前端内部工作,因为我们看不到任何内容。这类插件的例子包括来自缓存、分析和翻译类别的插件。

  • WordPress 导入器 (wordpress.org/plugins/wordpress-importer/): 此插件通过文件将数据导入 WordPress 数据库。这类插件在前端或后端界面中没有任何功能。相反,这些插件通过更改数据库值来提供功能。这类插件的例子包括重复帖子插件和 All-in-One WP 迁移插件。

  • WooCommerce (wordpress.org/plugins/woocommerce/): 此插件通过创建和销售产品来简化在线购物过程。这类插件在后台有界面以添加内容,在前端也有界面以显示内容。除此之外,这些插件包含广泛的数据处理。这是你在开发任务中最常见的插件类型。这类插件的例子包括 BuddyPress 和 bbPress。

  • Meta Slider (wordpress.org/plugins/ml-slider/):此插件为网站的前端添加了优雅的图片滑块。这类插件具有用于添加内容的后端界面和用于在现代 UI 元素中显示内容的客户端界面。然而,不涉及数据处理。此类插件的例子包括 Easy FancyBox 和 Max Mega Menu。

  • 管理员菜单编辑器 (wordpress.org/plugins/admin-menu-editor/):此插件允许你更改菜单和相关设置。这类插件允许你添加、更改和删除内置的后端功能以满足你的需求。此类插件不与前端或数据交互。此类插件的例子包括 Admin Columns 和 Simple Page Ordering。

  • Easy Google Fonts (wordpress.org/plugins/easy-google-fonts/):此插件允许你为网站内容使用 Google 字体。这类插件通常没有与后端或前端相关的功能。相反,它们将第三方服务与 WordPress 连接。此类插件的例子包括 AddToAny 分享按钮和 Google 文档嵌入器。

  • 高级自定义字段 (wordpress.org/plugins/advanced-custom-fields/):此插件充当框架,允许开发者和客户使用现有组件构建表单。这类插件与后端和前端都进行交互。然而,这些插件不提供特定于站点的功能。相反,管理员决定向用户显示哪些元素以及如何处理数据。此类插件的例子包括 Elementor 页面构建器和 Pods 自定义内容类型和字段。

我们可以构建的插件类型非常多,这里无法全部涵盖,即使我们有一个专门章节来介绍它们。因此,我们选择了一些包含最流行开发任务的插件。

创建你的第一个插件

开发一个简单插件的过程并不复杂。它遵循了我们用于主题的相同流程,其中我们使用了 PHP 头部注释。首先,你必须在wp-content/plugins目录内创建一个新的目录,并给它一个首选的名称。在这个例子中,我们将将其命名为wpquick-after-post-content。一旦创建了插件目录,我们就可以创建主要的插件文件,即wpquick-after-post-content.php。你可以为主插件文件使用首选的名称。现在,我们必须添加以下头部注释部分来定义它为一个插件:

<?php
/*
Plugin Name: WPQAPC After Post Content
Plugin URI: http://www.wpexpertdeveloper.com/wpquick-after-post
Description: Add dynamic content after the post
Version: 1.0
Author: Rakhitha Nimesh
Author URI: http://www.wpexpertdeveloper.com
*/

插件定义注释与主题定义类似,其中主题名称和主题 URI 被插件名称和插件 URI 所替换。一旦注释被添加,这个目录就变成了一个插件,你可以刷新后端插件列表以反映插件的详细信息。现在,你可以在主插件文件或子文件中添加任何 PHP 代码,以开始使用 WordPress 进行开发。与常规 PHP 开发不同的是,这里使用的是 WordPress 钩子。我们将在本章中介绍必要的钩子。

让我们给这个插件添加一些功能。我们把这个插件命名为“帖子内容之后”。这个功能是在网站的每篇帖子之后添加动态内容。在第三章,“使用主题开发设计灵活的前端”,我们向头部部分添加了一个动态广告栏。在这里,我们将在帖子内容之后显示相同的广告。考虑以下代码来实现这个功能:

add_filter( 'the_content', 'wpqapc_file_attachment_display' );
function wpqapc_file_attachment_display( $content ){
  global $post;
  if( is_singular( 'post' ) ){
    $after_content = '<div id="wpquick-article-ads" style="padding:20px;text-align:center;font-size:20px;background:red;color:#FFF;">GET MEMBERSHIP WITH 30% DISCOUNT</div>';
    return $content . $after_content;
  }
  return $content;
}

这段代码被添加到主插件文件中,在头部注释部分之后。通常,我们不得不修改帖子模板来在纯 PHP 开发中添加这样的内容。在 WordPress 中,所有内容都钩接到一个动作或过滤器上,因此我们可以使用它们而不修改模板文件。在这里,我们使用了the_content过滤器与一个回调函数。我们已经在第三章,“使用主题开发设计灵活的前端”中讨论了过滤器的作用。这个过滤器用于修改帖子/页面内容,并且包含在所有正确编码的主题中。默认的帖子内容传递给这个过滤器,插件可以通过实现这个钩子来改变这个内容。

在这个场景中,我们使用了is_singular函数进行条件检查。这个函数用于检查我们是否正在查看任何给定帖子类型的单页。一旦条件匹配,我们就在现有帖子内容之后添加动态广告栏。现在,你可以在前端创建和查看新帖子。我们添加的内容将以与以下截图所示类似的方式显示:

图片

我们在非常短的时间内创建了一个简单的插件。WordPress 插件可以从几行代码如这一例,到数千行代码的复杂系统。因此,正如我们所看到的,开发 WordPress 插件并不困难。将即使是几行代码也分离成单独的插件总是一个好选择,假设这些功能对许多网站都有用。在这个例子中,我们只向每篇帖子添加了一个固定的栏。在实际应用中,我们还需要更改这些广告,并具有从后端将它们添加到每篇帖子的能力。

探索插件的生命周期

产品生命周期是一系列从产品或流程开始到完成的事件或阶段。WordPress 插件包含生命周期事件,从初始激活到卸载。理解所有这些事件对于保持插件正常运行和数据一致性至关重要,即使在退出插件之后也是如此。许多开发者只知道其中的一些事件,导致插件质量低下。

在本节中,我们将探讨插件的整个生命周期以及如何管理生命周期中的每个事件。以下图表展示了插件中事件的基本示意图:

插件生命周期图

如您所见,插件的生命周期包含五个事件,除了其功能之外。插件通过激活事件开始生命周期。通过停用或卸载事件完成周期,然后再次通过激活事件开始新的周期。让我们了解这五个事件:

  • 激活:这是一个内置事件,当从 WordPress 插件列表或安装屏幕点击激活按钮时触发。这可以用于在激活时执行某些操作,以及初始化插件的数据和设置。理想情况下,初始化任务应该通过一个单独的安装过程来完成,因为插件激活不是一次性的事件。然而,只有少数插件提供单独的安装过程,因为它是一个特定于插件的事件。因此,大多数开发者将使用 WordPress 的内置激活过程来初始化插件,并进行必要的条件检查。在激活过程中执行的一些实际功能包括添加重写规则、创建/修改数据库表、添加默认设置以及创建演示数据。

  • 安装:这不是一个内置事件,大多数 WordPress 插件使用激活事件进行安装。然而,对于复杂的插件,我们可能需要一个类似于 WordPress 的逐步安装过程。通过这种方式,我们可以在激活后使用自定义代码实现自己的安装过程。您可以通过安装 WooCommerce 来查看如何通过自定义屏幕处理特定于插件的安装过程。

  • 升级:这是一个 WordPress 插件库中插件的内置事件,在版本升级完成后使用。然而,除非我们构建一个自定义的升级过程从我们的服务器获取文件,否则此事件对其他插件不可用。因此,我们需要通过结合现有的 WordPress 钩子和条件检查来处理这种情况,在没有自定义自动升级过程的情况下。此事件可以用于在新版本中添加默认值以设置附加设置、更改数据库表、将现有数据转换为新的格式等。

  • 停用:这是一个由点击 WordPress 插件列表中的停用链接触发的内置事件。这个动作应该用于在插件停用之前执行某些任务。通常,开发者会误解这个动作的用途,并倾向于将其用作卸载过程。停用是一个临时事件,因此你在这个钩子中不应该删除插件数据。理想情况下,这个事件应该用于诸如删除重写规则、清除缓存和临时数据库值等功能。

  • 卸载:这也是一个由点击 WordPress 插件列表中的删除链接触发的内置事件。这是处理卸载的正确事件,必须用于清除插件特定的数据。许多插件没有卸载过程,导致插件数据留在数据库中。一旦插件被停用,插件数据不会影响网站的功能。然而,这些数据会影响网站的性能,并使其容易与未来将使用的插件发生冲突。因此,为插件实现卸载事件以及指导用户在卸载后会发生什么非常重要。

在下一节中,我们将使用插件最重要的和强制性的生命周期事件来开发一个插件。

开发一个帖子附件插件

在前两个章节中,我们讨论了构建基本插件和生命周期事件的步骤。在本节中,我们将开发一个插件,同时涵盖生命周期事件在插件开发中的实现和关键特性。让我们总结一下插件的需求。

在大多数 WordPress 网站上,使用博客文章并在帖子后显示动态内容是常见的。我们已经在前开发的第一个插件中查看过在帖子后添加内容的过程。现在,我们将扩展功能,允许在帖子后下载附件。在一些网站上,允许用户下载与帖子相关的文件是必不可少的。这个插件将允许管理员从后端向帖子添加文件,并允许用户通过在帖子内容后显示这些文件来下载这些文件。

让我们从在wp-content/plugins目录内创建一个名为wpquick-post-attachments的新目录开始,来开发我们的插件。然后,我们需要在新目录内创建主插件文件,命名为wpquick-post-attachments.php。像往常一样,我们需要在这个文件中添加插件头部注释部分,以便 WordPress 将其列为插件。你可以使用之前插件的插件头部注释,只需修改名称和描述即可。

现在,我们必须确定这个插件开发过程中涉及的任务:

  • 创建一个自定义表来存储每篇帖子的附件详情

  • 在帖子创建屏幕中添加一个文件字段来上传附件

  • 在自定义表中上传和保存附件

  • 在帖子内容后显示附件并具有下载功能

让我们从在之前讨论的插件生命周期事件中添加这些功能开始开发过程。

在插件激活时创建设置

插件的激活过程由内置的register_activation_hook函数处理。在这种情况下,我们正在开发一个简单的插件来阐述插件开发中概念的重要性。在这里,我们将使用内置的激活事件来初始化此插件设置和数据,而不是创建单独的安装过程。将以下代码添加到wpquick-post-attachments.php文件中,在头部注释部分之后:

register_activation_hook( __FILE__, 'wpqpa_activate' );
function wpqpa_activate(){
  global $wpdb;
  $table_attachments = $wpdb->prefix . 'wpqpa_post_attachments';
  $sql_attachments = "CREATE TABLE IF NOT EXISTS $table_attachments (
                 id int(11) NOT NULL AUTO_INCREMENT,
                 file_name varchar(255) NOT NULL,
                 user_id int(11) NOT NULL,
                 post_id int(11) NOT NULL,
                 file_path longtext NOT NULL,
                 updated_at datetime NOT NULL,
                 uploaded_file_name varchar(255) NOT NULL,
                 PRIMARY KEY (id)
                 );";

  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
  dbDelta( $sql_attachments );
  $default_headers = array('Version' => 'Version');
  $plugin_data = get_file_data(__FILE__, $default_headers, 'plugin');
  update_option( 'wpqpa_version',$plugin_data['Version'] );
}

我们使用register_activation_hook函数来注册一个在激活过程中要执行的自定义函数。第一个参数接受主插件文件的路径,而第二个参数用于定义函数名称。在这里,我们使用了一个名为wpqpa_activate的函数。

我们通过创建一个自定义表来存储帖子附件路径和其他必要细节来开始插件激活过程。我们已经在第二章中讨论了创建自定义表和dbDelta函数的使用,因此本节中不会重复解释。除了表创建之外,我们还可以保存初始数据或运行插件所需的设置。在这种情况下,我们通过使用get_file_data函数从头部注释部分捕获插件版本,并将其保存到具有特定键wpqpa_versionwp_options表中。

我们使用激活过程在数据库中生成必要的配置。在开发这些功能之前,我们需要指定必要的路径以访问插件中的其他文件。因此,让我们通过以下代码在主插件文件中添加一些常量:

if ( ! defined( 'WPQPA_PLUGIN_DIR' ) ) {
  define( 'WPQPA_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
}
if ( ! defined( 'WPQPA_PLUGIN_URL' ) ) {
  define( 'WPQPA_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
}

首先,我们必须使用defined函数来检查是否已经定义了具有给定名称的常量,以防止错误。在这里,我们添加了两个常量来定义插件目录内插件文件的 URL 和完整目录路径。这两个常量将在后续部分中用于加载插件的脚本、样式和 PHP 文件。现在,我们可以进入构建插件功能的过程。

实现帖子附件上传

帖子创建屏幕设计为允许开发者通过使用自定义元框来扩展功能。在这种情况下,我们需要一种让管理员能够将文件上传到帖子中的方法。因此,我们需要定义一个自定义元框来显示输入元素,如下面的代码所示:

add_action( 'add_meta_boxes_post', 'wpqpa_post_attachments_meta_box' );
function wpqpa_post_attachments_meta_box( $post ){
  add_meta_box( 'wpqpa-post-attachments', __( 'Post attachments', 'wpqpa' ),
'display_post_attachments_meta_box', 'post', 'normal', 'high' );
}

add_meta_boxes_post动作用于为普通帖子注册一个新的元框。在这个动作内部,我们可以使用add_meta_box函数创建一个新的元框。

如果你正在为特定文章类型注册元框,建议你使用add_meta_boxes_{post_type}动作。在元框为多个文章类型注册的场景中,你可以使用通用的add_meta_boxes动作。

此函数的前三个参数是必需的,分别用于唯一的元框 ID、元框标题和回调函数。在这种情况下,我们正在调用主插件文件中的一个函数,因此我们可以直接使用函数名。然而,如果回调函数位于 PHP 类中,你需要使用以下语法来在类的对象上调用函数:

$wpqpa = new WPQPA();
add_meta_box( 'wpqpa-post-attachments', __( 'Post attachments', 'wpqpa' ),
array($wpqpa, 'display_post_attachments_meta_box') , 'post', 'normal', 'high' );

上述代码中的剩余三个参数分别用于屏幕、上下文和优先级。让我们看看这三个参数的使用方法:

  • 屏幕: 此参数指定了用于显示元框的 WordPress 屏幕 ID 或 ID 数组。WordPress 中的每个屏幕都有一个特定的 ID,以便开发者为特定屏幕构建功能。在这里,我们使用了“文章”作为屏幕,因为我们只为普通文章显示元框。您可以通过访问codex.wordpress.org/Plugin_API/Admin_Screen_Reference来检查管理屏幕的可用的屏幕 ID。

  • 上下文: 此参数定义了屏幕内的位置。对于文章屏幕,我们有三个选项:普通、高级和侧边。默认值是高级,在文章编辑器下方显示元框。然而,如果创建了一个普通元框,它将显示在具有高级上下文的元框之前。侧边选项将元框移动到侧边栏,与发布按钮一起。

  • 优先级: 此参数定义了元框在上下文中的位置。我们有四个值,称为高、核心、默认和低。高值将元框放置在上下文顶部,而随着我们移动到低选项,优先级将降低。

接下来,在我们可以检查文章屏幕上的新元框之前,我们需要完成回调函数的实现。让我们考虑以下用于实现display_post_attachments_meta_box函数的代码:

function display_post_attachments_meta_box( $post, $metabox ){
  global $wpdb;
  $display = '<div class="wpqpa-files-panel" >
   <div class="wpqpa-files-add-form" >
     <div class="wpqpa-files-msg" style="display:none" ></div>
     <div class="wpqpa-files-add-form-row">
     <div class="wpqpa-files-add-form-label">'.__("File Title","wpqpa").'</div>
     <div class="wpqpa-files-add-form-field">
     <input type="text" class="wpqpa-file-name" name="wpqpa_file_name" />
     </div>
     </div>
     <div class="wpqpa-files-add-form-row">
     <div class="wpqpa-files-add-form-label">'.__("File","wpqpa").'</div>
     <div class="wpqpa-files-add-form-field">
     <input type="file" class="wpqpa-file" name="wpqpa_file" />
     </div>
     </div>
    <div class="wpqpa-clear"></div>
  </div>';
  $display .= wp_nonce_field( "wpqpa_attachment", "wpqpa_nonce", true, false );
  echo $display;
}

此函数自动接收文章对象和元框详情作为参数。大部分代码都是自我解释的,因为它包含容器元素和必要的 HTML 字段。然而,最后一行对于开发者来说非常重要。WordPress 使用一种称为nonce的技术来提高你代码的安全性。让我们看看 nonce 的定义,它已经从官方手册中提取出来:

“一次性数是一个“一次使用”的数字,用于帮助保护 URL 和表单免受某些类型的滥用,无论是恶意的还是其他类型的。WordPress 的一次性数不是数字,而是由数字和字母组成的哈希值。它们也不是只使用一次,而是在有限的生命周期后过期。在此期间,给定用户在给定上下文中将生成相同的一次性数。该动作的一次性数将保持不变,直到该一次性数生命周期完成。”

简而言之,一次性数用于验证用户请求并保持提交数据的完整性。在插件开发中,尤其是在开发供公众使用的插件时,使用一次性数验证是必须的。wp_nonce_field函数用于添加一个带有一次性数值的隐藏字段。此函数的第一个参数是在验证过程中将使用的动作名称,第二个参数是 HTML 隐藏字段的名称。我们可以不使用这些参数中的任何一个来使用wp_nonce_field函数,但强烈建议使用这两个选项。以下是从使用wp_nonce_field函数生成的示例输出:

<input type="hidden" id="wpqpa_nonce" name="wpqpa_nonce" value="bff32cce02">
<input type="hidden" name="_wp_http_referer" value="/wp-admin/post.php?post=9&action=edit">

这个一次性值将在数据保存过程中用于验证。到目前为止,我们已经添加了必要的 HTML 来显示文件上传输入字段。然而,如果没有必要的样式,这些元素可能会到处显示。因此,我们需要在我们的插件目录内创建一个名为css的目录,并添加一个 CSS 文件,命名为wpqpa.css。然后,我们需要通过使用推荐的 WordPress 操作来包含这个 CSS 文件。以下是一个包含 CSS 文件的代码示例:

add_action( 'admin_enqueue_scripts', 'wpqpa_load_scripts',9 );
add_action( 'wp_enqueue_scripts', 'wpqpa_load_scripts',9 );
function wpqpa_load_scripts(){
  wp_register_style( 'wpqpa_css', WPQPA_PLUGIN_URL . 'css/wpqpa.css' );
  wp_enqueue_style( 'wpqpa_css' );
}

在 WordPress 中,我们需要使用admin_enqueue_scripts动作来添加脚本和样式文件到后端,并使用wp_enqueue_scripts动作来处理前端。在这种情况下,我们在前端和后端都显示了相同的文件列表。因此,我们使用这两个动作来加载我们创建的 CSS 文件。首先,我们必须使用带有唯一键和路径的wp_register_style函数注册样式文件。然后,我们使用wp_enqueue_style函数将样式文件添加到页面中。脚本和样式加载的更多细节和高级用法将在后续章节中讨论。本章节不讨论此插件的 CSS 样式。你可以使用本章的源文件来理解和修改它们。

现在,我们可以看一下带有我们的元框修改后的帖子创建屏幕,如下面的截图所示:

图片

自定义元框显示在帖子编辑器下方。然而,在一个单站点中,我们可能会使用许多带有自定义元框的插件。因此,除非你愿意更改这些插件的代码,否则你无法将元框放置在我们需要的确切位置。

上传附件和保存附件数据

在此过程中下一步是上传并保存管理员选择的文件。在这种情况下,我们通过定义的字段在元框中添加文件和文件标题。然后,管理员需要点击文章的“发布”或“更新”按钮来保存附件。

理想情况下,附件详情应使用 AJAX 上传并保存,以支持多文件上传而无需更新文章。在这种情况下,我们使用了正常的表单提交,因为我们尚未引入 AJAX,并且我们需要了解处理正常 POST 请求的过程。

让我们通过以下代码查看附件保存过程,该代码已被添加到我们的插件主文件中:

add_action( 'init', 'wpqpa_save_private_attachment_files' );
function wpqpa_save_private_attachment_files(){
  global $wpdb;
  if( ! isset( $_POST['wpqpa_file_name'] ) ){
    return;
  }
  $file_name = isset($_POST['wpqpa_file_name']) ?        sanitize_text_field($_POST['wpqpa_file_name']) : '';
  $file_nonce = isset( $_POST['file_nonce'] ) ? ( $_POST['file_nonce'] ) : '';
  $post_id = isset( $_POST['post_ID'] ) ? (int) ( $_POST['post_ID'] ) : 0;
  $user_id = get_current_user_id();
  // Remaining code
}

通常,开发者可以直接在任何网站文件中访问 POST 请求数据。然而,WordPress 使用基于事件的架构,因此我们需要实现适当的钩子来访问这些数据。在适当的 WordPress 事件之外访问请求数据可能会导致冲突或数据不可用。因此,我们使用 init 动作来访问 POST 数据,因为用户请求在到达 init 动作时已经完全加载。关于动作执行过程的更多内容将在第五章“使用插件扩展、过滤器、和动作”中讨论,第五章。

init 动作将为每个请求执行,因此我们需要确保我们仅在必要时使用自定义代码。因此,我们检查用户请求中 wpqpa_file_name 字段的可用性,当它不可用时,我们不执行自定义代码并返回。然后,我们必须从 POST 请求中获取必要的数据。在这里,我们需要三个值:文件名、nonce 和文章 ID。

你可能已经注意到了一个名为 sanitize_text_field 的函数的使用。此函数用于从文本字段提交中删除不必要的标签、空格、换行符和字符。你不应该信任传入的数据;这就是为什么在记录之前你应该使用 sanitize_text_field。我们应该始终验证并限制用户输入值,以提高代码的安全性并防止数据库中的冲突。WordPress 通过验证、清理和转义来使用三个步骤来保护数据:

  • 清理:在这个阶段,我们必须获取用户提交的数据并确保它包含接受的值。WordPress 提供了一组用于清理用户输入并使其安全的函数。sanitize_text_field 是其中的一个函数,共有 13 个清理函数。可用的函数及其用途在 codex.wordpress.org/Validating_Sanitizing_and_Escaping_User_Data#Sanitizing:_Cleaning_User_Input 中解释。

  • 验证:在这个阶段,我们验证上一阶段清理后的输入值。此步骤的目的是检查用户提交的数据是否与接受的值匹配。与清理不同,没有特定的验证函数。我们可以使用 PHP 和 WordPress 函数的自己的条件进行验证。验证数据类型、字符串长度、数据格式和空值是此步骤的一些常见用途。

  • 转义:此步骤用于向用户显示数据时。此步骤的目的是确保要显示的数据安全。WordPress 提供了一套基于不同数据的内置转义函数。我们将在接下来的章节中讨论转义的使用。可用的转义函数及其使用在codex.wordpress.org/Data_Validation#Output_Sanitation中解释。

根据表单中使用的字段类型和接受的值,您应该从这三个部分中选择必要的函数来验证用户数据。

处理过程的下一步是验证输入数据和上传附件。考虑以下代码,它放置在输入数据捕获代码之后:

if ( ! isset( $_POST['wpqpa_nonce'] ) || ! wp_verify_nonce( $_POST['wpqpa_nonce'], 'wpqpa_attachment' ) ) {
  $result_upload = wpqpa_process_file_upload();
  // Upload code section 1
}else{
  // Handle error for invalid data submission
}

首先,我们需要通过使用我们在自定义元框中添加的 nonce 值来验证用户请求。我们首先通过使用isset($_POST['wpqpa_nonce'])条件来检查 nonce 值的存在。然后,我们可以使用wp_verify_nonce函数来验证提交的 nonce 值。这个函数的第一个参数接受 nonce 字段的值,第二个参数接受 nonce 字段的动作名称。一旦 nonce 成功验证,我们就可以上传附件。

在这里,我们使用一个名为wpqpa_process_file_upload的函数来处理附件上传。wpqpa_process_file_upload函数中的大部分代码不是 WordPress 特定的,因此是自解释的。您可以使用本章的源代码文件来了解此函数的实现。

现在,我们必须回到wpqpa_save_private_attachment_files函数,我们在其中添加了注释上传代码部分 1。从上传文件的功能生成数据存储在$result_upload变量中。让我们继续实现,将附件详细信息保存到自定义数据库表中:

if( isset( $result_upload['status'] ) && $result_upload['status'] == 'success' ){
  $file_date = date("Y-m-d H:i:s");
  $uploaded_file_name = $result_upload['base_name'];
  $wpqpa_post_attachments_table = "{$wpdb->prefix}wpqpa_post_attachments";
  $wpdb->insert(
    $wpqpa_post_attachments_table,
    array(
      'file_name' => $file_name,
      'user_id' => $user_id,
      'post_id' => $post_id,
      'file_path' => $result_upload['relative_file_path'],
      'updated_at' => $file_date,
      'uploaded_file_name' => $uploaded_file_name,
    ),
    array( '%s', '%d', '%d', '%s', '%s', '%s' ) );
}else{
  // Handle file upload errors
}

首先,我们通过使用$result_upload变量的状态选项来检查附件上传是否完成。一旦条件匹配,我们就从$result_upload变量中获取文件名,并在一个变量中定义自定义表名。接下来,我们使用全局$wpdb对象的insert函数将数据保存到插件激活事件中创建的wpqpa_post_attachments表中。我们已经在第二章中讨论了insert函数的使用,因此在此部分中不再解释。

现在,附件上传过程的代码已经完成。然而,在这个阶段你将无法上传附件。为了上传文件,你需要将 HTML 表单的enctype更改为multipart/form-data。默认情况下,WordPress 不会向帖子提交表单添加enctype属性。而且,由于表单代码是在 WordPress 内部生成的,所以无法手动添加。因此,我们需要使用内置的post_edit_form_tag动作来添加必要的enctype,如下面的代码所示:

add_action( 'post_edit_form_tag' , 'wpqpa_post_edit_form_tag' );
function wpqpa_post_edit_form_tag( ) {
  echo ' enctype="multipart/form-data" ';
}

现在,整个过程已经完成,你应该能够通过使用帖子发布或更新按钮上传文件来测试它。上传的附件数据应该反映在自定义表中。

显示上传的附件

我们需要一种方法在帖子编辑屏幕以及在前端帖子内容之后显示上传的文件。为此,我们将使用一个名为wpqpa_file_attachment_list的通用函数来生成附件列表,如下面的代码所示:

function wpqpa_file_attachment_list( $post ){
  global $wpdb;
  $display = '<div class="wpqpa-files-list" >';
  $sql = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wpqpa_post_attachments WHERE post_id = %d order by updated_at desc ", $post->ID );

  $files_list = $wpdb->get_results( $sql );
  foreach( $files_list as $file_row ){
    $url = get_permalink( $file_row->post_id );
    $url = wpqpa_add_query_string( $url, "wpqpa_file_download=yes&wpqpa_private_file_id =". $file_row->id."&wpqpa_post_id=".$file_row->post_id );

    $display .= '
      <div class="wpqpa-file-item" id="PF'.$file_row->id.'" data-file-id="'.$file_row->id.'" >
      <div class="wpqpa-file-item-row" >
      <div class="wpqpa-file-item-name wpqpa-files-list-name" >'.$file_row->file_name.'</div>
      <div class="wpqpa-file-item-download" ><a href="'.$url.'" >'.__("Download","wpqpa").'</a></div>
      <div class="wpqpa-clear"></div>
      </div>
      <div class="wpqpa-clear"></div>
      </div>';
  }
  $display .= '</div>';
  return $display;
}

此函数放置在主插件文件中,并接受一个帖子对象作为参数。首先,我们使用$wpdb对象的prepare函数来安全地准备执行 SQL 查询。在这个函数中,我们需要为所有用户输入数据分配占位符,并将值作为参数传递。一旦查询已准备就绪,我们就可以使用get_results函数来执行查询,并获取与特定帖子相关联的文件列表。

接下来,我们使用foreach语句遍历结果附件。在循环内部,我们通过使用内置的get_permalink函数获取帖子的 URL。然后,我们使用自定义的wpqpa_add_query_string函数向 URL 中添加文件特定的查询参数。你可以在本章的源代码目录中的主插件文件中检查此函数的实现。此 URL 用于识别附件,并在实现的后阶段提供下载功能。最后,我们添加必要的 HTML 元素和容器,包含附件信息和下载链接。准备好的 HTML 将从此函数返回。

现在,我们需要在display_post_attachments_meta_box函数中调用此函数,并在文件上传字段之后显示附件列表。上传几个文件后,你的屏幕将类似于以下截图所示:

在这里,我们只使用了下载作为操作按钮以保持实现简单且对本书有用。通常,你将需要其他操作,如编辑和删除附件。

在帖子中显示附件

此插件的主要要求是将文件附加到帖子中,并允许用户从帖子页面下载它们。到目前为止,我们已经创建了保存和显示附件的背景。现在,是时候通过在每篇帖子的内容后显示它们来实现主要要求。

在本章开始时,我们创建了一个插件来使用the_content过滤器并在帖子后添加动态内容。同样的技术将在此场景中使用。让我们考虑以下代码来实现the_content过滤器:

add_filter( 'the_content', 'wpqpa_file_attachment_display' );
function wpqpa_file_attachment_display( $content ){
  global $post;
  if( is_singular( 'post' ) ){
    return $content . wpqpa_file_attachment_list( $post );
  }
  return $content;
}

如同往常,我们添加了the_content过滤器以及回调函数来处理附件的显示。首先,我们使用is_singular条件函数检查我们是否正在查看单个帖子。如果条件不匹配,我们返回内容,因为附件仅适用于正常帖子。一旦条件匹配,我们调用我们之前创建的可重复使用的函数来显示帖子的附件列表。以下截图预览了在单个帖子页面中附件的显示方式:

现在,我们已经到达了最后阶段,需要实现一个文件下载链接以完成插件。

实现帖子附件下载

对于已经使用纯 PHP 实现文件下载的开发者来说,本节内容非常直接。在前一节中,我们使用一些自定义参数创建了一个下载链接。在这里,我们必须使用这些参数来识别附件。让我们通过在init动作上添加一个自定义下载函数来开始此实现:

add_action( 'init', 'wpqpa_file_attachment_download' );
function wpqpa_file_attachment_download(){
  global $wpdb;
  if( isset( $_GET['wpqpa_file_download'] ) && sanitize_text_field($_GET['wpqpa_file_download']) =='yes' && isset( $_GET['wpqpa_private_file_id'] ) ){

    $wpqpa_file_download = $_GET['wpqpa_file_download'];
    $wpqpa_file_id = isset( $_GET['wpqpa_private_file_id'] ) ? (int) $_GET['wpqpa_private_file_id'] : '';
    $wpqpa_post_id = isset( $_GET['wpqpa_post_id'] ) ? (int) $_GET['wpqpa_post_id'] : '';

    if( $wpqpa_file_id != '' && $wpqpa_post_id != '' ){
      $sql = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wpqpa_post_attachments WHERE id = %d AND post_id = %d order by updated_at desc ", $wpqpa_file_id, $wpqpa_post_id );
      $attachments = $wpdb->get_results( $sql,ARRAY_A );
      if( ! isset( $attachments[0] ) ){
        return;
      }
      $file_link = site_url() . $attachments[0]['file_path'];
      $upload_dir = wp_upload_dir();
      $file_dir = $upload_dir['basedir'] . $attachments[0]['file_path'];
      // Remaining code for file downloads
    }
  }
}

正如我们已经讨论过的,将每个自定义函数钩入合适的 WordPress 动作非常重要。在这种情况下,我们选择了 init 动作,因为 WordPress 已经完成加载,我们需要从 GET 请求中访问数据。我们从这个函数开始,检查 URL 中的 GET 参数以确保这是一个下载帖子附件的请求。你应该注意 sanitize_text_field 的使用,即使是对于 GET 参数,它仅用于验证。然后,我们通过使用带有必要验证的 URL 参数来访问附件 ID 和帖子 ID。接下来,我们执行一个 SQL 选择查询,使用之前章节中讨论的数据库函数来查找附件详情。然而,get_results 函数有一个细微的差别,那就是我们使用了一个额外的参数,称为 ARRAY_A。这是用来定义结果集格式的。在之前的章节中,我们没有使用这个参数,因此我们得到了一组对象作为结果集。一旦我们使用这个选项,结果将以关联数组的形式提供。接下来,我们检查匹配的附件是否存在,并使用结果集来定义附件 URL 和目录路径。

这里是编写文件下载代码的一种方法:

$file_mime_type = mime_content_type( $file_dir );
if( $file_mime_type != '' ){
  header( 'Cache-Control: public' );
  header( 'Content-Description: File Transfer' );

  if( isset( $attachments[0]['uploaded_file_name'] ) && $attachments[0]['uploaded_file_name'] != '' ){
    header( 'Content-disposition: attachment;filename='.$attachments[0]['uploaded_file_name'] );
  }else{
    header( 'Content-disposition: attachment;filename='.basename( $file_dir ) );
  }
  header( 'Content-Type: '. $file_mime_type );
  header( 'Content-Transfer-Encoding: binary' );
  header( 'Content-Length: '. filesize( $file_dir ) );
  readfile( $file_dir);
  exit;
}

首先,我们使用文件目录路径来获取文件的 MIME 类型,因为保持下载文件的完整性非常重要。当 MIME 类型不为空时,我们使用必要的 PHP 头部来下载文件。我们使用基于时间数值的动态名称上传文件。让用户下载这样的文件名称并不理想。因此,我们使用条件检查来添加存储在我们自定义表中的原始文件名。当原始文件名在数据库中不可用时,我们回退到使用文件的基名。其余的代码包含文件下载的常规 PHP 头部和 readfile 函数来输出文件内容。现在,我们已经完成了插件的函数功能。你应该能够从显示在帖子内容之后的列表中下载附件。

处理插件版本升级

版本升级过程由内置的 upgrader_process_complete 钩子处理。我们可以实现这个钩子来添加升级所需的所有数据更改。我们没有内置的升级事件可以同时处理来自 WordPress 目录的插件以及自定义插件。因此,我们必须通过使用现有的钩子和条件检查来构建自定义的升级过程。

正如我们已经讨论过的,WordPress 插件目录提供了一个自动升级过程。自定义插件默认不会有这样的功能。因此,我们需要为自定义插件构建一个自动升级过程。许多自定义插件没有这样的功能,因此我们已将此类实现视为本章范围之外的内容。在这种情况下,用户必须停用当前版本并上传新版本,或者用新版本替换当前版本文件。因此,升级过程需要在文件替换后的初始请求中发生。

考虑以下代码,我们使用了wp_loaded动作来处理此插件的升级过程:

add_action( 'wp_loaded', 'wpqpa_upgrade_process' );
function wpqpa_upgrade_process(){
  $default_headers = array('Version' => 'Version');
  $plugin_data = get_file_data( __FILE__, $default_headers, 'plugin' );
  $stored_version = get_option('wpqpa_version');
  $current_version = $plugin_data['Version'];
  if ( !$stored_version && $current_version ) {
    update_option( 'wpqpa_version', $plugin_data['Version'] );
  }
  if ( version_compare($current_version, $stored_version ) >= 0 ) {
    update_option( 'wpqpa_max_upload_limit',20 );
  }
  update_option( 'wpqpa_version', $plugin_data['Version'] );
}

首先,我们使用wp_loaded钩子和回调函数,因为所有插件在执行此钩子时都会被加载。然后,我们需要比较我们是否正在使用插件的旧版本或新版本。因此,我们从插件头部注释中获取当前版本,从数据库中获取存储的版本。接下来,我们检查版本是否已在数据库中存储。版本在数据库中的不可用意味着我们正在首次安装插件,因此我们更新版本。

下一步是比较两个版本,看当前版本是否高于已安装的版本。在这种情况下,我们可以执行升级过程的任务。如果这些值相同,版本没有变化,不需要升级过程。在这个例子中,我们为一个新的设置添加一个默认值。在实际应用中,我们可以执行诸如更改现有数据、添加新的设置数据以及添加数据库表或列等任务。

处理插件停用

此事件由内置的register_deactivation_hook函数处理。此函数的使用与激活钩子类似。由于此插件包含基本功能,因此我们无法找到合适的实际用途来使用停用钩子。因此,我们将不会为此插件实现此事件。

卸载插件以清理资源

停用和卸载插件之间有很大的区别。清理资源应在停用后进行。WordPress 提供了两种实现卸载过程的方法。第一种方法是通过一个内置函数register_uninstall_hook来处理卸载过程。这类似于我们在激活中使用的技巧,通过回调函数。第二种方法是在插件的根目录内使用一个名为uninstall.php的文件。在这个插件中,我们将使用后一种技术,即uninstall.php文件。

您可以在developer.wordpress.org/reference/functions/register_uninstall_hook/查看有关其他技术的更多详细信息。

首先,我们必须在wpquick-post-attachments插件目录内创建一个名为uninstall.php的文件。当从插件部分删除插件时,此文件会自动运行。我们可以在插件中直接包含卸载过程的代码。这是一个基本的插件,因此我们只需要删除为插件创建的特定设置和自定义表。让我们看看插件内uninstall.php文件的实现:

<?php
global $wpdb;
if (!defined('WP_UNINSTALL_PLUGIN')) {
  die;
}
delete_option('wpqpa_version');
$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}wpqpa_post_attachments");

首先,我们需要检查WP_UNINSTALL_PLUGIN常量的可用性。这个常量是在 WordPress 卸载插件的过程中设置的。如果常量不可用,请求可能是一个无效的请求,试图删除插件数据。一旦常量被设置,我们就可以实现资源清理过程。在这种情况下,我们已经从wp_options表中删除了插件版本,并删除了整个wp_wpqpa_post_attachments表。

我们通过涵盖每个生命周期事件的功能,完成了本章计划中的简单插件。通常,开发者经常考虑插件的功能和激活事件。然而,升级和卸载事件在插件开发中至关重要,因此你必须格外小心地使用它们。除非这些事件得到妥善处理,否则升级可能会破坏整个站点的功能,而卸载可能会留下大量未使用的数据。

作为开发者识别自定义插件开发的优点

WordPress 已经占据了超过 35%的网站,庞大的插件库正在以快速的速度持续增长。因此,网站所有者可以找到现有的插件来构建他们想要的任何东西。开发者可能会认为这是一个风险,因为 WordPress 站点可以不通过开发者来构建。这在一定程度上是正确的,因为基本级别开发者的需求正在下降。另一方面,对高质量 WordPress 开发者的需求比以往任何时候都更加突出。在本节中,我们将重点讨论为什么自定义插件开发对你这个开发者来说很重要。让我们看看开发自定义插件的优势:

  • 在许多项目中实现复用性:在 WordPress 开发者中,有较高比例的开发是为个别客户针对特定网站需求进行的。因此,你可能觉得你可以随意添加任何你想要的功能,因为它们不会随时间改变。然而,你可以将非项目特定功能分离成独立的插件,并为多个客户使用它们来构建快速且成本效益高的解决方案。

  • 性能和安全:有一些工具和技术可以衡量 WordPress 插件的性能和安全。然而,这个过程并不完美,我们没有时间检查现有插件中的每一行代码。因此,我们别无选择,只能使用现有的插件而不了解风险。在性能和安全是首要任务的网站上,定制插件开发是最佳解决方案,因为您了解安全和性能问题,并可以在此之上进行改进。

  • 为您的个人资料和工作建立一个社区:根据我的个人经验,开发插件并将其免费发布给公众是向世界展示您作为开发者的理想方法。有成千上万的网站需要开发者。然而,也有数千名开发者能够构建 WordPress 解决方案。因此,这是接触数千需要开发者的理想方式,并在过程中找到潜在客户。您还将获得其他好处,因为大量的人会讨论您的工作并帮助您修复这些问题。

  • 生成一个稳定的收入来源:随着近年来 WordPress 的发展,付费插件开发已成为一项主要业务。有些开发者利用业余时间开发插件,赚取了数百万美元,并将它们在自己的平台上销售。因此,开发插件可能是一个理想的解决方案,可以不依赖于少数客户或每天需要寻找新客户来谋生。

  • 消除版本更新的担忧:这是大多数网站所有者的一大担忧,因为他们使用自由职业开发者的服务,因此在升级阶段开发者无法提供支持。无论免费还是付费,现有的插件总是会发布带有更多功能和修复的更新版本。一方面,如果没有适当的知识进行升级,可能会破坏现有功能,这存在风险。另一方面,不升级可能会带来安全风险。在我们进行定制开发的情况下,我们不需要担心这个问题,因为您会构建特定于网站的插件。一旦网站完成,您不需要发布插件的新版本,除非您想添加更多功能或发现与 WordPress 版本不兼容的问题。

  • 提供设计上的灵活性和一致性:大多数基本到中级水平的 WordPress 网站都是通过使用现有插件来构建的。使用现有插件的问题在于设计的灵活性和不一致性。大多数插件是为通用目的开发的。因此,设计要么非常基础以适应任何网站,要么固定且缺乏灵活性。所以,使用任意数量的插件意味着插件生成的元素将与主题设计完全不同。这导致了一个繁琐的过程,即插件定制以使设计保持一致。另一方面,定制插件可以设计得与网站主题相匹配,并保持设计的一致性。因此,当与现有插件的定制相比具有成本效益时,你可以开发定制插件。

这些优势只是众多直接和间接好处中的一小部分。你应该阅读与 WordPress 相关的博客,并探索知名开发者的作品,以了解插件开发的潜力和能力。

构建高质量插件的指南

到目前为止,我们已经讨论了使用推荐实践构建简单插件的过程。你们中的大多数人将开发 WordPress 网站供个人客户使用,因此你们开发的插件不需要经过质量验证过程。然而,构建可扩展和可维护的插件,这些插件与 WordPress 版本升级以及其他第三方插件兼容,这一点很重要。另一方面,如果你正在为 WordPress 插件目录或高级插件市场开发插件,你将不得不对代码和功能进行广泛的验证和审查过程。因此,插件的质量需要符合最低质量标准。

让我们来看看构建高质量 WordPress 插件的一些常见指南,除了需要遵循 WordPress 编码标准之外:

  • 使用内置的 WordPress 函数:这是 WordPress 开发中常见的问题,由于开发者对 WordPress 缺乏了解,往往倾向于创建自己的函数。WordPress 的力量来自于其现有的模块,因此我们应该始终使用现有的函数,而不是创建自己的函数。此外,现有的函数是由经验丰富的 WordPress 开发者优化和编写的,因此产生错误或兼容性问题的可能性较小。

  • 使用现有功能中的必要钩子:某些钩子在核心功能中使用,以便开发者自定义和扩展其功能。如果你正在实现或修改核心功能,你应该包含必要的钩子,以便其他开发者创建的解决方案与你的解决方案兼容。

  • 始终加载内置库:一个网站中可能包含数十个插件,并且通常其中一些插件需要相同的库,例如 jQuery。因此,一个开发者可能为特定的 jQuery 版本构建解决方案,而另一个开发者可能为不同的版本构建。包含同一库的多个版本通常会导致冲突。因此,你应该始终检查库是否在 WordPress 核心中可用,并通过使用推荐的钩子加载内置版本。

  • 使用插件特定的前缀:当与来自多个开发者的插件一起工作时,代码和数据中产生冲突的可能性很高。因此,我们应该始终为我们的插件添加一个唯一的前缀。我们应该在函数名、类名、常量、数据库表以及如wp_optionswp_postmetawp_usermeta等公共表中的插件特定数据上使用这个前缀。然而,大多数现代插件遵循面向对象风格,因此你可能不需要为函数名使用前缀。

  • 使用钩子扩展可扩展功能:你可能已经对需求进行了彻底的研究,并开发了一个具有常见解决方案的插件。但更常见的情况是,客户会要求对你的代码的所有部分进行定制。因此,最好让你的代码尽可能开放,以适应未来的变化。因此,你应该始终使用过滤器来处理可能改变的价值,并使用动作来扩展过程或模板的地方。

这些是一些知名且推荐的指南。随着你对插件开发的经验积累,你可以根据用户反馈和其他插件的工作方式,将更多指南添加到前面的列表中。确保尽可能多地使用这些指南,并在可能的情况下将插件发布给公众。

摘要

插件在 WordPress 的定制开发中可以被认为是最重要的方面。如果我们摒弃现有的插件库,WordPress 将仅仅是一个没有引起用户或开发者主要关注的另一个 CMS。作为一名开发者,了解插件开发的方方面面对于在日益增长的 WordPress 社区中生存至关重要。因此,在本章中,我们通过讨论常见的插件相关任务来探讨了插件在开发中的作用。我们选择了流行的插件来解释不同类型的插件以及它们如何与 WordPress 站点交互。接下来,我们研究了创建 WordPress 基本插件的过程。大多数初学者开发者都能够使用 PHP 构建解决方案并将其作为 WordPress 插件集成。然而,没有了解完整生命周期事件的插件在项目后期可能会变成一场噩梦。因此,我们研究了 WordPress 插件的生命周期事件以及每个事件的功能。接下来,我们构建了一个 WordPress 插件,用于将文件附加到帖子中,并允许用户从前端下载它们。在这个过程中,我们涵盖了生命周期事件、数据验证、净化、使用元框以及 nonce 的概念。最后,我们探讨了构建高质量插件的一般指南以及插件开发如何作为 WordPress 开发者对你有益。

在第五章《使用插件扩展、过滤器和动作》中,我们将探讨高级插件概念以及定制现有流行插件。

第五章:使用附加组件、过滤器以及动作扩展插件

使用 WordPress 构建网站涉及使用现有插件或创建自己的插件。你应该能够通过添加新功能来扩展现有插件,同时使你的插件可由其他开发者扩展。我们使用附加组件来扩展核心插件的功能。因此,保持你的插件可扩展非常重要,这样你就可以在以后使用附加组件来添加或删除功能。另一方面,使用和自定义现有插件与附加组件一起,可以让你提供低成本和高品质的解决方案。因此,你需要掌握构建附加组件以自定义插件功能和集成多个插件的技巧。

在本章中,我们解释了在遵循创建插件附加组件的逐步指南的同时,附加组件的重要性。WordPress 使用基于钩子的架构,因此内置钩子按照预定义的顺序执行。我们查看这些钩子的正确执行以防止不必要的冲突。然后,我们进入插件的自定义技术,同时学习脚本加载和内置 AJAX 功能的先进用法。我们将开发一个用于 WooCommerce 的产品文件附加组件,以便实际体验自定义过程。最后,我们将集成 WooCommerce、MyCred 和 BuddyPress 插件,以学习插件集成的最佳实践及其局限性。

在本章中,我们将涵盖以下主题:

  • 附加组件开发简介

  • 为帖子附件插件创建一个基本的附加组件

  • 理解 WordPress 核心动作执行过程

  • 自定义第三方插件

  • 识别集成插件的技巧

  • 集成多个插件以实现连续工作流程

到本章结束时,你将能够使用不同的技术自定义第三方插件,并集成多个插件以构建你网站的连续工作流程。

技术要求

遵循此程序需要安装 WordPress 4.9.8。即使你没有

如果你有一个较新版本的 WordPress,描述的示例应该没有重大问题可以运行。

本章的代码文件可以在 GitHub 上找到:

github.com/PacktPublishing/WordPress-Development-Quick-Start-Guide/tree/master/Chapter05

查看以下视频,以查看代码的实际运行情况:

bit.ly/2Q8LtBa

附加组件简介

从一般意义上讲,附加组件是改变核心组件行为的组件。作为一名开发者,您可能熟悉使用浏览器扩展来支持开发任务。在这种情况下,网络浏览器充当核心组件,扩展充当安装在浏览器之上的附加组件。WordPress 中的附加组件遵循相同的概念。然而,WordPress 附加组件本身是改变主插件行为的插件。与浏览器扩展不同,WordPress 中的附加组件不会安装在插件之上。相反,它们作为独立的附加插件。

通常,附加组件用于向核心组件添加新功能。然而,WordPress 附加组件的开发是为了添加、更改或删除核心组件的功能。让我们看看一些具有大型附加组件库的流行插件:

  • WooCommerce:这是一个电子商务插件,允许您销售实体商品以及数字产品。此插件拥有超过 250 个各种类别的广泛附加组件库。支付网关是 WooCommerce 中最受欢迎的附加组件类型,拥有超过 75 个附加组件。您可以在woocommerce.com/product-category/woocommerce-extensions/查看并了解更多关于附加组件的信息。

  • Easy Digital Downloads:这是一个电子商务解决方案,允许您销售数字产品。此插件还提供超过 100 个附加组件,其中许多属于营销类别。您可以在easydigitaldownloads.com/downloads/了解更多关于附加组件的信息。

  • WP Bakery Page Builder:这是一个用于使用预构建组件构建页面的插件。WooCommerce 和 Easy Digital Downloads 都是 WordPress 插件目录中的免费插件。然而,这是一个仅限高级版的插件,拥有超过 250 个各种类别的附加组件。UI 元素是这个插件最受欢迎的附加组件类别。您可以在wpbakery.com/addons/查看并了解更多关于附加组件的信息。

这些是一些具有大型附加组件库的插件。附加组件的存在意味着插件是用必要的钩子编写的,以便未来扩展。您可以检查更多流行的免费和高级插件,以检查附加组件的可用性。此外,您应该检查每个附加组件的角色以及它如何与主插件交互。一旦您探索了不同类型的附加组件,您将了解开发中需要的钩子类型以及如何将它们添加到代码中,以保持代码可扩展。

为插件创建附加组件

创建插件的流程与我们用于插件的过程类似。然而,创建插件的可行性取决于核心插件的代码质量。核心插件应该提供构建插件的 API 或扩展所需的钩子。由于许多插件不包含单独的 API,大多数插件都是通过使用现有的钩子构建的。让我们看看我们如何为上一章创建的帖子附件插件创建一个插件。假设我们有以下要求作为插件开发:

  • 根据默认的 WordPress 用户角色限制附件文件类型。让我们只为非订阅会员提供 PDF 文件访问权限,并为网站中的所有其他用户开放所有其他文件类型。

  • 附件下载计数器。我们必须分别对访客用户和登录用户计算帖子的所有附件的下载次数。

为了在不修改核心插件文件的情况下实现这些要求,我们需要检查插件中是否有任何钩子。不幸的是,我们没有在插件中找到任何钩子,因为它没有为未来的扩展而设计。因此,我们必须通过添加必要的钩子使插件可扩展。

添加过滤器钩子以限制附件

实施附件限制需要我们根据用户权限进行条件检查并隐藏未授权用户的附件。因此,我们需要一个可以修改列表中附件输出的过滤器。让我们在wpqpa_file_attachment_list内部的文件列表代码中添加一个过滤器。我们需要替换以下代码:

$display .= '
      <div class="wpqpa-file-item" id="PF'.$file_row->id.'"  data-file-id="'.$file_row->id.'" >
        <div class="wpqpa-file-item-row"    >
            <div class="wpqpa-file-item-name wpqpa-files-list-name" >'.$file_row->file_name.'</div>
            <div class="wpqpa-file-item-download" ><a href="'.$url.'" >'.__("Download","wpqpa").'</a></div>
            <div class="wpqpa-clear"></div>
        </div>
        <div class="wpqpa-clear"></div>
      </div>';

首先,你应该删除前面的代码,并将以下代码添加到相同的位置:

$file_display = '
  <div class="wpqpa-file-item" id="PF'.$file_row->id.'" data-file-id="'.$file_row->id.'" >
    <div class="wpqpa-file-item-row" >
      <div class="wpqpa-file-item-name wpqpa-files-list-name" >'.$file_row->file_name.'</div>
      <div class="wpqpa-file-item-download" ><a href="'.$url.'" >'.__("Download","wpqpa").'</a>          </div>
      <div class="wpqpa-clear"></div>
    </div>
    <div class="wpqpa-clear"></div>
  </div>';

$file_display = apply_filters('wpqpa_post_attachment_list_item', $file_display, $file_row);
$display .= $file_display;

我们不是直接将每个文件的 HTML 代码添加到$display变量中,而是使用一个名为wpqpa_post_attachment_list_item的自定义过滤器,并将文件详细信息作为参数传递。这个过滤器允许我们在文件发送到浏览器之前修改每个文件的 HTML。

添加动作钩子以计数下载

在上一节中,我们探讨了使用过滤器钩子扩展插件的过程。我们也可以通过插件来使用动作钩子扩展插件。下载次数的计数过程应该在用户点击下载链接之后和浏览器显示文件下载弹出窗口之前初始化。因此,我们需要在这两个事件之间添加一个动作钩子。让我们修改wpqpa_file_attachment_download函数以包含一个新的动作,如下面的代码所示:

$file_mime_type = mime_content_type( $file_dir );
if( $file_mime_type != '' ){ 
  do_action('wpqpa_before_download_post_attachment',$attachments[0]); 
  header( 'Cache-Control: public' );
  header( 'Content-Description: File Transfer' );

突出的行显示了添加到现有代码中的修改。这个动作允许我们在文件作为下载发送到浏览器之前实现额外的功能。现在,我们有创建插件和实现功能的必要钩子。

创建附件插件

如同往常,我们必须首先创建一个插件目录,并包含一个主插件文件,插入头部注释来定义它为一个插件。我们不会在这一节重复代码。你可以在源代码的wpquick-attachments-addon目录中找到一个附件插件目录和文件。我们将通过限制 PDF 文件只对具有订阅者角色的用户可见来开始开发。让我们实现上一节中添加的wpqpa_post_attachment_list_item过滤器:

add_filter( 'wpqpa_post_attachment_list_item', 'wpqaa_post_attachment_list_item' , 10 ,2 );
function wpqaa_post_attachment_list_item( $display, $file_data ){
 $upload_dir = wp_upload_dir();
 $file_dir = $upload_dir['basedir'] . $file_data->file_path;
 $file_mime_type = mime_content_type( $file_dir );

 if($file_mime_type == 'application/pdf'){
   if( !is_user_logged_in() || ( is_user_logged_in() && current_user_can('subscriber') ) ){
     $display = '';
   }
 }
 return $display;
}

这段代码的作用如下:

  1. 回调函数有两个参数,第一个是显示文件链接的 HTML,第二个是从数据库中关于文件的详细信息。

  2. 首先,我们使用 WordPress 的wp_upload_dir函数和从我们的自定义表中捕获的文件路径来构造文件路径。

  3. 然后,我们使用 PHP 的mime_content_type函数来获取附件的 MIME 类型。

  4. 接下来,我们使用 PDF 的 MIME 类型来过滤文件。

  5. 然后,我们使用条件来检查文件是否应该显示给用户。条件的第一部分检查我们是否登录到网站,因为文件需要限制对访客用户的访问。条件的第二部分检查用户是否以订阅者的身份登录。

  6. 当满足这些条件之一时,我们通过清空内容来限制文件。

现在,我们已经使用过滤器钩子实现了一个插件功能。接下来,我们可以使用动作钩子来实现我们的第二个需求,即统计下载次数。让我们使用以下代码实现自定义的wpqpa_before_download_post_attachment动作:

add_action( 'wpqpa_before_download_post_attachment', 'wpqaa_before_download_post_attachment' );
function wpqaa_before_download_post_attachment( $data ){
 $post_id = $data['post_id'];
 if( is_user_logged_in() ){
   $count = get_post_meta( $post_id, 'wpqaa_member_download_count',true );
   update_post_meta( $post_id, 'wpqaa_member_download_count', $count + 1);
 }else{
   $count = get_post_meta( $post_id, 'wpqaa_guest_download_count', true );
   update_post_meta( $post_id, 'wpqaa_guest_download_count', $count + 1 );
 }
}

首先,我们使用回调函数定义动作,wpqaa_before_download_post_attachment。从wp_wpqpa_post_attachments表中接收的附件数据以数组类型参数传递给此函数。我们使用条件检查来过滤已登录用户和访客用户。我们的需求是统计单个文件的附件的总下载次数。因此,我们可以使用wp_postmeta表来存储下载次数。

在实际实现中,我们可能需要统计单个附件的下载次数,而不是文章中所有附件的总下载次数。在这种情况下,我们不能使用wp_postmeta表,因为我们只能根据文章 ID 存储数据。因此,我们需要在wp_wpqpa_post_attachments表中添加一个额外的列来保存和显示每个附件的下载次数。

我们可以使用get_post_meta函数获取文章的现有下载次数。在这里,我们使用两个键wpqaa_member_download_countwpqaa_guest_download_count来区分访客和成员的计数。稍后,我们可以使用这些键来显示计数和文件。

只需几行代码,我们就有了一个为核心插件添加功能并独立工作的插件。因此,为任何插件开发插件都很简单。然而,核心插件需要提供正确的动作和过滤器,以及开发者能够找到适当的钩子。

WordPress 动作执行过程

到目前为止,我们使用了一些内置的动作和过滤器,同时解释了其实际用途。然而,你可能仍在尝试掌握这个概念,因为它不是纯 PHP 开发中常用的实践。当处理动作执行过程的知识缺乏时,这个过程变得更加困难。

动作执行过程是什么?

WordPress 有一组内置的动作,这些动作在每个请求的加载过程中执行。这些动作中的每一个在加载过程中都有特定的职责。用于加载过程的动作按照预定义的顺序执行。然而,WordPress Codex 特别指出,我们不应完全依赖加载过程,因为它可以基于网站的其他组件而变化。

此列表可能仅显示每个动作第一次被调用的情形,并且在许多情况下没有函数被连接到该动作。主题和插件可能导致动作被多次调用,并在请求的不同时间点调用。此列表应被视为 WordPress 动作执行顺序的指南或近似,而不是具体规范。

如果不考虑执行顺序,使用这些动作实现功能往往会引起冲突。在 WordPress 中,这些动作在管理页面请求中的执行与典型页面请求不同。因此,我们必须了解前端动作执行过程以及后端动作执行过程。

前端动作执行过程

前端动作执行过程从muplugins_loaded动作开始,该动作在必须使用和已激活网络插件完成加载后触发。过程通过执行shutdown动作来完成。在典型请求中,会调用 40 多个动作,其中一些在开发任务中具有更高的重要性。然而,一些动作会根据请求而变化,因此我们只能将其作为指导。让我们看一下以下插图,以了解一般动作和执行顺序:

图片

让我们了解加载过程的示意图。该过程从muplugins_loaded动作开始,向下移动直到达到admin_bar_init动作。然后,它从第二列的add_admin_bar_menus动作开始,向下移动,依此类推。我们突出了一些在开发中常用的一些动作。您可以在codex.wordpress.org/Plugin_API/Action_Reference了解更多关于动作执行过程的信息。这些动作的执行顺序在自定义开发中非常重要,尤其是在我们使用 WordPress 的内置全局对象时。

首先,我们将创建一个名为WPQAL Action Loading的新插件,用于测试加载特定动作时的功能。创建该插件的过程与之前的情况相同。因此,您可以查看本章的源代码目录,以找到 WPQAL Action Loading 插件的实现。让我们通过前面的插图和一些实际使用场景来了解正确使用动作的重要性。

场景 1 – 使用父插件功能

这在插件开发中常用,因为我们依赖于主插件的功能。在插件开发中,我们必须使用主插件中的常量、函数、类和全局对象。除非我们使用正确的 WordPress 动作,否则这些功能可能无法在插件中访问。假设我们有一个在父插件之前加载的插件插件。我们可以将WPQPA Post Attachments插件视为主插件,将 WPQAL Action Loading 视为插件插件来检查此场景。WPQPA Post Attachments 插件在 WPQAL Action Loading 插件之后加载。因此,我们可以在 WPQAL Action Loading 插件的主文件中添加以下代码行,并通过刷新浏览器来检查输出:

echo WPQPA_PLUGIN_URL;exit;

这是用于定义WPQPA Post Attachments插件插件路径的常量,因此它应该打印出实际的路径到浏览器。相反,输出将是WPQPA_PLUGIN_URL字符串。问题是我们试图访问一个在我们试图访问时未定义的常量。因此,我们需要在所有插件完成加载过程时访问这样的常量、函数和变量。我们可以参考图像并找到一个名为plugins_loaded的动作。这个动作在 WordPress 完成加载所有活动插件后执行。因此,让我们看看前面代码的正确实现来打印插件路径:

add_action( 'plugins_loaded', 'wpqal_plugins_loaded_action' );
function wpqal_plugins_loaded_action() {
  echo WPQPA_PLUGIN_URL;
}

现在,您应该能在浏览器中看到正确的插件路径。您可以在 WPQAL Action Loading 插件中取消注释场景 1的代码来测试该场景。要访问其他插件的功能,我们必须使用plugins_loaded或更晚的动作。

场景 2 – 访问 WordPress 帖子对象

通常,我们可以在帖子详情页中使用 WordPress 全局$post对象来获取有关帖子的必要信息。假设我们想在插件函数中获取加载帖子的 ID 以执行某些任务。让我们使用init操作在访问单个帖子时将帖子 ID 打印到浏览器:

add_action( 'init', 'wpqal_init_action' );
function wpqal_init_action() {
  global $post;
  print_r($post->ID);exit;
}

我们期望在浏览器上打印出帖子 ID。然而,由于在此阶段$post对象尚未加载,我们将得到一个空输出。因此,我们必须使用加载过程中稍后执行的操作。全局$post对象仅在wp操作及其后续操作中可访问。使用以下代码来测试此过程:

add_action( 'wp', 'wpqal_wp_action' );
function wpqal_wp_action() {
  global $post;
  print_r($post->ID);exit;
}

现在,你可以看到打印到浏览器中的 ID。你还可以通过在加载过程中的initwp之间使用操作来测试此过程。

场景 3 – 访问 WordPress 查询对象

WordPress 在加载特定帖子、页面或屏幕的过程中执行许多数据库查询。在开发过程中,我们可能需要访问查询的详细信息,如查询变量、条件,甚至由 WordPress 函数生成的完整sql查询。因此,我们使用全局$wp_query变量来访问查询详细信息以及执行必要的修改。

让我们尝试使用在查询变量对象创建后执行的pre_get_posts操作来访问$wp_query变量:

add_action( 'pre_get_posts', 'wpqal_pre_get_posts_action' );
function wpqal_pre_get_posts_action( $query ) {
  global $wp_query;
  print_r($wp_query); 
}

一旦帖子刷新,你将看到包含一些参数的$wp_query变量的详细信息。但是,在此阶段,大多数查询条件和查询尚未设置。尽管查询变量对象已创建,但实际的查询在此阶段并未执行。此操作允许我们使用传递给函数的$query变量对查询进行修改。让我们使用在处理过程中稍后执行的操作来使用以下代码检查$wp_query的详细信息:

add_action( 'wp', 'wpqal_wp_action' );
function wpqal_wp_action() {
  global $wp_query;
  print_r($wp_query); 
}

现在,你将看到包含所有查询参数、条件和实际 SQL 查询的完整$wp_query变量。

如我们在这些三个场景中体验到的,动作加载过程在开发中起着重要作用。我们需要使用适当的行为钩子来访问内置 WordPress 变量以及执行某些核心 WordPress 函数。确定用于特定功能的钩子方法可能并不直接。你可以定义必要的操作并将详细信息打印到浏览器,而无需使用exit语句。然后你将看到动作执行过程以及哪个动作负责处理每个 WordPress 内置变量和方法。

后端操作执行过程

后端操作执行过程从muplugins_loaded操作开始,该操作在必须使用和已激活网络插件完成加载后触发。通过执行wp_dashboard_setup操作而不是shutdown操作来完成此过程。

与前端过程相比,后端执行过程中有六个更多动作。让我们看一下下面的图片,以了解这些动作和执行顺序:

图片

如您所见,加载过程的初始部分与前端过程相似。然后,我们看到一些与菜单、样式和页眉部分相关的管理相关动作被执行。因此,您可以使用我们之前使用的相同技术来了解每个动作的执行、其责任以及何时应该使用它们。

如何使用动作和过滤器的优先级

我们学习了动作加载过程以及如何使用它来获取适当的数据并执行核心功能。然而,即使知道了加载过程,开发者仍然可能会遇到麻烦。这是由于执行动作的优先级。正如我们讨论的,WordPress 核心、主题以及其他插件可以在请求内多次执行这些动作。因此,相同的动作钩子将以不同的优先级实现。考虑以下代码来使用pre_get_posts动作:

function wpqal_pre_get_posts_action1( $query ) {
  if ( !is_admin() && $query->is_main_query() ) {
    if ($query->is_search) {
      $query->set('post_type', 'post');
    }
  }
}
add_action( 'pre_get_posts', 'wpqal_pre_get_posts_action1' );

function wpqal_pre_get_posts_action2( $query ) {
  if ( !is_admin() && $query->is_main_query() ) {
    if ($query->is_search) {
      $query->set('post_type', array( 'post', 'product' ) );
    }
  }
}
add_action( 'pre_get_posts', 'wpqal_pre_get_posts_action2', 20 );

在第一部分,我们使用pre_get_posts动作来调用wpqal_pre_get_posts_action1函数,并将站点搜索限制为仅帖子。然而,另一个插件以 20 的更高优先级执行了相同的动作,并将可搜索的帖子类型更改为帖子和产品。因此,我们的实现并没有按预期工作。所以,在处理拥有许多插件的站点时,考虑动作加载过程以及优先级是很重要的。在实现关键钩子之前,你应该检查站点其他插件中相同钩子的使用情况,并确保使用正确的优先级以避免冲突。

识别第三方插件的可扩展功能

正如我们在为插件创建插件部分中已经发现的,并非所有插件都是可扩展的。即使在可扩展的插件中,我们也有从低到高的可扩展性特征。因此,识别可扩展功能不是一项容易的任务,尤其是在与 WooCommerce、BuddyPress 和 bbPress 等高级插件一起工作时。

在第三章《使用主题开发设计灵活的前端》中,我们通过搜索内置动作和过滤器来识别主题的可扩展功能。我们可以使用相同的过程来处理插件,除非插件中的每个钩子都在插件网站上进行了文档说明。让我们快速看一下流行的 WooCommerce 插件的可扩展功能。使用代码编辑器在 WooCommerce 目录中搜索动作和过滤器。

我们正在使用 WooCommerce 3.4.4 版本,我们获得了 849 次动作执行和 1,553 次过滤器执行。这意味着我们可以在超过 2,000 个位置自定义 WooCommerce 插件。在自定义过程中,我们必须在文档的支持下找到必要的钩子,检查代码文件以及开发社区如 StackOverflow 中其他开发者分享的经验。

钩子的可用性因插件而异。有时,我们会发现钩子数量有限的插件和拥有数千个钩子的流行插件。因此,你必须掌握识别钩子及其对插件影响的过程,才能在自定义任何插件时取得成功。

自定义第三方插件

作为开发者,我们更喜欢构建自己的解决方案,而不是使用第三方解决方案。构建自己解决方案的主要原因是为了获得更多对功能和未来增强的控制。然而,使用 WordPress 的主要目的是利用现有功能开发快速、低成本的解决方案。很明显,在您的开发生涯中,您必须在某些时候处理自定义第三方插件。这些第三方插件是为了提供标准解决方案来解决常见问题而开发的。因此,这些插件不会完全符合大多数网站的需求。通常,我们必须通过添加、更改或删除功能来适应这些插件。在本节中,我们将通过示例场景来探讨插件自定义的技术和实现。

自定义第三方插件的技术

就像主题一样,插件由各种类型的常见自定义组成。大多数网站所有者和开发者误解了插件自定义的含义。他们经常认为这是一个完全改变插件以适应网站需求的过程。然而,我们可能只需要进行一些样式更改作为自定义。有时,与使用高级过程相比,我们有更快、更简单的方式来自定义插件。因此,了解不同类型的自定义及其实现技术非常重要。让我们来看看一些常见的自定义类型。

使用样式自定义外观和感觉

这是一种常用的自定义,需要所有插件与主题的样式相匹配。我们有三种方法来为插件应用样式自定义:

  • 使用主题样式文件:我们可以将样式添加到主题的 style.cs 文件中,并覆盖插件样式。这种方法应仅用于子主题,当对插件进行少量样式更改时。

  • 使用自定义 CSS 插件:有许多现有的 CSS 插件允许我们在不需要创建 CSS 文件的情况下,向网站的各个部分添加动态自定义样式。这些插件提供了一个设置部分,我们可以在此处添加要存储在数据库中并加载到网站上的动态 CSS。这种技术可用于许多插件的细微样式自定义。

  • 使用插件插件:这是将样式更改与其他插件独立保持的推荐方法。在此方法中,我们必须创建一个简单的插件插件并包含一个新的 CSS 文件。然后,我们可以通过为新 CSS 类使用新样式来覆盖插件样式。我们必须使用wp_register_style函数的依赖参数将插件 CSS 文件作为依赖项包含,并在加载插件 CSS 文件之后加载我们的 CSS 文件。

这些是更改插件外观和感觉的常见方法。然而,某些插件可能提供设置以添加动态插件特定 CSS 或通过修改值调整不同部分的样式。

使用钩子自定义功能

在本章的开头,我们看到了钩子的执行并不总是简单的。WordPress 到处都在使用钩子,这有时会使事情变得复杂。然而,在插件自定义中,这种解决方案非常有帮助,我们将看到使用这种解决方案的好处。

我们可能每天都要使用不同的插件。因此,学习我们在不同项目中使用的每个插件的每个功能是不可能的。相反,我们可以寻找实现自定义功能的文件或类。然后,我们可以搜索支持我们自定义的可能钩子。一旦找到必要的钩子,我们就可以根据指南实现它们。实现钩子确保了功能完整过程的执行。如果没有钩子,我们必须遍历整个插件文件和类来识别自定义的位置。此外,我们还需要遍历后续代码以检查它是否会影响我们的自定义或可能的未来修改。

让我们考虑一个来自流行 WooCommerce 插件的例子。假设我们想在用户完成产品支付后执行某些代码。因此,我们只需搜索支付成功钩子和其实施。WooCommerce 在支付后执行woocommerce_payment_complete动作。因此,我们可以使用以下代码在支付完成后执行任何类型的自定义代码:

add_action( 'woocommerce_payment_complete', 'wpaql_payment_complete' );
function wpaql_payment_complete( $order_id ){
  // Custom code
}

如您所见,在短短几行代码内,我们就已经定制了 WooCommerce,而没有花费太多时间或深入理解 WooCommerce。假设 WooCommerce 中没有这样的钩子。在这种情况下,我们首先必须找到用于处理支付过程的 WC_Order 类和 payment_complete 函数。然后,我们必须逐行检查 payment_complete 函数中的代码,以了解支付完成的定位。您已经可以看到,与使用钩子的方法相比,第二个过程存在难度。因此,我们应该始终寻找可能的钩子来定制插件。在插件开发中,有两种类型的特性定制:

  • 定制现有功能:有时,我们想要更改或删除插件中的现有功能。在这种情况下,我们可以实现现有的钩子,并使用传递给函数的参数更改现有数据或设置。有时,我们可能想要更进一步,通过我们自己的实现完全更改一个功能。在这种情况下,我们可以使用内置的 remove_actionremove_filter 函数删除现有的钩子。然后,我们添加具有我们自己的实现的相同钩子。

  • 添加新功能:在定制中,与修改现有功能相比,涉及添加新功能的任务占很大比例。在此方法中,我们实现现有的钩子类似于上一节,但我们使用自己的代码,在现有功能之上添加新功能,而不是修改现有功能。

我们讨论了插件中不同类型的定制。在下一节中,我们将通过实现一个现实世界的需求来定制一个插件。

构建 WooCommerce 产品文件管理器

使用钩子更改或添加新功能的过程是第三方插件中最常见的定制类型。这些钩子允许我们在不花费太多时间的情况下构建高级功能。在本节中,我们将实现一个现实世界的用例,以详细了解基于钩子的定制过程。因此,我们选择了定制 WooCommerce。

假设我们想要提供与每个 WooCommerce 产品相关的文件。这些文件可能包含产品规格、用户指南或与产品相关的任何信息。因此,我们需要一种方法在创建或编辑产品时上传这些文件。然后,我们必须在前端产品页面上将它们列出来作为一个单独的标签页。这可能对不熟悉 WooCommerce 的开发者来说是一项艰巨的任务。然而,一旦我们找到了必要的 WooCommerce 钩子,实现起来就相当简单了。此外,这种实现的另一个目的是重用来自帖子附件插件的代码,并学习 AJAX 的使用,而不是正常的表单提交。

让我们从创建一个名为 WQWPF 产品文件 的新插件开始实施,这个过程与之前相同。你可以在这个章节的源代码目录中找到这个插件的文件。考虑插件的初始代码:

register_activation_hook( __FILE__, 'wqwpf_activate' );
function wqwpf_activate(){
  global $wpdb,$wp_roles;
  $table_product_files = $wpdb->prefix . 'wqwpf_product_files';
$sql_product_files = "CREATE TABLE IF NOT EXISTS $table_product_files (
      id int(11) NOT NULL AUTO_INCREMENT,
      user_id int(11) NOT NULL,
      post_id int(11) NOT NULL,
      file_path longtext NOT NULL,
      updated_at datetime NOT NULL,
      uploaded_file_name varchar(255) NOT NULL,
      PRIMARY KEY (id)
    );";
  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
  dbDelta( $sql_product_files );
}

if ( ! defined( 'WQWPF_PLUGIN_DIR' ) ) {
  define( 'WQWPF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
}
if ( ! defined( 'WQWPF_PLUGIN_URL' ) ) {
  define( 'WQWPF_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
}

add_action( 'plugins_loaded', 'wqwpf_plugins_loaded_action' );
function wqwpf_plugins_loaded_action() {
  if( class_exists('WooCommerce')){
    // All actions and filters need to be added here
  }
}

代码的初始部分与我们在 WPQPA Post Attachments 插件中使用的代码类似,只是从产品文件的定制表中移除了 file_name 列。这将是一个 WooCommerce 的附加组件,因此我们需要在执行任何附加功能之前检查 WooCommerce 是否已激活。因此,我们使用 plugins_loaded 动作来检查 WooCommerce 类的可用性。我们可以使用常量、类或函数来检查核心插件的可用性。当 WooCommerce 类可用时,我们添加所有与附加组件相关的动作和过滤器,以确保它们仅在 WooCommerce 可用的情况下执行。现在,我们可以开始构建所需的定制功能。

将文件上传字段添加到 WooCommerce 产品中

开发过程中的第一步是为管理员提供一个接口,让他们能够将文件上传到产品中。我们可以通过使用我们在构建帖子附件插件时使用的元框技术来实现这一点。然而,我们正在尝试理解插件定制,因此我们将把这个功能集成到 WooCommerce 中。在 WooCommerce 产品创建屏幕中,我们可以找到一个名为“产品数据”的元框,其中包含各种标签页,如常规、库存、运输等。我们打算在元框中添加一个新的标签页,称为“产品文件”。首先,我们必须寻找允许我们修改“产品数据”标签页的动作或过滤器。你可以找到一个名为 woocommerce_product_data_tabs 的过滤器来定制标签页。让我们使用以下代码通过这个过滤器添加一个新的标签页:

add_filter( 'woocommerce_product_data_tabs', 'wqwpf_custom_product_tabs' );
function wqwpf_custom_product_tabs( $tabs ) {
  $tabs['wqwpf_files'] = array(
        'label'     => __( 'Product Files', 'wqwpf' ),
        'target'    => 'wqwpf_file_options',
        'class'     => array( 'show_if_simple' ),
    );
  return $tabs;
}

这个过滤器依赖于 WooCommerce,因此 add_filter 行应该放在 wqwpf_plugins_loaded_action 函数内部。现有的标签页作为参数传递给这个过滤器的回调函数。我们添加一个带有唯一键的新标签页,并分配必要的选项。target 设置包含用于显示此标签页内容的 HTML 元素的 ID。标签页内容将在下一阶段添加。class 设置定义了一个分配给此标签页的类数组。在这里,我们使用了 show_if_simple 作为类。因此,我们的标签页将仅对简单 WooCommerce 产品可见。如果我们想使标签页对其他产品类型可用,我们需要添加更多的类。

在这种情况下,我们添加了一个新的标签页。我们也可以使用这个过滤器通过在 $tab 数组元素上使用 unset 函数来删除现有的标签页,或者通过使用适当的数组键来更改现有标签页的设置。

下一步是在标签页中添加一个文件字段以上传文件。我们可以找到一个名为 woocommerce_product_data_panels 的另一个动作钩子来添加标签页内容。让我们使用它来添加文件字段,如下面的代码所示:

function wqwpf_product_files_panel_content() {
  global $post;    ?>

  <div id='wqwpf_file_options' class='panel woocommerce_options_panel'>
    <div class='options_group'>
      <div id="wqwpf-product-files-msg"></div>
      <p class="form-field _wqwpf_product_files_field show_if_simple" style="display: block;">
       <label for="_wqwpf_product_files"><?php _e('Product Files','wqwpf'); ?></label>
       <input type="file" name="wqwpf_product_files" id="wqwpf_product_files" />
       <input type="hidden" id="wqwpf_product_file_nonce" name="wqwpf_product_file_nonce" />
       <input type="button" name="wqwpf_product_file_upload" id="wqwpf_product_file_upload"  value="<?php echo __('Upload','wqwpf'); ?>" />
     </p>
   </div>
  </div>
    <?php
}

在此代码中,我们添加了一个文件字段用于上传文件,一个按钮用于初始化上传过程,以及一个隐藏字段以保持 nonce 值。HTML 结构是从其他可用的标签中复制的。最重要的部分是理解这个标签内容是如何与之前创建的标签连接的。我们使用了 wqwpf_file_options 作为标签主要容器的 ID。我们使用了与我们的新标签的 target 设置相同的 ID。因此,一旦点击标签,WooCommerce 将使用目标设置来查找容器并向用户显示。以下屏幕截图显示了使用自定义代码后的新标签:

图片

产品文件的新标签页添加在库存和运输标签之间。现在,我们准备好开始上传文件。

添加上传文件的脚本

在第四章,“使用插件开发构建自定义模块”,我们创建了一个插件来上传帖子附件。然而,在更新每个文件时,我们必须选择文件并逐个上传文件。在需要上传许多文件的情况下,这并不理想。相反,我们必须使用 AJAX 并允许用户在不刷新浏览器的情况下上传多个文件。在进入上传文件的过程之前,我们需要将必要的脚本添加到插件中。首先,你必须在 wpquick-woo-product-files 内部创建一个名为 js 的新目录,并创建一个名为 wqwpf-admin.js 的新文件。接下来,我们可以将脚本添加到插件中,如下面的代码所示:

add_action( 'admin_enqueue_scripts', 'wqwpf_admin_load_scripts',9 );
function wqwpf_admin_load_scripts(){   
  wp_register_script( 'wqwpf_admin_js', WQWPF_PLUGIN_URL . 'js/wqwpf-admin.js', array('jquery') );
  wp_enqueue_script( 'wqwpf_admin_js' );
  $custom_js_strings = array(       
        'AdminAjax' => admin_url('admin-ajax.php'),
        'Messages' => array('fileRequired' => __('File is required.','wqwpf') ),
        'nonce' => wp_create_nonce('wqwpf-private-admin'),
    );
  wp_localize_script( 'wqwpf_admin_js', 'WQWPFAdmin', $custom_js_strings );
}

我们可以使用前面的代码来学习脚本加载的一些重要技术。让我们列出脚本加载的重要部分:

  • 注册和队列脚本:直到这一点,我们只学习了如何包含 CSS 文件。因此,我们现在可以使用 wp_register_script 函数来加载脚本。在这里,我们正在包含管理端的脚本,因此我们必须使用 admin_enqueue_scripts 动作来使用回调函数包含脚本。前面代码的第一行应放置在 wqwpf_plugins_loaded_action 函数内部,因为它依赖于 WooCommerce 的存在。在回调函数内部,我们可以使用 wp_register_script 函数为 WordPress 注册一个自定义脚本。此函数的参数包括脚本的唯一键、脚本的路径和依赖脚本文件。脚本仅在此时注册,而不包含在浏览器中。然后,我们使用 wp_enqueue_script 在任何我们想要将脚本包含到浏览器中的地方使用脚本注册时使用的键。

  • 定义脚本依赖:我们可以使用wp_register_script函数的第三个参数来定义依赖脚本。在这种情况下,我们的脚本是用jQuery编写的,因此它是一个依赖项。所以,我们将 jQuery 作为依赖数组的键添加。WordPress 有一组内置脚本,具有特定的键。我们需要使用这些键,而无需从我们自己的插件或外部来源加载这些库。可用的脚本库列表可以在developer.wordpress.org/reference/functions/wp_register_script#core-registered-scripts找到。您可以使用“处理”列中的键来加载这些依赖项。一旦我们将script 2定义为script 1的依赖项,第二个脚本将在第一个脚本之前加载。除了使用核心脚本文件外,我们还可以使用自定义脚本文件作为依赖项。考虑以下代码行:在这个代码中,我们正在注册另一个插件的脚本,该脚本使用jQuerywpwpf_admin_js脚本。因此,这两个文件将在加载具有upme_admin_js作为处理的脚本之前加载。

      wp_register_script( 'upme_admin_js', WQWPF_PLUGIN_URL . 'js/
      wqwpf-admin.js',   
      array('jquery','wqwpf_admin_js') );   
  • 本地化脚本:有时,我们需要向特定脚本添加必要的设置和动态数据。WordPress 允许我们通过使用wp_localize_script函数来添加此类数据。此函数使用三个参数,从脚本处理程序开始,变量名称和数据。一旦使用,这些数据将被添加到浏览器中,在加载脚本文件之前。因此,这些数据将在指定的脚本以及之后加载的脚本中可用。

在这个实现中,我们将使用 AJAX,因此我们通过使用admin_url('admin-ajax.php')函数将 AJAX URL 添加到脚本中。关于 AJAX 的更多内容将在本节的后面讨论。接下来,我们使用首选数组键添加必要的数据。在这里,我们只添加消息作为数组。我们还向脚本添加了一个 nonce 值,以从服务器端验证 AJAX 请求。一旦页面加载,您可以使用“查看源代码”选项来检查在wqwpf_admin_js脚本之前加载的这些数据。现在,我们已经完成了脚本加载过程,因此我们可以进入上传产品文件。

将文件上传到产品

在本节中,我们将使用 AJAX 请求上传产品文件。在深入研究 AJAX 之前,我们需要完成初始脚本,包括必要的数据检索和验证。将以下代码添加到我们插件的wqwpf-admin.js文件中:

jQuery(document).ready( function( $ ) {
  $('#wqwpf_product_file_upload').click(function(e){
      e.preventDefault();
      var file_form = $('#post');
      var file = file_form.find('#wqwpf_product_files').val();
      var post_id = $('#post_ID').val();
      var msg_container = file_form.find('#wqwpf-product-files-msg");
      msg_container.removeClass('wqwpf-message-info-error').removeClass('wqwpf-message-info-success');

      var err = 0;
      var err_msg = '';
      if(file == '' ){
        err_msg += '' + WQWPFAdmin.Messages.fileRequired + '<br/>';
        err++;
      }
      if(err != 0){
        msg_container.html(err_msg).addClass('wqwpf-message-info-error').show();
      }else{
          // AJAX request for uploading files 
      }
    });
});

这段代码的作用如下:

  1. 首先,我们在 jQuery 的ready函数中为上传按钮的click事件定义一个回调函数。

  2. click事件内部,我们需要捕获文件和帖子 ID。我们不能添加自己的表单,因为元框位于 WordPress 创建帖子的表单内。因此,我们使用具有 IDpost的表单来捕获表单字段的值。

  3. 接下来,我们检索用于显示消息的容器元素并重置 CSS 类。

  4. 然后,我们开始对字段进行验证过程。在这里,我们只有字段字段,因此检查空值是唯一需要的验证。

  5. 最后,我们检查是否有任何错误,并通过添加必要的 CSS 类和启用消息容器来显示错误消息。

在上一节中,我们使用了 wp_localize_script 函数将数据添加到脚本中。在本节中,我们使用以下行来添加错误消息:

WQWPFAdmin.Messages.fileRequired

在这一行,WQWPFAdmin 是我们之前使用的变量名,而 Messages 是主数组的子键。Messages 数组中的 fileRequired 选项将包含实际的消息。同样,我们可以使用相应的键来访问其他数据。现在,我们需要在验证无错误完成时实现 AJAX 请求。在此之前,我们将简要介绍 AJAX。

WordPress 中 AJAX 的介绍

AJAX 是异步 JavaScript 和 XML 的缩写。这项技术允许我们在不刷新浏览器的情况下发送和检索数据。因此,使用 AJAX 可以简化重复任务,如多次文件上传。通常,我们使用直接 URL 来执行 AJAX 请求。然而,WordPress 提供了一个内置接口来执行 AJAX 请求,建议使用这种技术而不使用我们自己的 AJAX 处理器。

WordPress 使用一个名为 admin-ajax.php 的文件来处理请求,该文件位于 wp-admin 目录中。此文件包含内置核心 AJAX 请求的必要代码,以及通过 actions 支持自定义请求的代码。动作负责处理 AJAX 请求并提供输出。让我们考虑以下代码:

add_action( 'wp_ajax_sample_action', 'wqwpf_sample_action' );
add_action( 'wp_ajax_nopriv_sample_action', ' wqwpf_sample_action ' );
function wqwpf_sample_action(){
  // Get data from client side request, process and return the output
}

上述代码定义了我们在 WordPress 中如何使用 AJAX。每个不同的 AJAX 请求都有一个动作名称。我们必须使用 wp_ajax_{action name}wp_ajax_nopriv_{action name} 动作来定义每个请求的处理函数。带有 wp_ajax 前缀的动作用于用户已经登录到网站时的请求。带有 wp_ajax_nopriv 前缀的动作用于网站的访客用户。因此,你必须根据谁被允许执行请求来定义一个或两个这些动作。

现在,我们已经了解了 WordPress 中 AJAX 的基本知识以及从服务器端处理请求的方法。因此,我们可以通过实现上传文件的 AJAX 调用来回到文件上传过程。以下代码应添加到 wqwpf-admin.js 文件中 click 事件的 else 语句:

msg_container.html('').hide();
var formObj = file_form;
var formURL = WQWPFAdmin.AdminAjax+'?action=wqwpf_save_product_files';
var formData = new FormData();
var file_data = $('#wqwpf_product_files').prop('files')[0];  

formData.append('post_id', post_id);      
formData.append('file_nonce', WQWPFAdmin.nonce);
formData.append('file_data', file_data);
jQuery.ajax({
  url: formURL,
  type: 'POST',
  data:  formData,
  mimeType:'multipart/form-data',
  contentType: false,
  cache: false,
  dataType : 'json',
  processData:false,
  success: function(data, textStatus, jqXHR){

    if(data.status == 'success'){
      msg_container.html(data.msg).removeClass('wqwpf-message-info-error').addClass('wqwpf-message-info-success').show();
      $('#wqwpf-files-container').html(data.files);                                         file_form.find('#wqwpf_product_files').val('');
    }else if(data.status == 'error'){
      msg_container.html(data.msg).removeClass('wqwpf-message-info-success').addClass('wqwpf-message-info-error').show();
    }
  }
});

这里是这段代码的功能:

  1. 前两行重置消息容器并将表单对象分配给一个变量。

  2. 然后,我们使用WQWPFAdmin变量检索 WordPress AJAX URL 并分配一个名为 action 的自定义参数,该参数将用于与wp_ajaxwp_ajax_nopriv操作连接客户端请求与服务器端。

  3. 接下来,我们创建一个新的FormData对象以将数据发送到服务器。我们可以通过使用带有键值对的append函数将必要的数据分配给FormData对象。在这里,我们添加了一个文件、nonce 值和帖子 ID 作为表单数据。

  4. 下一步是使用 jQuery ajax函数配置 AJAX 请求。您应该熟悉 AJAX 函数的设置。现在,当用户点击上传按钮时,请求从客户端执行。

  5. 最后,我们使用ajax函数的成功事件来显示结果消息、重置字段并显示上传到产品的文件列表。

现在,我们可以通过处理上传的服务器端函数来完成此过程。让我们考虑以下使用 WordPress AJAX 操作的实现:

add_action( 'wp_ajax_wqwpf_save_product_files','wqwpf_save_product_files');
function wqwpf_save_product_files(){
  global $wpdb;
  $file_nonce   = isset( $_POST['file_nonce'] ) ? ( $_POST['file_nonce'] ) : '';           
  $post_id = isset( $_POST['post_id'] ) ? (int) ( $_POST['post_id'] ) : 0;   
  $user_id    = get_current_user_id();  

  if(check_ajax_referer( 'wqwpf-private-admin', 'file_nonce',false )){               $result_upload = wqwpf_process_file_upload();
    if( isset( $result_upload['status'] ) && $result_upload['status'] == 'success' ){
      $file_date = date("Y-m-d H:i:s");   
      $uploaded_file_name = $result_upload['base_name']; 
      $wqwpf_product_files_table = "{$wpdb->prefix}wqwpf_product_files";
      $wpdb->insert(
              $wqwpf_product_files_table,
              array(
                  'user_id'           => $user_id,
                  'post_id'           => $post_id,
                  'file_path'         => $result_upload['relative_file_path'],
                  'updated_at'        => $file_date, 
'uploaded_file_name' => $uploaded_file_name,
              ),
              array( '%d','%d','%s', '%s','%s' ) );
        $files_list = wqwpf_product_file_list( $post_id ); 
        $result = array( 'status' => 'success', 'msg' => $result_upload['msg'] , 'files' => $files_list );
    }else{
      $result = array( 'status' => 'error', 'msg' => $result_upload['msg'] );
    }
  }else{
    $result = array( 'status' => 'error', 'msg' => __('Invalid file upload request.','wqwpf') );
  }
  echo json_encode($result);exit;
}

只有管理员可以上传文件到产品,因此我们只为登录用户使用带有wp_ajax前缀的操作。以下是此代码的功能:

  1. 首先,我们必须捕获帖子 ID 和 nonce 的POST请求数据。

  2. 然后,我们使用check_ajax_referer函数检查 nonce 值。此函数的第一个参数使用我们通过调用wp_create_nonce函数在wqwpf_admin_load_scripts函数内部定义的操作。第二个和第三个参数分别用于 POST 请求中的 nonce 值键以及如果 nonce 无效是否die。我们将第三个参数设置为false,因为我们想显示自定义错误。

一旦验证 nonce,我们就使用wqwpf_process_file_upload函数上传文件并获取结果。wqwpf_process_file_upload函数与我们用于帖子附件插件中的函数类似,因此您可以检查本章的源代码以了解实现。

  1. 然后,我们将文件详细信息保存到自定义表中,类似于帖子附件插件。然后,我们使用wqwpf_product_file_list函数检索包含上传文件的 HTML 列表。此函数的实现与我们用于帖子附件插件中的第四章,使用插件开发构建自定义模块中使用的实现类似。因此,我们不会包括并讨论源代码。您可以在本章的源代码中找到此函数的实现。

  2. 最后,我们将输出作为带有消息、状态和上传文件列表 HTML 的 JSON 编码数组返回。

在产品中显示上传的文件

在上一节中,我们在产品编辑屏幕中显示了文件列表,并且每次我们使用 AJAX 上传新文件时都会更新它。现在,我们需要通过在前端产品页面以新标签页的形式显示文件列表来完成实现。我们已经使用了一个通用的函数来生成产品的文件列表,因此这只是在产品页面标签中使用它的问题。让我们使用以下代码来显示文件列表:

add_filter( 'woocommerce_product_tabs', 'wqwpf_product_files_tab' );
function wqwpf_product_files_tab( $tabs ) {
  $tabs['wqwpf_tab'] = array(
    'title' => __( 'Product Files', 'wqwpf' ),
    'priority' => 50,
    'callback' => 'wqwpf_product_files_tab_content'
 );
 return $tabs;
}

function wqwpf_product_files_tab_content() {
 global $post;
 echo wqwpf_product_file_list( $post->ID );
}

WooCommerce 提供了一个名为 woocommerce_product_tabs 的过滤器,用于添加、修改或从前端产品页面删除标签页。现有的标签页详细信息作为参数传递给此函数。因此,我们通过使用唯一键和分配必要的设置来添加一个新标签页。我们为标签使用了一个标题,并自定义了一个名为 wqwpf_product_files_tab_content 的回调函数。优先级设置定义了标签在产品页面中的顺序。您可以通过在 $tabs 数组上执行 var_dump 并根据您想要显示标签的位置分配优先级来检查现有标签的优先级。在回调函数内部,我们使用全局 $post 对象来检索帖子 ID,并将其传递给 wqwpf_product_file_list 函数以生成文件列表。

现在,我们已经完成了定制,并为上传和显示 WooCommerce 产品的文件添加了一个新功能。产品页面将类似于以下屏幕,其中包含产品文件标签页:

图片

在本章中,我们省略了文件下载、上传和列表的代码和解释,因为我们使用了帖子附件插件的相同功能,只是做了轻微的修改。请确保检查本章源代码中这些函数的实现。

集成多个插件以实现连续的工作流程

构建插件的目的在于提供独立的功能或功能集作为可重用的模块。因此,大多数第三方插件都是设计来提供针对特定功能及其相关子功能解决方案的。我们很少找到能够驱动整个系统的第三方插件。BuddyPress 是少数几个提供完整系统许多功能的插件之一,如社交网络。在上一节中,我们讨论了定制第三方插件的需求。在这里,我们必须进一步了解如何定制和集成多个插件。

插件集成类型

我们可以使用 WordPress 钩子系统集成两个或更多插件。然而,根据谁负责提供集成,集成类型有所不同。了解这些集成类型很重要,以便在集成插件更改行为时避免或控制风险。让我们看看插件集成类型。

自定义集成

当两个插件之间没有集成时,使用这种方法。因此,每个插件都不知道其他插件的存在。这在 WordPress 开发中是最常见的场景,因为我们很少找到必要的插件与我们的网站所需的插件集成。在这种情况下,我们必须通过实现与必要插件的集成来创建一个附加插件。在这个过程中,我们使用必要插件的现有钩子或函数,并将它们连接起来以满足我们的需求。

这种方法有一个主要限制,即连接越来越多的插件会增加破坏网站功能的风险。我们对集成插件没有控制权,因为它们是由第三方开发者开发的。因此,在实现此类集成时,我们必须使用相同的插件版本而不进行更新,或者准备好在更新每个插件的更新时检查集成并应用必要的修复。您将看到许多网站使用具有固定版本集成插件的定制集成。这不是理想的过程,因为保持插件的固定版本会增加安全风险。因此,您应该开发与每个插件最小依赖的集成,并准备好在必要时更改集成以兼容插件更新。

单边积分

在这种方法中,两个插件中的一个是与其他插件进行集成的。然而,第二个插件并不知道第一个插件的存在或集成。我们可以找到许多具有这种集成类型的现有插件。集成是在插件或插件的附加组件中实现的。让我们看看一些具有与其他插件单边集成的流行插件:

  • MyCred 与 WooCommerce 的集成:MyCred 是一个用于 WordPress 的积分管理系统,并通过 Gateway 附加组件与 WooCommerce 集成。MyCred 团队负责提供集成,因此他们将确保集成与 WooCommerce 版本更新兼容。您可以在 mycred.me/add-ons/gateway/ 找到有关集成的更多详细信息。

  • Easy Digital Downloads 与 Gravity Forms 的集成:Easy Digital Downloads 是一个用于销售数字产品的插件。该插件通过名为 Gravity Forms checkout 的附加组件与流行的 Gravity Forms 插件集成。Easy Digital Downloads 团队负责提供集成,因此他们将确保集成与 Gravity Forms 版本更新兼容。您可以在 easydigitaldownloads.com/downloads/gravity-forms-checkout/ 找到有关集成的更多详细信息。

  • 用户资料简易集成 WooCommerce:用户资料简易是一个前端资料管理系统,并提供了内置的 WooCommerce 集成。在前两个示例中,集成是由一个单独的插件提供的。在这种情况下,集成是内置在插件中的。用户资料简易团队负责提供集成,因此他们将确保集成与 WooCommerce 版本更新兼容。您可以在codecanyon.net/item/user-profiles-made-easy-wordpress-plugin/4109874找到有关集成的更多详细信息。

作为开发者,我们可以使用这些现有的集成进行少量修改。因此,我们可以节省时间并避免与自定义集成方法相比的风险。

相互集成

在这种方法中,集成由两个插件共同支持。因此,两个插件都了解另一个插件的存在和集成。通常,这是通过为其他插件设计的函数或 API 实现的。两个插件的开发者同意以与另一个插件集成的形式保持功能。作为开发者,我们可以直接使用这些集成,因为功能已由两个插件测试。这种集成的风险相当低,所需时间较少。

我们研究了三种常见的集成方法。我们很少找到具有相互集成的插件,因为这需要两个插件的开发者投入大量的努力。因此,当出现集成需求时,我们必须检查单方面集成的可用性,主要通过插件。你应该始终使用可用的集成,因为它们已被该插件的多位用户使用和测试。在找不到现有集成的场景中,我们必须使用自定义集成过程。在自定义集成过程中,我们必须通过减少对两个插件的影响来规划解决方案。

自定义插件集成时的注意事项

插件集成的过程可能简单到只需要几行代码,也可能复杂到需要数千行代码。没有推荐的插件集成过程或技术。我们必须找到连接插件的方法,而不影响其他功能或破坏版本升级的功能。让我们看看在自定义集成过程中需要考虑的一些重要事项。

检查集成的可行性

这是第一步,我们探索了可用于集成的可用钩子。在创建插件部分,我们确定了由于缺少操作和过滤器,帖子附件插件存在限制。在插件集成中,我们可能会遇到类似的情况,其中第二个插件没有提供必要的钩子来与第一个插件的功能集成。在这种情况下,我们唯一的选项是在其中一个插件中添加自定义钩子。尽管这不被推荐且难以管理,但开发人员在没有其他替代方案时使用这种方法。

假设我们使用一个前端登录插件,以及一个为每个用户都有一个私有页面的内容限制插件。默认情况下,登录插件在登录后将用户重定向到后端个人资料。因此,我们的集成需要我们在登录后重定向用户到私有页面。我们需要在登录插件中至少有一个钩子来修改重定向。让我们假设我们有一个以下过滤器在登录插件中更改重定向 URL:

$login_redirect = apply_filters('wpquick_login_redirect_url', $default_url);

然后,我们可以在内容限制插件中使用以下代码来修改 URL 并集成两个插件:

add_filter('wpquick_login_redirect_url','wpquick_content_restriction_redirect_url',10);
function wpquick_content_restriction_redirect_url($url){
  $url = "URL of private page";
  return $url;
}

集成之所以可行,是因为登录重定向钩子的可用性。因此,我们必须确保必要的钩子存在,并且两个插件之间的集成是可行的。

识别确切的数据更改和钩子

在集成过程中,通常我们使用第一个插件中的数据来在第二个插件中使用,或者在第一个插件中发生事件时执行第二个插件的一定功能。主要问题是数据的一致性和完整性。

假设我们使用一个前端个人资料管理插件来捕获和显示用户详情,使用自定义字段。我们还使用 WooCommerce 来捕获会员的支付。假设我们想要集成这两个插件以同步用户个人资料数据与 WooCommerce 客户数据。因此,当从个人资料管理插件更新个人资料详细信息时,我们还需要更新 WooCommerce 客户数据,反之亦然。如果两个插件对同一字段使用不同的数据格式,将出现冲突。在这种情况下,我们必须在两个插件之间切换数据时转换数据。

此外,在集成中正确执行钩子也很重要。假设我们有一个用户注册插件和一个用户组插件。集成要求我们在注册过程之后将用户分配到组中。通常,我们会在组插件中寻找一个函数,允许我们通过传递必要的数据直接将用户添加到组中。然后,我们通过使用第一个插件的注册成功钩子并在钩子中执行将用户添加到组的函数来集成这两个插件。主要的集成过程是有效的。但是,组插件可能有其他在将用户添加到组之后执行的钩子。这些钩子可能包括发送电子邮件、分配组权限等功能。因此,整体过程失败,尽管主要的集成没有问题。因此,考虑与集成过程相关的所有钩子以及数据格式非常重要。

检查与其他插件的影响

通常,我们每个网站至少使用几个插件,有些网站可能包含数十个插件。其中一些插件之间可能已经存在集成。让我们假设我们需要通过集成两个或更多插件来实现一个新功能。在这种情况下,检查这两个插件的数据、钩子和集成点是不够的。我们必须还要检查其他现有插件的影响,或者对未参与集成的其他现有插件的影响。

让我们考虑在检查集成可行性部分讨论的相同场景。我们集成了两个插件,在登录后重定向用户到内容限制插件的个人页面。假设我们有一个未参与集成的第三个插件。此插件用于根据用户角色更改登录重定向 URL。考虑以下第三个插件中的集成:

add_filter('wpquick_login_redirect_url','wpquick_user_role_redirect_url',20);
function wpquick_user_role_redirect_url($url){
  // Get the role of the user being logged in
  if($role == 'subscriber'){
   $url = "Subscriber redirect URL";
  }
  return $url;
}

用户角色基于的重定向插件中的 URL 修改钩子具有更高的优先级值,因此比前一种情况中使用的钩子晚执行。因此,登录用户将被重定向到用户角色特定的页面,而不是内容限制插件的个人页面。因此,我们的集成完全崩溃。

我们可以通过考虑其他插件如何使用数据以及与我们的集成过程相关的钩子来防止此类问题。在这种情况下,我们可以在第一种情况下为钩子使用更高的优先级值来防止这种情况:

add_filter('wpquick_login_redirect_url','wpquick_content_restriction_redirect_url',30);

这行代码在基于角色的重定向插件中的过滤器代码之后执行,因此我们的集成没有问题。然而,现在你也必须检查我们的集成如何影响基于角色的重定向插件的功能。

实现多个插件集成

到目前为止,我们讨论了插件集成中使用的方法和重要考虑因素。现在,是时候集成一些插件来了解它们的实际应用了。我们将集成三个流行的插件来展示这个过程。让我们确定用于此集成的插件及其功能:

  • WooCommerce: 这是最受欢迎的电子商务插件。主要功能是销售实体和数字商品。如今,它也通过修改功能通过插件来销售服务、预订和会员资格。

  • MyCred: 这是一个积分管理插件,您可以让用户通过在网站上执行各种任务(如评论、查看内容、发布内容、注册网站等)来赚取积分。然后,您可以通过使用这些积分来为这些用户提供奖励。

  • BuddyPress: 这是一个用于在您的网站上构建在线社区的插件。默认功能包括用户管理、群组、消息、活动、朋友和通知。许多人使用这个插件来构建微型社交网络。

现在,我们可以看看集成这三个插件的要求。

假设我们在网站上使用MyCred有一个积分系统。用户可以通过各种任务赚取积分,并用这些积分换取各种福利。在这种情况下,我们假设用户只能通过从WooCommerce商店购买产品来获得积分。积分将根据订单价值发放给完成的订单。一旦用户达到特定的积分数量,用户将被自动添加到私人BuddyPress群组中。

在开始实施之前,我们需要在名为wpquick-plugin-integrations的新插件目录中创建一个名为WPQPI Plugin Integrations的新插件。创建主文件和使用头部注释定义插件的过程将与之前的场景相同。

为完成的订单分配积分

我们必须通过连接 WooCommerce 和 MyCred 插件来为完成的订单奖励积分来开始实施。首先,我们需要找到跟踪 WooCommerce 订单完成的方法。最简单的方法是使用搜索引擎检查订单完成钩子的可用性,或者查阅 WooCommerce 插件的文档。我们可以找到一个名为woocommerce_order_status_completed的动作来处理订单完成后的任务。让我们看看订单完成钩子的实现:

add_action( 'woocommerce_order_status_completed',  'wpqpi_payment_complete' );
public function wpqpi_payment_complete( $order_id ) {
    // Execute code after the order is successfully completed
}

我们可以使用add_action函数和特定的回调函数来定义动作。订单 ID 作为参数传递给这个函数,因此我们可以根据订单详情执行任何任务。下一个任务是确定我们如何使用自定义代码在 MyCred 中添加积分。

在我们可以向订单添加点数之前,我们有一些任务需要实现。首先,我们必须添加一个新的钩子以启用 WooCommerce 订单的点数。MyCred 点数通过内置和自定义钩子添加。可以通过访问 WordPress 管理中的“点数 | 钩子”部分来查看可用钩子。你需要查阅 MyCred 文档以找到添加自定义 MyCred 钩子所需的必要钩子。因此,让我们创建一个新的钩子以启用 WooCommerce 订单的点数:

add_filter( 'mycred_setup_hooks', 'wpqpi_woocommerce_hooks', 10, 2 );
function wpqpi_woocommerce_hooks( $installed, $point_type ) {
  $installed['wpqpi_woo_purchase'] = array('title' => __( 'Points for WooCommerce Purchases', 'wpqpi' ), 'description'  => __( 'User will get points for completing product purchases.', 'wpqpi' ), 'callback' => array( 'WPQPI_WooCommerce_Hooks' ) );
  return $installed;
}

mycred_setup_hooks 过滤器用于向 MyCred 添加自定义钩子或删除现有钩子。在这种情况下,我们添加了一个名为 wpqpi_woo_purchase 的新钩子,用于为 WooCommerce 购买授予点数。数组定义了钩子的名称、描述以及实现点数授予过程的 PHP 类。

我们添加了一个名为 WPQPI_WooCommerce_Hooks 的自定义类。一旦添加此代码,你将在可用钩子部分看到一个新的钩子,名为 WooCommerce 购买点数。你可以将钩子拖到活动钩子部分以使其工作,如下面的截图所示:

截图

下一步是实现为 WooCommerce 购买授予点数的类。因此,我们需要实现 mycred_load_hooks 动作来加载上一节中设置的钩子。让我们看看 mycred_load_hooks 动作中 WPQPI_WooCommerce_Hooks 类的实现:

add_action( 'mycred_load_hooks', 'wpqpi_load_custom_taxonomy_hook', 10 );
function wpqpi_load_custom_taxonomy_hook() {
  class WPQPI_WooCommerce_Hooks extends myCRED_Hook {

    public function __construct( $hook_prefs, $type ) {
      parent::__construct( array(   'id'       => 'wpqpi_woo_purchase',       'defaults' => array( 'creds'   => 1,  'log'     => '%plural% for purchasing a product' ) ), $hook_prefs, $type );
    }

  }
}

在钩子内部,我们只需通过扩展 MyCred 插件的 myCRED_Hook 核心类来定义类。然后,我们添加具有唯一 ID 和默认设置的类构造函数。这些设置包括默认授予的点数和授予点数的日志条目文本。此类在加载钩子时执行一个名为 run 的内置函数。这将是我们定义 WooCommerce 订单完成钩子的集成点。让我们看看我们如何在类构造函数之后实现 run 函数:

public function run() {
    add_action( 'woocommerce_order_status_completed', array( $this, 'wpqpi_payment_complete') );
}

我们可以在之前创建的 MyCred 钩子的加载过程中定义任意数量的动作。在这里,我们有 woocommerce_order_status_completed 动作和回调函数,它们在订单成功时执行。现在,我们可以进入实现 wpqpi_payment_complete 函数的过程,如下所示:

public function wpqpi_payment_complete( $order_id ) {
    $order = wc_get_order( $order_id );
    $total = $order->get_total();
    $credits = (int) $total / 10;
    $user = $order->get_user();
    $user_id = $user->ID;
    $this->core->add_creds( 'wpqpi_woo_purchasing', $user_id, $credits,              $this->prefs['log'], 0, '', $m  );
}

首先,我们使用 $order_id 参数通过 wc_get_order 函数加载订单详情。接下来,我们通过在 $order 对象上调用 get_total 函数来获取订单总额,并将其除以 10 以生成订单的 MyCred 点数。然后,我们通过在 $order 对象上调用 get_user 函数来获取购买产品的用户的 ID。最后,我们调用 MyCred 的 add_creds 函数将点数添加到用户的订单中。

add_creds函数有三个必需参数和一些可选参数。前三个参数分别用于引用 ID、用户 ID 和要授予的积分数量。其他参数对于本书中使用的场景来说并不重要。现在,这两个插件已经集成,在成功的 WooCommerce 订单完成后向用户授予 MyCred 积分。我们通过修改第二个插件的数据,使用一个插件的事件来与另一个插件集成。

将用户添加到 BuddyPress 私密群组

在上一节中,我们完成了 MyCred 和 WooCommerce 集成的第一部分。下一步是通过将 MyCred 与 BuddyPress 群组连接来完成集成。正如我们讨论的,要求是在用户达到特定的 MyCred 积分数量后,将用户分配到 BuddyPress 私密群组。因此,我们必须寻找一种方法将用户添加到 BuddyPress 群组。我们可以检查文档或使用搜索引擎来找到适当的实现。BuddyPress 提供了一个名为groups_join_group的函数,用于将用户添加到群组。让我们使用这个函数来修改wpqpi_payment_complete函数,并将用户添加到群组:

public function wpqpi_payment_complete( $order_id ) {
    // Existing code for adding MyCred points
    $balance = mycred_get_users_balance( $user_id );
    if($balance > 100){
      groups_join_group(1, $user_id);
    }
}

首先,我们使用内置的mycred_get_users_balance函数来接收用户的可用积分。然后,当用户余额达到我们指定的限制时,我们使用 BuddyPress 的groups_join_group函数,并传入群组 ID 和用户 ID。

我们在这个场景中简化了代码,以说明插件集成过程。在理想情况下,在将用户添加到群组或授予用户积分之前,我们必须进行更多的权限检查。

在这个实现中,我们只为 WooCommerce 订单授予积分,用户只能使用积分加入群组。因此,我们可以在同一个函数中实现积分检查过程。当我们为多个任务授予积分时,这段平衡检查代码需要在这些所有任务中实现。

现在,我们已经完成了集成,并使三个插件协同工作以满足我们的需求。同样,您可以使用现有的钩子和函数来连接满足您需求的必要插件。然而,我强烈建议不要在一个网站上自定义集成大量插件,因为插件更新时很难管理。

摘要

在快速构建网站的过程中,自定义和扩展插件的过程至关重要。因此,我们开始通过创建前一章中开发的帖子附件插件的一个附加组件来扩展插件。接下来,我们通过示例场景来查看 WordPress 核心动作执行过程及其重要性。然后,我们探讨了使用各种技术来定制插件的过程,同时修改 WooCommerce 产品功能。最后,我们在集成流行插件以了解过程的同时,研究了插件之间不同的集成类型。

在第六章《WordPress API 的实用应用》中,我们将探讨内置的 WordPress API,同时学习如何在插件开发中实际使用它们。

第六章:WordPress APIs 的实际应用

在现代网站上,应用程序编程接口的使用很常见。我们使用 Google Maps、Google Analytics 和 Facebook 等 API 来利用第三方服务的功能。基本上,API 是由开发者创建的,以使他们的功能对其他开发者和平台可用。同样,WordPress APIs 让我们能够以标准方式使用核心功能来改变和扩展功能。我们还可以使用这些功能在 WordPress 之上创建自己的 API,并将它们暴露给第三方服务和平台。

在本章中,我们将探讨可用的 API、它们的功能以及在开发中的应用。我们将更多地关注在自定义开发中起主要作用但尚未在前几章中介绍的三个 API。开发者经常使用 WordPress 短代码来提供可重用功能,客户也熟悉使用这些短代码。因此,我们研究了创建短代码的技术以及识别限制。接下来,我们探讨 Rewrite API 在构建不影响核心功能的功能中的重要性。最后,我们查看 REST API 的使用,通过标准接口使功能对第三方开发者可用。

本章我们将涵盖以下主题:

  • WordPress APIs 的简要概述

  • 介绍短代码 API

  • 创建自定义短代码

  • 理解短代码的使用

  • 使用 Rewrite API 管理自定义路由

  • 使用 REST API 建立远程连接

在本章结束时,你应该了解开发任务所需的 API 函数。此外,你应该能够使用短代码构建可重用功能,并通过 REST API 将你的功能暴露给第三方应用程序。

技术要求

遵循此程序需要安装 WordPress 4.9.8。即使你有 WordPress 的更高版本,描述的示例也应该没有重大问题地运行。

本章的代码文件可以在 GitHub 上找到:

github.com/PacktPublishing/WordPress-Development-Quick-Start-Guide/tree/master/Chapter06

查看以下视频,以查看代码的实际运行情况:

bit.ly/2EQa24y

WordPress APIs 的简要概述

WordPress API 是一组协同工作的子 API,允许开发者在核心功能之上构建。单个 API 涵盖一个或多个核心功能,而一些 API 可以用于 WordPress 核心功能之外。REST API、Rewrite API 和 Shortcode API 是我们可以用于 WordPress 核心功能以及自定义功能的一些 API。与构建自己的函数相比,使用 WordPress API 可以减少你的开发工作量。让我们看看在 WordPress 中使用 API 的优势:

  • 使用动作和过滤器:WordPress API 函数在过程中执行必要的动作和过滤器,允许开发者自定义功能。使用我们自己的自定义函数会省略这些动作和过滤器。因此,将许多插件组合或一起使用将变得困难,因为一些插件可能依赖于 API 函数中的这些钩子。

  • 输入验证和安全:内置的 API 函数是由世界上最优秀的 WordPress 开发者开发和测试的。因此,数据验证和过程中的安全性非常可靠。使用自定义构建的函数而不是 API 函数会增加开发者的工作量,以实现这些安全功能和验证。

  • 效率:内置的 API 函数经过优化,以提高与数据库交互以及处理核心功能和文件时的性能。因此,使用这些函数可以提高您解决方案的效率。

  • 向后兼容性:WordPress 团队高度重视与旧版本的兼容性,因此很少移除对旧函数和功能的支持。因此,使用 API 函数保证了当前 WordPress 版本以及未来版本中功能的兼容性。

上述列表解释了为什么开发者应尽可能使用 API 来节省开发时间,并构建无风险的解决方案。

使用内置 API

根据最新版本,WordPress 提供了十八个独立的 API,用于处理核心的不同部分。我们已经在之前的章节中使用了一些 API 函数。在本章中,我们将详细介绍几个 API。让我们来看看可用的 API 以及它们在开发中的作用:

  • 仪表板小部件 API:它用于添加、编辑或修改管理仪表板上的小部件。仪表板是用户从后端登录后重定向到的位置。默认仪表板小部件包括概览快速草稿活动。此 API 通过删除不必要的 widget 并添加特定插件的 widget,有助于构建项目特定的仪表板。

  • 数据库 API:它用于简化并优化核心数据库表中的操作。我们已经在第二章,管理数据库结构、存储和检索中使用过核心数据库 API 功能,通过global $wpdb对象和查询函数。

  • HTTP API:它用于安全地执行 HTTP 操作,例如发送数据请求或检索数据。API 函数检查 PHP 中各种可用的连接类型中服务器支持的连接类型。我们之前章节中没有使用 HTTP API。

  • 文件头 API:它用于在主题和插件中处理文件头。该 API 的函数通过文件的头部注释部分识别主题和插件。从开发者的角度来看,该 API 的功能在开发任务中不太常用。

  • 文件系统 API:它用于在多种托管环境中读取和写入本地文件到文件系统。从开发者的角度来看,该 API 的功能在开发任务中不太常用。

  • 元数据 API:它用于以标准方式处理 WordPress 元对象类型。WordPress 为帖子、用户和评论提供了元数据表。这些函数允许您以最佳性能添加、编辑、修改和删除帖子元数据。我们在第五章,通过插件扩展、过滤器、和动作中,使用update_user_metaget_user_meta等帖子元数据函数与元数据 API 进行了交互。从开发者的角度来看,该 API 的功能非常重要,并且在开发任务中常用。

  • 选项 API:它用于处理 WordPress 中的选项表,以存储网站范围的设置以及主题和插件特定的设置。这些 API 函数允许您以最佳性能添加、编辑、修改和删除选项。我们在第四章,使用插件开发构建自定义模块中,使用get_optionupdate_option等函数与选项 API 进行了交互。从开发者的角度来看,该 API 的功能非常重要,并且在开发任务中常用。

  • 插件 API:它在插件开发中使用,因此对于您作为开发者来说非常重要。该 API 包含用于管理 WordPress 钩子的函数。我们已通过使用add_filterapply_filtersadd_action函数与该 API 进行了交互。

  • 快速标签 API:它用于允许开发者向 WordPress 内容编辑器的文本模式添加额外的按钮。我们没有使用此 API,因为它仅关注修改内容编辑器。从开发者的角度来看,在内容创建是网站主要功能的情况下,此 API 很重要。

  • 设置 API:它用于创建和管理您网站的设置以及主题或插件的设置。您可以使用此 API 向现有设置页面添加设置,或将您自己的自定义设置部分添加到 WordPress 设置菜单中。这是添加和保存设置的最简单方法之一,因为大部分功能都由 API 处理。从开发者的角度来看,此 API 对于构建任何类型的网站都非常重要。

  • 主题修改 API:它旨在让主题开发者添加和检索主题的设置作为 WordPress 选项。除非您正在构建自定义主题,否则该 API 在开发中不扮演关键角色。

  • 主题定制 API:它用于定制主题设置、小工具、样式,并可以立即预览更改。您可以通过外观 | 定制部分访问由该 API 生成的功能。开发者可以使用此 API 添加他们自己的设置、部分和控制。

  • Transients API:它用于在wp_options表中临时存储或缓存信息。与 WordPress 选项不同,transients 有一个过期时间,因此仅用于短期保存信息。API 函数允许您使用各种选项存储、检索和删除 transients。

  • Widgets API:它用于在 WordPress 中创建和管理小工具。在 WordPress 中,小工具是一个可重用的组件,主要用于网站的侧边栏。我们将在接下来的章节中更详细地讨论小工具和 API 函数。

  • XML – RPC API:它用于将网站功能暴露给第三方服务和应用程序。这是提供 API 功能的老方法,随着 REST API 的出现,它正在逐渐被弃用。

在本节中,我们对所有 WordPress API 进行了简要概述,除了三个。短代码、重写规则和 REST API 在定制开发中起着重要作用,因此我们将在接下来的章节中详细讨论它们。您可以在codex.wordpress.org/WordPress_API%27s查看有关先前 API 的更多详细信息。

介绍短代码

WordPress 中的短代码是一段代码,它根据各种要求为网站生成动态内容或更改给定内容。这个功能是为了允许用户向帖子或页面添加动态内容而引入的。然而,现代网站将短代码用于其预期目的之外。我们可以看到短代码被用于小工具、模板文件,甚至插件内部以提高可重用性。短代码 API 是一组函数,允许开发者根据他们的偏好轻松创建和使用短代码。现代网站使用高级主题以及众多插件来提供具有吸引力的设计的高级功能。因此,您将看到短代码的使用越来越多。短代码是向网站添加功能的最简单方法之一,因此了解 API 函数非常重要。

短代码的结构

在本节中,我们将从不同用户类型的视角来理解 WordPress 短代码。作为一个开发者,理解所有三种用户视角对于在开发任务中构建和使用短代码非常重要。让我们看一下以下图表:

图片

上述图表基于使用短代码过程中涉及的三种用户视角。

短代码的内容使用是可选的。我们可以找到许多自闭合短代码,它们在不需要使用任何内容的情况下生成输出。

让我们了解这三个用户视角中的每一个。

短代码的开发者视角

开发者负责构建短代码,除非你只计划使用 WordPress 核心中可用的短代码。短代码需要使用 API 函数创建,并通过插件或主题注册到 WordPress 中。前面的图示显示了从开发者视角的基本短代码语法。

短代码的内容创作者视角

通常,网站管理员为大多数网站创建内容。然而,你也可能有其他用户角色或具有创建内容权限的特定用户。这些用户负责在网站开发过程中使用带有必要属性和内容的短代码。前面的图示说明了短代码的主要语法,这些用户将在帖子或页面中使用。

他们可以使用 WordPress 核心、主题或网站上提供的短代码。内容创作者不必知道短代码的来源。然而,在你想切换主题或插件的情况下,了解短代码是否由主题、插件或 WordPress 核心生成变得非常有用。在这些情况下,缺乏对短代码来源的了解可能会导致冲突,甚至导致网站完全崩溃。除非他们想探索主题或插件内部的代码,否则这些用户不会意识到短代码的源代码。

短代码的用户视角

用户是任何访问网站的人,包括访客和成员。他们将只看到由短代码生成的输出。这些用户不知道短代码的存在,因为短代码在浏览器视图或页面源代码中是不可见的。此外,短代码输出没有预定义的 ID 或类,因此这些用户无法从前端跟踪短代码。

现在你应该对 WordPress 短代码的创建和使用以及如何生成动态输出有一个基本的了解。关于短代码的创建和使用将在后续章节中详细讨论。

使用内置短代码

WordPress 核心包含一组内置短代码,主要专注于向帖子或页面添加各种内容类型。内容创作者可以直接在帖子、页面或任何支持的位置使用这些短代码,无需任何修改。让我们看看现有的短代码:

  • 音频:用于嵌入和播放音频文件

  • 标题:用于添加内容标题。主要用于图像

  • 嵌入:用于嵌入由 WordPress 支持的网站的内容

  • 相册:通过传递图像 ID 来显示图像相册

  • 视频:用于嵌入和播放视频文件

  • 播放列表:用于显示音频和视频文件的集合

正如你所见,这些短代码不使用 WordPress 网站的主要数据,如文章、评论、用户。因此,我们将不得不与外部插件中的短代码或创建自定义短代码来满足涉及 WordPress 数据库的功能。

构建自定义短代码

构建自定义短代码的过程并不像许多人想象的那样复杂。短代码的最小实现只需要几行代码,如最后部分的图中所示。一个短代码由四个主要部分组成:

  • 开标签和闭标签:开标签和闭标签类似于 HTML,WordPress 短代码使用方括号的开标签和闭标签。这些标签由内容创建者用于向文章或页面添加功能。另一方面,开发者需要使用这个标签来注册短代码作为唯一元素。一些短代码没有闭标签,并使用自闭合的开标签。

  • 属性:属性是一组数据设置,用于在短代码函数内部处理功能并返回输出。属性列表从短代码名称在开标签之后开始。每个属性都有一个键和值,如短代码图像的结构所示。属性应该由一个或多个空格分隔。在短代码内部,开发者可以使用属性变量的数组键访问传递的属性。

  • 内容:内容是我们添加到短代码开标签和闭标签之间的内容。一旦内容被使用,短代码将作为 WordPress 过滤器工作,在那里我们检索、处理和返回修改后的内容。我们也可以通过使用自闭合的开标签来定义没有内容短代码。

  • 输出:输出是基于传递的属性和内容,由短代码返回的值。输出将是短代码内部修改的内容,或者内容将被用来捕获和显示网站数据。许多开发者倾向于直接使用 PHP echo语句将短代码输出打印到浏览器。然而,短代码应该始终返回输出而不是直接打印到浏览器。

现在我们可以继续创建自定义短代码的过程,使用这些部分。

创建自定义短代码

在本节中,我们将考虑两种场景来构建自定义短代码,并解释短代码的不同用途。让我们列出为每个构建短代码的两个要求:

  • 根据用户角色限制内容

  • 显示带有附件的文章列表

让我们开始吧。

使用短代码限制内容

这种情况解释了使用之前章节中讨论的四个短代码组件创建短代码的过程。基本上,我们需要一个短代码,它接受内容,并且只对授权用户显示输出,同时为未授权用户提供自定义输出。让我们考虑这个场景所需的短代码:

[wpquick_restrict_content  role=subscriber]  Content to be protected [/wpquick_restrict_content]

之前的短代码有一个属性用于指定不允许查看内容的用户角色和需要保护的内容,现在我们必须将之前的短代码与短代码结构图中的代码相匹配。考虑以下代码来实现这个短代码:

add_shortcode( 'wpquick_restrict_content', 'wpquick_restrict_content_display' );
function wpquick_restrict_content_display( $atts, $content ){
  $sh_attr = shortcode_atts( array( 'role' => '' ), $atts );

  if( $sh_attr['role'] != '' && ! current_user_can( $sh_attr['role'] ) ){
    $content = __('You don\'t have permission to view this content','wqsa');
  } 
  return $content;
}

在这种情况下,一个回调函数接受短代码的属性和内容。WordPress 提供了一个名为shortcode_atts的函数,用于合并传递的属性和默认属性,并创建一个用于处理短代码所需的属性数组。在shortcode_atts函数内部定义允许的属性和默认值作为一个数组是一个好的做法。在这种情况下,我们为role参数传递了一个值,因此它将覆盖默认参数。

如果我们没有指定属性的值,shortcode_atts将查找默认值。接下来,我们使用$sh_attr数组访问属性并应用条件。最后,根据条件返回原始内容或消息。这是构建短代码所需的最基本的代码。

显示带有附件的帖子

这种情况从开发者和内容创作者的角度解释了短代码的不同用法。在这里,我们需要显示至少有一个附件的帖子列表。与之前的场景不同,我们不是检索和修改内容。相反,我们根据短代码属性生成动态内容。让我们看看这个场景所需的短代码:

[wpquick_attachment_posts/]

如您所见,没有关闭标签,关闭部分是在开标签内完成的。这类短代码被称为自闭合短代码。我们不需要在这些短代码中使用任何内容。在这种情况下,我们正在显示所有带有附件的帖子,因此不需要短代码属性。如果我们需要显示特定类别的帖子附件,短代码将如下所示:

[wpquick_attachment_posts category='1' /]

现在我们可以通过将其与我们在短代码图中使用的语法相匹配来实现之前的短代码:

add_shortcode( 'wpquick_attachment_posts', 'wpquick_attachment_posts_display' );
function wpquick_attachment_posts_display( $atts, $content ){
  global $wpdb;
  $post_attachments_table = $wpdb->prefix.'wpqpa_post_attachments'; 
  $sql = "SELECT P.post_title,P.guid from $post_attachments_table as PA inner join $wpdb->posts as P on P.ID=PA.post_id group by PA.post_id ";

  $result = $wpdb->get_results($sql);
  $html = '';
  if($result){
    foreach ( $result as $key => $value ) {
      $html .= '<a href="'. $value->guid .'">'. $value->post_title .'</a><br/>'; 
    }
  } 
  return $html;
}

在这种情况下,我们没有属性,因此我们通过将其与帖子表连接来查询自定义表以生成结果。$content变量将为空,因为我们没有通过开闭标签传递任何数据。最后,我们返回一个 HTML 字符串以显示帖子列表。

理解短代码的使用

我们在上一节中讨论了构建短代码的技术。现在我们需要了解在不同网站部分使用这些短代码的技术。让我们看看我们可以使用短代码的不同位置:

  • 帖子与页面:这是短代码最常见的使用方式,内容创建者直接在帖子或页面编辑器中添加短代码。然后,WordPress 执行短代码,并在前端帖子或页面上显示结果。

  • 小部件:我们可以在 WordPress 小部件中使用短代码来提供类似于帖子或页面的动态内容。然而,我们必须根据所使用的小部件使用额外的代码。WordPress 的文本小部件允许你直接在内容编辑器中添加短代码,并在前端显示输出。有时,你可能想使用短代码与 HTML 元素一起,因此需要使用HTML 小部件而不是文本小部件。HTML 小部件默认不支持短代码。因此,我们必须在主题的functions.php文件或任何插件中以下代码来执行短代码:

     add_filter( 'widget_text', 'do_shortcode' );

WordPress 有一个名为widget_text的过滤器,它在所有小部件的内容上执行。我们可以使用这个过滤器来按需更改内容。到目前为止,我们使用了add_filter语句和一个回调函数。这里,我们还有一个名为do_shortcode的回调函数。然而,你可能想知道为什么实现缺失。do_shortcode函数是内置在 WordPress 中的,因此我们可以直接调用它,而无需为该函数添加实现。此函数执行内容中的任何短代码。

  • 模板文件:有时,我们可能需要在标题、页脚或任何模板文件中调用短代码。当你正在自定义现有主题以集成其他插件的功能时,这非常有用。在这种情况下,将短代码添加到模板文件中不会起作用。我们必须使用 PHP 脚本并使用do_shortcode函数来执行短代码。考虑以下代码,用于在模板文件中使用短代码:
      <?php echo do_shortcode('wpquick_attachment_posts'); ?> 

我们还需要使用echo语句将输出打印到浏览器,因为在之前的场景中它不是自动打印的。

  • 插件与主题:有时,我们希望将短代码功能与其他插件或主题集成。在这种情况下,我们可以直接调用短代码,而不是复制短代码的代码。假设我们想在每篇帖子内容之后显示带有附件的帖子。在这种情况下,我们可以在我们的插件内部调用短代码,生成带有附件的帖子列表,而不是编写单独的函数或复制代码。考虑以下用于在主题或插件代码中使用短代码的语句:

这些是使用短代码的一些常见方法和位置。

使用短代码的技巧

短代码是一种简单灵活的方法,可以将可重用的功能添加到网站中。然而,使用短代码既有优点也有缺点。作为一名开发者,你需要了解何时使用短代码以及如何构建高质量的短代码。以下提示将帮助你改进短代码,同时避免不必要的麻烦:

  • 过滤短代码输出:通常,我们在短代码内创建一些内容,并将其直接作为输出返回。有些插件会在短代码的输出上使用过滤器。这是一种增加灵活性的好方法,开发者可以控制其他插件的短代码输出。

  • 使用嵌套短代码:我们可以在其他短代码内添加短代码以获取输出。然而,在使用嵌套短代码时,您需要在主短代码中使用do_shortcode来执行其内容内的短代码。

  • 验证短代码属性:属性是由内容创建者添加的,因此需要将其视为用户输入。由于我们不应信任任何类型的用户输入,因此对属性值进行验证是必须的。

  • 使用唯一的短代码名称:许多插件使用通用的短代码名称,如 [product][event] 等。这可能导致多个插件之间的冲突,因此您应该始终使用插件特定的前缀来使其独特。

  • 过度使用短代码:短代码是添加大量内容的一种简单方法。然而,在文章或页面中使用过多的短代码可能导致后期维护噩梦。由于文章或页面依赖于短代码,更改内容将变得困难。

我们已经介绍了创建和使用短代码的过程。现在,让我们看看使用短代码的插件,并识别各种用途和开发技术。

使用 Rewrite API 管理自定义路由

默认的 WordPress URL 结构使用查询参数来加载必要的文章和页面。因此,文章的 URL 将看起来像www.example.com/?p=130。这并不理想,因为很难记住文章 ID,搜索引擎也不会给予此类 URL 更高的优先级。因此,我们需要一个更好的 URL 结构,不使用查询参数,并提供 SEO 友好的 URL。因此,我们使用 WordPress 的永久链接部分来更改 URL 结构。然后,WordPress 将内部将这些 SEO 友好的 URL 转换为默认的 URL 结构。我们可以从设置 | 永久链接访问永久链接设置部分。让我们选择文章名称作为我们网站的 URL 结构。然后,前面的 URL 将转换为http:// www.example.com/sample-post/

如您所见,这是一个更符合用户和 SEO 友好的 URL 结构。一旦设置好永久链接,WordPress 前端的所有核心功能都将使用这种结构。然而,在自定义开发中,我们可能需要自定义 URL 结构来处理功能。在这种情况下,我们可以使用重写 API 来定义我们自己的路由并管理功能,而不会干扰默认的 WordPress 功能。

什么是重写 API?

WordPress 重写 API 是一组函数,允许您使用标签、规则和端点来管理自定义路由。让我们确定用于实现重写 API 的函数:

  • add_rewrite_rule: 这个函数用于向 WordPress 注册新的重写规则以生成自定义模板。

  • add_rewrite_tag: 这个函数用于注册新的查询变量。我们需要与add_rewrite_rule一起使用来为自定义模板创建重写规则。

  • add_rewrite_endpoint: 这个函数用于为 WordPress 核心组件创建额外的重写规则。

  • flush_rewrite_rules: 这个函数用于删除重写规则然后重新创建重写规则。

在本节中,我们将使用这些函数(除了add_rewrite_endpoint)来创建和管理自定义规则。您可以在codex.wordpress.org/Rewrite_API/add_rewrite_endpoint查看关于重写端点的更多详细信息。

理解自定义路由的需求

在开发超出默认 WordPress 功能的插件时,自定义路由的使用变得至关重要。我们将考虑两个场景来理解自定义重写的需求:

  • 假设我们有一个用于显示用户个人资料详情的短代码,并且我们在名为Profile的 WordPress 页面中使用它。因此,该页面的 URL 将是www.example.com/profile。现在,我们需要使用相同的页面来显示每个用户的详细信息。目前,这是不可能的,因为我们没有在访问此页面时识别用户的方法。因此,我们需要以某种方式修改 URL,以便我们可以识别每个用户。让我们假设我们有一个类似www.example.com/profile/john123的 URL,用户名位于页面之后。然后,我们可以使用用户名组件来识别用户并显示个人资料详情。在这种情况下,我们必须使用重写 API 函数来匹配 URL 与必要的查询参数并执行我们的功能。

  • 正如我们已经讨论过的,将短代码添加到如此重要的功能中可能会在管理员不小心删除或移除短代码时成为一个问题。因此,依靠帖子或页面来处理此类功能可能被视为一种风险。在这种情况下,我们可以通过使用不使用帖子或页面的自定义 URL 来避免风险。假设我们想在www.example.com/user/login显示登录表单,在www.example.com/user/register显示注册。默认情况下,当你没有名为user的帖子或页面时,这些 URL 将重定向到404 未找到页面。因此,我们需要使用重写 API 来注册自定义路由并管理这些 URL,以提供不干扰核心 WordPress 功能的自定义功能。

在本节中,我们将实现第二个场景,因为处理没有帖子或页面的自定义 URL 是自定义功能的理想解决方案。

创建路由规则

我们可以使用add_rewrite_rule函数在 WordPress 中注册新的自定义路由。这个函数可以在 WordPress 的许多动作中实现。然而,我们通常使用init动作来处理重写规则。让我们添加一个自定义重写规则来处理用户登录和注册的场景:

add_action('init','wqraf_manage_user_routes');
function wqraf_manage_user_routes() {
  add_rewrite_rule( '^user/([^/]+)/?', 'index.php?wpquick_action=$matches[1]', 'top' );
}

你可以在源代码目录中的wpquick-rewrite-api插件中找到本节的代码。add_rewrite_rule函数接受两个必需参数和一个可选参数。让我们看看这些参数及其作用:

  • regex:这是第一个参数,它使用正则表达式将 URL 与规则匹配。

  • redirect:这是第二个参数,它指定当当前 URL 与正则表达式匹配时请求的 URL。正如你所见,我们通过传递带有唯一标识我们功能的自定义参数将请求传递给index.php

  • after:这个可选参数决定何时使用这个重写规则。如果使用top值,重写规则将优先于其他默认 WordPress 重写规则。如果使用bottom值,将优先考虑现有规则。

现在,你可能认为我们可以使用$_GET查询参数访问wpquick_action并实现登录和注册的功能。然而,你不会在$_GET参数中找到wpquick_action。WordPress 不允许你在查询字符串中使用任何类型的变量。它将检查现有列表中的查询变量,而所有其他变量将被忽略。因此,在我们实际上可以在代码中使用它们之前,我们必须指定自定义查询变量。

添加查询变量

WordPress 注册了超过 40 个参数以供查询使用。在添加自定义查询变量之前,让我们检查现有的查询变量。将以下代码添加到wpquick-rewrite-api插件的主体文件中,并打开你网站的首页:

add_filter( 'query_vars', 'wqraf_manage_user_query_vars' );
function wqraf_manage_user_query_vars( $query_vars ) {
  echo "<pre>";print_r($query_vars);exit;
}

你将看到所有现有的查询参数作为一个数组。现在我们需要创建我们自己的查询参数来处理用户登录和注册。因此,我们在wpquick-rewrite-api插件中添加以下代码:

add_filter( 'query_vars', 'wqraf_manage_user_query_vars' );
function wqraf_manage_user_query_vars( $query_vars ) {
  $query_vars[] = 'wpquick_action';
  return $query_vars;
}

query_vars是一个内置的过滤器,允许我们在请求处理之前添加或删除查询变量。在这种情况下,我们向现有的查询参数数组中添加了一个名为wpquick_action的新查询参数。

WordPress 核心和其他插件注册了相当数量的查询变量。因此,最好使用带有前缀的唯一名称以避免与其他插件的查询参数冲突。

现在我们有能力从 URL 中访问wpquick_action变量。在这种情况下,我使用了query_vars过滤器来添加新的查询变量,并用于识别内置的查询变量。重写 API 提供了一个名为add_rewrite_tag的函数来实现相同的功能,这是推荐的方式。因此,我们可以从我们的插件中移除query_vars过滤器,并将wqraf_manage_user_routes函数修改如下以获得相同的功能:

function wqraf_manage_user_routes() {
  add_rewrite_rule( '^user/([^/]+)/?', 'index.php?wpquick_actions=$matches[1]', 'top' );
  add_rewrite_tag('%wpquick_actions%', '([^&]+)');
}

第一个参数接受一个由%包围的动作名称,第二个参数定义了一个regex来验证值。与使用query_vars过滤器相比,添加重写标签相对容易。

我们通过添加新的规则修改了 WordPress 的重写规则。因此,在新的规则生效之前,我们必须刷新重写规则。有两种刷新重写规则的方法。首先,我们可以访问设置 | 永久链接部分来自动刷新重写规则。然而,在开发中这并不理想,因为用户需要手动进入设置部分。因此,我们需要在我们的插件中自动刷新重写规则的方法,而不需要用户的输入。

刷新重写规则

我们可以很容易地通过在init动作中调用 WordPress 的flush_rewrite_rules函数来刷新重写规则。然而,它会在每次请求时刷新重写规则,产生不必要的性能开销。因此,我们需要使用插件的激活处理程序来刷新规则。让我们看看以下激活处理程序中的实现:

register_activation_hook( __FILE__, 'wqraf_activate' );
function wqraf_activate(){
  flush_rewrite_rules(); 
}

现在,转到管理面板,停用插件,然后再次激活插件。然后,转到 URL www.example.com/user/login 并检查它是否工作。不幸的是,你仍然会得到 404 错误。你可能想知道出了什么问题。让我们回到并思考这个过程,以了解问题所在。我们在插件激活时刷新了规则。因此,新规则应该成功持久化。然而,我们在init动作中定义了规则,这个动作只有在插件激活后才执行。因此,新规则在刷新时不可用。所以,我们必须更新函数如下以使其工作:

register_activation_hook( __FILE__, 'wqraf_activate' );
function wqraf_activate(){
  wqraf_manage_user_routes();
  flush_rewrite_rules(); 
}

现在请先禁用插件,然后再重新激活它。接着,访问网址 www.example.com/user/login。这次,你不会遇到 404 错误,因为重写规则已经被正确添加并刷新。现在我们已经准备好了用户功能的路由规则。

显示自定义路由的内容

我们必须构建一个自定义路由来管理站点的登录和注册。现在我们需要从自定义路由访问自定义查询变量,并生成登录和注册的屏幕。以下代码用于实现登录和注册表单的实现:

add_action( 'template_redirect', 'wqraf_front_controller' );
function wqraf_front_controller() {
  global $wp_query;
  $wpquick_action = isset ( $wp_query->query_vars['wpquick_action'] ) ? $wp_query->query_vars['wpquick_action'] : '';
  switch ( $wpquick_action ) {
    case 'register':
      echo "<h1>REgistration Form</h1>";exit;
      break;
    case 'login':
      echo "<h1>Login Form</h1>";exit;
      break;
  }
}

WordPress 在决定加载哪个模板之前执行template_redirect动作。因此,我们可以使用这个动作来拦截请求并加载我们自己的模板。首先,我们使用全局对象$wp_query访问wpquick_action值。在这个场景中,这个变量将包含注册或登录。然后,我们使用switch语句来过滤动作并加载必要的模板。在这里,我们只打印标题以供说明。你必须生成登录或注册表单而不是标题。

由于我们正在拦截请求,我们的内容将会被加载而不是 WordPress 模板。在这里,它只会打印标题,而不会包含头部、底部或其他 WordPress 模板的部分。因此,你必须通过包含头部、底部、内容区域和侧边栏来生成一个完整的模板。

我们已经探讨了使用重写 API 函数来创建自定义路由和为插件加载自定义模板的过程。当开发具有非 WordPress 特定屏幕的高级插件时,这将非常有用。

使用 REST API 建立远程连接

WordPress REST API 正在超越旧的 XML-RPC API,并成为网站开发的行业标准。该 API 允许开发者将 WordPress 站点与其他站点以及第三方应用程序连接起来。现代网站正朝着基于 JS 框架的前端发展,以优化性能并简化用户体验。在这种情况下,开发者可以使用 WordPress 作为核心功能的后端,并通过 REST API 公开数据来构建不使用 WordPress 的前端。REST API 很可能是 WordPress 开发的未来。

REST API 最初是在一个外部插件中引入用于测试的。最终,它在 WordPress 4.7 版本中被包含到 WordPress 核心中,现在它完全支持 WordPress 中所有主要数据模型的 REST API 端点。

让我们确定一些在 REST API 操作中常用的术语:

  • 路由:这是一个可以映射到 HTTP 方法的明确定义的 URL

  • 端点:将特定路由与 HTTP 方法匹配的过程

  • 请求:带有必要数据的 API 端点调用

  • 响应:API 对特定请求提供的数据

  • 模式(Schema): 这用于结构化 API 数据并提供关于 API 请求中可用属性和输入参数的信息

介绍核心 REST API 端点

WordPress 为处理大多数主要核心功能提供了内置的 API 端点。文档定义了端点、属性和示例请求,简化了开发者的学习曲线。在本节中,我们将探讨核心 REST API 端点以及使用测试请求来理解这个过程。您可以在 WordPress REST API 文档中查看所有可用端点和附加信息,网址为developer.wordpress.org/rest-api/reference/。WordPress REST API 默认启用。您可以通过在浏览器中访问www.example.com/wp-json来检查您的网站上是否启用了 API。如果 API 已启用,您将获得一个包含可用设置和端点的大型 JSON 字符串。

理解和测试核心 API 端点

我们必须识别和理解 API 端点、参数和返回数据格式,以使用 API 功能构建功能。测试 API 请求是理解过程的最佳方式。我们可以使用现有的工具来简化 API 请求测试过程。有许多这样的工具,我们将使用 Google Chrome 浏览器的 Postman 扩展。

Postman 是一个通过提供测试 API 请求、构建 API 文档和监控 API 使用等特性来简化管理 API 过程的工具。此工具在免费和 PRO 版本中提供了许多额外的 API 相关功能。免费扩展对于处理基本 API 测试项目来说已经足够。您可以使用 PRO 版本的高级功能,例如团队协作、创建模拟服务器以及与许多第三方 API 的集成。

您可以从www.getpostman.com/在您的 Chrome 浏览器中安装此扩展。一旦安装,您将获得一个独立的程序,如下面的截图所示:

图片

现在我们准备使用 WordPress REST API 测试 API 请求。主要来说,我们在开发任务中主要使用 HTTP GETPOST 请求。然而,这个工具提供了使用各种 HTTP 请求类型的能力,例如 PUTDELETE。REST API 包含端点,允许我们直接使用 GET 请求以及需要认证的POST请求。因此,我们将检查这两种类型的请求。

让我们从测试一个 GET 请求开始。基本上,这些端点暴露了 WordPress 中现有的数据。首先,你必须在输入请求 URL字段中输入端点的 URL,并将 HTTP 请求选择为GET。端点需要添加到 URL 中的www.example.com/wp-json/部分之后。因此,通过 REST API 访问帖子列表的请求看起来与以下截图所示的参数相似:

截图

底部的屏幕显示了我们选择的请求结果的格式。我们必须选择 JSON 作为结果类型,因为成功的 API 请求返回 JSON 数据。由于我们正在使用 GET 请求,类型也被设置为无认证。

现在我们可以继续测试用于检索数据以显示以及通过 API 请求修改数据库的 POST 请求。与使用 GET 请求相比,测试 POST 请求需要更多的工作。这些请求始终需要某种认证,并且在没有有效认证的情况下返回错误。因此,首先,你必须在输入请求 URL中添加 URL,并将方法选择为POST。接下来,转到授权标签页,并选择基本认证作为类型。然后添加具有创建和编辑帖子能力的网站用户的用户名和密码。

通常,POST 请求需要额外的参数和值。在这里,我们正在尝试创建一个帖子,因此你可以在“主体”标签页的表单数据部分添加帖子详情作为键值对。最后,点击发送按钮发送 API 请求。然而,你将得到以下错误消息而不是创建帖子:

{ "code": "rest_cannot_create", "message": "Sorry, you are not allowed to create posts as this user.", "data": { "status": 401 } }

你可能不明白为什么即使我们提供了基本的认证信息,它仍然返回权限错误。WordPress 认为基本认证是一种不安全的处理 REST API 请求的方式,因此,基本认证默认是禁用的。所以,我们必须使用 WordPress API 团队开发的名为JSON 基本认证的插件。此插件包含在本章的源代码文件夹中。一旦此插件被激活,你就可以再次发送请求,并看到响应和表单参数,如下面的截图所示:

截图

响应将包含关于我们请求中创建的新帖子的详细信息。你可以通过更改请求类型并提供必要的参数值,使用UPDATEDELETE操作类似地使用。

由于我们必须在每次请求中以纯文本格式传输用户名和密码详情,因此基本认证不是推荐用于认证 REST API 的方式。WordPress 团队建议我们仅将基本认证用于测试目的或在高度安全的私有网络中。

管理自定义路由和端点

REST API 的真正力量来自于自定义路由和端点的使用。在调整现有路由和端点以满足我们的要求方面存在某些限制。在某些情况下,我们可能必须创建自己的类并扩展 WordPress 核心类来实现自定义功能。因此,自定义路由和端点允许我们以最少的努力实现自定义 REST API 功能,而不会干扰 WordPress 核心 REST API 功能。

在本节中,我们将实现以下功能,以学习自定义 REST API 路由和端点的基础:

  1. 禁用默认帖子端点并使用自定义路由仅在选择类别中选择帖子数据公开帖子

  2. 通过列出帖子中的可用附件来提供自定义表数据的 REST API 访问

让我们开始实现这两个功能。

为帖子创建自定义路由

默认帖子相关端点可通过/wp/v2/posts访问。我们可以对该端点执行不带任何参数的GET请求,以检索包含所有主要帖子细节的帖子列表。假设我们想要使用自定义路由为该端点的每个功能提供更多控制,因此我们必须在定义自己的路由之前阻止现有端点。

我们将使用一个名为WQ REST API的新插件来实现本章 REST API 部分的所需功能。您可以使用前几章中讨论的插件创建技术,通过创建一个主文件来创建该插件。让我们通过在插件的主文件中使用以下代码来阻止默认的帖子端点:

add_filter( 'rest_endpoints', 'wpquick_rest_endpoints');
function wpquick_rest_endpoints( $endpoints ){
    if ( isset( $endpoints['/wp/v2/posts'] ) ) {
        unset( $endpoints['/wp/v2/posts'] );
    }
    return $endpoints;
}        

WordPress 提供了一个名为rest_endpoints的过滤器,用于过滤现有端点并通过删除或创建新的端点来自定义它们。在这种情况下,我们通过从端点数组中取消设置来禁用了默认的帖子相关端点。这将移除列出、创建、更新和删除帖子的能力。因此,我们必须为每个这些功能定义自定义路由,或者使用相同的自定义路由来支持所有与帖子相关的操作。让我们首先添加一个自定义路由来为第三方源提供帖子数据:

function wqra_routes() {
  register_rest_route( 'wqra/v1', '/read_posts', 
        array( 'methods'  => WP_REST_Server::READABLE, 
                'callback' => 'wqra_read_posts_handler',                                       'permission_callback' => function () {
                         return true;
               }
    ) );
}
add_action( 'rest_api_init', 'wqra_routes' );

REST API 包括一个名为rest_api_init的操作,它在执行 API 请求之前被触发。因此,将新路由引入 API 的推荐操作是它。在回调函数内部,我们使用register_rest_route函数为我们应用程序创建新路由。让我们确定register_rest_route函数中使用的参数的基本参数:

  • namespace:这将是在主 REST API URL 之后的第一个 URL 段。在这里,我们使用了wqra/v1,因此它将替换我们用于访问核心 WordPress 路由的wp/v2

  • route:这是路由的基本 URL。在这里,我们使用了read_posts作为路由,它将替换帖子的默认路由。

  • methods:在这里,我们使用的方法是WP_REST_Server::READABLE。这确保了当WP_REST_Server更改我们的可读端点时,我们的自定义路由可以正常工作。WP_REST_Server类的readable常量是GET方法的别名。

  • callback:这是当路由与给定的端点匹配时执行的函数。我们定义了一个名为wqra_read_posts_handler的回调函数。所有应在 REST API 响应中发送的帖子数据都在此函数中准备。

  • permission_callback:这是在执行回调函数之前检查请求权限的函数。我们返回 TRUE 以提供权限。在实际场景中,我们需要使用current_user_can函数检查用户权限,并根据用户权限级别返回布尔值。

现在,我们可以查看我们生成特定于网站的帖子列表的回调函数的实现。考虑以下wqra_read_posts_handler函数的实现:

function wqra_read_posts_handler() {
  $posts_query = new WP_Query(array('post_type' => 'post','post_status' =>'publish', 'order' => 'desc', 'orderby' => 'date', 'category__not_in' => array( 20 ),'posts_per_page'=>-1 ));
  $data = array();
  if($posts_query->have_posts()){
    while($posts_query->have_posts()) : $posts_query->the_post();
      array_push($data, array("ID" => get_the_ID(), "title" => get_the_title() ));
    endwhile;
  }

  return rest_ensure_response(($data));
}

首先,我们使用WP_Query对象传递自定义设置并生成基于我们要求的帖子列表。在这种情况下,我们只允许 API 用户查看帖子,除了所选分类的帖子。然后,我们只添加帖子的 ID 和标题,因为我们只想为 API 用户提供这两条数据。默认的帖子列表提供了对帖子所有主要数据的访问权限,因此它不适合我们的要求。最后,我们将结果数据数组传递给rest_ensure_response函数。此函数将我们的数据转换为 JSON,并返回与WP_REST_Response兼容的响应。

为自定义表数据创建自定义路由和端点

在上一节中,我们探讨了禁用现有的 API 端点并创建我们自己的端点来处理核心 WordPress 数据的过程。默认的 WordPress REST API 没有用于处理自定义表数据的任何路由或端点。因此,我们必须始终使用我们自己的自定义路由和端点来操作自定义表数据。

在前面的章节中,我们创建了一个插件来添加附件到帖子。现在,我们将创建一个自定义 API 端点,允许我们检索给定帖子的附件。让我们从向我们的 REST API 插件添加另一个自定义路由开始,如下面的代码所示:

function wqra_routes() {
  // Custom route for read posts
  register_rest_route( 'wqra/v1', '/list_post_attachments/(?P<id>\d+)', array(
     'methods' => 'POST','callback' => 'wqra_list_post_attachments_handler',
     'args' => array( 'id' => array( 'validate_callback' => function($param, $request, $key) {
                 return is_numeric($param);
     }
     ) ) ) );
}

我们使用与上次相同的命名空间,并使用list_post_attachments作为新的路由。此外,我们在括号中添加了一个额外的参数,其中我们可以传递帖子的 ID。我们还已将方法设置为POST而不是使用GET。由于我们正在检索记录并在 URL 中传递 ID,我们可以使用GET作为方法。然而,我们已启用POST方法,以便我们可以传递额外的参数并过滤结果。我们已使用一个新的回调函数wqra_list_post_attachments_handler来处理新的自定义路由。

在这种情况下,与之前的自定义路由相比,还有一个额外的参数称为args。这个args数组用于指定路由的附加参数。在这种情况下,我们使用它来验证传递给端点的参数。帖子 ID 是一个数值,因此我们需要在执行 REST API 请求之前对其进行验证。因此,我们执行 ID 参数的validate_callback函数。如果您为帖子 ID 传递字符串值,您将得到以下无效参数错误:

{"code":"rest_invalid_param","message":"Invalid parameter(s):id","data":{"status":400,"params":{"id":"Invalid parameter."}}}

我们可以使用这项技术来验证具有不同条件的多个参数,并确保请求只包含请求的格式中的数据。现在我们可以进入实现wqra_list_post_attachments_handler函数,该函数用于处理对新路由的请求:

function wqra_list_post_attachments_handler($data){
  global $wpdb;
  $data = $data->get_params();      
  $post_id = isset($data['id']) ? $data['id'] : 0;
  $post_attachments_table = $wpdb->prefix.'wpqpa_post_attachments';
  $sql  = $wpdb->prepare( "SELECT * from $post_attachments_table where post_id = %d ", $post_id);
  $result = $wpdb->get_results($sql);
  $post_attachments = array();
 if($result){
   foreach ($result as $key => $value) {
     $post_attachments[] = array('ID'=> $value->id, 'file_name' => $value->file_name, 'file_path' => $value->file_path);
   }
  }

  return rest_ensure_response(($post_attachments));
}

将 REST API 请求对象作为参数传递给此函数。因此,我们使用$data对象的get_params函数来检索请求中可用的参数。接下来,我们使用$data对象的id属性捕获帖子 ID。接下来,我们在wpqpa_post_attachments表上执行一个自定义查询。结果将包含给定帖子的文件附件。然后,我们遍历结果并将附件详情分配到一个数组中。最后,我们使用rest_ensure_response函数将附件数据以 JSON 格式传递到响应中。

在我们有能力使用现有路由和端点的情况下,使用自定义路由和端点也有一些缺点。对于自定义端点,我们需要实现自己的分页和排序,而 WordPress 默认为现有端点提供这些功能。我们可以在 WordPress 核心端点中默认使用用户权限检查,而我们需要传递额外的 nonce 参数来检查用户权限。

现在我们可以调用这个新路由,并检索给定帖子的文件附件列表。

构建 REST API 客户端

API 客户端是使用 REST API 提供的数据的用户或服务。我们使用客户端来访问其他站点的 REST API。客户端站点可以是 WordPress 或非 WordPress 站点。客户端也可以在不使用 PHP 的情况下用不同的编程语言实现。让我们确定 API 客户端的用法。在本章中,我们将探讨构建 REST API 客户端以从外部站点访问我们的数据的过程,因为这是 API 最重要的用途之一。

来自外部站点的 REST API 客户端

即使我们将 REST API 用于构建同一站点的功能,它也在这里达到了最大潜力。创建 REST API 的主要目的是为第三方 WordPress 和非 WordPress 基于的应用程序提供访问权限。因此,API 客户端可以用任何编程语言实现。在本节中,我们将构建一个基于 PHP 的 API 客户端,以支持 CURL 访问其他应用程序的 REST API。

让我们添加使用 CURL 的 API 客户端实现。你应该将此代码放置在一个 PHP 文件中,并从应用程序外部访问它:

function wpquick_rest_api_client( $url, $post_data = '' ){
  $api_route = $url;
  $ch = curl_init( $api_route );
  $headers = array(
    'Authorization:Basic YWRtaW46dTh1OHU4dTg='// <---
    );
  curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
  curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false );
  curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
  curl_setopt( $ch, CURLOPT_POST, true );
  curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
  if( $post_data != '' ){
    curl_setopt( $ch, CURLOPT_POSTFIELDS, $post_data );
  }
  curl_setopt( $ch, CURLOPT_VERBOSE, 1 );
  $return = curl_exec( $ch );
  echo "<pre>";
  print_r( $return );
  exit;
}

$post_data = array();
$post_data = json_encode( $post_data );
$api_route ="http://www.example.com/wp-json/wqra/v1/list_post_attachments/5";
wpquick_rest_api_client( $api_route, $post_data );

我们创建了一个名为wpquick_rest_api_client的函数,用于接受 API 端点的两个参数:URL 和文章数据。然后,我们通过在指定路由上执行curl_init函数来初始化一个curl请求。接下来,我们添加一个包含username:password组合的 base-64 编码字符串的授权头。我们需要某种认证方法来执行 POST 和 DELETE 请求。我们应该只为测试目的使用授权头。我们将在下一节讨论各种认证技术。然后,我们将指定必要的curl参数,并在请求中添加文章数据(如果提供)。最后,我们执行并打印从服务器返回的 API 响应。

现在我们有一个基本的 API 客户端函数,我们可以使用必要的参数调用该函数来执行 REST API 请求,如前述代码所示。在这种情况下,我们正在以 JSON 字符串的形式检索 ID 为 5 的论坛的文件附件。如果我们请求 ID 作为字符串,我们将得到一个无效参数错误,因为它是经过验证和限制的。因此,我们已经确定了从外部来源构建 API 客户端以访问网站的过程。

REST API 正在成为现代 Web 应用程序的趋势,因此 WordPress 似乎提供了越来越多的功能来构建功能齐全的 REST API。所以,在未来,我们将看到 REST 成为必须使用的 WordPress 功能。

摘要

WordPress API 是为了简化并标准化 WordPress 核心功能的使用而构建的。我们可以使用这些 API 函数来构建未来证明的解决方案,而无需在开发上浪费不必要的时间。

在本章中,我们探讨了使用 WordPress API 的重要性,并对每个 API 的功能进行了简要介绍。我们选择了三个在开发中扮演重要角色的 API,并对其进行了详细讨论。我们首先了解了短代码的结构和使用方法。然后,我们讨论了使用短代码的各种技术和位置。接下来,我们转向 Rewrite API,它管理着超出 WordPress 核心功能的自定义路由。最后,我们研究了 REST API,用于开发 API 函数并将应用程序数据暴露给第三方服务。

在第七章,“管理自定义文章类型和处理表单”,我们将通过教授用户如何构建各种功能和使用自定义表单来捕获和显示高级数据,来探讨 WordPress 开发中最重要两个方面。

第七章:管理自定义帖子类型和处理表单

在网络开发中,我们使用模型-视图-控制器(Model-View-Controller)架构将网站的不同方面分离到各自的层中。模型层旨在管理应用程序的数据和业务逻辑。在 WordPress 开发中,很难使用这种架构的原生形式。然而,对数据和逻辑进行建模对任何类型的网站都是重要的。因此,我们使用内置的自定义帖子类型和自定义表单来管理 WordPress 中的数据捕获过程。

在本章中,我们将探讨自定义帖子类型特征在自定义开发任务中的重要性及其应用。我们将构建一个新的自定义帖子类型来处理房地产列表网站中的属性,同时使用最重要的功能,如分类法、自定义字段和模板加载。默认情况下,自定义帖子类型功能在后台已启用,因此我们将探索在前端使用自定义表单进行数据管理的过程。最后,我们来看看自定义帖子类型和自定义表单在开发中的优缺点。

在本章中,我们将涵盖以下主题:

  • 自定义帖子类型简介

  • 构建面向对象的插件

  • 为属性构建自定义帖子类型

  • 表单管理简介

  • 构建和使用自定义表单

  • 在自定义帖子类型和自定义表单之间进行选择

  • 使用表单管理插件

到本章结束时,您将能够使用自定义帖子类型和自定义表单进行开发,并了解在不同场景下两种技术的优缺点。

技术要求

您需要安装 WordPress 4.9.8 才能遵循此过程。即使您

如果您使用的是较新版本的 WordPress,所描述的示例应该没有重大问题。

本章的代码文件可以在 GitHub 上找到:

github.com/PacktPublishing/WordPress-Development-Quick-Start-Guide/tree/master/Chapter07

查看以下视频以查看代码的实际应用:

bit.ly/2AyINar

自定义帖子类型简介

自定义帖子类型是在 WordPress 3.0 中引入的,作为管理超出默认帖子之外的不同内容需求的解决方案。然而,“自定义帖子类型”这个名称是模糊的,因为我们可能会将其理解为一种不同于普通帖子的类型。理想情况下,我们应该将其视为自定义内容类型,因为它提供了足够灵活的功能来处理复杂的内容类型,如产品、属性和事件。自定义帖子类型默认存储在wp_posts表中。在自定义帖子类型引入之前,我们必须在代码中使用大量的过滤器和条件来保持不同的帖子类型彼此分离。当在同一个网站上管理多种不同内容类型时,这可能会让许多开发者感到头疼。

自定义文章类型的引入极大地扩展了 WordPress 作为开发具有高级数据需求和逻辑的网站的框架的能力。现代主题和插件已经充分利用了这些功能。让我们看看一些基于自定义文章类型的流行插件:

  • WooCommerce 插件:我们在第五章,通过插件扩展、过滤器和使用动作中已经探讨了此插件的功能。WooCommerce 被用作电子商务解决方案来销售产品。此插件使用五个内置的自定义文章类型来处理产品产品变体订单退款优惠券。此外,我们还获得了针对自定义文章类型的特定产品模板设计。您可以将此插件作为理解自定义文章类型使用的最佳示例之一。

  • 事件日历插件:这是一个提供日历来创建和管理与事件相关的详细信息的插件,内置了用于事件组织者场地的自定义文章类型。

  • bbPress 插件:这是最受欢迎的论坛管理插件之一,内置了用于论坛主题回复的自定义文章类型。

如您所见,许多流行的插件使用多个自定义文章类型来构建插件功能。使用自定义文章类型大大简化了过程,同时也使插件与 WordPress 核心功能兼容。

为什么我们使用自定义文章类型来建模数据?

自定义文章类型是 WordPress 的一个伟大补充。但是,我们需要了解它如何在如此短的时间内变得如此受欢迎。主要原因是可以捕获、存储和显示高级数据需求,而不需要大量的开发工作。让我们看看自定义文章类型提供的主要功能,以减少开发时间。在不到二十行代码的情况下,我们可以获得一个具有以下所有功能的完全功能的自定义文章类型:

  • 创建和更新数据:一旦添加了新的自定义文章类型,我们就会得到一个用于添加和编辑数据的屏幕,类似于正常的文章屏幕。主要内容细节会自动保存,只需点击一下按钮,因此我们可以避免需要构建自己的带有表单的自定义屏幕。考虑以下截图,突出显示内置功能:

图片

  • 灵活的数据列表:通常,构建具有分页的数据列表需要相当的开发时间。使用自定义文章类型,我们为每个自定义文章类型获得内置的列表,具有分页、搜索、过滤和删除功能。此外,我们可以轻松更改数据库查询以过滤和显示默认数据列表。除了这些功能之外,我们还获得一个名为“批量操作”的下拉菜单,其中我们可以对单个自定义文章类型的多个记录执行内置功能。最重要的功能是能够添加我们自己的操作并在多个记录上以最少的自定义代码执行它们。 考虑以下截图,突出显示内置功能:

图片

  • 内置 URL 结构:我们为每个自定义文章类型的所有屏幕获取默认的 URL 结构。在创建自定义文章类型时,我们可以轻松地更改内置的 URL 结构。因此,我们可以避免设置自己的 URL 结构以及条件性地管理不同屏幕所需的时间。

  • 自定义字段管理:我们可以使用 WordPress 文章的默认自定义字段功能,其中我们可以为每个字段定义一个键和一个值。当我们想要对自定义字段有更多控制时,可以使用元框(metaboxes)以比从头开始开发自定义字段更少的开发工作量来添加和管理自定义字段。

  • 数据分类:自定义文章类型为每个文章类型的记录提供内置的分类支持。根据需求,我们有能力将新的自定义文章类型数据分类为层次分类或平面标签。除了过滤之外,我们还获得这些分类页面的默认模板。因此,与通常的自定义开发相比,数据分类过程极其简单。

  • 内置模板:构建模板并显示数据需要相当的开发工作量。默认情况下,自定义文章类型使用主题的内置模板,因此我们不需要做任何努力就可以以基本形式显示数据。即使对于高级需求,我们也可以简单地复制默认模板,并花费最少的工作量来调整它,以包含我们的设计元素或自定义数据。

这些原因可能让你对自定义文章类型如何在 WordPress 中成为如此有价值的功能有所了解。现在,是时候了解使用和开发这些功能的自定义解决方案的过程了。

构建面向对象的插件

到目前为止,我们在前面的章节中创建了几个插件。然而,我们使用了过程式方法,直接在主插件文件或子文件中定义必要的钩子和函数。随着网站或应用的复杂化,我们将很难使用过程式方法管理开发。因此,我们需要一种方法来使用最佳开发实践并将代码模块化到必要的类中。在本节中,我们将查看使用面向对象概念创建插件的基结构。让我们考虑以下基于 OOP 的插件结构代码:

if( !class_exists( 'WPQuick_CPT' ) ) {   
  class WPQuick_CPT{   
    private static $instance;
    public static function instance() { 
      if ( ! isset( self::$instance ) && ! ( self::$instance instanceof WPQuick_CPT ) ) {
        self::$instance = new WPQuick_CPT();
        self::$instance->setup_constants();
        self::$instance->includes();

        add_action( 'admin_enqueue_scripts',array( self::$instance, 'load_admin_scripts' ),9);                
        add_action( 'wp_enqueue_scripts',array( self::$instance,'load_scripts' ),9);                

        self::$instance->model_property= new WQCPT_Model_Property(); 
      }
      return self::$instance;
    }

    public function setup_constants() {
      if ( ! defined( 'WQCPT_PLUGIN_DIR' ) ) {
        define( 'WQCPT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
      }
    }       
    public function load_scripts(){ }   
    public function load_admin_scripts(){ }  
    private function includes() { 
      require_once WQCPT_PLUGIN_DIR . 'classes/class-wqcpt-model-property.php';
    }
  }
}

WordPress 没有提供推荐的方法或样板代码来使用面向对象的方法构建插件。因此,这是开发者的选择,使用更适合他们的技术。前面的结构被一些有趣的插件所使用,这也是我的个人偏好。因此,你可以将其用作指南,构建一个更适合你风格的更好解决方案。

我们首先检查主插件类的可用性,并为插件定义主类。我们将使用主类来处理所有其他文件以及类。然后,我们定义一个变量来保存类的实例,并使用instance函数从这个类生成一个对象。这个static函数和私有的instance变量确保我们只有一个插件类的实例。instance函数作为我们插件初始化的起点,因此我们可以在这个函数内包含必要的函数调用、类初始化和钩子。

instance函数内部,我们调用了两个函数setup_constantsincludes。这些函数分别定义了插件功能所需的常量,并使用 PHP 的require_once函数添加插件文件。随着我们开发复杂的插件,我们可以有许多这样的函数来分离这些基本配置。然后,我们可以定义所有actionfilter钩子,这些钩子旨在在整个插件中用于配置目的。在这里,我们使用了两个动作来在网站的前端和后端包含脚本和样式。

下一个部分应该包括插件主类的对象创建。在这个场景中,我们只有一个类,尽管在高级插件开发中我们可能需要许多类。我们需要使用self::$instance对象,因为我们处于static函数中。最后,我们返回主类的实例。

这是面向对象类型插件的基结构。我们可以在类中包含所有主要配置和通用功能,并使用其他类来处理业务逻辑。现在,我们需要使用以下代码初始化主类:

add_action( 'plugins_loaded', 'wqcpt_plugin_init' );
function wqcpt_plugin_init(){
  global $wqcpt;
  $wqcpt = WPQuick_CPT::instance();
}

我们在plugins_loaded动作上执行一个回调函数,以确保在我们初始化插件的主类之前所有插件都已加载。此外,这将是我们在进行集成或构建插件时检查其他插件之间依赖关系的位置。我们调用主WPQuick_CPT类的instance函数并将结果实例定义为global对象。正如你所看到的,现在我们的插件与之前的插件相比已经简化并且结构良好,只包含通用事物。特定于站点的实现将由其他类处理。现在,我们已经准备好开发插件以处理自定义帖子类型的功能。

为属性构建自定义帖子类型

我们可以使用自定义帖子类型来模拟广泛商业类别中的任何数据。在本章中,我们将为房地产列表网站构建一个属性自定义帖子类型,以说明自定义帖子类型功能的使用。在本节中,我们将开发创建自定义帖子类型、分配自定义分类和使用自定义字段的功能。

创建属性自定义帖子类型

首先,我们需要创建一个单独的类来处理与属性相关的功能,因为我们正在使用面向对象的插件。因此,在主插件目录内创建一个名为classes的新目录,并添加一个名为class-wqcpt-property.php的新文件。然后,我们需要在我们的插件中包含这个文件并创建一个对象以初始化功能。我们需要在WPQuick_CPT类的includes函数内部添加以下行来包含该文件:

require_once WQCPT_PLUGIN_DIR . 'classes/class-wqcpt-model-property.php';

接下来,我们需要在instance函数内部使用以下代码行来创建这个类的对象:

self::$instance->model_property  = new WQCPT_Model_Property();

现在,我们有了将功能添加到这个类的能力。WordPress 提供了一个名为register_post_type的函数来创建新的自定义帖子类型。这个函数接受广泛的参数来配置满足不同需求的帖子类型。这个函数应该在init动作内部调用,以防止任何不必要的冲突。让我们将以下代码行添加到WQCPT_Model_Property类的构造函数中:

$this->post_type   =  'wqcpt_property';
add_action( 'init', array( $this, 'create_property_post_type' ) );

在第一行,我们将帖子类型键添加到一个类变量中以便在所有函数内部重用。然后,我们使用init动作来调用create_property_post_type函数以创建一个新的帖子类型。由于我们使用的是类,我们需要通过使用$this实例来在类对象上调用该函数。现在,我们可以看看create_property_post_type函数的实现:

public function create_property_post_type() {
  global $wqcpt; 
  $post_type = $this->post_type;
  $singular_post_name = __( 'Property','wqcpt' );
  $plural_post_name = __( 'Properties','wqcpt' );

  $labels = array(
    'name' => sprintf( __( '%s', 'wqcpt' ), $plural_post_name),
    'singular_name' => sprintf( __( '%s', 'wqcpt' ), $singular_post_name),
    'add_new' => __( 'Add New', 'wqcpt' ),
    'add_new_item' => sprintf( __( 'Add New %s ', 'wqcpt' ), $singular_post_name),
  );

  $args = array(
    'labels' => $labels,
    'hierarchical' => true,
    'description' => __( 'Property Description', 'wqcpt' ),
    'supports' => array( 'title', 'editor' ),
    'public' => true,
    'show_ui' => true,
    'show_in_menu' => true,
    'show_in_nav_menus' => true,
    'publicly_queryable' => true,
    'exclude_from_search' => false,
    'has_archive' => true,
    'query_var' => true,
    'can_export' => true,
    'rewrite' => true
  );
  register_post_type( $post_type, $args );
}

我们从定义用于帖子类型和标签的必要变量开始函数。代码的下一部分包含标签数组。我们需要定义每个标签,因为 WordPress 将使用帖子作为所有条目的默认标签。当我们实际上正在处理属性时,在消息中显示帖子并不理想。下一段代码包含创建帖子类型的参数列表。让我们讨论最重要的设置及其作用:

  • 分层:定义新帖子类型将作为页面还是帖子使用。WordPress 页面提供了分层功能,我们可以定义父页面,而帖子没有分层。

  • 支持:此设置定义了新帖子类型允许的字段和功能。我们已经为属性启用了帖子标题和内容。我们还可以使用作者、缩略图、摘要、引用、自定义字段、评论、修订、页面属性和帖子格式等值来为不同功能使用。

  • 公开:定义新帖子类型对内容创建者和访客的可见性。我们可以使用truefalse作为值。根据此设置的值,我们将看到show_in_menushow_uiexclude_from_search等其他设置中的变化。

  • 重写:定义帖子类型的重写规则状态或设置。默认情况下,它使用$post_type作为 slug。我们可以通过使用自定义 slug 来修改它。

通过使用codex.wordpress.org/Function_Reference/register_post_type上的文档,可以识别有关可用设置及其在不同组合中的作用详情。

最后,我们通过传递帖子类型 slug 和参数列表来调用register_post_type函数创建帖子类型。现在,属性帖子类型已在 WordPress 中注册,你将在主 WordPress 菜单中看到一个名为属性的新部分。你可以创建一个属性并在前端使用属性链接查看它。然而,你将得到一个 WordPress 404 页面而不是属性详情页面。原因是 WordPress 不知道新注册的自定义帖子类型的新 URL 结构,直到我们刷新重写规则。正如我们在第六章中讨论的,WordPress API 的实际应用,我们需要手动通过访问 WordPress 设置 | 永久链接部分来更新重写规则,或者通过插件激活时刷新重写规则。你可以使用我们在上一章中讨论的技术来实现此插件的激活处理程序。一旦刷新了重写规则,你将能够在前端看到具有默认帖子布局的属性详情页面。

创建属性类型分类法

WordPress 分类法是一种对文章或自定义文章类型进行分组的方法。我们在前几章讨论数据库表和模板时已经了解了分类法。在本节中,我们将探讨分类法如何与自定义文章类型相结合以及它们的实现。在这种情况下,我们正在构建一个房产列表网站。我们可以使用分类法根据房产类型对房产进行分组。让我们首先向WQCPT_Model_Property类的构造函数中添加以下操作:

$this->property_category_taxonomy  = 'wqcpt_property_listing_type';
add_action( 'init', array( $this, 'create_property_custom_taxonomies' ) );

与自定义文章类型一样,我们必须使用init操作来为 WordPress 注册分类法。让我们考虑create_property_custom_taxonomies函数的实现:

public function create_property_custom_taxonomies() {
  $category_taxonomy = $this->property_category_taxonomy;
  $singular_name = __('Property Listing Type','wqcpt');
  $plural_name = __('Property Listing Types','wqcpt');

  register_taxonomy(
    $category_taxonomy,
    $this->post_type,
    array(
      'labels' => array(
      'name' => sprintf( __( '%s ', 'wqcpt' ) , $singular_name),
      'singular_name' => sprintf( __( '%s ', 'wqcpt' ) , $singular_name),
      'search_items' => sprintf( __( 'Search %s ', 'wqcpt' ) , $singular_name),
    ),
    'hierarchical' => true,
  )
 );
}

我们首先在函数中定义分类法和必要的标签,类似于自定义文章类型。然后,我们可以使用必要的参数调用register_taxonomy函数来创建房产类型的分类法。让我们详细看看这些参数:

  • taxonomy:这是第一个参数,其中我们必须传递分类法的名称。我们只能使用小写字母和字符。我们将其指定为wqcpt_property_type,并使用前缀使其与其他插件区分开来。

  • object type:这是第二个参数,其中我们必须指定将使用此分类法的文章类型。在这种情况下,我们指定了wqcpt_property作为文章类型。在有多种文章类型的情况下,我们可以使用数组。然而,在多个文章类型中使用分类法会使存档页面成为不同文章类型条目的混合。由于设计和数据的不同,处理具有多个文章类型的存档页面可能很困难。因此,理想情况下,为每个文章类型使用单独的分类法,除非所有文章类型的存档页面都包含类似的设计和数据。

  • arguments:这是第三个参数,其中我们可以从可用选项列表中传递不同的设置。在这种情况下,我们使用了房产列表类型的标签分层选项。分层设置定义了分类法应该作为标签还是类别。默认值是false,使其作为标签。我们将其设置为true,使其作为类别。有许多其他设置类似于自定义文章类型注册过程。您可以在codex.wordpress.org/Function_Reference/register_taxonomy查看有关所有可用参数的更多详细信息。

对于分类法中类别与标签的选择取决于你的需求。通常,当我们想要有不同级别的子类别以及有一个固定的主要选项集时,我们会使用类别。标签不提供子级别,通常使用动态值集来解释数据。在这种情况下,房产列表类型有预定义的选项,如销售、租赁和抵押。因此,我们选择类别而不是标签来表示房产列表类型。

一旦使用此代码,你将看到在房产菜单中添加了一个新的分类法。你可以创建房产,并根据需要将房产列表类型分配给分类,以对房产进行分类。在实际需求中,你必须根据功能将每个自定义帖子类型的分类法需求与分类或标签相匹配。

管理自定义帖子类型的自定义字段

在常规帖子中,我们很少使用自定义字段,因为主要功能是在 WordPress 编辑器中显示内容,包括特色图片、标签和分类。然而,自定义帖子类型实现需要比 WordPress 编辑器中的内容更多的数据。因此,我们需要一种方法来处理每个自定义帖子类型的自定义数据。我们可以使用默认的帖子自定义字段功能,将数据存储在wp_postmeta表中。让我们考虑一个房产列表网站的自定义字段需求:

房产列表网站需要大量的自定义数据,包括通过 WordPress 编辑器提供的房产标题和主要房产内容。我们可以将一些房产数据,如房产列表类型,匹配为分类。然而,大部分数据需要通过自定义字段来处理。这些数据包括城市、房产类型、面积、价格、年份、房产规划等字段。

在本节中,我们将添加一些自定义房产字段,以了解在自定义帖子类型中添加和使用自定义字段的过程。让我们首先添加一个元框来定义自定义字段,类似于我们在帖子附件插件中使用的技术。我们需要更新WQCPT_Model_Property构造函数,添加以下操作:

add_action( 'add_meta_boxes', array( $this, 'add_property_meta_boxes' ) );

现在,我们必须定义元框,并在WQCPT_Model_Property类内部使用以下代码实现显示元框内容的函数:

public function add_property_meta_boxes() {
  add_meta_box( 'wqcpt-property-meta', __('Property Details','wqcpt' ), array( $this, 'display_property_meta_boxes' ), $this->post_type );
}

public function display_property_meta_boxes( $property ) {
  global $wqcpt,$template_data;

  $template_data['property_post_type'] = $this->post_type;
  $template_data['property_nonce'] = wp_create_nonce('wqcpt-property-meta');
  $template_data['wqcpt_pr_type'] = get_post_meta( $property->ID, '_wqcpt_pr_type', true );
  $template_data['wqcpt_pr_city'] = get_post_meta( $property->ID, '_wqcpt_pr_city', true );

  ob_start();
  $wqcpt->template_loader->get_template_part( 'property','meta');
  $display = ob_get_clean();
  echo $display;
}

我们已经讨论了在帖子附件插件中使用add_meta_box函数以及如何使用回调函数生成内容。在帖子附件插件中,我们使用了 PHP 变量内的 HTML 来生成输出。这在大型项目中并不理想,因为很难在引号内管理输出,以及为模板提供可扩展的能力。在 MVC 模式中,我们使用一个单独的层来表示模板,称为视图

模型-视图-控制器(Model–view–controller),也称为MVC,是一种常用的架构模式,用于开发将应用程序分为三个相互关联部分的用户界面。MVC 设计模式解耦了这些主要组件,允许高效地重用代码和并行开发。

– 来源:en.wikipedia.org/wiki/Model-view-controller

同样,我们尽可能地将模板代码分离,以允许未来的增强。

因此,我们定义了一个名为 $template_data 的全局变量来保存此函数中使用的模板所需的数据。然后,我们将帖子类型、nonce 值和属性数据分配给 $template_data 数组。我们使用 wp_postmeta 表来存储属性的定制字段详情。因此,我们可以使用 get_user_meta 函数来获取要传递给模板的现有属性值。最初,这些函数将返回空字符串,直到我们第一次保存数据。然后,我们必须使用模板加载器来加载属性自定义字段的模板。

在本节中,我们使用了我们自己的模板加载器。您可以在我们插件的源代码目录中的 classes/class-wqcpt-template-loader.php 文件中找到其实现。这个类包含在我们插件的主体文件中,并在 instance 函数内部创建对象。这个类的基本功能是从我们插件的模板目录中包含 PHP 文件。那些熟悉在纯 PHP 项目中使用模板引擎如 TwigSmartyMustache 的开发者可能会认为这是一种类似的技术。然而,这仅仅是基本的 PHP 文件包含和模板代码,因此不提供模板引擎提供的任何优势。

我们可以在 WordPress 插件中使用 PHP 模板引擎,但需要一些额外的开发工作。然而,大多数插件包括流行的插件都没有使用模板引擎,并且仅限于将模板作为 PHP 文件加载。

在这个函数中,我们调用了 ob_start 函数来开始模板加载过程。这个函数用于开启输出缓冲,允许我们只向浏览器发送必要的内容。接下来,我们通过使用我们插件的全局 $wqcpt 实例来访问模板加载器类的对象,并调用 get_template_part 函数。我们向这个函数传递了两个字符串 propertymeta。因此,我们需要在我们的插件模板目录中有一个名为 property-meta.php 的模板文件。然后,我们使用 ob_get_clean 函数来获取模板并清理输出缓冲。最后,我们使用 echo 语句将内容发送到浏览器。在过程中的下一步是构建模板并使用为模板指定的数据。

构建属性字段模板

我们需要通过在插件内部创建一个名为 templates 的新目录和一个名为 property-meta.php 的新文件来开始这个过程。这个文件应该生成自定义字段以捕获属性数据,同时将 PHP 代码和逻辑的数量保持在最低。让我们看看属性自定义字段模板的实现:

<?php
  global $template_data;
  extract($template_data);
?>

<input type="hidden" name="property_nonce" value="<?php echo $property_nonce; ?>" />
<table class="form-table">
  <tr>
    <th style=''><label><?php _e('Status','wqcpt'); ?>*</label></th>
    <td><select class='widefat' name="wqcpt_pr_type" id="wqcpt_pr_type">
      <option <?php selected( $wqcpt_pr_type, '0' ); ?> value='0' ><?php _e('Please Select','wqcpt'); ?></option>
      <option <?php selected( $wqcpt_pr_type, 'house' ); ?> value='house' ><?php _e('House','wqcpt'); ?></option>
      <option <?php selected( $wqcpt_pr_type, 'office' ); ?> value='office' ><?php _e('Office','wqcpt'); ?></option>
         </select></td>
  </tr>
  <tr>
    <th style=''><label><?php _e('City','wqcpt'); ?></label></th>
    <td><input class='widefat' name='wqcpt_pr_city' id='wqcpt_pr_city' type='text' value='<?php echo $wqcpt_pr_city; ?>' /></td>
  </tr> 
</table>

让我们通过以下步骤来理解属性字段模板的实现:

  1. 我们通过使用全局 $template_data 数组并提取作为模板数据传递的值来开始模板。

  2. 然后,我们必须添加 HTML 字段以保留 nonce 值和属性数据。在前面的代码中,我们为了说明目的将字段限制为城市和属性类型。

  3. 接下来,我们必须使用从WQCPT_Model_Property传递的数据来加载这些字段的现有值。

现在,我们的模板已经准备好了。你可以创建或编辑一个属性来查看属性自定义字段,如下面的截图所示:

图片

我们可以使用这项技术将所有属性字段分组到一个单个的元框中,或者使用多个元框来保存相关字段。此外,我们还可以更改元框在属性屏幕中的位置和优先级。

保存属性自定义字段

我们将属性设置为一个自定义文章类型,因此主要属性细节将在属性发布或更新事件上自动保存。然而,自定义字段数据不会在这个过程中保存,因此我们需要一个自定义实现来存储这些数据。我们选择将自定义字段数据存储在wp_postmeta表中。让我们看看使用 WordPress 的save_post操作保存自定义字段的过程。首先,我们需要将以下代码行添加到WQCPT_Model_Property类的构造函数中:

add_action( 'save_post', array( $this, 'save_property_meta_data' ) );

此操作会在我们创建或更新文章或自定义文章类型时触发。现在,我们必须实现以下实现的回调函数:

public function save_property_meta_data( $post_id ) {
  global $post,$wqcpt;
  if (   isset($_POST['property_nonce']) &&  ! wp_verify_nonce($_POST['property_nonce'], 'wqcpt-property-meta')){
    return $post_id;
  }
  if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) {
    return $post_id;
  }

  if (  isset($_POST['post_type']) &&  $this->post_type == $_POST['post_type'] && current_user_can( 'edit_post', $post->ID ) ) {

    $wqcpt_pr_type = isset( $_POST['wqcpt_pr_type'] ) ? sanitize_text_field( trim($_POST['wqcpt_pr_type']) ) : '';
    $wqcpt_pr_city = isset( $_POST['wqcpt_pr_city'] ) ? sanitize_text_field( trim($_POST['wqcpt_pr_city']) ) : ''; 
    update_post_meta( $post_id, '_wqcpt_pr_type', $wqcpt_pr_type );
    update_post_meta( $post_id, '_wqcpt_pr_city', $wqcpt_pr_city );
  } else {
    return $post_id;
  }
}

我们在函数开始时验证分配给模板的 nonce 值。如果验证失败,我们可以直接返回帖子 ID。接下来,我们必须进行另一个验证,以检查自动保存。WordPress 的自动保存功能会定期自动保存帖子内容。我们不需要在每次自动保存时更新自定义字段值。因此,验证只允许在用户手动保存帖子时处理函数。下一个验证是最重要的,因为这个操作在 WordPress 的所有文章类型上都会执行。

我们必须确保只在必要的文章类型中使用此操作,以避免不必要的冲突。因此,我们检查属性是否正在保存,以及当前用户是否有权限通过使用current_user_can函数保存属性。我们没有在自定义文章类型注册中分配属性特定的能力。因此,我们必须检查默认的文章编辑权限。一旦所有验证都完成,我们就可以从 POST 请求中检索和清理值。最后,我们使用update_post_meta函数将每个属性字段保存到wp_postmeta表中。

我们已经完成了从后端管理属性的功能。现在,你应该能够创建带有自定义字段数据的属性,并在编辑属性屏幕中显示这些数据。一旦创建,你可以通过使用属性链接在前端查看属性。

主题将使用默认的single.php模板来显示属性。由于我们需要显示额外的属性相关字段,我们可以在主题内部创建一个模板或使用我们的插件来加载一个用于处理单个属性页面的模板。

在这种情况下,我们使用了两种基本字段类型的属性字段。在实际应用中,您将不得不使用更复杂的字段,如日期、地图、可搜索的下拉菜单、颜色选择器等。因此,手动构建自定义字段可能成为大多数开发时间较短的初创网站的问题。在这种情况下,我们可以使用充当管理自定义字段框架的自定义帖子类型插件,而无需手动编码。让我们看看一些提供广泛自定义字段类型的自定义帖子类型插件:

  • Toolset Types:此插件允许您管理自定义帖子类型、自定义分类和自定义字段,而无需任何编码。此插件提供了超过 20 种内置自定义字段类型。内置界面允许您通过提供必要的设置来管理自定义字段。有关功能的更多详细信息,请查看插件页面wordpress.org/plugins/types/

  • Pods:此插件也提供了相同的功能集,同时还具有连接多个自定义帖子类型的能力。此插件提供了大约 10 种自定义字段类型,用于管理每个自定义帖子类型的字段,使用现有的屏幕。有关功能的更多详细信息,请查看插件页面wordpress.org/plugins/pods/

  • 元框:与其他两个插件相比,此插件主要专注于通过允许您在帖子创建屏幕中添加它们到元框来管理自定义字段。此插件提供了超过 40 种内置字段类型,覆盖了广泛的应用领域。有关功能的更多详细信息,请查看插件页面wordpress.org/plugins/types/

这些插件对需要快速开发过程且对功能的灵活性和可扩展性优先级较低的网站很有用。

存储数据作为元值的限制

我们在大多数情况下使用帖子元数据表来存储与不同自定义帖子类型相关的自定义数据。然而,随着元数据表变大,管理所有元值将变得困难。网站速度会变慢,从而产生重大的性能问题。因此,在规划网站初期阶段时,我们必须考虑这一限制。一旦使用了帖子元数据表,后期迁移到自定义解决方案将变得困难,尤其是在我们决定使用自定义帖子类型插件来管理自定义字段时。

因此,使用帖子元数据表来存储自定义字段数据对于小型到中型规模的网站来说很理想,在这些网站上,帖子元数据表不会因数据过多而超载。在高级应用中,我们应该考虑使用自定义表来存储自定义字段,同时使用帖子表来存储自定义帖子类型项的主要细节。

形式管理简介

大多数 WordPress 网站由少数关于网站、服务或产品的页面组成,同时拥有一个博客。在其余部分中,高比例的网站专注于捕获、处理和显示用户数据。因此,表单管理成为 WordPress 开发中的一个重要方面。表单通常用作前端的数据捕获方法,因为我们使用自定义帖子类型在后台捕获数据。我们可以在前端开发自己的自定义表单,或者我们可以通过使用现有的表单管理插件来自动化表单管理的过程。自定义表单和表单插件之间的选择取决于网站的需求。让我们来看看使用这两种方法的优缺点。

  • 开发时间:表单插件提供了一个快速的开发过程,通过拖放创建表单,并通过配置处理所有表单字段功能。另一方面,自定义表单需要相当的开发努力,并且对现有字段的更改需要修改代码。

  • 数据库使用:每个现有的表单插件都限制使用wp_postmeta表或单个自定义表,而自定义表单可以根据我们的偏好开发以使用这两种技术,同时将不同的表单数据存储在不同的表中。

  • 可扩展性:构建自定义表单使得保持功能开放以供未来增强成为可能。另一方面,在考虑高级网站的增强时,我们将不得不与表单插件的限制作斗争。

  • 设计一致性:我们可以创建自己的自定义表单设计以匹配主题的样式。然而,现有的插件可能无法提供 100%的灵活性来修改设计以匹配主题样式。

由于这些原因,我们应该根据每个网站的需求仔细选择自定义表单的开发方法。一旦我们完成了即将到来的关于表单管理的部分,你将对做出正确决策的过程有更好的了解。

在前端构建自定义表单

我们有几种方法可以将表单添加到 WordPress 网站的前端。在本节中,我们将讨论流行插件用于添加表单的两种方法:

  • 使用短代码添加表单

  • 使用自定义 URL 加载表单

我们将实施这两种技术,首先从短代码方法开始。

使用短代码添加表单

这是最受欢迎的方法之一,许多流行的插件用它来向网站添加数据捕获表单。在这个方法中,我们使用短代码来生成表单的 HTML。然后,我们将短代码添加到 WordPress 文章或页面中,并让用户通过文章或页面访问它。这对于基本到中级水平的功能来说是一个理想的技巧,因为表单的使用并不在网站功能中扮演关键角色。

使用短代码是添加表单最简单的方法之一。然而,短代码是添加到文章或页面中的,因此管理员可能会不小心删除页面或错误地破坏短代码。因此,它不应用于短代码功能至关重要的高级网站。例如,考虑一个基本社交网络网站上的注册表单。由于人们免费注册以分享他们的活动,注册页面短暂不可用并不是一个大问题。然而,考虑一个提供时间关键服务的网站的支持论坛上的注册表单。在这样的网站上,即使注册页面仅短暂不可用也是不可接受的,并会影响用户。因此,我们不应该在这样的网站上使用基于短代码的表单,或者防止对带有短代码的页面进行修改访问。

在上一节中,我们使用了自定义文章类型来管理后端中的属性详情。现在,我们需要将属性创建功能添加到前端。让我们在WQCPT_Model_Property类的构造函数中使用以下行代码来定义属性表单的短代码:

add_shortcode( 'wqcpt_property_form', array( $this, 'display_property_form' ) );

接下来,我们需要实现回调函数以在前端显示属性创建表单。让我们考虑display_property_form函数的实现:

public function display_property_form( $atts, $content ){
  global $wqcpt,$template_data;
  $template_data['property_nonce'] = wp_create_nonce('wqcpt-property-meta');

  ob_start();
  $wqcpt->template_loader->get_template_part( 'property','form');
  $display = ob_get_clean();
  return $display;
}

这个函数的实现与我们在后端自定义文章类型中使用的display_property_meta_boxes函数类似。在这种情况下,这个表单仅用于属性创建,因此我们不需要将现有数据作为模板变量传递。我们使用一个不同的模板,称为property-form.php,用于前端自定义表单。现在,我们可以看看property-form.php模板的实现:

<?php
  global $template_data;
  extract($template_data); ?>
<form action="" method="POST" >
  <input type="hidden" name="property_nonce" value="<?php echo $property_nonce; ?>" />
  <table class="form-table">
    <tr>
      <th><label><?php _e('Property Title','wqcpt'); ?>*</label></th>
      <td><input type="text" name="wqcpt_prfr_title" id="wqcpt_prfr_title" value="" /></td>
    </tr>
    <tr>
      <th><label><?php _e('Property Content','wqcpt'); ?>*</label></th>
      <td><textarea name="wqcpt_prfr_content" id="wqcpt_prfr_content" ></textarea></td>
    </tr>
    <!—HTML for other custom fields -->
    <tr>
      <th>&</th>
      <td><input name="wqcpt_prfr_submit" id="wqcpt_prfr_submit" type="submit" value="<?php _e('Add Property','wqcpt'); ?>" /></td>
    </tr>
  </table>
</form>

模板的结构与后端的前一个场景完全相同。然而,我们增加了两个额外的字段,用于属性标题和内容,以及一个表单和一个提交按钮。在自定义文章类型中,我们有标题和内容作为内置字段。我们还使用了内置的文章表单和发布或更新按钮。在前端,我们没有任何内置功能,因此我们必须手动添加它们。一旦短代码被添加到页面中,属性创建表单将类似于以下屏幕截图:

图片

现在,我们来到了最后的部分,即表单提交时创建属性。

使用自定义表单创建属性

在后端自定义文章类型中,我们只编写了自定义字段保存部分,因此我们可以使用save_post动作来处理这个过程。在前端,我们必须从头开始构建这个过程,因此init动作是实现的最佳钩子。让我们向我们的WQCPT_Model_Property类的构造函数中添加以下代码行:

add_action( 'init', array( $this, 'save_property_form' ) );

接下来,我们可以看看save_property_form函数的实现,如下面的代码所示:

public function save_property_form() {
  global $post,$wqcpt;
  if( ! isset( $_POST['wqcpt_prfr_submit'] ) ){
    return;
  }

  if ( !wp_verify_nonce($_POST['property_nonce'], 'wqcpt-property-meta' ) || ! current_user_can( 'edit_post' ) ) {
    // Handle error
  }

   $wqcpt_prfr_title = isset( $_POST['wqcpt_prfr_title'] ) ? sanitize_text_field( trim($_POST['wqcpt_prfr_title']) ) : '';
   $wqcpt_prfr_content = isset( $_POST['wqcpt_prfr_content'] ) ? wp_kses_post( trim($_POST['wqcpt_prfr_content']) ) : '';
   $wqcpt_pr_type = isset( $_POST['wqcpt_prfr_type'] ) ? sanitize_text_field( trim($_POST['wqcpt_prfr_type']) ) : '';
   $wqcpt_pr_city = isset( $_POST['wqcpt_prfr_city'] ) ? sanitize_text_field( trim($_POST['wqcpt_prfr_city']) ) : '';

 // Validations and generate errors
 // post fields and existence of a post
 // Rest of the code for saving data
}

我们从使用if条件检查前端文章创建请求中提交按钮的可用性开始函数,以确保我们的代码只在前端属性创建表单上执行。然后,我们使用另一个条件检查 nonce 值和属性创建权限。在自定义文章类型部分,我们通过传递文章 ID 执行权限检查,因为我们只为已创建的属性保存自定义字段。在这种情况下,我们是从头开始创建属性,因此我们还没有文章 ID。所以,我们只能检查一般的文章编辑权限。

一旦生成权限错误,我们可以将错误消息分配给一个类变量,并在短代码函数中使用它来显示它们。

一旦完成验证,我们可以从POST请求中检索属性数据,并在将其分配给必要的变量之前对其进行清理。在这里,我们对属性内容使用了名为wp_kses_post的函数。这个函数默认将内容清理为仅保留 WordPress 文章内容中允许的标签。接下来,我们必须验证从POST请求中检索的数据,并生成要显示在短代码中的错误。现在,我们可以看看这个函数的其余代码:

$post_id = wp_insert_post(
             array(
               'post_author' => get_current_user_id() ,
               'post_name' => sanitize_title( $wqcpt_prfr_titley ),
               'post_title' => $wqcpt_prfr_title,
               'post_status' => 'publish',
               'post_content' => $wqcpt_prfr_content,
               'post_type' => $this->post_type
             )
         );

if ( !is_wp_error( $post_id ) ) {
  update_post_meta( $post_id, '_wqcpt_pr_type', $wqcpt_pr_type );
  update_post_meta( $post_id, '_wqcpt_pr_city', $wqcpt_pr_city );
} else {
 // Handle errors
}

在验证所有属性数据后,我们使用wp_insert_post函数在wp_posts表上创建属性。我们可以将所有必要的设置作为一个数组传递给这个函数。在这里,我们使用了当前登录用户作为作者。然后,我们在使用sanitize_title函数生成文章的 URL 的同时,将属性标题和内容分配给相应的设置。最后,我们设置正确的文章类型和状态为发布。这是一个内置函数,在执行 WordPress 核心内部的所有必要过滤器和动作的同时创建文章。如果我们使用自己的查询来保存属性,我们必须检查并执行所有必要的过滤器和动作,以确保我们的功能与其他插件兼容。这个函数将根据执行状态返回true或 WordPress 错误。接下来,我们可以检查错误并使用update_post_meta函数将属性自定义字段保存到wp_postmeta表。

我们可以轻松地使用前端表单在自定义数据库表中存储自定义字段数据,以获得额外的灵活性。在这种情况下,我们只需将wp_insert_post函数替换为自定义查询和必要的数据。

现在,我们已经完成了使用前端表单添加数据的过程,你应该能够通过将短代码分配到帖子或页面并提交必要的数据来测试这个过程。

使用自定义 URL 加载表单

我们已经在第六章,“WordPress API 的实际应用”,“重写 API”部分讨论了这种技术背后的概念。短代码方法对于高级网站来说并不可靠。因此,我们创建了自己的 URL 结构,并使用这些自定义 URL 来处理表单,而不是使用帖子、页面或自定义帖子类型。由于我们已经讨论了这种技术,我们将不带主要解释地通过代码。让我们从添加一个新的重写规则和一个查询参数来处理前端属性创建开始:

add_action( 'init', 'wqcpt _manage_property_routes' );
function wqcpt_manage_property_routes() {
  add_rewrite_rule( '^property-listing/([^/]+)/?', 'index.php?wpquick_property_actions=$matches[1]', 'top' );
  add_rewrite_tag('%wpquick_property_actions%', '([^&]+)');
}

此代码添加了一个新的重写规则,可以通过www.example.com/property-listing/add访问,用于从前端自定义表单创建属性。我们还使用了一个名为wpquick_property_actions的标签来识别属性的功能。理想情况下,我们需要将此代码包含在我们的插件的主要类中,以保持我们插件的对象化特性。然而,我们讨论了在激活时刷新重写规则的需要。因此,由于我们的插件是在plugins_loaded动作上初始化的,而激活处理程序是在该动作之前执行的,所以存在冲突。因此,我们必须在插件的主要类之外使用前面的代码,以及激活处理程序。以下是这个插件的激活处理程序代码:

register_activation_hook( __FILE__, 'wqcpt_activate' );
function wqcpt_activate(){
  wqcpt_manage_property_routes();
  flush_rewrite_rules(); 
}

正如我们在第六章,“WordPress API 的实际应用”中做的那样,我们在激活处理程序上注册重写规则,在调用flush_rewrite_rules函数之前。接下来,我们需要过滤 URL 并加载用于创建属性的定制表单。我们使用内置的template_redirect动作来实现这个功能,并且我们可以在WQCPT_Model_Property类中包含这个动作,如下面的代码所示:

public function property_controller() {
  global $wp_query,$wqcpt,$template_data;
  $wpquick_actions = isset ( $wp_query->query_vars['wpquick_property_actions'] ) ? $wp_query->query_vars['wpquick_property_actions'] : '';

  switch ( $wpquick_actions ) {
    case 'add':
      $template_data['property_nonce'] = wp_create_nonce('wqcpt-property-meta');

      ob_start();
      $wqcpt->template_loader->get_template_part( 'property','form' );
      $display = ob_get_clean();
      echo get_header();
      echo $display;
      echo get_footer();
      exit;
      break;
  }
}

首先,我们使用 WordPress 查询变量接收wpquick_property_actions的值。然后,我们使用switch语句来过滤不同属性的动作。在这个场景中,我们只使用了add作为过滤器。在完整的实现中,我们至少需要包括editdeletelist动作。在add情况下,我们加载与短代码处理相同的模板。然后,我们在网站的头和尾之间包含它以显示前端表单。现在,你应该能够通过在浏览器 URL 中访问www.example.com/property-listing/add来访问和创建属性。

在自定义文章类型和自定义表单之间进行选择

我们在上一节中探讨了自定义文章类型和自定义表单的特点及流程。我们本章一开始就讨论了那些改变了我们使用 WordPress 进行开发的自定义文章类型特点。现在,我们可以将它们与自定义表单进行比较,并识别出它们的优缺点。让我们先看看自定义文章类型的优点。

你可能已经注意到了,为了显示自定义表单并使用它来捕获数据,我们需要编写大量的代码。而我们使用自定义文章类型做同样的事情,所花费的时间甚至不到一半。让我们总结一下,与自定义文章类型相比,在自定义表单开发中我们需要额外做的事情:

  • 创建标题和内容字段:在自定义文章类型中,我们内置了标题字段和内容字段,并带有富文本编辑器,而在自定义表单中,我们必须创建两个新字段来处理它们。

  • 添加表单和提交按钮:在自定义文章类型中,我们不需要添加表单或提交按钮,因为我们正在使用 WordPress 内置的文章功能。

  • 保存属性:在自定义文章类型中,我们不需要创建属性并保存主要细节,因为 WordPress 核心进程会自动完成这些操作。

  • 处理属性验证:在自定义文章类型中,所有必要的属性数据验证都由核心功能处理。然而,我们需要检查属性的存在、URL,并确保手动清理属性内容。

  • 文章编辑和列表:使用后端自定义文章类型,我们可以在创建文章后立即编辑、列出或删除文章。然而,我们只使用自定义表单构建了创建表单,因此我们必须从头开始使用更多自定义表单来开发编辑、列表和删除功能。

因此,从这个角度来看,内置的自定义文章类型在自定义表单之上提供了更多的优势。以下是使用自定义表单而非自定义文章类型的优势:

  • 前端界面:自定义文章类型不提供前端的数据捕获功能,因此用户需要被重定向到后端。因此,我们无法将表单与主题的样式相匹配,而自定义表单允许我们创建任何类型的设计。这也意味着我们将在后端管理其他菜单项的权限。

  • 前端和后端的混合:使用自定义表单时,我们可以让用户在前端使用其他网站功能以及数据提交,使用户能够拥有一致的屏幕集。然而,使用自定义文章类型意味着用户将需要在网站的前端和后端之间切换,从而产生不必要的复杂性。

  • 验证的灵活性:WordPress 自动使用自动保存功能保存自定义帖子类型,因此我们无法等待自定义帖子创建完成,直到自定义验证完成。因此,我们必须使用一些变通方法来验证自定义字段并显示错误消息,同时保持帖子类型记录不可访问。在自定义表单中,我们可以在实际上传wp_posts表中的记录之前对数据进行完全自由的验证。

  • 使用数据库表时的灵活性:在后端自定义帖子类型中,新创建的帖子总是保存到wp_posts表中。然而,在自定义表单中,我们可以决定是使用现有表还是使用我们自己的自定义表来管理数据。

通过考虑每种技术的优缺点,我们可以得出结论,后端自定义帖子类型主要适用于预算较低且需要快速开发流程的网站。此外,当基本功能比灵活性更重要时,我们也可以考虑使用自定义帖子类型。另一方面,自定义表单可以用于需要高性能和未来增强灵活性的高级网站。

使用表单管理插件

我们探讨了使用自定义帖子类型和自定义表单来捕获和显示 WordPress 网站所需的数据。然而,许多初创网站没有预算或时间来开发针对其网站的特定解决方案。因此,开发者必须使用现有解决方案,并在其基础上构建功能。在这种情况下,WordPress 表单插件成为理想的解决方案。有一些流行的表单插件是为了满足特定领域而开发的,例如联系表单。我们还可以找到如 Gravity Forms、Ninja Forms 等表单插件,它们是为了满足任何目的而开发的。让我们了解一个高质量的表单插件所期望的功能:

  • 拖放构建器:这是通过现有插件加速自定义表单开发的功能。我们不需要为每个字段编写自己的 HTML,而应该能够拖放现有的自定义字段,并在几分钟内构建完全工作的自定义表单。

  • 基于 Ajax 的表单提交:这是现代网站中的一个非常重要的功能,因为开发者不会为许多功能使用常规的帖子提交。因此,表单插件应该能够使用常规表单提交以及基于 Ajax 的提交。

  • 不同的字段类型:在这个插件中,我们只使用了基本的文本和下拉字段。然而,在高级网站上,我们需要许多字段类型,如日期、谷歌地图和可搜索的下拉菜单。因此,能够通过表单插件内置字段处理此类高级要求无疑是一个巨大的优势。

  • 表单字段验证:通常,我们必须通过应用必要的验证规则来验证每个字段。在一个高质量的表单插件中,我们应该能够从现有规则集中选择并验证字段,而无需编写任何代码。

  • 条件逻辑:在自定义表单中,我们为特定需求开发每个表单,因此我们可以在加载某些字段之前手动应用条件。在表单插件中,我们动态地将字段添加到表单中,因此我们应该有能力在使每个字段在网站上可见之前定义条件。

  • 显示和导出数据:我们应该有能力在前端显示提交的数据,并在必要时将其导出为常见文件类型。

  • 将表单数据保存到自定义数据库表:通常,许多表单插件将数据保存到wp_postmeta表或使用相同的自定义表将所有表单的数据保存到单个自定义表中。我们至少应该有能力在现有表或自定义表之间进行选择。

这些是我们期望从高质量表单插件中获得的一些功能。让我们看一下以下屏幕截图,这是最受欢迎的表单插件之一的形式创建界面:

图片

上一张截图预览了我们在上一节中讨论的一些功能。然而,要找到一个以我们期望的方式提供所有这些功能的表单插件将很困难。此外,几乎所有表单插件都不提供前端界面,让用户编辑提交的数据或列出它们。因此,表单插件最适合我们想要使用一次性表单来捕获用户数据的场景。流行的例子包括注册表单、联系表单、支付表单和调查。我们应该始终使用自定义表单或自定义文章类型来管理需要用户提交、更新和列出所有提交数据的表单。

摘要

管理数据的过程是网站或应用最重要的方面之一。WordPress 自定义文章类型允许我们以标准方式建模不同的内容类型,并使用内置功能进行快速开发。我们通过理解自定义文章类型的重要性及其在插件开发中成为流行选择的功能开始本章。然后,我们研究了创建自定义文章类型的过程,同时理解了与自定义文章类型一起使用分类和自定义字段。接下来,我们转向开发网站前端自定义表单的过程和技术。最后,我们探讨了使用表单插件的需要,并比较了自定义文章类型和自定义表单的优缺点。

在第八章《发现开发中的关键模块》中,我们将探讨现代网站中关键模块的开发,例如 UI 组件集成、提高可用性和自定义核心管理功能。

第八章:发现开发中的关键模块

现代网站界面大多基于块的概念,网站所有者可以拖放可重复使用的组件来构建界面。UI 组件和页面构建器在开发这些界面中扮演着重要角色。因此,对于开发者来说,了解如何集成任何第三方 UI 组件以构建具有交互性的惊人界面是非常重要的。另一方面,WordPress 因其现有的 CMS 功能而被许多开发者选择,这些功能可以加速开发过程。因此,开发者需要深入了解使用后端功能以及根据定制需求定制现有功能。

在本章中,我们将重点关注 UI 组件集成以及常见后端功能的定制。我们首先通过使用菜单页面来捕获数据,将 jQuery 滑块集成到 WordPress 中。然后,我们将使用自定义帖子类型来捕获数据,集成另一个流行的组件,称为手风琴。在此之后,我们将查看使用图像滑块和手风琴的两种集成方法来整合任何 UI 组件的基本步骤。此外,我们还将探讨现代页面构建器在网站开发中的重要性,并讨论使用任何页面构建器创建自定义组件的基础知识。最后,我们将查看通过创建基本仪表板小部件和修改列表表来提供自定义功能的后端功能定制过程。

在本章中,我们将涵盖以下主题:

  • 集成 UI 组件

  • 集成 jQuery 图像滑块

  • 集成 jQuery 手风琴

  • 确定集成 UI 组件的步骤

  • 理解页面构建器的使用

  • 创建自定义仪表板小部件

  • 定制后端列表表

到本章结束时,您将能够使用最新的趋势方法定制后端管理功能以及将 UI 组件集成到网站中。

技术要求

为了遵循此程序,您需要安装 WordPress 4.9.8。即使您有 WordPress 的更高版本,描述的示例也应无重大问题地工作。

如果您有 WordPress 的更高版本,描述的示例应无重大问题地工作。

本章的代码文件可以在 GitHub 上找到:

github.com/PacktPublishing/WordPress-Development-Quick-Start-Guide/tree/master/Chapter08

查看以下视频,以查看代码的实际效果:

bit.ly/2yGYX03

UI 组件

现代网站建立在交互式 UI 元素之上。许多高级 WordPress 主题包括由开源 JavaScript 和 CSS 库支持的广泛 UI 组件。这些 UI 组件通过简化界面以及在小空间内提供大量内容来吸引用户。在数百个 UI 元素中,我们发现图片滑块和相册、标签是最受欢迎的。作为开发者,您需要使用或构建这些组件以跟上现代开发中日益增长的 UI 需求。因此,了解使用插件或主题将任何 UI 组件集成到 WordPress 中的基本过程非常重要。在本节中,我们将探讨两个 UI 组件的集成。让我们开始吧。

集成 jQuery 图片滑块

图片滑块和相册是 WordPress 网站中最受欢迎的 UI 元素。您可以访问wordpress.org插件目录或像 Codecanyon 这样的高级市场,以了解此类插件的下载量和购买量。我们可以使用这些滑块来显示一组基本图片,以及显示高级内容和图片以推广您的产品、服务和功能。

在本节中,我们将使用 Basic jQuery 滑块(basicslider.com/)来解释集成图片滑块的过程。我们将创建一个单独的图片滑块,用于在网站的首页或其他任何首选位置显示产品图片。

图片滑块插件使您能够使用自定义帖子类型或自定义表单创建多个可重复使用的滑块。在这种情况下,我们只关注一个预定义的滑块。

在开始实施之前,我们必须为这一章创建另一个插件。我们将将其命名为WPQuick Key Modules,并将其放在名为wpquick-key-modules的目录中。我们将使用此插件来实现本章讨论的大多数功能。该插件将与之前的插件具有相同的结构,其中主类初始化所有其他功能和类。

构建产品图片滑块

我们可以使用滑块来显示任何类型的内容,其中最常见的类型是图片。集成任何 UI 组件的第一步是数据捕获的过程。在这种情况下,所需的数据将是一组图片。因此,我们需要构建一个界面,让管理员上传一组图片用于滑块。我们有两种为这种需求创建界面的选择:

  • 菜单页面:我们可以使用add_menu_page函数来为我们的插件创建一个新的菜单页面。这将是一个 WordPress 管理员菜单中的顶级菜单项,具有支持子菜单页面的能力。当你需要突出显示你的插件菜单以及为插件的各个功能创建不同的菜单页面时,这非常适合。关于add_menu_page函数的更多详细信息,请参阅developer.wordpress.org/reference/functions/add_menu_page/

  • 选项页面:我们可以使用add_options_page函数来创建一个新的选项页面用于自定义设置。这个函数将在设置菜单项下创建一个子级菜单项。然而,它不支持更高级别的菜单,因此对于需要多个菜单页面的插件来说,使用起来可能会有困难。这对于添加通用的站点特定设置而不是插件设置很有用。关于add_options_page函数的更多详细信息,请参阅developer.wordpress.org/reference/functions/add_options_page/

在这个场景中,我们将选择用于图片上传界面的菜单页面,因为这是一个插件特定的自定义要求。让我们创建一个用于上传图片的菜单页面。

创建插件菜单页面

WordPress 通过插件提供了内置函数来添加菜单页面。我们可以将界面添加为主菜单项或子菜单项。在我们的插件类目录中创建一个名为WQKM_UI_Components的类。然后,我们可以在插件主类的includesinstance函数中执行常规的文件包含和对象创建。接下来,我们可以在admin_menu动作中添加以下代码行来注册一个新的菜单页面:

add_menu_page( __('UI Component Settings', 'wqkm' ), __('UI Component Settings', 'wqkm' ),  'manage_options','wqkm-settings', array( &$this,'ui_settings' ) );

这个函数的前两个参数定义了菜单页面的标题和菜单项的标题。第三个参数定义了加载此页面的能力。在这里,我们使用了manage_options作为能力来限制它只对管理员开放。你可以根据负责使用此界面的人员来更改这个能力。第四个参数定义了一个唯一的 slug,可以通过 URL 访问菜单项。最后一个参数定义了用于生成此界面内容的回调函数。这些都是这个函数使用的主要参数。

现在我们需要实现回调函数来显示上传图片到滑块的必要字段。让我们看看ui_settings函数的结构:

public function ui_settings(){
  // Define global variables
  // Load template and return to browser
}

我们已经在之前的章节中讨论了用于模板加载的回调函数,因此我们将不会详细讨论代码。首先,我们定义全局变量以保存我们插件的主要实例并将数据传递到模板中。然后,我们使用我们在上一章中使用的技术,借助自定义模板加载器加载自定义模板。您可以参考源代码以获取完整的实现。接下来,我们必须构建用于上传文件的自定义模板,如下面的代码所示:

<?php
 global $wqkm_template_data;
 extract($wqkm_template_data);
?>
<form id="wqkm_slider_frm" action="" method="POST" >
 <div id="wqkm-slider-msg"></div>
 <table class="form-table">
   <tr>
     <th><label><?php _e('Product Slider Images','wqkm'); ?>*</label></th>
     <td> <div id="wqkm-slider-images-panel"></div>
       <input type="file" name="wqkm_slider_image" id="wqkm_slider_image" value="" />
     </td>
   </tr> 
   <tr>
     <th>&</th>
     <td><input name="wqkm_ui_submit" id="wqkm_ui_submit" type="button" value="<?php _e('Add Image','wqkm'); ?>" /></td>
   </tr>
 </table>
</form>

让我们了解此模板的结构,用于处理图像上传:

  1. 首先,我们定义一个全局变量以接受从WQKM_UI_Components类传递的模板数据。最初,此变量将为空,因为我们没有模板数据或设置。

  2. 然后,我们必须定义一个表单来启用数据提交。在表单内部,我们有一个名为wqkm-slider-msg的空容器,用于处理表单的错误和成功消息。

  3. 接下来,我们添加一个文件输入字段来选择和上传滑块的图像。我们还添加了一个名为wqkm-slider-images-panel的 DIV 元素,与文件字段内联,以显示上传到滑块的当前图像。

  4. 最后,我们添加一个按钮而不是提交按钮,因为我们将通过 AJAX 处理表单提交。

现在界面已准备好上传图像到滑块。您可以通过使用主菜单中的UI 组件设置菜单项来访问此界面。

使用 AJAX 保存滑块图像

我们可以使用常规的 POST 提交以及 AJAX 提交,因为这是一个没有其他依赖项的后端屏幕。然而,AJAX 提供了更好的用户体验,因此我们将通过 AJAX 提交表单数据。首先,我们必须使用load_admin_scripts函数添加一个新的脚本,以处理保存滑块图像的 AJAX 请求。我们将使用与WooCommerce Product Files插件相同的代码,因此您可以参考源代码目录中的主文件以了解load_admin_scripts函数的实现。

接下来,我们必须使用 AJAX 处理表单提交过程。文件表单提交的代码可在源代码目录的wqkm-admin.js文件中找到。实现将与我们在WQWPF Product Files插件的wqwpf-admin.js文件中使用的实现相同,除了以下差异:

  • 在这种情况下,我们通过表单 ID 获取自定义表单,而不是使用内置的 POST 提交表单

  • 我们使用wqkm_save_slider_images操作代替wqwpf_save_product_files

  • 我们移除了 post-ID 参数,因为我们不是提交与帖子相关的表单

现在我们来到图像保存过程的最后部分,通过上传和捕获文件数据。我们必须首先在WQKM_UI_Components类的构造函数中添加以下操作:

add_action( 'wp_ajax_wqkm_save_slider_images', array( $this, 'save_slider_images') );

此过程仅由登录用户执行,因此我们省略了wp_ajax_nopriv动作。让我们看看save_slider_images函数的实现:

public function save_slider_images(){
 global $wpdb; 
 $file_nonce = isset( $_POST['file_nonce'] ) ? ($_POST['file_nonce']) :'';
 $user_id = get_current_user_id();

 if(check_ajax_referer( 'wqkm-private-admin', 'file_nonce',false )){
   $result_upload = $this->process_file_upload();
   if(isset($result_upload['status']) && $result_upload['status'] == 'success' ){
     $file_date = date("Y-m-d H:i:s"); 
     $uploaded_file_name = $result_upload['base_name'];
     $slider_images = (array) get_option('wqkm_slider_images');
     $slider_images[] = $result_upload['relative_file_path'];
     update_option( 'wqkm_slider_images', $slider_images );
     $upload_dir = wp_upload_dir();
     $display_images = '';
     foreach ($slider_images as $slider_image) {
       if( $slider_image != '' )
         $display_images .= '<img src="img/' . $upload_dir['baseurl'] . $slider_image . '" style="width:100px;height:100px" />';
     }

     $result = array( 'status' => 'success', 'msg' => $result_upload['msg'] , 'images' => $display_images );
   }else{
     $result = array( 'status' => 'error','msg'=> $result_upload['msg']);
   }
 }else{
   $result = array( 'status' => 'error', 'msg' => __('Invalid file upload request.','wqkm') );
 }
 echo json_encode($result);exit;
}

以下代码做了什么:

  1. 我们从 POST 请求中检索 nonce 值并将其分配给登录用户的 ID。

  2. 然后,我们在完成 nonce 验证后,使用process_file_upload函数上传请求中的图片。我们已经从之前开发的Post Attachments插件中复制了process_file_upload函数。唯一的区别是使用图像特定的文件类型,而不是我们之前使用的文档文件类型。

  3. 一旦图片上传,我们使用get_option函数检索现有图片。然后,我们添加上传的图片并使用update_option保存新的图片集。在这种情况下,我们已在wp_options表中存储滑块图片,因为它是一个预定义的滑块。在需要多个动态滑块的情况下,我们必须使用wp_posts表或自定义表来存储这些数据。

  4. 最后,我们遍历图片列表并为作为 AJAX 请求响应一部分的图片生成 HTML。在 AJAX 请求中,我们必须使用必要的键并将现有图片列表内联显示在图片上传字段中。

现在我们已经捕获了图像滑块组件所需的所有必要数据。下一步是使用这些数据以及必要的脚本生成 UI 组件。

准备滑块图片

我们需要准备 HTML 代码以包含滑块图片和必要的容器。每个滑块的元素结构根据库而异。因此,我们需要参考滑块 JavaScript 库的文档,找出预期的结构。所以,我们必须访问basicslider.com/并转到标记部分。本部分解释了实现滑块所需的元素:

<div id="banner-fade">
 <ul class="bjqs">
 <li><!-- Any content you like --></li>
 </ul>
</div>

现在我们必须使用这个结构,并在<li>元素内添加滑块图片。我们可以直接将 HTML 添加到 WordPress 帖子或页面中,以显示滑块。然而,我们可能需要重用滑块,因此将其实现为短代码或小工具始终是一个好选择。短代码是完美的解决方案,因为它也可以包含在小工具中。让我们从向WQKM_UI_Components类的构造函数中添加短代码开始实现:

add_shortcode( 'wqkm_product_slider', array( $this, 'product_slider') );

现在我们可以实现短代码回调函数,以使用上传的图片并生成滑块所需的 HTML 结构。考虑以下代码:

public function product_slider( $atts, $content ){
 $sh_attr = shortcode_atts( array(
   'width' => '520',
   'height' => '320',
 ), $atts );

 $slider_images = (array) get_option('wqkm_slider_images');
 $upload_dir = wp_upload_dir();

 $display = '<div id="banner-fade"><ul class="bjqs">';
 foreach ($slider_images as $slider_image) {
   if( $slider_image != '' )
     $display .= '<li><img src="img/' . $upload_dir['baseurl'] . $slider_image . '" /></li>';
 }
 $display .= ' </ul><div>';
 return $display;
}

我们从定义可用的短代码属性及其默认值开始函数。在这里,我们只使用了滑块的 widthheight 作为设置。你可能需要根据你选择的图片滑块库定义更多设置。然后,我们使用 get_option 函数和 wqkm_slider_images 键检索滑块图片列表。最后,我们遍历图片,在 <li> 元素内添加图片 HTML,同时使用主要的 <ul><div> 容器来初始化滑块。

一旦短代码被添加到帖子或页面中,你将看到作为项目符号的图片列表,没有任何滑动功能。

集成 jQuery 滑块

这是集成的最后一部分,我们将 JavaScript 或 CSS 库的功能应用于初始化 UI 组件的功能。因此,我们需要了解所需的库文件及其包含顺序。所需的文件在 basicslider.com/用法 部分中显示。因此,我们必须下载必要的库并将它们添加到我们的插件中。一旦文件被添加,我们就可以在主类的 load_scripts 函数中使用以下脚本和样式注册代码:

wp_register_style( 'wqkm-slider', WQKM_PLUGIN_URL . 'css/bjqs.css' );
wp_register_style( 'wqkm-slider-demo', WQKM_PLUGIN_URL . 'css/demo.css' );
wp_register_script( 'wqkm-slider', WQKM_PLUGIN_URL . 'js/bjqs-1.3.js', array('jquery') );
wp_register_script('wqkm-front',WQKM_PLUGIN_URL .'js/wqkm-front.js', array('jquery','wqkm-slider') );

我们包含了 bjqs.cssdemo.css 文件用于样式,以及 bjqs-1.3.js 文件用于脚本,jQuery 作为依赖项。第三行注册了一个插件特定的自定义脚本以初始化滑块。因此,我们必须将 jQuery 和滑块库作为依赖项使用。

现在我们需要将这些库包含到 WordPress 请求中。我们可以使用 wp_enqueue_stylewp_enqueue_script 函数以及注册代码来包含这些文件。然而,这意味着这些文件将在每个请求中包含,即使页面上没有图片滑块。作为一个解决方案,我们可以在短代码属性的默认值之后将这些文件包含在短代码函数中。你可以参考源代码以了解这些文件的包含方式。

然后,我们来到最后一步,使用必要的脚本初始化组件。你可以参考 basicslider.com/激活 部分,了解初始化过程,如下面的代码所示。此代码应添加到 wqkm-front.js 自定义脚本文件中:

jQuery(document).ready(function($) { 
  $('#banner-fade').bjqs({
    animtype : 'slide',
    height : WQKMFront.height,
    width : WQKMFront.width,
    responsive : true,
    randomstart : true,
    showmarkers   : false, 
  });
});

在这个库中,bjqs 作为初始化函数。我们将其应用于具有 ID #banner-fade 的容器。我们还使用 wp_localize_script 函数添加的数据为 widthheight 分配了短代码属性值。现在你可以刷新浏览器,看到图片滑块,而不是项目符号列表。

这是一个非常基本的滑块,具有基本功能。你可以找到更多具有图片、内容、视频和一些惊人效果的先进滑块。你可以尝试不同的滑块,因为无论滑块如何,集成过程都是相同的。

重复使用滑块作为小部件

我们对小部件及其在 WordPress 中的作用进行了简要介绍。小部件的目的是向网站的侧边栏添加功能块。然而,随着现代主题和小部件区域的出现,小部件可以被视为类似于短代码的可重复使用的功能块。因此,我们将查看在 WordPress 中构建前端小部件的过程。让我们首先在我们的插件 classes 目录中添加一个名为 WQKM_Product_Slider_Widget 的新文件。

在此场景中,我们将构建一个显示在上一节中创建的产品滑块的小部件。因此,我们可以为这个小部件重用短代码功能。让我们看一下以下代码中小部件的实现:

class WQKM_Product_Slider_Widget extends WP_Widget {
  public function __construct() {
    parent::__construct( 'wqkm_product_slider', esc_html__( 'Product Slider', 'wqkm' ), array( 'description' => esc_html__( 'Main product slider', 'wqkm' ) ) );
  }

  public function widget( $args, $instance ) {
    echo $args['before_widget']; 
    echo do_shortcode('[wqkm_product_slider width="'.$instance['width'].'" height="'.$instance['height'].'" /]');
    echo $args['after_widget'];
  }

  public function form( $instance ) {
    $width = ! empty($instance['width']) ? (int) $instance['width']: 640;
    $height= ! empty($instance['height']) ? (int) $instance['height']:320;?>
    <p>
      <label><?php esc_attr_e( 'Width:', 'wqkm' ); ?></label>
      <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'width' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'width' ) ); ?>" type="text" value="<?php echo esc_attr( $width ); ?>">
    </p>
    <p>
      <label><?php esc_attr_e( 'Height:', 'wqkm' ); ?></label>
      <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'height' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'height' ) ); ?>" type="text" value="<?php echo esc_attr( $height ); ?>">
    </p>
 <?php  }

  public function update( $new_instance, $old_instance ) {
    $instance = array();
    $instance['width'] = ( ! empty( $new_instance['width'] ) ) ? (int)          $new_instance['width'] : '';
    $instance['height'] = ( ! empty( $new_instance['height'] ) ) ? (int) $new_instance['height'] : '';
    return $instance;
  }
}

WordPress 使用 WP_Widget 类作为处理小部件功能的基础。所有的小部件,包括核心小部件,都应该扩展这个类以使用小部件功能。因此,我们在自定义的 WQKM_Product_Slider_Widget 类声明中扩展了这个类。现在我们可以通过以下步骤查看小部件的主要功能:

  1. constructor: 我们必须使用此函数来调用父 WP_Widget 类的构造函数,并使用必要的参数。在这种情况下,我们使用了一个唯一的 ID、标题和描述。

  2. widget: 此函数用于在前端生成小部件的输出。在这里,我们使用了 wqkm_product_slider 短代码来生成小部件的输出。你可以使用短代码或调用一个函数来生成输出。$instance 变量包含所有小部件的设置,这些设置添加到后端。因此,我们使用宽度和高度设置作为短代码的属性。你可能也会注意到对 $args['before_widget']$args['after_widget'] 语句的使用。这些是一组预定义的参数,传递给小部件,这些参数的值在注册小部件时指定。这些参数对于在前后每个小部件添加常见内容非常有用。

  3. form: 此函数用于显示表单以捕获小部件的设置。我们在小部件函数中使用了宽度和高度,因此我们需要设置来定义这些值。首先,我们使用自动传递给此函数的 $instance 变量获取设置的现有值。然后,我们生成宽度和高度设置的输入字段。你可能注意到了对 get_field_idget_field_name 函数的使用。这些是在核心 WP_Widget 类中定义的函数,用于以标准方式提供对小部件细节的访问。你应该始终使用这些函数,而不是硬编码名称和 ID。

  4. 更新:此函数用于将设置数据保存或更新到数据库中。一旦点击了小部件的“保存”按钮,此函数将使用旧值以及提交的新值被调用。我们必须进行必要的验证并将新值分配给具有相应键的$instance变量。然后,WordPress 核心小部件类将自动将值保存到$instance变量中。

这是构建任何自定义小部件的标准流程。一旦创建了小部件类,你必须使用这四个函数来生成小部件所需的设置和输出。尽管我们已经完成了小部件的创建,但它不会在“外观 | 小部件”部分中可见。我们必须在它们出现在小部件部分之前注册新小部件。让我们考虑以下用于注册我们小部件的代码:

add_action( 'widgets_init', array( $this, 'register_product_slider_widget' ) );
public function register_product_slider_widget() {
  register_widget( 'WQKM_Product_Slider_Widget' );
}

首先,我们需要在主类的includes函数中包含该文件。然后,我们可以在WQKM_Admin_Features类的构造函数中添加widgets_init动作。最后,我们在回调函数中使用register_widget和类名来注册新小部件。现在你应该能够使用这个新小部件将产品图片滑块添加到你的侧边栏中。

集成 jQuery 手风琴

在本节中,我们将探讨另一个流行的 UI 组件——手风琴的集成。手风琴是一个组件,允许你通过可折叠的部分在有限的空间内显示大量内容。我们选择了jQuery UI库中的手风琴组件([jqueryui.com/accordion/](https://jqueryui.com/accordion/))作为基本解决方案。你可以找到许多高级手风琴组件库。

在图片滑块部分,我们使用了一个菜单页面来捕获数据,并将其限制为单个固定的图片滑块。然而,在网站的各个地方使用相同组件并具有不同内容的能力也同样重要。因此,我们将构建具有创建无限组件和动态内容能力的手风琴。我们可以使用自定义文章类型并将数据存储在wp_posts表中,或者我们可以使用自定义表。由于我们正在开发一个 UI 元素,我们将使用自定义文章类型来简化流程。让我们开始吧。

创建手风琴模型并捕获数据

我们选择使用自定义文章类型来构建手风琴。因此,我们需要使用带有必要设置的register_post_type函数注册一个新的自定义文章类型。我们已经在之前的章节中创建了一个自定义文章类型并讨论了与属性文章类型一起工作的过程。因此,你可以参考源代码目录中的WQKM_UI_Components类来了解注册过程。

与属性不同,手风琴不是为了作为单独的帖子显示或实现页面项目。相反,我们使用它们来捕获要在帖子或页面中的 UI 元素中重用的数据。因此,在创建自定义帖子类型时,我们必须将public属性设置为false。此设置确保手风琴不会作为单独的帖子类型公开可见。

一旦注册了帖子类型,我们需要使用元框来包含必要的字段并捕获手风琴的数据。手风琴有许多具有动态内容的部分。因此,我们需要文本区域字段来捕获每个部分的内容。让我们从为手风琴添加元框开始:

public function add_accordion_meta_boxes() {
  add_meta_box( 'wqkm-accordion-meta', __('Accordion Details','wqkm'), array( $this, 'display_accordion_meta_boxes' ), $this->post_type );
}

在这里,我们为手风琴帖子类型注册了一个新的元框。add_meta_boxes动作和帖子类型初始化可以在WQKM_UI_Components类的构造函数中找到。现在我们需要使用以下代码实现回调函数:

public function display_accordion_meta_boxes( $accordion ) {
 global $wqkm,$template_data;
 $template_data['accordion_post_type'] = $this->post_type;
 $template_data['accordion_nonce'] = wp_create_nonce('wqkm-accordion-meta');
 $template_data['wqkm_tab_1'] = get_post_meta( $accordion->ID, '_wqkm_tab_1', true );

 ob_start();
 $wqkm->template_loader->get_template_part( 'accordion','meta');
 $display = ob_get_clean();
 echo $display;
}

我们在函数开始时定义全局变量,并将帖子类型和 nonce 添加到全局模板数据变量中。然后,我们使用get_post_meta函数获取手风琴部分现有数据。在这里,我们只包含了一个部分。您可以在源代码中找到三个部分。

我们已将手风琴部分固定为三个,以简化开发。理想情况下,我们应该允许管理员动态添加无限数量的手风琴部分。

最后,我们为手风琴部分输入字段加载了一个自定义模板。手风琴元框的模板可以在templates目录下的accordion-meta.php文件中找到。

此模板与用于Properties帖子类型的模板类似,因此是自我解释的。然而,您可能会注意到使用了以下代码行而不是文本区域的代码:

<td><?php wp_editor( $wqkm_tab_1, 'wqkm_tab_1' ); ?></td>

我们本可以使用文本区域字段来处理手风琴部分。然而,这些部分可能包含相当数量的 HTML,因此使用文本区域可能很困难。作为解决方案,我们使用了wp_editor函数,该函数生成带有所有格式的 WordPress 内容编辑器字段。我们必须传递默认内容和内容编辑器的 ID。现在我们可以使用 WordPress 丰富内容编辑器来添加内容到手风琴部分。

我们需要保存部分数据以完成手风琴的数据捕获过程。保存过程是通过save_post动作实现的,实现方式与属性详情保存过程类似。唯一的区别是使用wp_kses_post函数来过滤由wp_editor函数生成的部分内容。您可以参考WQKM_UI_Components类中部分保存过程的源代码。

准备手风琴内容

我们已经确定了短代码对于在帖子或页面中添加 UI 元素的重要性。因此,我们将使用另一个短代码来准备手风琴的内容。让我们将以下代码添加到手风琴短代码的实现中:

add_shortcode( 'wqkm_accordian', array( $this, 'display_accordian') ); 
public function display_accordian( $atts, $content ){
 $sh_attr = shortcode_atts( array( 'id' => '0' ), $atts );
 extract($sh_attr);

 $display = '<div id="accordion">';
 if( trim( get_post_meta( $id, '_wqkm_tab_1' , true ) ) != '' ){
   $display .= '<h3>'.get_the_title( $id ).'</h3>';
   $display .= '<div><p>'.get_post_meta( $id, '_wqkm_tab_1' , true ).' </p> </div>';
 }

 // other fields
 $display .= '</div>';
 return $display;
}

以下代码做了什么:

  1. 我们在 WQKM_UI_Components 类的构造函数中添加了一个名为 wqkm_accordian 的新短代码。我们使用自定义帖子类型来允许创建无限数量的手风琴,因此我们需要一种方法来识别每个手风琴。所以,我们使用帖子 ID 作为短代码属性来识别手风琴。

  2. 接下来,我们通过使用 ID 属性加载数据来生成手风琴的内容部分。

  3. 在这里,我们使用每个部分的主容器内的部分标题和部分内容。这是所选手风琴库所需的结构。如果您选择不同的库进行实现,您将需要找到元素结构。

  4. 最后,我们通过从短代码中返回输出以包含手风琴部分来返回输出。

现在,您可以使用必要的 ID 在帖子或页面中创建并添加手风琴。然而,就像图片滑块一样,您在浏览器上只会看到一堆 HTML 部分。为了启用手风琴功能,我们需要在主类的 load_scripts 函数内部注册必要的脚本和样式,如下面的代码所示:

wp_register_style('wqkm-jquery-ui-style', WQKM_PLUGIN_URL . 'css/jquery-ui.css', false, null);
wp_register_script( 'wqkm-accordion', WQKM_PLUGIN_URL . 'js/wqkm-accordion.js', array('jquery','jquery-ui-accordion') );

首先,我们已经下载并添加了 jQuery UI CSS 文件到我们的插件中,因为它不在 WordPress 核心中提供。然后,我们添加了一个名为 wqkm-accordion.js 的自定义脚本,其依赖项为 jQuery 和 jQuery UI 手风琴脚本。jQuery UI 组件库在 WordPress 核心中可用,因此我们可以直接调用脚本句柄而不是单独注册脚本。然后,我们可以将以下代码添加到短代码函数中,以排队文件:

wp_enqueue_style('wqkm-jquery-ui-style');
wp_enqueue_script('wqkm-accordion');

我们在手风琴短代码中包含了手风琴脚本和样式。最后,我们必须通过在 wqkm-accordion.js 文件中添加以下初始化代码来初始化手风琴:

jQuery(document).ready(function($) {
  $( "#accordion" ).accordion({ collapsible: true, active: false, heightStyle: "content"   }); 
});

使用我们为短代码输出创建的主要容器的 ID 来初始化手风琴。这可能会根据您选择的手风琴库而有所不同。现在,我们已经完成了手风琴集成过程,您的手风琴应该看起来与以下截图相似:

如您所见,我们可以在有限的空间内使用这些 UI 组件来显示大量内容。

集成 UI 组件的步骤

到目前为止,我们查看了两套使用不同的数据捕获技术集成 jQuery 滑块和手风琴 UI 组件的实现。实现可能根据 UI 组件的类型及其功能而有所不同。然而,对于大多数常见的 UI 组件,基本过程是相似的。所以,让我们总结一下将任何 UI 组件与 WordPress 集成的关键步骤:

  1. 识别组件数据:每个 UI 组件都基于现有数据或元素工作。因此,我们需要识别使用每个 UI 组件所需的数据以及存储这些数据的方法。

  2. 生成组件输出:在这一步,我们必须使用捕获的数据,对其进行处理,并生成启用 UI 组件功能的 HTML 输出。通常,我们使用可重复使用的组件,如短代码或小工具,来添加和生成 UI 组件的输出。

  3. 包含组件库:大多数 UI 组件通过应用 JavaScript 或 CSS 到生成的输出中生成其功能。这些组件的功能通常以开源库的形式提供。因此,在这一步,我们需要为组件添加所有必要的脚本和样式,以及必要的设置。在这个过程中,我们使用wp_register_stylewp_register_script函数通过将它们添加到插件或主题中包含这些资源。然后,你可以在主类或 UI 组件生成函数中条件性地将它们排队。

  4. 传递脚本数据:一些 UI 元素库直接在生成的输出上应用 CSS 或 JavaScript 功能。然而,一些元素脚本需要某些数据或设置来初始化元素。在这种情况下,我们必须使用wp_localize_script函数将必要的数据传递给必要的脚本。

  5. 初始化元素:一旦必要的库和 HTML 输出准备就绪,我们可以通过使用这些库的初始化函数来初始化元素。这些初始化通常只需要一到几行代码。通常,我们使用 CSS 类或 HTML 元素 ID 在生成的输出上进行初始化。

一旦完成这些步骤,你将在 UI 元素中看到交互式功能或样式。这是最常见的 UI 元素的一般过程。然而,可能会有例外,需要不同的过程来初始化 UI 元素。

使用页面构建器简化开发

页面构建器是一组简化内容创建或构建网站功能的组件。这些组件允许开发者通过选择和配置内置组件,以超级快速的时间构建复杂的界面。页面构建器最初用于简化使用预构建设计元素的内容创建。然而,页面构建器现在已经发展到我们可以使用现有组件添加高级网站功能,如表单管理、可搜索的谷歌地图、视差图像和视频。

直到最近,开发者们使用主题中包含的页面构建器,以及免费和付费的页面构建器插件。WordPress 引入了一个基于块的 内容构建器,称为 Gutenberg。在这个阶段,Gutenberg 编辑器支持使用一组基本元素进行内容编辑。目前,它远远落后于 Elementor、Beaver Builder 和 Visual Composer 等流行页面构建器提供的功能。然而,未来似乎光明,因为 WordPress 团队正在支持 Gutenberg 编辑器,将其作为默认核心功能。因此,我们很快将看到 Gutenberg 编辑器更强大的功能,以及其他页面构建器提供的功能。

理解页面构建器的功能

正如我们所讨论的,页面构建器提供了一系列可以拖放或分配给任何帖子、页面或自定义帖子类型的组件。然而,许多这些页面构建器提供了超出基本内置组件使用的先进功能。因此,了解这些功能对于使用现有组件以及使您的解决方案与页面构建器集成非常重要。让我们看看现代页面构建器提供的主要功能:

  • 后端和前端编辑:通常,我们使用后端内容编辑器来编辑或构建网站界面,因此我们必须切换到前端来查看更改。大多数页面构建器提供前端编辑功能,允许实时预览并即时调整。此外,还有一个后端编辑器,如果您想使用其他后端功能。

  • 内置模板:许多页面构建器通过使用现有组件提供内置的模板设计。因此,开发者可以使用这些为常见需求设计的模板,并根据需要调整它们,而无需编写任何代码。

  • 高级样式:每个组件都允许各种样式选项,例如边距、填充、颜色等。因此,您可以在不进入代码的情况下微调设计。

  • 响应式设计:在现代开发中,为了适应不同的设备,如个人电脑、手机、平板电脑等,这是必须的。通常,我们必须花费大量时间使我们的设计完全响应式。页面构建器结构和组件默认是响应式的。因此,只要您遵循自己元素的适当编码标准,您就不需要做任何事情来使内容响应式。

如您所见,页面构建器为构建您网站的内容和设计提供了很多功能。然而,这些强大的功能也伴随着一些限制。一些页面构建器完全依赖于短代码,因此您将锁定在插件中。此外,当使用页面构建器的某些复杂功能时,您可能会遇到性能问题。因此,您在选择合适的页面构建器和功能时需要负责任。

为页面构建器开发组件

最受欢迎的页面构建器附带了一系列内置组件以及通过插件提供的附加组件。因此,我们可以使用现有的组件来构建通用目的的 WordPress 网站。然而,当开发高级应用程序或具有自定义要求的网站时,这些组件的默认功能可能不足以满足需求。在这种情况下,我们必须构建自己的页面构建器组件来提供必要的功能。我们始终可以创建短代码并要求客户在页面构建器组件中使用它们。但是构建我们的组件可以大大简化客户的过程,因此我们有必要时必须构建组件。

主要的页面构建器为他们的插件提供 API 或指南来开发组件。每种页面构建器的技术都有所不同。因此,您需要使用文档来了解和为每个页面构建器开发组件。从头开始开发具有必要解释的组件超出了本章的范围。然而,我们将以 Elementor 页面构建器为例,探索组件创建的基本步骤。让我们看看构建页面构建器组件的主要步骤:

  • 每个页面构建器都将其组件标识为模块、小部件、块等。因此,应该有一种方法可以为页面构建器注册新的组件。Elementor 使用Widget_Base类和register_widget_type函数来注册新的组件。其他页面构建器提供类似的技术。

  • 在注册组件后,您需要指定用于捕获组件数据的输入字段。大多数页面构建器提供不同类型的输入字段来捕获和保存组件的数据。Elementor 在Widget_Base类上提供了一个名为add_control的函数,支持超过 30 种不同的控件类型。其他页面构建器将提供类似的功能来为不同字段类型添加输入字段,以及不同的数据存储机制。

  • 最后,我们需要通过构建 HTML 代码或执行现有的短代码来提供组件的输出。Elementor 提供了一个名为render的函数,用于检索保存的组件数据并显示输出。其他页面构建器应该有类似的方法,具有类似的名字。

我们已经使用这些步骤为 Elementor 页面构建器构建了一个滑块组件。您可以在本章源代码的wpquick-elementor-components目录中找到此插件的代码。以下截图预览了带有设置的由插件生成的组件:

图片

Elementor 是目前最受欢迎且正在崛起的页面构建器之一。您可以在developers.elementor.com/creating-an-extension-for-elementor/找到文档,并使用附加代码来理解步骤。其他页面构建器的过程将类似,需要插件特定组件创建函数的支持。

简化和定制管理功能

我们必须尽可能使用核心功能,以获得使用 WordPress 进行网站开发的最佳效益。这包括使用前端主题功能以及现有的后端功能,这些功能主要关注与帖子相关的功能。因此,很明显,开发者需要根据每个网站的需求对这些功能进行定制。在本节中,我们将探讨常用后端功能的几种定制方法。

创建自定义仪表板小部件

我们在第六章“WordPress API 的实际应用”中简要介绍了仪表板小部件 API。基本上,一个仪表板是为管理目的而构建的小部件集合。与前端主题小部件不同,我们没有添加或删除管理仪表板小部件的界面。因此,我们必须构建自定义解决方案来创建新的仪表板小部件以及删除现有的小部件。在本节中,我们将探讨创建基本仪表板小部件的过程。

注册和构建仪表板小部件

首先,我们必须使用wp_dashboard_setup动作来注册仪表板小部件,而不是我们在前端小部件中使用的widgets_init。以下代码展示了小部件注册过程:

add_action( 'wp_dashboard_setup', array( $this, 'dashboard_widgets' ) );
public function dashboard_widgets() {
  wp_add_dashboard_widget( 'wqkm_post_attachments', __('Post Attachments','wqkm'), array( $this, 'post_attachments_widget' ) );
}

在回调函数内部,我们使用wp_add_dashboard_widget函数来注册一个新的小部件。此函数的参数包含一个唯一的 widget ID、widget 标题和回调函数。然后,我们必须实现该函数以显示小部件的内容,如下面的代码所示:

public function post_attachments_widget() {
  echo do_shortcode('[wpquick_attachment_posts]');
}

在这里,我们通过使用在早期章节中开发的短代码简化了过程。基本上,我们用不到 10 行代码就有一个仪表板小部件。与前端仪表板小部件创建过程相比,这个过程更简单。

默认仪表板小部件大多适用于博客。在许多情况下,我们必须移除现有的小部件并添加自定义小部件以满足高级站点特定业务需求。在自定义站点中,仪表板小部件通常用于显示站点统计信息、报告或需要定期执行的功能,而无需访问单个屏幕。您可以尝试带有仪表板小部件的现有插件来了解其用法。WooCommerce、bbPress 和 Easy Digital Downloads 是一些内置自定义仪表板小部件的流行插件。

定制后端列表表

WordPress 为主内容类型提供后端列表表。我们在第七章中讨论了列表表的功能,管理自定义帖子类型和处理表单。默认功能为帖子、页面、用户和评论以及所有注册的自定义帖子类型提供数据列表表。此外,我们还可以扩展用于列表表的核心类,并创建我们自己的自定义数据列表表。因此,了解后端列表表现有功能以及可定制功能非常重要。在本节中,我们将查看后端用户列表的一些常见自定义设置。让我们开始吧。

创建和执行自定义批量操作

WordPress 列表表在列表的右上角部分包含一个名为“批量操作”的下拉菜单。此功能用于通过单个预定义事件修改表中的多个记录。我们必须选择一个或多个记录,从批量操作下拉菜单中选择一个操作,然后点击应用按钮以完成此过程。当您想要修改大量记录以实现自定义功能时,这是一个非常有用的功能。

WordPress 核心操作可用于修改批量操作下拉菜单并包括我们项目的自定义操作。让我们将以下操作添加到WQKM_Admin_Features类的构造函数中,以了解使用自定义操作的过程:

add_filter( 'bulk_actions-users', array( $this, 'user_actions' ) );

此操作用于过滤用户列表表中的操作。该过滤器由bulk_action前缀后跟屏幕名称组成。因此,您可以通过将用户替换为列表的屏幕名称来使用相同的操作来处理其他核心列表表。让我们看看自定义操作的实现:

public function user_actions($bulk_actions) { 
  $bulk_actions['wpquick_featured_user'] = __('Mark Featured Profile','wqkm'); 
  return $bulk_actions;
}

在此代码中,我们通过修改现有的批量操作列表向默认后端用户列表添加了一个自定义操作。我们必须使用唯一的 slug 作为此操作的数组键,并定义标签作为数组值。在这种情况下,我们正在构建一个允许我们标记某些用户为特色用户的操作。现在您将在批量操作下拉菜单中看到一个新选项来处理用户特色状态。

一旦选择了记录和操作,我们就可以点击应用按钮来修改所选记录。因此,我们需要通过在类的构造函数中添加以下操作来实现此操作:

add_filter( 'handle_bulk_actions-users', array( $this,'users_page_loaded'),10,3);

在此代码中,我们使用了handle_bulk_actions-过滤器后跟用户列表表的屏幕名称。因此,让我们看看自定义操作的实现:

public function users_page_loaded($redirect_to,$doaction,$featured_users ){
  if ( $doaction !== 'wpquick_featured_user' ) {
    return $redirect_to;
  }

  foreach ($featured_users as $featured_user) {
    update_user_meta($featured_user, 'wqkm_featured_status', 'ACTIVE');
  }

  $redirect_to = add_query_arg( 'bulk_featured_users', count( $featured_users ), $redirect_to );
  return $redirect_to;
}

以下代码做了什么:

  1. 此函数自动接收用于重定向 URL、操作名称和要修改的记录 ID 列表的三个参数。

  2. 首先,我们检查操作并确保我们的代码仅针对wpquick_featured_user操作执行。

  3. 接下来,我们遍历所选用户 ID 的列表,并修改值以标记用户为特色用户。我们可以使用不同的操作或切换相同的操作来移除特色状态。

  4. 最后,我们通过向 URL 添加一个名为bulk_featured_users的自定义参数来将用户重定向到列表。此值将用于过滤请求,并在源代码中使用bulk_admin_notices函数显示自定义消息。

现在我们为用户列表创建了一个工作的自定义操作。你可以通过向一些用户添加特色状态并检查数据库值来测试这个功能。这是一种简单的方法,用于修改或应用自定义数据以满足特定需求。我们不能使用输入字段进行这些操作,因此操作应该具有固定的特性,并具有预定义的值。

添加自定义列表列

数据列表表包含默认的列集,这些列根据列表类型的不同而有所变化。在上一节中,我们更改了用户的特色状态。然而,我们必须使用数据库来检查值。相反,我们可以通过使用自定义列在数据列表表中显示此类重要信息。因此,在本节中,我们将探讨将自定义列添加到默认用户列表的过程,以及识别其他列表所需的钩子。让我们首先向WQKM_Admin_Features类的构造函数添加以下过滤器和操作:

add_filter( 'manage_users_columns', array( $this,'manage_user_custom_columns') );
add_action( 'manage_users_custom_column', array( $this, 'manage_user_custom_column_values' ), 10, 3 );

manage_users_columns过滤器用于修改或删除现有列,以及向用户列表添加新列。以下代码预览了包括特色状态作为列的实现:

public function manage_user_custom_columns( $column ) {
 $column['featured_user_status'] = __('Featured Status','wqkm');
 return $column;
}

现在,你应该在用户列表中看到一个新的列,但没有数据。下一步是显示每个用户的列数据。我们必须使用manage_users_custom_column操作来为我们自定义列提供数据。让我们看看回调函数的实现:

public function manage_user_custom_column_values( $val, $column_name, $user_id) {
  $featured_user_status = get_user_meta( $user_id ,'wqkm_featured_status', TRUE);
  $featured_user_status = ( $featured_user_status == 'ACTIVE') ? __('ACTIVE','wqkm') : __('INACTIVE','wqkm');

  switch ($column_name) {
    case 'featured_user_status' :
      return $featured_user_status;
      break;
    default:
      return $val;
      break;
  }
}

此函数接受三个参数:列值、列名和用户 ID。我们可以使用这些参数来获取所选用户的自定义列值。在这里,我们使用get_user_meta函数,因为我们把数据存储在wp_usermeta表中。我们也可以使用自定义表来存储这些值并显示它们。接下来,我们使用switch语句根据列返回值。使用default情况并返回默认值是很重要的,以确保与其他插件兼容。现在你应该在用户列表中看到自定义列,并为每个用户显示自定义值。

排序自定义列值

默认列表表为第一列和剩余列中的一个或多个提供排序功能。然而,这些是 WordPress 核心功能提供的内置列。因此,我们需要排序自定义列值的能力,以有效地使用和过滤自定义数据。WordPress 提供了一个内置过滤来指定可排序的列。让我们考虑以下过滤及其实现:

add_filter('manage_users_sortable_columns', array($this,'users_sortable_columns'));
public function users_sortable_columns( $columns ) {
  $columns['featured_user_status'] = 'featured_user_status';
  return $columns;
}

这个过滤需要添加到类的构造函数中。这个过滤接受现有可排序列的列表作为参数。在这里,我们将我们自定义列的键添加到可排序列数组中,并返回列表。现在你应该能够在自定义列中看到一个链接来更改排序值。过程的最后一步是更改默认查询并启用自定义列的排序。让我们考虑以下动作及其实现:

add_action( 'pre_user_query', array( $this, 'users_orderby_filters' ) );
public function users_orderby_filters( $userquery ){
  global $wpdb;
  if( 'featured_user_status' == $userquery->query_vars['orderby'] ) {
    $userquery->query_from .= " LEFT OUTER JOIN $wpdb->usermeta AS wpusermeta ON ($wpdb->users.ID = wpusermeta.user_id) ";
    $userquery->query_where.= " AND wpusermeta.meta_key='featured_user_status' ";
    $userquery->query_orderby = " ORDER BY wpusermeta.meta_value ".( $userquery->query_vars["order"] == "ASC" ? "asc " : "desc " ); 
  }
}

让我们通过以下步骤来理解这个函数的实现:

  1. 我们可以使用pre_user_query动作在执行之前修改默认查询,该函数将WP_User_Query对象作为参数传递。

  2. 接下来,我们使用query_vars数组检查查询的orderby子句,以确保我们的自定义代码仅在featured_user_status列上执行。

  3. 然后,我们必须修改查询,从from子句中的join语句开始,包括usermeta表。修改后的where子句确保排序只应用于featured_user_status列的值。

  4. 最后,我们将orderby子句更改为使用featured_user_status列的元值来排序列表。

现在过程已经完成,你应该能够点击列标题,使用特色状态对用户进行两种方式的排序。以下截图预览了我们添加到用户列表中的功能:

图片

你可以通过必要的过滤来应用这个技术,以管理用户列表上的更多列。

我们研究了后端用户列表的列表表自定义。同样,我们可以使用相同的技术,通过必要的动作和过滤来定制其他 WordPress 列表表的列。

在大多数情况下,我们需要自定义文章和自定义文章类型列表,以包括自定义列。我们必须使用manage_edit-{post_type}_columns过滤来定义自定义列,并使用manage_{post_type}_posts_custom_column过滤来添加自定义列的值。然后,我们可以使用manage_edit-{post_type}_sortable_columns过滤来定义可排序的列,并使用pre_get_posts动作来修改排序的查询。

因此,我们需要在必要时使用这些动作和过滤来在列表表中显示额外的数据,并避免耗时地访问编辑部分来检查值。

摘要

使用 WordPress 的主要目的是简化开发过程。因此,我们需要了解自定义重要后端功能的过程。同时,使用现代用户界面组件在有限的空间内使用吸引人的技术提供所有重要功能也非常重要。我们首先通过理解 UI 组件集成的重要性开始了本章,因此我们在使用两种不同的数据捕获方法的同时集成了 jQuery 滑动器和手风琴组件。我们还探讨了在现代开发中页面构建器的重要性以及为页面构建器开发自定义组件的步骤。接下来,我们通过构建一个简单的仪表盘小部件来自定义管理功能。最后,我们研究了向内置 WordPress 列表表添加自定义功能的过程。

在第九章《增强安全性、性能和维护》中,我们将通过查看开发的非功能性方面,如安全性、性能、测试和维护,来完成本书的内容。

第九章:提升安全性、性能和维护

到目前为止,我们探讨了使用现有功能以及自定义功能开发 WordPress 站点。作为开发者,我们可以使用最新的趋势技术、现代开发技术,并构建具有惊人界面的站点。然而,如可利用的代码、性能不佳、维护限制和迁移困难等原因可能会破坏具有所有功能和技术的站点。非功能性方面,如安全性、性能、代码质量和维护,在幕后起着重要作用。因此,了解使用现有工具和服务来处理这些开发领域是非常重要的。

我们从理解测试的重要性开始本章。我们将讨论不同类型的测试,包括单元测试、代码质量和性能,以及使用必要的工具。然后,我们将探讨保护您的 WordPress 站点的技术。接下来,我们将查看使用必要工具将站点从测试服务器迁移到实时服务器的流程。最后,我们将学习维护站点所需的各个方面。

在本章中,我们将涵盖以下主题:

  • 测试简介

  • 单元和集成测试

  • 代码质量和性能测试

  • 保护 WordPress 站点

  • 迁移 WordPress 站点

  • 维护 WordPress 站点

到本章结束时,您应该能够构建安全且易于维护的站点,同时优化功能性能。

测试简介

测试网站和代码的功能过程对于构建成功的网站或应用程序至关重要。这个过程使我们能够在将网站发布到实时环境之前检测到任何缺陷。在 Web 开发中使用了多种类型的测试,其中一些测试与其他测试相比起着非常重要的作用。我们可以使用这些测试类型测试 WordPress 站点的功能和非功能性方面。在本节中,我们将简要介绍最重要的功能性和非功能性测试类型。

单元和集成测试

单元测试用于测试每个小组件,使其独立于其他组件,而集成测试用于测试应用程序与所有模块的组合。这两个测试都属于功能性类别,因为我们正在检查代码的功能和逻辑。与其他流行的框架相比,WordPress 代码不是最容易测试的。然而,我们可以使用PHPUnit对 WordPress 中的主题以及插件进行测试。安装和实现 PHPUnit 测试用例超出了本章的范围。您可以在make.wordpress.org/core/handbook/automated-testing/找到使用 PHPUnit 的指南。

WordPress 提供了一套用于测试主要功能的测试用例。许多开发者对现有的测试用例了解有限,因为它们不在核心中可用。你可以在unit-tests.svn.wordpress.org/trunk/tests/访问完整的测试用例列表。确保通过查看现有的测试用例来获取有关测试 WordPress 的知识。然后,你可以为你的插件和主题编写测试用例,用于单元测试目的。

代码质量测试

代码质量测试属于非功能性类别。这是测试最重要的方面之一,因为最终用户并不知道代码的质量。因此,你可以很容易地使用低质量代码的插件来构建网站。然而,在后期阶段,当你使用不同版本的 WordPress 或使用与你的代码冲突的插件时,这将会成为问题。

在 WordPress 网站上,我们可以构建自己的插件,也可以使用现有的插件。我们可以通过审查代码并修复代码质量问题来测试我们的解决方案。然而,当使用具有数千行代码的现有插件时,这可能并不实用。因此,我们需要一个允许我们自动测试代码并提供包含可能问题的报告的解决方案。不幸的是,目录中没有更新插件允许我们检查其他插件的代码。所以,自动化代码测试过程的唯一方法是通过使用第三方服务来检查代码并提供报告。

不幸的是,用于测试插件代码的服务也不多。有一个名为PluginTests的服务,位于plugintests.com,用于测试 WordPress 插件的编码错误。然而,它仅限于 WordPress 插件目录中的插件。因此,你将无法使用此服务测试自定义插件或付费插件。在开发过程中,我们使用来自 WordPress 插件存储库的许多插件。此外,只要源代码是开放的,我们还可以将自定义插件添加到存储库中。因此,我们将研究如何使用此服务快速测试存储库中的插件并查找错误,在决定使用它们之前。

使用 PluginTests 服务

一旦你访问了plugintests.com,你将获得一个搜索部分来检查存储库中的任何插件。你还会看到两个其他部分,列出流行的插件和最近测试的插件。如果期望的插件在这些列表中,你只需点击即可立即获得该插件的报告。让我们考虑以下最近测试的插件的截图:

图片

我们在列表中可以看到插件名称、最后一次测试的时间和结果。你可以看到不同结果的可用性,例如okwarningfailureok状态表示插件测试成功,没有问题。warningfailure状态表示插件代码存在问题。通常,我们会得到带有warning状态的轻微 JavaScript 或 PHP 错误。然而,failure状态意味着服务无法激活插件或插件代码中存在致命错误。因此,我们可以通过查看列表中的状态来简要了解插件的质量。

当插件未列在热门或最新部分时,你可以使用名称或短码搜索插件,并点击搜索按钮来加载结果。以下屏幕显示了热门 WooCommerce 插件生成的结果:

图片

这些不同的部分意味着:

  • 错误:本节充满了绿色的勾选标记,意味着插件没有可识别的编码错误。

  • 环境:本节显示了用于测试的 WordPress、PHP 和 MySQL 版本,以及服务器的内存限制。

  • 性能:本节简要总结了使用的资源以及插件是否对性能有任何影响。正如你所见,WooCommerce 使用的内存非常有限,影响微乎其微,这意味着我们可以使用该插件而不必过于担心性能。

  • 插件信息:本节显示了插件信息,例如最后更新日期、活跃安装和来自thewordpress.org插件页面的 URL。这些徽章直接给出了与最新 WordPress 和 PHP 版本兼容性的直观想法。WooCommerce 与所有最新版本兼容。当存在不兼容性时,你会看到带有红色徽章的插件,特别是与 PHP 7 的不兼容性。

在这些部分之后,你将看到插件的一个列表测试屏幕,包括 HTTP 状态、加载时间、内存使用、JavaScript 和 PHP 错误。这些屏幕将帮助你识别单个功能中的错误。PHP 错误日志位于此报告的底部。你可以检查错误日志并修复影响网站功能的任何编码问题,或者决定不使用具有大量错误的第三方插件。

除了这些功能之外,我们还可以获得有关插件的有用信息,如下所示:

  • 角色和权限:了解第三方插件创建的角色和权限很重要,因为这些权限可能与某些其他第三方插件不兼容。

  • 小工具和自定义文章类型:此外,你还将了解通过插件注册的自定义文章类型和小工具。

  • 自定义表格:这也很重要,因为我们需要了解第三方插件如何以及在哪里存储数据。此外,了解自定义表格的使用对于迁移需要备份的网站也很重要。

  • 资源使用:本节显示了插件激活和未激活时常见 WordPress 管理界面和前端屏幕的资源使用情况。因此,您可以决定哪些区域最易受插件使用的影响。

总体而言,这是一个跟踪插件中编码错误以及了解每个插件如何影响您的数据库和网站功能的优秀服务。

性能测试

在过去,我们拥有缓慢的互联网连接,用户愿意等待结果。如今,我们有了超高速的互联网连接和超忙碌的日程安排。因此,当使用网站或应用程序时,用户期望即时结果。即使是几秒钟的延迟也可能导致用户转向其他网站。因此,性能已成为网站开发中的一个非常重要的因素。因此,我们需要考虑所有可能提高应用程序性能的行动。

通常,提高性能需要我们减少加载或执行网站功能所需的时间,同时减少请求使用的带宽。因此,我们希望网站尽可能快地提供结果,同时使用最少的数据量,尤其是对于基于移动端的用户。

让我们找出提高 WordPress 网站性能的可能方法:

  • 缓存应用程序数据:缓存允许我们在不连接到数据库或执行复杂且耗时的逻辑的情况下向用户提供网站页面。它主要适用于不经常更改的网站。

  • 压缩和最小化文件:通常,响应中发送的数据量会影响加载或执行时间。随着数据量的减少,应用程序可以更快地提供结果。压缩和最小化 CSS、JavaScript 和图像等文件可以显著减少响应的大小。您可以在应用程序中找到许多用于压缩和最小化文件的插件。

  • 从快速 CDN 加载第三方文件:通常,我们在开发应用程序时至少使用几个开源库。jQuery 是这类库中最常见的一个。我们将在 WordPress 文件中加载 jQuery 以保持一致性并防止重复。然而,通过内容分发网络CDN)加载此类文件可以提高这些文件的加载时间。

  • 移除低质量插件:我们在网站开发中倾向于使用许多插件,有时由于时间限制,我们无法检查我们使用的每个插件的代码质量。因此,一些代码质量低或实现不当的插件可能会在我们的网站上造成严重的性能问题。因此,我们需要检查每个插件对网站性能的影响,并用替代品替换低质量插件。

  • 限制数据库查询数量:我们在网络开发中使用了众多插件,每个插件都会使用大量的数据库查询。在大多数情况下,这些查询可能会在不为我们所知的情况下减慢应用程序的速度。此外,还可能有许多可以转换为单个查询的重复查询。因此,检查应用程序的数据库查询使用情况并将其尽可能限制在最低限度是很重要的。

  • 优化现有查询:有时,当这些功能可以通过优化的 WordPress 函数实现时,开发者倾向于使用自己的查询。此外,由于缺乏知识,开发者可能会给查询添加不必要的复杂性。因此,识别对性能影响最大的数据库查询并对其进行优化是很重要的。

这些是影响网站性能的一些主要标准。还有许多其他这样的标准可以用来提高网站的性能。我们使用现有的插件/服务来衡量开发中的性能并确定可能的改进。WordPress 插件目录中有许多现有的插件可以用来衡量性能。我们将查看其中之一,因为我们已经在第一章,WordPress 开发简介中设置开发环境时查看了一些插件。

P3 - 插件性能分析插件

尽管这个插件已经有一段时间没有更新了,但它仍然是检查单个插件性能的流行解决方案。我们可以用它来测量任何给定页面中每个激活的插件的影响。您可以从wordpress.org/plugins/p3-profiler下载并安装此插件。一旦插件被激活,您可以在工具菜单中点击 P3 插件分析器菜单项来加载扫描功能。然后,您可以点击开始扫描按钮,您将看到一个类似于以下屏幕截图的界面:

图片

我们可以使用自动扫描或手动扫描选项来开始扫描网站。自动扫描选项会随机扫描您网站的不同内容并生成报告。另一方面,手动扫描允许我们通过点击网站的链接来仅访问我们偏好的内容。一旦生成结果,您将看到一个类似于以下屏幕的界面,其中包含各种详细信息:

图片

我们可以在可用的标签页中看到插件加载时间、插件影响以及每次访问的 MySQL 查询量,并伴随着各种图表。这使我们能够直接看到影响性能最大的插件。在这种情况下,WooCommerce 插件的影响最大。之前,我们使用了 PluginTest 服务,并确定了 WooCommerce 的资源使用量微不足道。因此,我们可以考虑这两个结果,并得出结论,这个网站没有使用影响性能的插件,因为 WooCommerce 是影响最大的插件。如果其他插件有更大的影响,我们可以查看代码并使用其他工具来识别插件中是否存在对性能产生重大影响的重大问题。在完成此类验证后,我们可以保留插件或用替代插件替换它们,以减少插件对我们网站的影响。

保护 WordPress 网站

在使用 WordPress 开发网站或应用程序时,网站的安全性是主要关注点之一。开源代码和保持向后兼容性的旧编码技术是这些安全担忧的两个原因。然而,主要威胁来自于第三方插件和主题的使用,因为在很多情况下,对这些插件的质量没有保证。因此,尽可能确保 WordPress 网站的安全,以防止通过插件、主题或已知的网络安全问题进行攻击。

WordPress 编码手册提供了一个名为强化 WordPress的独立部分,用于定义必要的安全约束。您可以在codex.wordpress.org/Hardening_WordPress上阅读此安全指南。让我们找出 WordPress 漏洞的常见原因和基本预防措施:

  • 管理员用户名:这是 WordPress 网站漏洞的最基本和最根本的原因之一。许多新手客户和开发者将admin作为网站管理员的用户名。因此,黑客攻击的过程变得简单,攻击者只需要找到密码。所以,必须将其更改为一个包含字母、数字,甚至可能包含一些字符的高级用户名。

  • WordPress 核心更新:由于它是开源的,社区非常大,因此每个 WordPress 发布都存在很大可能性有人发现一个错误,或者利用它来攻击现有的 WordPress 网站。然而,这些问题被非常快速地识别出来,并立即发布更新版本以防止问题。许多 WordPress 网站由没有开发者支持的普通管理员维护。因此,这些管理员没有更新 WordPress 的知识,或者他们害怕更新会破坏网站。攻击者可以轻易利用旧 WordPress 版本中的问题并加以利用。

  • 插件和主题更新:这比 WordPress 更新更成问题。许多 WordPress 网站是由第三方一次性开发者完成的。因此,插件或主题的质量是可疑的,因为它们只关注在估算的成本内完成功能。所以,有很多插件和主题存在可利用的代码,其中许多甚至没有积极维护。因此,我们不能期望每次都有一个新版本来修复错误。此外,这些网站使用数十个 WordPress 插件开发,因此许多管理员不倾向于手动更新每个插件的版本。因此,这些插件和主题的过时版本可能造成重大的安全威胁。作为解决方案,您可以通过使用auto_update_pluginauto_update_theme过滤器来启用基于 WordPress.org 的插件和主题的自动更新,并将值返回为__return_true。然而,某些插件或主题的更新可能会在其他插件中引起冲突。因此,在使用自动更新时,您需要密切关注网站的功能。

  • 配置文件位置:默认情况下,配置文件位于 WordPress 安装的根目录中,并包含最重要的数据库登录详细信息。因此,攻击者知道文件位置,这更容易。因此,我们需要通过将文件位置更改为 WordPress 网站中的私有目录来保护此文件。

  • 用户角色和权限:有五个内置的用户角色,具有不同的能力。在大多数网站上,有两人或更多人编辑内容和管理网站。因此,通过使用用户角色和能力仅给予这些用户必要的功能,同时防止访问 WordPress 网站的重要部分,这是非常重要的。

  • 登录表单安全:默认的 WordPress 登录功能是安全的。然而,攻击者可能会使用工具猜测密码并获取对网站的访问权限。因此,我们需要在 WordPress 登录上实施额外的安全措施。我们可以通过实施限制失败的登录尝试、使用双因素认证、限制到某些 IP 地址等功能来保护登录。我们可以轻松地找到现有的插件来实现这些功能。

  • 后端文件编辑:如果攻击者以某种方式获得了对网站的访问权限,他可以使用主题或插件编辑器来修改文件,添加恶意代码,或者通过删除数据和文件来完全破坏网站。因此,我们需要在配置文件中使用define( 'DISALLOW_FILE_EDIT', true )来移除所有用户的文件编辑能力,或者通过自定义代码有条件地为某些用户使用它。

  • 密码复杂性:这是任何网站的基本关注点之一,不仅限于 WordPress。大多数管理员倾向于使用易于记忆的简单密码,这使得攻击者可以猜出它们。因此,密码需要足够长,以便网站能够防止攻击。

这些只是保护 WordPress 网站的一些基本预防措施。对于具有敏感数据和功能性的网站,我们需要使用高级安全功能,以及这些功能。除非你的意图是构建一个安全插件,否则不可能在开发中使用这些高级安全功能。因此,我们必须使用一个或多个顶级安全插件或服务来保护网站。以下是在 WordPress 插件目录中提供的最受欢迎的安全插件列表:

我们可以根据特定网站所需的网络安全功能选择这些插件之一。使用多个安全插件提供不同的安全功能是可能的。然而,它们之间可能存在冲突的风险,因此选择一个高质量的插件是理想的。

WordPress 网站迁移

通常,我们在迁移到实际服务器之前在预发布服务器上开发网站。在大多数情况下,手动迁移网站的过程并不实用,因为我们必须手动备份数据库、文件、上传的媒体,并通过 FTP 上传到另一个服务器。因此,我们需要使用一个工具,允许我们通过平滑迁移自动化这个过程。有一些插件允许你分别备份数据库、文件和媒体,然后手动导入到实际服务器。

我们将查看一个提供所有这些功能的同一插件,使迁移变得超级简单。你可以找到一个名为 All-in-One WP Migration 的插件。这个插件可以在wordpress.org/plugins/all-in-one-wp-migration/找到。一旦激活,你可以在左侧菜单的 All-In-One WP Migration 菜单项中点击导出项目。你将看到一个类似于以下屏幕的界面:

导出功能从数据库替换功能开始。我们可以使用这个功能在数据库中查找并替换任何内容。这对于将自定义数据的 URL 更改为与实际网站 URL 匹配非常有用。接下来,我们可以从“导出到”设置中选择文件选项,插件将生成一个以wpress为扩展名的单个文件作为网站的备份。我们可以将备份下载到本地机器。

现在,我们必须前往实际网站并安装相同的插件。这次,我们必须使用导入部分,如下面的截图所示:

我们可以点击“从导入”按钮,并添加上一节中生成的导出 wpress 文件。然后,插件将在几分钟内迁移整个网站,使其成为一个超级简单的解决方案。与其他框架不同,我们可以在几分钟内完全迁移一个 WordPress 网站。

导出的 wpress 文件存储在您的服务器 /wpcontent/ai1wm-backups 文件夹中。即使对于基本网站,这些文件也可能非常大。因此,您必须确保删除不必要的备份,以防止占用大量服务器空间。

我们可以在插件的“备份”菜单中找到所有已导出和可用的备份文件。因此,我们可以通过单次点击轻松地将网站恢复到之前的状态。这是目前最好的网站迁移插件之一。您还可以考虑以下插件作为网站迁移的替代方案:

这些插件中的每一个都提供了不同的迁移功能,包括导出到 Google Drive 和其他第三方服务。因此,您将不得不尝试这些插件,并选择迁移网站最简单的插件,同时确保它与您的托管提供商兼容。

维护 WordPress 网站

维护网站的过程是 WordPress 开发中的另一个问题领域。许多网站是由没有开发者的非技术客户构建的,因此他们不了解维护网站的常规任务。一旦出现问题,这些客户就会寻找开发者来修复,而没有必要的流程和资源。因此,作为开发者,保持网站可维护性并将维护流程的知识传授给客户非常重要,以防你不再负责该网站。在本节中,我们将探讨维护 WordPress 网站的主要任务以及如何实施这些任务:

备份文件和数据库

这是在维护中必须完成的任务之一,许多非技术性的网站所有者都将其排除在外。WordPress 控制着超过 25% 的网络,因此针对 WordPress 网站的攻击数量同样很高。因此,我们需要定期备份文件和数据库,以便在类似情况下使用。不幸的是,许多网站所有者没有适当的备份流程,其中一些人认为这是由托管服务器自动完成的。

有一些托管提供商和功能可以从服务器端自动化备份流程。然而,最好使用手动流程或现有插件来拥有我们自己的备份。有许多插件允许我们安排网站的备份,如下所示:

我们已经讨论了使用All-In-One WP Migration插件迁移 WordPress 站点的用法。我们还可以使用此类插件作为同一站点的备份和恢复机制。因此,使用这些备份插件并安排您站点的备份是必须的。

更新 WordPress 插件和主题

在前面的章节中,我们已经讨论了更新 WordPress 核心、插件和主题以提升网站安全性的必要性。此外,这也是维护工作的重要组成部分,因为一些插件和主题可能无法与较旧或较新的 WordPress 版本兼容。因此,您需要跟踪插件和主题的更新,以及每个版本中可能的错误修复。否则,您将不得不使用自定义代码来自动化更新过程,或者使用像自动插件更新wordpress.org/plugins/automatic-plugin-updates/)这样的插件来仅对某些插件进行自动化更新。

优化数据库表

这是在维护工作中许多网站所有者没有考虑到的另一个方面。我们在 WordPress 站点中使用了许多具有不同数据需求的插件。许多这些插件在现有的数据库表中工作,因此很难追踪哪个插件负责核心表中的某些数据。一些这些插件提供了高质量的停用或删除过程,从数据库中删除所有与插件相关的数据。然而,许多插件没有这样的过程,即使删除了插件,我们仍然可以在数据库中找到许多未使用的数据。此外,WordPress 核心功能,如修订、瞬态、缓存等,都会在核心表中加载大量不必要的数据。

因此,我们需要定期通过删除这些不必要的数据来优化数据库。第一步是仅选择在卸载时删除其数据的插件。然后,我们可以选择一个数据库优化插件,如下所示:

这些插件的一些常见功能包括删除修订、自动草稿、具有特定状态的评论以及重复和孤立的元数据。我们必须使用这样的插件之一,通过删除未使用的数据来保持数据库的清洁。

检查请求错误和断链

在开发网站时,我们会使用许多来自外部网站的第三方库和资源。因此,这些资源通过外部 URL 加载。有时,我们可能会注意到由于服务器不可用或其他错误,这些文件不可用。因此,某些文件的不可用可能会在我们的网站功能中造成冲突。此外,我们可能会链接到不再可用的内容,导致 404 错误。因此,我们需要跟踪这些问题,并解决它们,以保持我们网站功能的连贯性。

我们可以使用像断链检查器wordpress.org/plugins/broken-link-checker/))这样的插件来识别我们网站上的断链帖子、页面或屏幕。此外,如果您使用像Google Analytics这样的分析工具,我们可以检查对 404 页面的请求,并追踪回找到导致问题的链接。

我们在前面几节讨论了性能测量插件。这些插件会向您显示一个加载时间列表的已加载文件,以及来自外部源的错误加载文件列表。最常见的问题是外部源中 CSS 或 JavaScript 文件的不可用。因此,我们可以使用这样的插件来识别这些文件,并更新 URL,或者下载并在插件中本地包含这些文件。

检查错误日志

一旦在实时环境中关闭调试模式,我们就不会在该网站上看到任何生成的错误。许多第三方低质量插件会产生轻微和严重的错误。因此,我们需要有一个错误日志来定期检查网站上的错误,以及当您的网站上出现问题时,确定原因。托管提供商也在服务器上提供错误日志功能。然而,我们应该通过使用 WordPress 的核心错误日志功能,或者使用像错误日志监控器wordpress.org/plugins/error-log-monitor/))这样的插件来跟踪现有错误,并尽快修复它们。

这些是维护网站的一些主要任务。您可以找到更多维护网站的小任务和插件。作为一名开发者,您应该根据每个网站的功能制定适当的维护常规。

摘要

在网站开发中,我们主要关注功能方面,因为客户或最终用户将直接参与功能。然而,我们必须给予非功能方面同等的重要性。这些非功能方面可能决定网站的成功或失败,因为这些因素是使用或离开网站的关键。

我们从理解测试的重要性开始本章。我们借助P3 插件分析器等工具和PluginTest等服务,探讨了多种功能性测试和非功能性测试。然后,我们在确定处理高级安全措施的插件时,讨论了实施关键安全预防措施的必要性。接下来,我们探讨了使用All-in-One WP Migration 插件自动化网站迁移过程的重要性。最后,我们在探索现有基于插件的维护解决方案的同时,审视了正确维护 WordPress 网站的重要任务。

我们这本书的开篇是向您介绍 WordPress 的核心功能、概念和数据使用。然后,我们通过使用核心功能、扩展核心功能以及构建定制解决方案来开发各种功能。最后,我们通过审视网站的重要非功能性需求来完成这本书。现在,这取决于你了!

posted @ 2025-09-09 11:30  绝不原创的飞龙  阅读(2)  评论(0)    收藏  举报