'', 'items_html' => '', 'totalPages' => 0, 'totalItems' => 0, ); } $parent_stack = array(); if ( isset( $context['parent_stack'] ) && is_array( $context['parent_stack'] ) && ! empty( $context['parent_stack'] ) ) { $parent_stack = $context['parent_stack']; } elseif ( isset( $GLOBALS['airowp_parent_stack'] ) && is_array( $GLOBALS['airowp_parent_stack'] ) ) { $parent_stack = $GLOBALS['airowp_parent_stack']; } $parent = empty( $parent_stack ) ? null : end( $parent_stack ); $parent_post_id = 0; if ( is_array( $parent ) && ! empty( $parent['postId'] ) ) { $parent_post_id = (int) $parent['postId']; } if ( ! $parent_post_id ) { return airowp_query_relationship_fallback( $atts, $context ); } $raw_value = false; if ( function_exists( 'get_field' ) ) { $raw_value = get_field( $field, $parent_post_id ); } if ( false === $raw_value || null === $raw_value ) { $raw_value = get_post_meta( $parent_post_id, $field, true ); } $ids = airowp_query_relationship_normalize_ids( $raw_value ); if ( empty( $ids ) ) { return airowp_query_relationship_fallback( $atts, $context ); } // Delegate to the Posts renderer — override attributes so WP_Query // runs with post__in + orderby=post__in + post_type=any (relationship // fields are frequently cross-type). require_once __DIR__ . '/render-posts.php'; $atts_override = array_merge( $atts, array( 'source' => 'manual', 'manualIds' => $ids, 'postType' => 'any', 'perPage' => max( 1, min( count( $ids ), (int) $atts['perPage'] ) ), 'manualPaginated' => true, ) ); return airowp_query_render_posts( $atts_override, $context ); } /** * Normalize ACF/meta field values to a list of post IDs. * * @param mixed $value Raw relationship value. * @return int[] Referenced post IDs. */ function airowp_query_relationship_normalize_ids( $value ) { if ( is_array( $value ) ) { $ids = array(); foreach ( $value as $v ) { if ( $v instanceof \WP_Post ) { $ids[] = (int) $v->ID; } elseif ( is_numeric( $v ) ) { $ids[] = (int) $v; } } return array_values( array_filter( $ids ) ); } if ( is_string( $value ) && '' !== $value ) { if ( false !== strpos( $value, ',' ) ) { return array_values( array_filter( array_map( 'absint', explode( ',', $value ) ) ) ); } if ( is_numeric( $value ) ) { return array( (int) $value ); } // Never instantiate objects from stored meta (PHP object injection). $unserialized = is_serialized( $value ) ? unserialize( $value, array( 'allowed_classes' => false ) ) : false; // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- allowed_classes is false. if ( is_array( $unserialized ) ) { return airowp_query_relationship_normalize_ids( $unserialized ); } } if ( is_numeric( $value ) ) { return array( (int) $value ); } return array(); } /** * Empty / all / parent fallback for no-result relationship queries. * * @param array $atts Query attributes. * @param array $context Query render context. * @return array Rendered query result and pagination metadata. */ function airowp_query_relationship_fallback( array $atts, array $context ) { $mode = isset( $atts['relationshipFallback'] ) ? (string) $atts['relationshipFallback'] : 'empty'; if ( 'empty' === $mode ) { return array( 'html' => airowp_query_wrap( '', $atts, $context, $context['wrapper_attrs'] ?? null ), 'items_html' => '', 'totalPages' => 0, 'totalItems' => 0, ); } require_once __DIR__ . '/render-posts.php'; if ( 'all' === $mode ) { $atts['source'] = 'posts'; return airowp_query_render_posts( $atts, $context ); } // 'parent' fallback: render a single item using the parent post itself. $fallback_stack = array(); if ( isset( $context['parent_stack'] ) && is_array( $context['parent_stack'] ) && ! empty( $context['parent_stack'] ) ) { $fallback_stack = $context['parent_stack']; } elseif ( isset( $GLOBALS['airowp_parent_stack'] ) && is_array( $GLOBALS['airowp_parent_stack'] ) ) { $fallback_stack = $GLOBALS['airowp_parent_stack']; } $parent = empty( $fallback_stack ) ? null : end( $fallback_stack ); if ( ! is_array( $parent ) || empty( $parent['postId'] ) ) { return array( 'html' => '', 'items_html' => '', 'totalPages' => 0, 'totalItems' => 0, ); } $atts['source'] = 'manual'; $atts['manualIds'] = array( (int) $parent['postId'] ); $atts['perPage'] = 1; return airowp_query_render_posts( $atts, $context ); } endif;