FAQPage schema or empty array. */ public function generate( \WC_Product $product ): array { $questions = $this->collect_questions( $product ); if ( empty( $questions ) ) { return array(); } $main_entity = array(); foreach ( $questions as $qa ) { $main_entity[] = array( '@type' => 'Question', 'name' => $qa['question'], 'acceptedAnswer' => array( '@type' => 'Answer', 'text' => $qa['answer'], ), ); } $schema = array( '@context' => 'https://schema.org', '@type' => 'FAQPage', 'mainEntity' => $main_entity, ); /** * Filters the FAQPage schema. * * @param array $schema The FAQPage data. * @param \WC_Product $product The product. * @param array $questions Raw Q&A pairs. */ return apply_filters( 'aicommerce_faq_schema', $schema, $product, $questions ); } /** * Collect all FAQ items from available sources. * * @param \WC_Product $product The product. * * @return array Q&A pairs. */ private function collect_questions( \WC_Product $product ): array { $questions = array(); // 1. Custom FAQ meta field (stored as JSON array). $custom_faq = $product->get_meta( '_aicommerce_faq' ); if ( ! empty( $custom_faq ) ) { $parsed = is_string( $custom_faq ) ? json_decode( $custom_faq, true ) : $custom_faq; if ( is_array( $parsed ) ) { foreach ( $parsed as $item ) { if ( ! empty( $item['question'] ) && ! empty( $item['answer'] ) ) { $questions[] = array( 'question' => sanitize_text_field( $item['question'] ), 'answer' => wp_strip_all_tags( $item['answer'] ), ); } } } } // 2. Auto-generate from product attributes (if no custom FAQs). if ( empty( $questions ) ) { $questions = array_merge( $questions, $this->generate_from_attributes( $product ) ); } // 3. Auto-generate from product data. $questions = array_merge( $questions, $this->generate_from_product_data( $product ) ); // Deduplicate by question text. $seen = array(); $dedup = array(); foreach ( $questions as $qa ) { $key = strtolower( $qa['question'] ); if ( ! isset( $seen[ $key ] ) ) { $seen[ $key ] = true; $dedup[] = $qa; } } // Limit to 6 Q&A pairs per product. return array_slice( $dedup, 0, 6 ); } /** * Generate Q&A pairs from product attributes. * * E.g., "What color is this product?" → "Available in Red, Blue, Green." * * @param \WC_Product $product The product. * * @return array */ private function generate_from_attributes( \WC_Product $product ): array { $questions = array(); $attributes = $product->get_attributes(); if ( empty( $attributes ) ) { return $questions; } foreach ( $attributes as $attribute ) { if ( ! $attribute->get_visible() ) { continue; } $name = wc_attribute_label( $attribute->get_name(), $product ); if ( $attribute->is_taxonomy() ) { $terms = wc_get_product_terms( $product->get_id(), $attribute->get_name(), array( 'fields' => 'names' ) ); $values = implode( ', ', $terms ); } else { $values = implode( ', ', $attribute->get_options() ); } if ( empty( $values ) ) { continue; } $questions[] = array( 'question' => sprintf( /* translators: 1: Attribute name 2: Product name */ __( 'What %1$s options are available for %2$s?', 'ai-discoverability-kit' ), strtolower( $name ), $product->get_name() ), 'answer' => sprintf( /* translators: 1: Product name 2: Attribute name 3: Values list */ __( '%1$s is available in the following %2$s options: %3$s.', 'ai-discoverability-kit' ), $product->get_name(), strtolower( $name ), $values ), ); } return $questions; } /** * Generate Q&A pairs from core product data. * * Shipping, returns, availability questions that apply broadly. * * @param \WC_Product $product The product. * * @return array */ private function generate_from_product_data( \WC_Product $product ): array { $questions = array(); $name = $product->get_name(); // Availability question. $stock_status = $product->get_stock_status(); if ( 'instock' === $stock_status ) { $questions[] = array( 'question' => sprintf( /* translators: %s: Product name */ __( 'Is %s currently in stock?', 'ai-discoverability-kit' ), $name ), 'answer' => sprintf( /* translators: %s: Product name */ __( 'Yes, %s is currently in stock and available for purchase.', 'ai-discoverability-kit' ), $name ), ); } // Weight/dimensions question (if set). $weight = $product->get_weight(); if ( ! empty( $weight ) ) { $weight_unit = get_option( 'woocommerce_weight_unit', 'kg' ); $questions[] = array( 'question' => sprintf( /* translators: %s: Product name */ __( 'How much does %s weigh?', 'ai-discoverability-kit' ), $name ), 'answer' => sprintf( /* translators: 1: Product name 2: Weight 3: Unit */ __( '%1$s weighs %2$s %3$s.', 'ai-discoverability-kit' ), $name, $weight, $weight_unit ), ); } return $questions; } }