post_meta = $post_meta; $this->meta_resolver = $meta_resolver; } public function get_id(): string { return 'HowTo'; } public function applies_to( \WP_Post $post ): bool { if ( ! (bool) $this->post_meta->get( $post->ID, MetaFields::HOWTO_ENABLED ) ) { return false; } /** * Filters whether the HowTo node has steps to show — the default is * "the first ordered list in the content", but a filter can flip it * to true when steps will come from elsewhere (Pro's ACF repeater * mapping, M30) even without any ordered list in the content. * * @param bool $has_steps * @param \WP_Post $post */ return (bool) apply_filters( 'acez_seo_how_to_applies', array() !== $this->steps( $post ), $post ); } public function build( \WP_Post $post ): array { $title = $this->meta_resolver->title( $post ); $title = '' !== $title ? $title : get_the_title( $post ); $data = array( '@type' => 'HowTo', '@id' => SchemaIds::how_to( $post ), 'name' => wp_strip_all_tags( $title ), 'step' => $this->steps( $post ), ); /** * Filters the HowTo schema node. * * @param array $data * @param \WP_Post $post */ return (array) apply_filters( 'acez_seo_how_to', $data, $post ); } /** * @return array> */ private function steps( \WP_Post $post ): array { $list_block = $this->first_ordered_list_block( parse_blocks( (string) $post->post_content ) ); if ( null === $list_block ) { return array(); } $html = render_block( $list_block ); preg_match_all( '#]*>(.*?)#is', $html, $matches ); $steps = array(); foreach ( $matches[1] as $index => $item_html ) { $text = trim( wp_strip_all_tags( $item_html ) ); if ( '' === $text ) { continue; } $steps[] = array( '@type' => 'HowToStep', 'position' => $index + 1, 'text' => $text, ); } return $steps; } /** * @param array> $blocks */ private function first_ordered_list_block( array $blocks ): ?array { foreach ( $blocks as $block ) { if ( 'core/list' === ( $block['blockName'] ?? '' ) && ! empty( $block['attrs']['ordered'] ) ) { return $block; } if ( ! empty( $block['innerBlocks'] ) ) { $found = $this->first_ordered_list_block( $block['innerBlocks'] ); if ( null !== $found ) { return $found; } } } return null; } }