wordpress-seo
/
src
/
schema-aggregator
/
infrastructure
/
schema-pieces
/
schema-piece-repository.php
schema-piece-repository.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI trunk, at src/schema-aggregator/infrastructure/schema-pieces/schema-piece-repository.php
| 1 | <?php |
| 2 | // phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure. |
| 3 | namespace Yoast\WP\SEO\Schema_Aggregator\Infrastructure\Schema_Pieces; |
| 4 | |
| 5 | use Throwable; |
| 6 | use Yoast\WP\SEO\Helpers\Indexable_Helper; |
| 7 | use Yoast\WP\SEO\Memoizers\Meta_Tags_Context_Memoizer; |
| 8 | use Yoast\WP\SEO\Schema_Aggregator\Application\Enhancement\Schema_Enhancement_Factory; |
| 9 | use Yoast\WP\SEO\Schema_Aggregator\Domain\External_Schema_Piece_Repository_Interface; |
| 10 | use Yoast\WP\SEO\Schema_Aggregator\Domain\Schema_Piece; |
| 11 | use Yoast\WP\SEO\Schema_Aggregator\Domain\Schema_Piece_Collection; |
| 12 | use Yoast\WP\SEO\Schema_Aggregator\Domain\Schema_Piece_Repository_Interface; |
| 13 | use Yoast\WP\SEO\Schema_Aggregator\Infrastructure\Aggregator_Config; |
| 14 | use Yoast\WP\SEO\Schema_Aggregator\Infrastructure\Indexable_Repository\Indexable_Repository_Factory; |
| 15 | use Yoast\WP\SEO\Schema_Aggregator\Infrastructure\Meta_Tags_Context_Memoizer_Adapter; |
| 16 | use YoastSEO_Vendor\Psr\Log\LoggerAwareInterface; |
| 17 | use YoastSEO_Vendor\Psr\Log\LoggerAwareTrait; |
| 18 | use YoastSEO_Vendor\Psr\Log\NullLogger; |
| 19 | |
| 20 | /** |
| 21 | * The Schema_Piece repository. |
| 22 | */ |
| 23 | class Schema_Piece_Repository implements Schema_Piece_Repository_Interface, LoggerAwareInterface { |
| 24 | use LoggerAwareTrait; |
| 25 | |
| 26 | /** |
| 27 | * The meta tags context memoizer. |
| 28 | * |
| 29 | * @var Meta_Tags_Context_Memoizer |
| 30 | */ |
| 31 | private $memoizer; |
| 32 | |
| 33 | /** |
| 34 | * The indexable helper. |
| 35 | * |
| 36 | * @var Indexable_Helper |
| 37 | */ |
| 38 | private $indexable_helper; |
| 39 | |
| 40 | /** |
| 41 | * The meta tags context adapter factory. |
| 42 | * |
| 43 | * @var Meta_Tags_Context_Memoizer_Adapter |
| 44 | */ |
| 45 | private $adapter; |
| 46 | |
| 47 | /** |
| 48 | * Configuration provider. |
| 49 | * |
| 50 | * @var Aggregator_Config |
| 51 | */ |
| 52 | private $config; |
| 53 | |
| 54 | /** |
| 55 | * The schema enhancement factory. |
| 56 | * |
| 57 | * @var Schema_Enhancement_Factory |
| 58 | */ |
| 59 | private $enhancement_factory; |
| 60 | |
| 61 | /** |
| 62 | * The indexable repository factory. |
| 63 | * |
| 64 | * @var Indexable_Repository_Factory |
| 65 | */ |
| 66 | private $indexable_repository_factory; |
| 67 | |
| 68 | /** |
| 69 | * The WordPress global state adapter. |
| 70 | * |
| 71 | * @var WordPress_Global_State_Adapter |
| 72 | */ |
| 73 | private $global_state_adapter; |
| 74 | |
| 75 | /** |
| 76 | * External schema piece repositories. |
| 77 | * |
| 78 | * @var array<External_Schema_Piece_Repository_Interface> |
| 79 | */ |
| 80 | private $external_repositories; |
| 81 | |
| 82 | /** |
| 83 | * Constructor. |
| 84 | * |
| 85 | * @param Meta_Tags_Context_Memoizer $memoizer The meta tags context memoizer. |
| 86 | * @param Indexable_Helper $indexable_helper The indexable helper. |
| 87 | * @param Meta_Tags_Context_Memoizer_Adapter $adapter The adapter factory. |
| 88 | * @param Aggregator_Config $config The configuration provider. |
| 89 | * @param Schema_Enhancement_Factory $enhancement_factory The schema enhancement factory. |
| 90 | * @param Indexable_Repository_Factory $indexable_repository_factory The indexable repository factory. |
| 91 | * @param WordPress_Global_State_Adapter $global_state_adapter The global state adapter. |
| 92 | * @param External_Schema_Piece_Repository_Interface ...$external_repositories The external schema piece repositories. |
| 93 | */ |
| 94 | public function __construct( |
| 95 | Meta_Tags_Context_Memoizer $memoizer, |
| 96 | Indexable_Helper $indexable_helper, |
| 97 | Meta_Tags_Context_Memoizer_Adapter $adapter, |
| 98 | Aggregator_Config $config, |
| 99 | Schema_Enhancement_Factory $enhancement_factory, |
| 100 | Indexable_Repository_Factory $indexable_repository_factory, |
| 101 | WordPress_Global_State_Adapter $global_state_adapter, |
| 102 | External_Schema_Piece_Repository_Interface ...$external_repositories |
| 103 | ) { |
| 104 | $this->memoizer = $memoizer; |
| 105 | $this->indexable_helper = $indexable_helper; |
| 106 | $this->adapter = $adapter; |
| 107 | $this->config = $config; |
| 108 | $this->enhancement_factory = $enhancement_factory; |
| 109 | $this->indexable_repository_factory = $indexable_repository_factory; |
| 110 | $this->global_state_adapter = $global_state_adapter; |
| 111 | $this->external_repositories = $external_repositories; |
| 112 | $this->logger = new NullLogger(); |
| 113 | } |
| 114 | |
| 115 | /** |
| 116 | * Gets the indexables to be aggregated. |
| 117 | * |
| 118 | * @param int $page The page number (1-based). |
| 119 | * @param int $page_size The number of items per page. |
| 120 | * @param string $post_type The post type to filter by. |
| 121 | * |
| 122 | * @return Schema_Piece_Collection The aggregated schema. |
| 123 | */ |
| 124 | public function get( int $page, int $page_size, string $post_type ): Schema_Piece_Collection { |
| 125 | $indexable_repository = $this->indexable_repository_factory->get_repository( $this->indexable_helper->should_index_indexables() ); |
| 126 | $indexables = $indexable_repository->get( $page, $page_size, $post_type ); |
| 127 | $schema_pieces = []; |
| 128 | |
| 129 | foreach ( $indexables as $indexable ) { |
| 130 | if ( ! \in_array( $indexable->object_sub_type, $this->config->get_allowed_post_types(), true ) ) { |
| 131 | continue; |
| 132 | } |
| 133 | |
| 134 | $page_type = $this->indexable_helper->get_page_type_for_indexable( $indexable ); |
| 135 | $context = $this->memoizer->get( $indexable, $page_type ); |
| 136 | |
| 137 | $this->global_state_adapter->set_global_state( $indexable, $context ); |
| 138 | try { |
| 139 | $context_array = $this->adapter->meta_tags_context_to_array( $context ); |
| 140 | $pieces_data = $context_array['@graph']; |
| 141 | |
| 142 | $pieces_data = $this->collect_external_schema( $pieces_data, $post_type, $indexable->object_id ); |
| 143 | |
| 144 | foreach ( $pieces_data as $piece_data ) { |
| 145 | $schema_piece = new Schema_Piece( $piece_data, $piece_data['@type'] ); |
| 146 | $enhancer = $this->enhancement_factory->get_enhancer( $this->get_all_schema_types( $context_array['@graph'] ) ); |
| 147 | if ( $enhancer !== null ) { |
| 148 | $schema_piece = $enhancer->enhance( $schema_piece, $indexable ); |
| 149 | } |
| 150 | $schema_pieces[] = $schema_piece; |
| 151 | } |
| 152 | } catch ( Throwable $e ) { |
| 153 | $this->logger->warning( |
| 154 | 'Schema aggregation failed for indexable #{indexable_id} ({object_type}/{object_sub_type}): {message}', |
| 155 | [ |
| 156 | 'indexable_id' => $indexable->id, |
| 157 | 'object_type' => $indexable->object_type, |
| 158 | 'object_sub_type' => $indexable->object_sub_type, |
| 159 | 'message' => $e->getMessage(), |
| 160 | ], |
| 161 | ); |
| 162 | } finally { |
| 163 | $this->global_state_adapter->reset_global_state(); |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | return new Schema_Piece_Collection( $schema_pieces ); |
| 168 | } |
| 169 | |
| 170 | /** |
| 171 | * Collects external schema pieces from all supporting repositories. |
| 172 | * |
| 173 | * @param array<array<string,string|int|bool|array>> $pieces_data The existing schema pieces. |
| 174 | * @param string $post_type The post type. |
| 175 | * @param int $post_id The post ID. |
| 176 | * |
| 177 | * @return array<array<string,string|int|bool|array>> The schema pieces with external pieces added. |
| 178 | */ |
| 179 | private function collect_external_schema( array $pieces_data, string $post_type, int $post_id ): array { |
| 180 | foreach ( $this->external_repositories as $repository ) { |
| 181 | if ( $repository->supports( $post_type ) ) { |
| 182 | $external_pieces = $repository->collect( $post_id ); |
| 183 | $pieces_data = \array_merge( $pieces_data, $external_pieces ); |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | return $pieces_data; |
| 188 | } |
| 189 | |
| 190 | /** |
| 191 | * All schema types present in the schema piece. |
| 192 | * |
| 193 | * @param array<array<string>> $graph The current graph. |
| 194 | * |
| 195 | * @return array<string> |
| 196 | */ |
| 197 | private function get_all_schema_types( array $graph ): array { |
| 198 | $schema_types = []; |
| 199 | foreach ( $graph as $schema_values ) { |
| 200 | foreach ( $schema_values as $key => $value ) { |
| 201 | if ( $key === '@type' ) { |
| 202 | if ( \is_array( $value ) ) { |
| 203 | foreach ( $value as $type_value ) { |
| 204 | $schema_types[ $type_value ] = $type_value; |
| 205 | } |
| 206 | continue; |
| 207 | } |
| 208 | $schema_types[ $value ] = $value; |
| 209 | } |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | return $schema_types; |
| 214 | } |
| 215 | } |
| 216 |