キャッシュ(リクエスト内) */
private array $counts_cache = array();
public function __construct( ACSIL_Database $db ) {
$this->db = $db;
}
public function register_column_hooks(): void {
$settings = ACSIL_Core::get_settings();
if ( ! $settings['show_list_columns'] ) {
return;
}
$post_types = $settings['post_types'];
foreach ( $post_types as $post_type ) {
add_filter( "manage_{$post_type}_posts_columns", array( $this, 'add_columns' ) );
add_action( "manage_{$post_type}_posts_custom_column", array( $this, 'render_column' ), 10, 2 );
add_filter( "manage_edit-{$post_type}_sortable_columns", array( $this, 'make_sortable' ) );
}
add_action( 'pre_get_posts', array( $this, 'sort_query' ) );
}
// ── カラム追加 ────────────────────────────────────────────────────────
public function add_columns( array $columns ): array {
// 「タグ」の直後に挿入
$new = array();
foreach ( $columns as $key => $label ) {
$new[ $key ] = $label;
if ( 'tags' === $key || 'author' === $key ) {
$new['acsil_outgoing'] = '' . esc_html__( 'Outbound', 'acs-internal-links' ) . '';
$new['acsil_incoming'] = '' . esc_html__( 'Inbound', 'acs-internal-links' ) . '';
}
}
// 挿入されなかった場合は末尾に追加
if ( ! array_key_exists( 'acsil_outgoing', $new ) ) {
$new['acsil_outgoing'] = esc_html__( 'Outbound', 'acs-internal-links' );
$new['acsil_incoming'] = esc_html__( 'Inbound', 'acs-internal-links' );
}
return $new;
}
// ── カラム内容表示 ────────────────────────────────────────────────────
public function render_column( string $column, int $post_id ): void {
if ( ! in_array( $column, array( 'acsil_outgoing', 'acsil_incoming' ), true ) ) {
return;
}
// 一覧ページの全カウントを1回だけ取得してキャッシュ
if ( empty( $this->counts_cache ) ) {
$this->counts_cache = $this->db->get_all_link_counts();
}
$outgoing = $this->counts_cache[ $post_id ]['outgoing'] ?? 0;
$incoming = $this->counts_cache[ $post_id ]['incoming'] ?? 0;
if ( 'acsil_outgoing' === $column ) {
$class = 0 === $outgoing ? 'acsil-count-zero-gray' : '';
printf( '%d', esc_attr( $class ), (int) $outgoing );
} elseif ( 'acsil_incoming' === $column ) {
$class = 0 === $incoming ? 'acsil-count-zero-red' : '';
printf( '%d', esc_attr( $class ), (int) $incoming );
}
}
// ── ソート対応 ────────────────────────────────────────────────────────
public function make_sortable( array $columns ): array {
$columns['acsil_outgoing'] = 'acsil_outgoing';
$columns['acsil_incoming'] = 'acsil_incoming';
return $columns;
}
public function sort_query( WP_Query $query ): void {
if ( ! is_admin() || ! $query->is_main_query() ) {
return;
}
$orderby = $query->get( 'orderby' );
if ( ! in_array( $orderby, array( 'acsil_outgoing', 'acsil_incoming' ), true ) ) {
return;
}
global $wpdb;
$table = $wpdb->prefix . 'acsil_links';
if ( 'acsil_outgoing' === $orderby ) {
$query->set( 'orderby', 'acsil_count' );
$query->set( 'meta_query', array() );
add_filter(
'posts_clauses',
function ( $clauses ) use ( $wpdb, $table, $query ) {
$order = strtoupper( $query->get( 'order' ) ) === 'ASC' ? 'ASC' : 'DESC';
$clauses['join'] .= " LEFT JOIN (SELECT source_post_id, COUNT(*) AS out_cnt FROM {$table} GROUP BY source_post_id) AS acsil_out ON acsil_out.source_post_id = {$wpdb->posts}.ID";
$clauses['orderby'] = "COALESCE(acsil_out.out_cnt, 0) {$order}";
return $clauses;
}
);
} elseif ( 'acsil_incoming' === $orderby ) {
add_filter(
'posts_clauses',
function ( $clauses ) use ( $wpdb, $table, $query ) {
$order = strtoupper( $query->get( 'order' ) ) === 'ASC' ? 'ASC' : 'DESC';
$clauses['join'] .= " LEFT JOIN (SELECT target_post_id, COUNT(*) AS in_cnt FROM {$table} GROUP BY target_post_id) AS acsil_in ON acsil_in.target_post_id = {$wpdb->posts}.ID";
$clauses['orderby'] = "COALESCE(acsil_in.in_cnt, 0) {$order}";
return $clauses;
}
);
}
}
}