logger = $logger; $this->cache = $cache; add_action( 'wp_enqueue_scripts', [ $this, 'register_frontend_assets' ] ); } public function register_frontend_assets() { wp_register_style( 'ait-airce-frontend', AIT_AIRCE_URL . 'assets/css/frontend.css', [], AIT_AIRCE_VERSION ); } protected function ensure_frontend_assets() { if ( ! wp_style_is( 'ait-airce-frontend', 'enqueued' ) ) { wp_enqueue_style( 'ait-airce-frontend' ); } } protected function post_has_index( $post_id ) { global $wpdb; $t_index = esc_sql( AIT_AIRCE_DB::table( 'index_item' ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from trusted source $found = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$t_index} WHERE post_id = %d AND status = 'active' LIMIT 1", (int) $post_id ) ); return ! empty( $found ); } /** * Render related content for a post. * * @param int $post_id * @param array $args * @return string */ public function render( $post_id, $args = [] ) { $post_id = intval( $post_id ); if ( $post_id <= 0 ) { return ''; } $this->ensure_frontend_assets(); $defaults = [ 'context_key' => 'default', 'location' => 'default', 'count' => intval( AIT_AIRCE_Settings::get( 'related_count' ) ), 'label' => strval( AIT_AIRCE_Settings::get( 'related_label' ) ), 'show_thumbnails' => (bool) AIT_AIRCE_Settings::get( 'show_thumbnails' ), ]; $args = array_merge( $defaults, (array) $args ); $args['count'] = max( 1, intval( $args['count'] ) ); // Respect per-post disable flag. $disabled = get_post_meta( $post_id, '_ait_airce_disable', true ); if ( $disabled ) { return ''; } // If there is no index yet, schedule an initial background index so the plugin // starts working seamlessly without requiring a manual “Rebuild Index”. if ( function_exists( 'AIT_AIRCE' ) ) { $plugin = AIT_AIRCE(); if ( $plugin && ! empty( $plugin->indexer ) ) { $plugin->indexer->schedule_initial_index_if_needed( 'render' ); } } // If a caller provides fixed IDs (e.g. WooCommerce “frequently bought together”), render those // instead of generating semantic recommendations. if ( ! empty( $args['fixed_ids'] ) && is_array( $args['fixed_ids'] ) ) { $fixed = array_values( array_filter( array_map( 'intval', (array) $args['fixed_ids'] ) ) ); $fixed = array_slice( $fixed, 0, intval( $args['count'] ) ); $items = []; foreach ( $fixed as $fid ) { if ( $fid && $fid !== $post_id ) { $items[] = [ 'post_id' => $fid, 'score' => 1.0 ]; } } } else { $items = $this->cache->get_cached( $post_id, $args['context_key'] ); if ( ! is_array( $items ) ) { $items = $this->cache->generate_for_post( $post_id, $args['context_key'] ); } if ( empty( $items ) && ! $this->post_has_index( $post_id ) && function_exists( 'AIT_AIRCE' ) ) { $plugin = AIT_AIRCE(); if ( $plugin && ! empty( $plugin->indexer ) ) { $indexed = $plugin->indexer->index_post( $post_id ); if ( ! is_wp_error( $indexed ) ) { $items = $this->cache->generate_for_post( $post_id, $args['context_key'] ); } } } } if ( empty( $items ) ) { return ''; } // Enforce requested count. $items = array_slice( (array) $items, 0, intval( $args['count'] ) ); /** * Fired when AIRCE prepares to render related content. * Pro can hook this to track impressions. */ do_action( 'ait_airce/rendered', $post_id, $args, $items ); $html = '