| @@ -1,15 +1,14 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace Yoast\WP\SEO\Builders; |
| 4 | 4 | |
| 5 | -use wpdb; | |
| 6 | 5 | use Yoast\WP\SEO\Exceptions\Indexable\Invalid_Term_Exception; |
| 6 | +use Yoast\WP\SEO\Exceptions\Indexable\Term_Not_Built_Exception; | |
| 7 | 7 | use Yoast\WP\SEO\Exceptions\Indexable\Term_Not_Found_Exception; |
| 8 | 8 | use Yoast\WP\SEO\Helpers\Post_Helper; |
| 9 | 9 | use Yoast\WP\SEO\Helpers\Taxonomy_Helper; |
| 10 | 10 | use Yoast\WP\SEO\Models\Indexable; |
| 11 | -use Yoast\WP\SEO\Repositories\Indexable_Repository; | |
| 12 | 11 | use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions; |
| 13 | 12 | |
| 14 | 13 | /** |
| 15 | 14 | * Term Builder for the indexables. |
| @@ -41,32 +40,22 @@ | ||
| 41 | 40 | */ |
| 42 | 41 | protected $post_helper; |
| 43 | 42 | |
| 44 | 43 | /** |
| 45 | - * The WPDB instance. | |
| 46 | - * | |
| 47 | - * @var wpdb | |
| 48 | - */ | |
| 49 | - protected $wpdb; | |
| 50 | - | |
| 51 | - /** | |
| 52 | 44 | * Indexable_Term_Builder constructor. |
| 53 | 45 | * |
| 54 | 46 | * @param Taxonomy_Helper $taxonomy_helper The taxonomy helper. |
| 55 | 47 | * @param Indexable_Builder_Versions $versions The latest version of each Indexable Builder. |
| 56 | 48 | * @param Post_Helper $post_helper The post helper. |
| 57 | - * @param wpdb $wpdb The WPDB instance. | |
| 58 | 49 | */ |
| 59 | 50 | public function __construct( |
| 60 | 51 | Taxonomy_Helper $taxonomy_helper, |
| 61 | 52 | Indexable_Builder_Versions $versions, |
| 62 | - Post_Helper $post_helper, | |
| 63 | - wpdb $wpdb | |
| 53 | + Post_Helper $post_helper | |
| 64 | 54 | ) { |
| 65 | 55 | $this->taxonomy_helper = $taxonomy_helper; |
| 66 | 56 | $this->version = $versions->get_latest_version_for_type( 'term' ); |
| 67 | 57 | $this->post_helper = $post_helper; |
| 68 | - $this->wpdb = $wpdb; | |
| 69 | 58 | } |
| 70 | 59 | |
| 71 | 60 | /** |
| 72 | 61 | * Formats the data. |
| @@ -75,9 +64,10 @@ | ||
| 75 | 64 | * @param Indexable $indexable The indexable to format. |
| 76 | 65 | * |
| 77 | 66 | * @return bool|Indexable The extended indexable. False when unable to build. |
| 78 | 67 | * |
| 79 | - * @throws Invalid_Term_Exception When the term is invalid. | |
| 68 | + * @throws Invalid_Term_Exception When the term is invalid. | |
| 69 | + * @throws Term_Not_Built_Exception When the term is not viewable. | |
| 80 | 70 | * @throws Term_Not_Found_Exception When the term is not found. |
| 81 | 71 | */ |
| 82 | 72 | public function build( $term_id, $indexable ) { |
| 83 | 73 | $term = \get_term( $term_id ); |
| @@ -89,8 +79,13 @@ | ||
| 89 | 79 | if ( \is_wp_error( $term ) ) { |
| 90 | 80 | throw new Invalid_Term_Exception( $term->get_error_message() ); |
| 91 | 81 | } |
| 92 | 82 | |
| 83 | + $indexable_taxonomies = $this->taxonomy_helper->get_indexable_taxonomies(); | |
| 84 | + if ( ! \in_array( $term->taxonomy, $indexable_taxonomies, true ) ) { | |
| 85 | + throw Term_Not_Built_Exception::because_not_indexable( $term_id ); | |
| 86 | + } | |
| 87 | + | |
| 93 | 88 | $term_link = \get_term_link( $term, $term->taxonomy ); |
| 94 | 89 | |
| 95 | 90 | if ( \is_wp_error( $term_link ) ) { |
| 96 | 91 | throw new Invalid_Term_Exception( $term_link->get_error_message() ); |
| @@ -105,9 +100,9 @@ | ||
| 105 | 100 | $indexable->blog_id = \get_current_blog_id(); |
| 106 | 101 | |
| 107 | 102 | $indexable->primary_focus_keyword_score = $this->get_keyword_score( |
| 108 | 103 | $this->get_meta_value( 'wpseo_focuskw', $term_meta ), |
| 109 | - $this->get_meta_value( 'wpseo_linkdex', $term_meta ) | |
| 104 | + $this->get_meta_value( 'wpseo_linkdex', $term_meta ), | |
| 110 | 105 | ); |
| 111 | 106 | |
| 112 | 107 | $indexable->is_robots_noindex = $this->get_noindex_value( $this->get_meta_value( 'wpseo_noindex', $term_meta ) ); |
| 113 | 108 | $indexable->is_public = ( $indexable->is_robots_noindex === null ) ? null : ! $indexable->is_robots_noindex; |
| @@ -182,22 +177,23 @@ | ||
| 182 | 177 | * @return array Lookup table for the indexable fields. |
| 183 | 178 | */ |
| 184 | 179 | protected function get_indexable_lookup() { |
| 185 | 180 | return [ |
| 186 | - 'wpseo_canonical' => 'canonical', | |
| 187 | - 'wpseo_focuskw' => 'primary_focus_keyword', | |
| 188 | - 'wpseo_title' => 'title', | |
| 189 | - 'wpseo_desc' => 'description', | |
| 190 | - 'wpseo_content_score' => 'readability_score', | |
| 191 | - 'wpseo_bctitle' => 'breadcrumb_title', | |
| 192 | - 'wpseo_opengraph-title' => 'open_graph_title', | |
| 193 | - 'wpseo_opengraph-description' => 'open_graph_description', | |
| 194 | - 'wpseo_opengraph-image' => 'open_graph_image', | |
| 195 | - 'wpseo_opengraph-image-id' => 'open_graph_image_id', | |
| 196 | - 'wpseo_twitter-title' => 'twitter_title', | |
| 197 | - 'wpseo_twitter-description' => 'twitter_description', | |
| 198 | - 'wpseo_twitter-image' => 'twitter_image', | |
| 199 | - 'wpseo_twitter-image-id' => 'twitter_image_id', | |
| 181 | + 'wpseo_canonical' => 'canonical', | |
| 182 | + 'wpseo_focuskw' => 'primary_focus_keyword', | |
| 183 | + 'wpseo_title' => 'title', | |
| 184 | + 'wpseo_desc' => 'description', | |
| 185 | + 'wpseo_content_score' => 'readability_score', | |
| 186 | + 'wpseo_inclusive_language_score' => 'inclusive_language_score', | |
| 187 | + 'wpseo_bctitle' => 'breadcrumb_title', | |
| 188 | + 'wpseo_opengraph-title' => 'open_graph_title', | |
| 189 | + 'wpseo_opengraph-description' => 'open_graph_description', | |
| 190 | + 'wpseo_opengraph-image' => 'open_graph_image', | |
| 191 | + 'wpseo_opengraph-image-id' => 'open_graph_image_id', | |
| 192 | + 'wpseo_twitter-title' => 'twitter_title', | |
| 193 | + 'wpseo_twitter-description' => 'twitter_description', | |
| 194 | + 'wpseo_twitter-image' => 'twitter_image', | |
| 195 | + 'wpseo_twitter-image-id' => 'twitter_image_id', | |
| 200 | 196 | ]; |
| 201 | 197 | } |
| 202 | 198 | |
| 203 | 199 | /** |
| @@ -248,25 +244,48 @@ | ||
| 248 | 244 | * |
| 249 | 245 | * @return object An object with last_modified and published_at timestamps. |
| 250 | 246 | */ |
| 251 | 247 | protected function get_object_timestamps( $term_id, $taxonomy ) { |
| 248 | + global $wpdb; | |
| 252 | 249 | $post_statuses = $this->post_helper->get_public_post_statuses(); |
| 253 | 250 | |
| 254 | - $sql = " | |
| 255 | - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at | |
| 256 | - FROM {$this->wpdb->posts} AS p | |
| 257 | - INNER JOIN {$this->wpdb->term_relationships} AS term_rel | |
| 258 | - ON term_rel.object_id = p.ID | |
| 259 | - INNER JOIN {$this->wpdb->term_taxonomy} AS term_tax | |
| 260 | - ON term_tax.term_taxonomy_id = term_rel.term_taxonomy_id | |
| 261 | - AND term_tax.taxonomy = %s | |
| 262 | - AND term_tax.term_id = %d | |
| 263 | - WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ") | |
| 264 | - AND p.post_password = '' | |
| 265 | - "; | |
| 251 | + $replacements = []; | |
| 252 | + $replacements[] = 'post_modified_gmt'; | |
| 253 | + $replacements[] = 'post_date_gmt'; | |
| 254 | + $replacements[] = $wpdb->posts; | |
| 255 | + $replacements[] = $wpdb->term_relationships; | |
| 256 | + $replacements[] = 'object_id'; | |
| 257 | + $replacements[] = 'ID'; | |
| 258 | + $replacements[] = $wpdb->term_taxonomy; | |
| 259 | + $replacements[] = 'term_taxonomy_id'; | |
| 260 | + $replacements[] = 'term_taxonomy_id'; | |
| 261 | + $replacements[] = 'taxonomy'; | |
| 262 | + $replacements[] = $taxonomy; | |
| 263 | + $replacements[] = 'term_id'; | |
| 264 | + $replacements[] = $term_id; | |
| 265 | + $replacements[] = 'post_status'; | |
| 266 | + $replacements = \array_merge( $replacements, $post_statuses ); | |
| 267 | + $replacements[] = 'post_password'; | |
| 266 | 268 | |
| 267 | - $replacements = \array_merge( [ $taxonomy, $term_id ], $post_statuses ); | |
| 268 | - | |
| 269 | - // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- We are using wpdb prepare. | |
| 270 | - return $this->wpdb->get_row( $this->wpdb->prepare( $sql, $replacements ) ); | |
| 269 | + //phpcs:disable WordPress.DB.PreparedSQLPlaceholders -- %i placeholder is still not recognized. | |
| 270 | + //phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery -- Reason: Most performant way. | |
| 271 | + //phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: No relevant caches. | |
| 272 | + return $wpdb->get_row( | |
| 273 | + $wpdb->prepare( | |
| 274 | + ' | |
| 275 | + SELECT MAX(p.%i) AS last_modified, MIN(p.%i) AS published_at | |
| 276 | + FROM %i AS p | |
| 277 | + INNER JOIN %i AS term_rel | |
| 278 | + ON term_rel.%i = p.%i | |
| 279 | + INNER JOIN %i AS term_tax | |
| 280 | + ON term_tax.%i = term_rel.%i | |
| 281 | + AND term_tax.%i = %s | |
| 282 | + AND term_tax.%i = %d | |
| 283 | + WHERE p.%i IN (' . \implode( ', ', \array_fill( 0, \count( $post_statuses ), '%s' ) ) . ") | |
| 284 | + AND p.%i = '' | |
| 285 | + ", | |
| 286 | + $replacements, | |
| 287 | + ), | |
| 288 | + ); | |
| 289 | + //phpcs:enable | |
| 271 | 290 | } |
| 272 | 291 | } |