WordPress本身添加自定义字段可以满足我们的需求,但是在管理方面确依然显得比较繁琐。本文提供为WordPress后台的分类法管理页面添加自定义列的方法将为我们便于管理和查看提供便利。
目录
第一步:添加你的自定义字段
添加自定义字段的具体方法这里不再赘述,详情请查看本笔记教程的“添加字段”篇章。
这里我们直接使用SCF插件(安全自定义字段)添加自定义字段,以下是实现代码:
/**核心说明:
* 配置:全局配置
* 面向:所有分类法中的所有分类
* 目标:创建统一的自定义字段
* 代码:由scf插件生成
* 依赖:scf插件 或 acf插件
* 多站:支持
* */
add_action( 'acf/include_fields', function() {
if ( ! function_exists( 'acf_add_local_field_group' ) ) {
return;
}
acf_add_local_field_group( array(
'key' => 'group_68ba527d082ce',
'title' => '全局配置-全部分类-自定义字段',
'fields' => array(
array(
'key' => 'field_68ba5327b56c3',
'label' => '封面大图',
'name' => 'zzw_term_img_pc',
'aria-label' => '',
'type' => 'image',
'instructions' => '可独立使用,也可用于PC端图片;字段值为图片ID,用于模板调取(字段名):zzw_term_img_pc',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => array(
'width' => '100',
'class' => 'zzw-term-img-pc',
'id' => 'zzw-term-img-pc',
),
'return_format' => 'id',
'library' => 'all',
'min_width' => '',
'min_height' => '',
'min_size' => '',
'max_width' => '',
'max_height' => '',
'max_size' => 1,
'mime_types' => '',
'allow_in_bindings' => 0,
'preview_size' => 'full',
),
array(
'key' => 'field_68ba53d0b56c4',
'label' => '封面小图',
'name' => 'zzw_term_img_m',
'aria-label' => '',
'type' => 'image',
'instructions' => '可独立使用,也可用于移动端图片;字段值为图片ID,用于模板调取(字段名):zzw_term_img_m',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => array(
'width' => '100',
'class' => 'zzw-term-img-m',
'id' => 'zzw-term-img-m',
),
'return_format' => 'id',
'library' => 'all',
'min_width' => '',
'min_height' => '',
'min_size' => '',
'max_width' => '',
'max_height' => '',
'max_size' => 1,
'mime_types' => '',
'allow_in_bindings' => 0,
'preview_size' => 'full',
),
array(
'key' => 'field_68ba592b97c07',
'label' => '显示到主站首页',
'name' => 'zzw_term_show_index',
'aria-label' => '',
'type' => 'checkbox',
'instructions' => '字段值为选项值,用于模板调取(字段名):zzw_term_show_index 。“三栏块”是主站首页中三栏部分中本站块部分中的推荐位置',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => array(
'width' => '100',
'class' => 'zzw-term-show-index',
'id' => 'zzw-term-show-index',
),
'choices' => array(
'indexfz' => '推至方阵块',
'indexjn' => '推至胶囊块',
'indexsl' => '推至三栏块',
),
'default_value' => array(
),
'return_format' => 'value',
'allow_custom' => 0,
'allow_in_bindings' => 0,
'layout' => 'horizontal',
'toggle' => 0,
'save_custom' => 0,
'custom_choice_button_text' => '添加新选择',
),
),
'location' => array(
array(
array(
'param' => 'taxonomy',
'operator' => '==',
'value' => 'all',
),
),
),
'menu_order' => 0,
'position' => 'acf_after_title',
'style' => 'default',
'label_placement' => 'top',
'instruction_placement' => 'label',
'hide_on_screen' => '',
'active' => true,
'description' => '全局配置-为全部分类法的全部分类添加统一自定义字段',
'show_in_rest' => 1,
) );
} );以上代码实现添加了3个自定义字段:
- PC图片 – 显示分类/标签的PC端图片
- 移动端图片 – 显示分类/标签的移动端图片
- 首页展示 – 显示该分类是否在首页特定区块中展示
第二步:添加自定义列
我们在添加自定义列时,还需要内容。空列是没有任何意义的。
我们以第一步添加的3个自定义字段举例,将这3个自定义字段添加到WordPress后台的分类法管理页面的3个自定义列中。
1. 列定义函数 zzw_add_custom_taxonomy_columns()
function zzw_add_custom_taxonomy_columns( $columns ) {
$new_columns = array();
// 在名称列之后添加我们的自定义列
foreach ( $columns as $key => $value ) {
$new_columns[$key] = $value;
if ( $key === 'name' ) {
$new_columns['zzw_term_img_pc'] = __( 'PC图片', 'textdomain' );
$new_columns['zzw_term_img_m'] = __( '移动端图片', 'textdomain' );
$new_columns['zzw_term_show_index'] = __( '首页展示', 'textdomain' );
}
}
return $new_columns;
}- 在”名称”列后插入三个新列
- 使用WordPress翻译函数
__()支持多语言
2. 列内容填充函数 zzw_fill_custom_taxonomy_columns()
function zzw_fill_custom_taxonomy_columns( $content, $column_name, $term_id ) {
switch ( $column_name ) {
case 'zzw_term_img_pc':
$image_id = get_term_meta( $term_id, 'zzw_term_img_pc', true );
if ( $image_id ) {
$url = wp_get_attachment_image_url($image_id,'full');
echo '<a href="'.esc_url( $url ).'" target="_blank">';
echo wp_get_attachment_image( $image_id, 'full', false, array( 'style' => 'max-width:100px;max-height:60px;width:auto !important;height:auto !important;object-fit: contain;' ) );
echo '</a>';
} else {
echo '—';
}
break;
case 'zzw_term_img_m':
$image_id = get_term_meta( $term_id, 'zzw_term_img_m', true );
if ( $image_id ) {
$url = wp_get_attachment_image_url($image_id,'full');
echo '<a href="'.esc_url( $url ).'" target="_blank">';
echo wp_get_attachment_image( $image_id, 'full', false, array( 'style' => 'max-width:100px;max-height:60px;width:100% !important;height:100% !important;object-fit: contain;' ) );
echo '</a>';
} else {
echo '—';
}
break;
case 'zzw_term_show_index':
$show_index = get_term_meta( $term_id, 'zzw_term_show_index', true );
if ( ! empty( $show_index ) ) {
$options = array();
if ( in_array( 'indexfz', $show_index ) ) {
$options[] = '方阵块';
}
if ( in_array( 'indexjn', $show_index ) ) {
$options[] = '胶囊块';
}
if ( in_array( 'indexsl', $show_index ) ) {
$options[] = '三栏块';
}
echo implode( ', ', $options );
} else {
echo '—';
}
break;
default:
break;
}
}- PC图片列:从术语元数据
zzw_term_img_pc获取图片ID并显示缩略图 - 移动端图片列:从术语元数据
zzw_term_img_m获取图片ID并显示缩略图 - 首页展示列:从术语元数据
zzw_term_show_index(数组)解析显示选项: indexfz→ “方阵块”indexjn→ “胶囊块”indexsl→ “三栏块”
3. 排序功能函数 zzw_make_custom_taxonomy_columns_sortable()
function zzw_make_custom_taxonomy_columns_sortable( $sortable_columns ) {
// 通常情况下,图片字段不适合排序,但复选框字段可以
$sortable_columns['zzw_term_show_index'] = 'zzw_term_show_index';
return $sortable_columns;
}- 只让”首页展示”列可排序(图片列通常不适合排序)
4. 主函数 zzw_paixu_term_show_index()
function zzw_paixu_term_show_index()
{
// 仅在管理后台执行
if (!is_admin()) {
return;
}
$args = array(
'public' => true,
//'hierarchical'=> false,
'_builtin' => false
);
$output = 'names'; // or objects
$operator = 'and'; // 'and' or 'or'
$taxs = get_taxonomies( $args, $output, $operator );
$taxs[] = 'category';
$taxs[] = 'post_tag';
foreach ($taxs as $key => $value) {
add_filter( 'manage_edit-'.$value.'_columns', 'zzw_add_custom_taxonomy_columns' );
add_action( 'manage_'.$value.'_custom_column', 'zzw_fill_custom_taxonomy_columns', 10, 3 );
add_filter( 'manage_edit-'.$value.'_sortable_columns', 'zzw_make_custom_taxonomy_columns_sortable' );
}
}
// 将执行时机推迟到 init 钩子
add_action( 'init', 'zzw_paixu_term_show_index', 20 ); // 优先级20确保其他插件已注册分类法- 只在后台运行(
is_admin()检查) - 获取所有公共分类法(包括自定义分类法)
- 为每个分类法添加必要的钩子:
add_filter()– 添加自定义列add_action()– 填充列内容add_filter()– 使列可排序
技术细节
图片显示方式
- 使用
wp_get_attachment_image_url()获取原图URL - 使用
wp_get_attachment_image()显示响应式缩略图 - 使用
get_term_meta()从术语元数据获取信息
分类法范围
- 自动应用到所有公开分类法
- 包括自定义分类法、文章分类(category)和标签(post_tag)
使用场景
这段代码可能用于:
- 电商网站 – 分类需要不同的PC/移动端展示图片
- 内容聚合网站 – 分类需要在首页不同区块展示
- 需要精细化分类管理的网站
测试说明
以上代码均有找找网测试并成功!但代码依然存在不足,主要体现在:
- 性能优化:图片列可能造成大量数据库查询,应考虑批量获取
- 代码结构:可以考虑使用类封装,避免全局函数污染
- 灵活性:可以通过过滤器让其他代码修改显示的列
- 可排序性:需要实现实际的排序逻辑(目前只是声明可排序)
改进升级版
<?php
/**
* 分类法管理列增强类
* 在分类法列表页面添加自定义列显示图片和首页展示设置
*/
class ZZW_Taxonomy_Columns_Manager {
/**
* 单例实例
*/
private static $instance = null;
/**
* 获取单例实例
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 构造函数
*/
private function __construct() {
$this->init_hooks();
}
/**
* 初始化钩子
*/
private function init_hooks() {
add_action( 'init', array( $this, 'setup_columns' ), 20 );
add_action( 'pre_get_terms', array( $this, 'handle_sorting' ) );
}
/**
* 设置列
*/
public function setup_columns() {
// 仅在管理后台执行
if ( ! is_admin() ) {
return;
}
$taxonomies = $this->get_supported_taxonomies();
foreach ( $taxonomies as $taxonomy ) {
// 允许其他插件修改支持的分类法
$taxonomy = apply_filters( 'zzw_taxonomy_columns_taxonomy', $taxonomy );
add_filter( "manage_edit-{$taxonomy}_columns", array( $this, 'add_columns' ) );
add_action( "manage_{$taxonomy}_custom_column", array( $this, 'fill_columns' ), 10, 3 );
add_filter( "manage_edit-{$taxonomy}_sortable_columns", array( $this, 'make_columns_sortable' ) );
}
}
/**
* 获取支持的所有分类法
*/
private function get_supported_taxonomies() {
$args = array(
'public' => true,
'_builtin' => false
);
$taxonomies = get_taxonomies( $args, 'names' );
$taxonomies[] = 'category';
$taxonomies[] = 'post_tag';
// 允许其他插件添加或移除分类法
return apply_filters( 'zzw_taxonomy_columns_supported_taxonomies', $taxonomies );
}
/**
* 添加自定义列
*/
public function add_columns( $columns ) {
// 获取列配置
$custom_columns = $this->get_custom_columns_config();
$new_columns = array();
// 在名称列之后添加自定义列
foreach ( $columns as $key => $value ) {
$new_columns[ $key ] = $value;
if ( 'name' === $key ) {
foreach ( $custom_columns as $column_key => $column_config ) {
$new_columns[ $column_key ] = $column_config['label'];
}
}
}
// 允许其他插件修改列结构
return apply_filters( 'zzw_taxonomy_columns_structure', $new_columns, $columns );
}
/**
* 获取自定义列配置
*/
private function get_custom_columns_config() {
$columns = array(
'zzw_term_img_pc' => array(
'label' => __( 'PC图片', 'zzw-taxonomy-columns' ),
'type' => 'image',
'meta_key' => 'zzw_term_img_pc',
'sortable' => false,
'image_style' => 'max-width:100px;max-height:60px;width:auto !important;height:auto !important;object-fit: contain;'
),
'zzw_term_img_m' => array(
'label' => __( '移动端图片', 'zzw-taxonomy-columns' ),
'type' => 'image',
'meta_key' => 'zzw_term_img_m',
'sortable' => false,
'image_style' => 'max-width:100px;max-height:60px;width:100% !important;height:100% !important;object-fit: contain;'
),
'zzw_term_show_index' => array(
'label' => __( '首页展示', 'zzw-taxonomy-columns' ),
'type' => 'checkbox_group',
'meta_key' => 'zzw_term_show_index',
'sortable' => true,
'options' => array(
'indexfz' => '方阵块',
'indexjn' => '胶囊块',
'indexsl' => '三栏块'
)
)
);
// 允许其他插件修改列配置
return apply_filters( 'zzw_taxonomy_columns_config', $columns );
}
/**
* 填充列内容
*/
public function fill_columns( $content, $column_name, $term_id ) {
// 获取当前页面所有术语的ID用于批量查询
static $term_ids = null;
static $cached_meta = array();
// 首次运行时收集术语ID
if ( null === $term_ids ) {
$term_ids = $this->get_current_page_term_ids();
if ( ! empty( $term_ids ) ) {
// 批量查询所有需要的数据
$cached_meta = $this->batch_get_term_meta( $term_ids );
}
}
// 从缓存获取数据
$column_config = $this->get_column_config( $column_name );
if ( ! $column_config ) {
return $content;
}
$meta_key = $column_config['meta_key'];
$value = isset( $cached_meta[ $term_id ][ $meta_key ] ) ? $cached_meta[ $term_id ][ $meta_key ] : get_term_meta( $term_id, $meta_key, true );
// 根据列类型输出内容
echo $this->render_column_content( $column_name, $value, $column_config );
return $content;
}
/**
* 获取当前页面所有术语ID
*/
private function get_current_page_term_ids() {
global $wp_query;
$term_ids = array();
if ( isset( $wp_query->terms ) && is_array( $wp_query->terms ) ) {
foreach ( $wp_query->terms as $term ) {
if ( is_object( $term ) && isset( $term->term_id ) ) {
$term_ids[] = $term->term_id;
}
}
}
return $term_ids;
}
/**
* 批量获取术语元数据
*/
private function batch_get_term_meta( $term_ids ) {
global $wpdb;
if ( empty( $term_ids ) ) {
return array();
}
$term_ids_str = implode( ',', array_map( 'absint', $term_ids ) );
// 获取所有支持的meta_key
$column_configs = $this->get_custom_columns_config();
$meta_keys = array();
foreach ( $column_configs as $config ) {
$meta_keys[] = $config['meta_key'];
}
if ( empty( $meta_keys ) ) {
return array();
}
$meta_keys_str = implode( "','", array_map( 'esc_sql', $meta_keys ) );
// 批量查询所有meta数据
$query = $wpdb->prepare(
"SELECT term_id, meta_key, meta_value
FROM {$wpdb->termmeta}
WHERE term_id IN ($term_ids_str)
AND meta_key IN ('%s')",
str_replace( "','", "', '", $meta_keys_str )
);
$results = $wpdb->get_results( $query );
$cached_meta = array();
// 组织数据格式
foreach ( $results as $row ) {
if ( ! isset( $cached_meta[ $row->term_id ] ) ) {
$cached_meta[ $row->term_id ] = array();
}
// 反序列化数组数据
$meta_value = maybe_unserialize( $row->meta_value );
$cached_meta[ $row->term_id ][ $row->meta_key ] = $meta_value;
}
return $cached_meta;
}
/**
* 获取列配置
*/
private function get_column_config( $column_name ) {
$columns = $this->get_custom_columns_config();
return isset( $columns[ $column_name ] ) ? $columns[ $column_name ] : false;
}
/**
* 渲染列内容
*/
private function render_column_content( $column_name, $value, $config ) {
$output = '';
switch ( $config['type'] ) {
case 'image':
if ( $value ) {
$url = wp_get_attachment_image_url( $value, 'full' );
$image = wp_get_attachment_image(
$value,
'thumbnail',
false,
array( 'style' => $config['image_style'] )
);
$output = sprintf(
'<a href="%s" target="_blank">%s</a>',
esc_url( $url ),
$image
);
} else {
$output = '—';
}
break;
case 'checkbox_group':
if ( ! empty( $value ) && is_array( $value ) ) {
$options = array();
foreach ( $config['options'] as $key => $label ) {
if ( in_array( $key, $value ) ) {
$options[] = $label;
}
}
$output = implode( ', ', $options );
} else {
$output = '—';
}
break;
default:
// 允许其他类型通过过滤器输出
$output = apply_filters(
'zzw_taxonomy_column_content_' . $column_name,
$value,
$config
);
break;
}
return apply_filters( 'zzw_taxonomy_column_output', $output, $column_name, $value, $config );
}
/**
* 使列可排序
*/
public function make_columns_sortable( $sortable_columns ) {
$columns = $this->get_custom_columns_config();
foreach ( $columns as $column_key => $config ) {
if ( $config['sortable'] ) {
$sortable_columns[ $column_key ] = $column_key;
}
}
return apply_filters( 'zzw_taxonomy_sortable_columns', $sortable_columns );
}
/**
* 处理排序逻辑
*/
public function handle_sorting( $query ) {
if ( ! is_admin() || ! $query->is_main_query() ) {
return;
}
$orderby = $query->get( 'orderby' );
// 只处理我们的自定义字段排序
$columns = $this->get_custom_columns_config();
$custom_sortable_columns = array();
foreach ( $columns as $column_key => $config ) {
if ( $config['sortable'] ) {
$custom_sortable_columns[] = $column_key;
}
}
if ( ! in_array( $orderby, $custom_sortable_columns ) ) {
return;
}
// 获取对应的meta_key
$config = $this->get_column_config( $orderby );
if ( ! $config ) {
return;
}
$meta_key = $config['meta_key'];
// 设置元查询用于排序
$meta_query = array(
'relation' => 'OR',
array(
'key' => $meta_key,
'compare' => 'EXISTS'
),
array(
'key' => $meta_key,
'compare' => 'NOT EXISTS',
'value' => '' // WordPress需要value参数
)
);
$query->set( 'meta_query', $meta_query );
$query->set( 'orderby', 'meta_value' );
$query->set( 'meta_key', $meta_key );
}
/**
* 防止克隆
*/
private function __clone() {}
/**
* 防止反序列化
*/
private function __wakeup() {}
}
// 初始化类
add_action( 'plugins_loaded', function() {
ZZW_Taxonomy_Columns_Manager::get_instance();
} );
// 提供便捷的访问函数(可选)
if ( ! function_exists( 'zzw_taxonomy_columns' ) ) {
function zzw_taxonomy_columns() {
return ZZW_Taxonomy_Columns_Manager::get_instance();
}
}主要改进说明:
1. 性能优化 – 批量查询
- 使用
batch_get_term_meta()方法批量获取所有术语的元数据 - 在
fill_columns()中通过静态变量缓存数据,减少数据库查询次数 - 使用
get_current_page_term_ids()获取当前页面所有术语ID
2. 代码结构 – 类封装
- 使用单例模式确保只有一个实例
- 私有化构造函数、克隆和反序列化方法
- 将功能分解为多个私有方法,提高可读性和维护性
3. 灵活性 – 过滤器支持
- 添加了多个过滤器:
zzw_taxonomy_columns_taxonomy– 修改单个分类法zzw_taxonomy_columns_supported_taxonomies– 修改支持的所有分类法zzw_taxonomy_columns_config– 修改列配置zzw_taxonomy_columns_structure– 修改列结构zzw_taxonomy_column_content_{$column_name}– 自定义列内容渲染zzw_taxonomy_column_output– 修改列输出zzw_taxonomy_sortable_columns– 修改可排序列
4. 可排序性 – 实际排序逻辑
- 实现了
handle_sorting()方法处理排序 - 添加了
pre_get_terms钩子 - 为可排序列设置了正确的
meta_query和orderby参数
5. 额外改进
- 配置化设计:列配置集中管理,便于修改
- 类型化列:支持图像、复选框组等不同类型的列
- 错误处理:增加了各种空值和错误检查
- 代码注释:添加了详细的PHPDoc注释
- 缓存机制:使用静态变量缓存数据
- 便捷访问函数:提供了
zzw_taxonomy_columns()便捷函数
使用示例:
// 添加过滤器来自定义列配置
add_filter( 'zzw_taxonomy_columns_config', function( $columns ) {
// 添加新列
$columns['zzw_term_custom'] = array(
'label' => __( '自定义列', 'my-theme' ),
'type' => 'text',
'meta_key' => 'zzw_term_custom',
'sortable' => true
);
// 修改现有列的标签
$columns['zzw_term_show_index']['label'] = __( '首页显示设置', 'my-theme' );
return $columns;
} );
// 自定义列内容渲染
add_filter( 'zzw_taxonomy_column_content_zzw_term_custom', function( $value, $config ) {
return ! empty( $value ) ? esc_html( $value ) : '—';
}, 10, 2 );这个升级版本保持了原有功能,同时提高了性能、可维护性和扩展性。
升级兼容版
<?php
/**
* 分类法管理列增强类
* 在分类法列表页面添加自定义列显示图片和首页展示设置
*
* @version 2.0.0
* @package zzw_Taxonomy_Columns
* @license GPL v2 or later
*/
// 防止直接访问
defined( 'ABSPATH' ) || exit;
/**
* 分类法列增强管理器
*/
class zzw_Taxonomy_Columns_Manager {
/**
* 单例实例
*
* @var zzw_Taxonomy_Columns_Manager|null
*/
private static ?zzw_Taxonomy_Columns_Manager $instance = null;
/**
* 缓存的分类法数据
*
* @var array<string, mixed>
*/
private array $cached_data = [];
/**
* 获取单例实例
*
* @return zzw_Taxonomy_Columns_Manager
*/
public static function get_instance(): zzw_Taxonomy_Columns_Manager {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 构造函数
*/
private function __construct() {
$this->init_hooks();
}
/**
* 初始化钩子
*/
private function init_hooks(): void {
add_action( 'init', [ $this, 'setup_columns' ], 20 );
add_action( 'pre_get_terms', [ $this, 'handle_sorting' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_styles' ] );
}
/**
* 设置列
*/
public function setup_columns(): void {
// 仅在管理后台执行
if ( ! is_admin() || wp_doing_ajax() ) {
return;
}
$taxonomies = $this->get_supported_taxonomies();
foreach ( $taxonomies as $taxonomy ) {
// 允许其他插件修改支持的分类法
$taxonomy = apply_filters( 'zzw_taxonomy_columns_taxonomy', $taxonomy );
if ( ! is_string( $taxonomy ) || ! taxonomy_exists( $taxonomy ) ) {
continue;
}
add_filter( "manage_edit-{$taxonomy}_columns", [ $this, 'add_columns' ] );
add_action( "manage_{$taxonomy}_custom_column", [ $this, 'fill_columns' ], 10, 3 );
add_filter( "manage_edit-{$taxonomy}_sortable_columns", [ $this, 'make_columns_sortable' ] );
}
}
/**
* 获取支持的所有分类法
*
* @return array<string>
*/
private function get_supported_taxonomies(): array {
$args = [
'public' => true,
'_builtin' => false,
'show_ui' => true, // 只显示有管理界面的分类法
];
$taxonomies = get_taxonomies( $args, 'names' );
$builtin_taxonomies = [ 'category', 'post_tag' ];
foreach ( $builtin_taxonomies as $taxonomy ) {
if ( taxonomy_exists( $taxonomy ) ) {
$taxonomies[] = $taxonomy;
}
}
// 去重并重新索引
$taxonomies = array_values( array_unique( $taxonomies ) );
// 允许其他插件添加或移除分类法
return apply_filters( 'zzw_taxonomy_columns_supported_taxonomies', $taxonomies );
}
/**
* 添加自定义列
*
* @param array<string, string> $columns 现有列
* @return array<string, string>
*/
public function add_columns( array $columns ): array {
// 获取列配置
$custom_columns = $this->get_custom_columns_config();
$new_columns = [];
// 在名称列之后添加自定义列
foreach ( $columns as $key => $value ) {
$new_columns[ $key ] = $value;
if ( 'name' === $key ) {
foreach ( $custom_columns as $column_key => $column_config ) {
$new_columns[ $column_key ] = $column_config['label'] ?? '';
}
}
}
// 如果名称列不存在,添加到末尾
if ( ! isset( $columns['name'] ) ) {
foreach ( $custom_columns as $column_key => $column_config ) {
$new_columns[ $column_key ] = $column_config['label'] ?? '';
}
}
// 允许其他插件修改列结构
return apply_filters( 'zzw_taxonomy_columns_structure', $new_columns, $columns );
}
/**
* 获取自定义列配置
*
* @return array<string, array{
* label: string,
* type: string,
* meta_key: string,
* sortable: bool,
* image_style?: string,
* options?: array<string, string>
* }>
*/
private function get_custom_columns_config(): array {
$columns = [
'zzw_term_img_pc' => [
'label' => esc_html__( 'PC图片', 'zzw-taxonomy-columns' ),
'type' => 'image',
'meta_key' => 'zzw_term_img_pc',
'sortable' => false,
'image_style' => 'max-width:100px;max-height:60px;width:auto;height:auto;object-fit:contain;'
],
'zzw_term_img_m' => [
'label' => esc_html__( '移动端图片', 'zzw-taxonomy-columns' ),
'type' => 'image',
'meta_key' => 'zzw_term_img_m',
'sortable' => false,
'image_style' => 'max-width:100px;max-height:60px;width:100%;height:100%;object-fit:contain;'
],
'zzw_term_show_index' => [
'label' => esc_html__( '首页展示', 'zzw-taxonomy-columns' ),
'type' => 'checkbox_group',
'meta_key' => 'zzw_term_show_index',
'sortable' => true,
'options' => [
'indexfz' => esc_html__( '方阵块', 'zzw-taxonomy-columns' ),
'indexjn' => esc_html__( '胶囊块', 'zzw-taxonomy-columns' ),
'indexsl' => esc_html__( '三栏块', 'zzw-taxonomy-columns' )
]
]
];
// 允许其他插件修改列配置
return apply_filters( 'zzw_taxonomy_columns_config', $columns );
}
/**
* 填充列内容
*
* @param string $content 原始内容
* @param string $column_name 列名称
* @param int $term_id 术语ID
* @return void
*/
public function fill_columns( string $content, string $column_name, int $term_id ): void {
$column_config = $this->get_column_config( $column_name );
if ( ! $column_config ) {
echo $content;
return;
}
// 批量获取当前页面所有术语的数据
if ( ! isset( $this->cached_data['batch_meta'] ) ) {
$term_ids = $this->get_current_page_term_ids();
if ( ! empty( $term_ids ) ) {
$this->cached_data['batch_meta'] = $this->batch_get_term_meta( $term_ids );
} else {
$this->cached_data['batch_meta'] = [];
}
}
$meta_key = $column_config['meta_key'];
$value = $this->cached_data['batch_meta'][ $term_id ][ $meta_key ] ?? get_term_meta( $term_id, $meta_key, true );
// 根据列类型输出内容
echo $this->render_column_content( $column_name, $value, $column_config );
}
/**
* 获取当前页面所有术语ID
*
* @return array<int>
*/
private function get_current_page_term_ids(): array {
global $wp_query;
$term_ids = [];
if ( isset( $wp_query->terms ) && is_iterable( $wp_query->terms ) ) {
foreach ( $wp_query->terms as $term ) {
if ( is_object( $term ) && isset( $term->term_id ) && is_numeric( $term->term_id ) ) {
$term_ids[] = (int) $term->term_id;
}
}
}
return array_unique( $term_ids );
}
/**
* 批量获取术语元数据
*
* @param array<int> $term_ids 术语ID数组
* @return array<int, array<string, mixed>>
*/
private function batch_get_term_meta( array $term_ids ): array {
global $wpdb;
if ( empty( $term_ids ) ) {
return [];
}
// 清理并验证术语ID
$term_ids = array_map( 'absint', $term_ids );
$term_ids = array_filter( $term_ids );
if ( empty( $term_ids ) ) {
return [];
}
// 获取所有支持的meta_key
$column_configs = $this->get_custom_columns_config();
$meta_keys = [];
foreach ( $column_configs as $config ) {
if ( isset( $config['meta_key'] ) && is_string( $config['meta_key'] ) ) {
$meta_keys[] = $config['meta_key'];
}
}
if ( empty( $meta_keys ) ) {
return [];
}
// 准备查询参数
$term_ids_placeholders = implode( ',', array_fill( 0, count( $term_ids ), '%d' ) );
$meta_keys_placeholders = implode( ',', array_fill( 0, count( $meta_keys ), '%s' ) );
// 构建查询参数数组
$query_params = array_merge( $term_ids, $meta_keys );
// 使用prepare安全查询
$query = $wpdb->prepare(
"SELECT term_id, meta_key, meta_value
FROM {$wpdb->termmeta}
WHERE term_id IN ({$term_ids_placeholders})
AND meta_key IN ({$meta_keys_placeholders})",
...$query_params
);
$results = $wpdb->get_results( $query, ARRAY_A );
$cached_meta = [];
// 组织数据格式
foreach ( $results as $row ) {
$term_id = (int) $row['term_id'];
$meta_key = (string) $row['meta_key'];
if ( ! isset( $cached_meta[ $term_id ] ) ) {
$cached_meta[ $term_id ] = [];
}
// 反序列化数组数据
$meta_value = maybe_unserialize( $row['meta_value'] );
$cached_meta[ $term_id ][ $meta_key ] = $meta_value;
}
return $cached_meta;
}
/**
* 获取列配置
*
* @param string $column_name 列名称
* @return array|null 列配置或null
*/
private function get_column_config( string $column_name ): ?array {
$columns = $this->get_custom_columns_config();
return $columns[ $column_name ] ?? null;
}
/**
* 渲染列内容
*
* @param string $column_name 列名称
* @param mixed $value 值
* @param array $config 列配置
* @return string
*/
private function render_column_content( string $column_name, $value, array $config ): string {
$output = '';
switch ( $config['type'] ?? '' ) {
case 'image':
$image_id = is_numeric( $value ) ? (int) $value : 0;
if ( $image_id > 0 ) {
$url = wp_get_attachment_image_url( $image_id, 'full' );
$image = wp_get_attachment_image(
$image_id,
[ 100, 60 ],
false,
[
'style' => $config['image_style'] ?? '',
'loading' => 'lazy',
'decoding' => 'async'
]
);
if ( $url && $image ) {
$output = sprintf(
'<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>',
esc_url( $url ),
$image
);
}
}
break;
case 'checkbox_group':
if ( is_array( $value ) && ! empty( $value ) ) {
$options = [];
$available_options = $config['options'] ?? [];
foreach ( $available_options as $key => $label ) {
if ( in_array( $key, $value, true ) ) {
$options[] = esc_html( $label );
}
}
if ( ! empty( $options ) ) {
$output = implode( ', ', $options );
}
}
break;
default:
// 允许其他类型通过过滤器输出
$output = apply_filters(
'zzw_taxonomy_column_content_' . sanitize_key( $column_name ),
$value,
$config
);
break;
}
// 如果输出为空,显示破折号
if ( empty( $output ) ) {
$output = '—';
}
return apply_filters( 'zzw_taxonomy_column_output', $output, $column_name, $value, $config );
}
/**
* 使列可排序
*
* @param array<string, string> $sortable_columns
* @return array<string, string>
*/
public function make_columns_sortable( array $sortable_columns ): array {
$columns = $this->get_custom_columns_config();
foreach ( $columns as $column_key => $config ) {
if ( $config['sortable'] ?? false ) {
$sortable_columns[ $column_key ] = $column_key;
}
}
return apply_filters( 'zzw_taxonomy_sortable_columns', $sortable_columns );
}
/**
* 处理排序逻辑
*
* @param WP_Term_Query $query 术语查询对象
*/
public function handle_sorting( WP_Term_Query $query ): void {
if ( ! is_admin() || ! $query->is_main_query() ) {
return;
}
$orderby = $query->get( 'orderby' );
if ( ! is_string( $orderby ) ) {
return;
}
// 只处理我们的自定义字段排序
$columns = $this->get_custom_columns_config();
$custom_sortable_columns = [];
foreach ( $columns as $column_key => $config ) {
if ( $config['sortable'] ?? false ) {
$custom_sortable_columns[] = $column_key;
}
}
if ( ! in_array( $orderby, $custom_sortable_columns, true ) ) {
return;
}
// 获取对应的meta_key
$config = $this->get_column_config( $orderby );
if ( ! $config ) {
return;
}
$meta_key = $config['meta_key'] ?? '';
if ( empty( $meta_key ) ) {
return;
}
// 获取排序方向
$order = strtoupper( $query->get( 'order', 'ASC' ) );
$order = in_array( $order, [ 'ASC', 'DESC' ], true ) ? $order : 'ASC';
// 设置元查询用于排序
$meta_query = [
'relation' => 'OR',
[
'key' => $meta_key,
'compare' => 'EXISTS'
],
[
'key' => $meta_key,
'compare' => 'NOT EXISTS',
'value' => '0' // WordPress需要value参数
]
];
$query->set( 'meta_query', $meta_query );
// 根据字段类型设置合适的排序方式
if ( 'checkbox_group' === ( $config['type'] ?? '' ) ) {
// 对于数组字段,按是否有值排序
$query->set( 'orderby', 'meta_value' );
} else {
$query->set( 'orderby', 'meta_value' );
}
$query->set( 'meta_key', $meta_key );
$query->set( 'order', $order );
}
/**
* 加载管理样式
*/
public function enqueue_admin_styles(): void {
$screen = get_current_screen();
if ( ! $screen || 'edit-tags' !== $screen->base ) {
return;
}
$taxonomies = $this->get_supported_taxonomies();
$current_taxonomy = $screen->taxonomy ?? '';
if ( in_array( $current_taxonomy, $taxonomies, true ) ) {
wp_add_inline_style( 'common', '
.column-zzw_term_img_pc,
.column-zzw_term_img_m {
width: 120px;
text-align: center;
}
.column-zzw_term_img_pc img,
.column-zzw_term_img_m img {
max-width: 100px;
max-height: 60px;
border-radius: 3px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.column-zzw_term_show_index {
width: 150px;
}
@media screen and (max-width: 782px) {
.column-zzw_term_img_pc,
.column-zzw_term_img_m,
.column-zzw_term_show_index {
display: none;
}
}
' );
}
}
/**
* 防止克隆
*/
private function __clone() {}
/**
* 防止反序列化
*/
public function __wakeup() {
throw new Exception( 'Cannot unserialize singleton' );
}
}
// 安全初始化
add_action( 'plugins_loaded', function() {
// 检查必要条件
if ( ! function_exists( 'add_action' ) ) {
return;
}
try {
zzw_Taxonomy_Columns_Manager::get_instance();
} catch ( Exception $e ) {
// 记录错误但不中断执行
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( 'zzw Taxonomy Columns Error: ' . $e->getMessage() );
}
}
} );
// 提供便捷的访问函数(可选)
if ( ! function_exists( 'zzw_taxonomy_columns' ) ) {
/**
* 获取分类法列管理器实例
*
* @return zzw_Taxonomy_Columns_Manager
*/
function zzw_taxonomy_columns(): zzw_Taxonomy_Columns_Manager {
return zzw_Taxonomy_Columns_Manager::get_instance();
}
}PHP 7.4-8.3兼容性升级要点:
1. 类型声明升级
- 使用PHP 7.4的属性类型声明:
private array $cached_data = [] - 使用PHP 8.0的联合类型:
private static ?zzw_Taxonomy_Columns_Manager $instance = null - 添加方法参数和返回类型声明:
: void,: array,: string - 使用PHP 8.0的
array<int, array{...}>文档注释格式
2. PHP 8.x特性适配
- 使用
?:运算符提供默认值:$config['type'] ?? '' - 使用
??=进行安全的默认值设置 - 添加严格比较:
===代替== - 使用
in_array($needle, $haystack, true)严格模式 - 使用
array_filter($array)不回调时自动过滤空值
3. 安全增强
- 添加
esc_html__()和esc_html()进行转义 - 使用
sanitize_key()清理键名 - 添加
rel="noopener noreferrer"防止安全漏洞 - 对SQL查询使用安全的预处理语句
- 验证所有外部输入和函数返回值
4. 错误处理改进
- 添加异常处理机制
- 使用
try...catch包裹初始化 - 添加WP_DEBUG日志记录
- 验证分类法存在性:
taxonomy_exists() - 验证屏幕类型和权限
5. 性能优化
- 使用
array_fill()创建占位符数组 - 使用展开运算符
...$query_params传递参数 - 添加图片懒加载:
loading="lazy" - 添加异步解码:
decoding="async" - 批量查询时清理和验证数据
6. 现代PHP特性
- 使用
is_iterable()检查可遍历对象 - 使用
is_numeric()和类型转换进行验证 - 使用
array_map()和array_filter()链式处理 - 使用
array_unique()去重 - 使用
strtoupper()和in_array()验证排序方向
7. WordPress最佳实践
- 使用
wp_add_inline_style()添加响应式样式 - 添加媒体查询隐藏小屏幕上的列
- 使用
ARRAY_A常量获取关联数组结果 - 添加适当的钩子和过滤器文档
- 遵循WordPress编码标准
8. 向后兼容性保持
- 保持原有函数名称和钩子
- 使用条件检查确保函数不存在
- 提供相同的过滤器接口
- 保持原有的列结构
- 使用兼容的查询方法
这个升级版本完全兼容PHP 7.4到8.3,同时利用了现代PHP特性提高代码质量和性能。
注:以上两个升级版均由AI辅助完成,未测试,请慎用!
