Files
WP-Multi-Wiki/includes/class-wmw-search.php
2026-03-18 21:56:45 +01:00

169 lines
5.9 KiB
PHP

<?php
if ( ! defined( 'ABSPATH' ) ) exit;
class WMW_Search {
public function init() {
add_action( 'save_post_wmw_article', array( $this, 'index_article' ), 10, 2 );
add_action( 'delete_post', array( $this, 'remove_from_index' ) );
}
/**
* Einen Artikel in den Suchindex schreiben.
*/
public function index_article( $post_id, $post ) {
global $wpdb;
if ( $post->post_status !== 'publish' ) return;
$wiki_id = (int) get_post_meta( $post_id, '_wmw_wiki_id', true );
$keywords = wp_strip_all_tags( $post->post_title . ' ' . $post->post_content . ' ' . $post->post_excerpt );
$wpdb->replace(
$wpdb->prefix . 'wmw_search_index',
array(
'article_id' => $post_id,
'wiki_id' => $wiki_id,
'keywords' => $keywords,
),
array( '%d', '%d', '%s' )
);
}
/**
* Artikel aus dem Index entfernen.
*/
public function remove_from_index( $post_id ) {
global $wpdb;
$wpdb->delete( $wpdb->prefix . 'wmw_search_index', array( 'article_id' => $post_id ), array( '%d' ) );
}
/**
* Alle vorhandenen Artikel in den Index aufnehmen.
* Wird aufgerufen wenn der Index leer ist (z.B. nach Gitea-Import).
*/
public static function reindex_all() {
global $wpdb;
$articles = get_posts( array(
'post_type' => 'wmw_article',
'post_status' => 'publish',
'posts_per_page' => -1,
) );
foreach ( $articles as $post ) {
$wiki_id = (int) get_post_meta( $post->ID, '_wmw_wiki_id', true );
$keywords = wp_strip_all_tags( $post->post_title . ' ' . $post->post_content . ' ' . $post->post_excerpt );
$wpdb->replace(
$wpdb->prefix . 'wmw_search_index',
array(
'article_id' => $post->ID,
'wiki_id' => $wiki_id,
'keywords' => $keywords,
),
array( '%d', '%d', '%s' )
);
}
return count( $articles );
}
/**
* Artikel suchen.
* 1. Versucht den schnellen Custom-Index.
* 2. Fällt auf WP_Query zurück, wenn der Index leer ist (z.B. nach Gitea-Import).
* 3. Baut den Index automatisch auf, wenn er leer war.
*/
public static function search( $query, $wiki_id = 0, $limit = 20 ) {
global $wpdb;
$table = $wpdb->prefix . 'wmw_search_index';
$like = '%' . $wpdb->esc_like( $query ) . '%';
// ── Prüfen ob der Index überhaupt Einträge hat ────────────────────
$index_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $table" );
$ids = array();
if ( $index_count > 0 ) {
// ── Custom-Index nutzen ───────────────────────────────────────
if ( $wiki_id ) {
$rows = $wpdb->get_results( $wpdb->prepare(
"SELECT article_id FROM $table WHERE wiki_id = %d AND keywords LIKE %s LIMIT %d",
$wiki_id, $like, $limit
) );
} else {
$rows = $wpdb->get_results( $wpdb->prepare(
"SELECT article_id FROM $table WHERE keywords LIKE %s LIMIT %d",
$like, $limit
) );
}
$ids = wp_list_pluck( $rows, 'article_id' );
}
// ── Fallback: WP_Query (wenn Index leer oder keine Treffer) ───────
if ( empty( $ids ) ) {
$args = array(
'post_type' => 'wmw_article',
'post_status' => 'publish',
'posts_per_page' => $limit,
's' => $query,
'orderby' => 'relevance',
);
if ( $wiki_id ) {
$args['meta_query'] = array(
array(
'key' => '_wmw_wiki_id',
'value' => $wiki_id,
),
);
}
$fallback = get_posts( $args );
// Index war leer → jetzt automatisch aufbauen (im Hintergrund)
if ( $index_count === 0 ) {
self::reindex_all();
}
foreach ( $fallback as &$post ) {
$excerpt = $post->post_excerpt ?: wp_trim_words( $post->post_content, 25 );
$post->wmw_excerpt = self::highlight( $excerpt, $query );
$post->wmw_title = self::highlight( $post->post_title, $query );
}
return $fallback;
}
// ── Artikel-Objekte aus IDs laden ─────────────────────────────────
$posts = get_posts( array(
'post_type' => 'wmw_article',
'post_status' => 'publish',
'post__in' => $ids,
'posts_per_page' => $limit,
'orderby' => 'post_title',
'order' => 'ASC',
) );
foreach ( $posts as &$post ) {
$excerpt = $post->post_excerpt ?: wp_trim_words( $post->post_content, 25 );
$post->wmw_excerpt = self::highlight( $excerpt, $query );
$post->wmw_title = self::highlight( $post->post_title, $query );
}
return $posts;
}
/**
* Suchtreffer hervorheben.
*/
public static function highlight( $text, $query ) {
$words = explode( ' ', preg_quote( trim( $query ), '/' ) );
foreach ( $words as $word ) {
if ( strlen( $word ) < 2 ) continue;
$text = preg_replace( '/(' . $word . ')/iu', '<mark class="wmw-highlight">$1</mark>', $text );
}
return $text;
}
}