PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.1
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.1
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / src / actions / indexing / indexable-post-indexation-action.php

indexable-post-indexation-action.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.1, at src/actions/indexing/indexable-post-indexation-action.php

223 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Yoast\WP\SEO\Actions\Indexing;
4
5 use wpdb;
6 use Yoast\WP\Lib\Model;
7 use Yoast\WP\SEO\Helpers\Post_Helper;
8 use Yoast\WP\SEO\Helpers\Post_Type_Helper;
9 use Yoast\WP\SEO\Models\Indexable;
10 use Yoast\WP\SEO\Repositories\Indexable_Repository;
11 use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions;
12
13 /**
14 * Reindexing action for post indexables.
15 *
16 * @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
17 */
18 class Indexable_Post_Indexation_Action extends Abstract_Indexing_Action {
19
20 /**
21 * The transient cache key.
22 *
23 * @var string
24 */
25 const UNINDEXED_COUNT_TRANSIENT = 'wpseo_total_unindexed_posts';
26
27 /**
28 * The transient cache key for limited counts.
29 *
30 * @var string
31 */
32 const UNINDEXED_LIMITED_COUNT_TRANSIENT = self::UNINDEXED_COUNT_TRANSIENT . '_limited';
33
34 /**
35 * The post type helper.
36 *
37 * @var Post_Type_Helper
38 */
39 protected $post_type_helper;
40
41 /**
42 * The post helper.
43 *
44 * @var Post_Helper
45 */
46 protected $post_helper;
47
48 /**
49 * The indexable repository.
50 *
51 * @var Indexable_Repository
52 */
53 protected $repository;
54
55 /**
56 * The WordPress database instance.
57 *
58 * @var wpdb
59 */
60 protected $wpdb;
61
62 /**
63 * The latest version of Post Indexables.
64 *
65 * @var int
66 */
67 protected $version;
68
69 /**
70 * Indexable_Post_Indexing_Action constructor
71 *
72 * @param Post_Type_Helper $post_type_helper The post type helper.
73 * @param Indexable_Repository $repository The indexable repository.
74 * @param wpdb $wpdb The WordPress database instance.
75 * @param Indexable_Builder_Versions $builder_versions The latest versions for each Indexable type.
76 * @param Post_Helper $post_helper The post helper.
77 */
78 public function __construct(
79 Post_Type_Helper $post_type_helper,
80 Indexable_Repository $repository,
81 wpdb $wpdb,
82 Indexable_Builder_Versions $builder_versions,
83 Post_Helper $post_helper
84 ) {
85 $this->post_type_helper = $post_type_helper;
86 $this->repository = $repository;
87 $this->wpdb = $wpdb;
88 $this->version = $builder_versions->get_latest_version_for_type( 'post' );
89 $this->post_helper = $post_helper;
90 }
91
92 /**
93 * Creates indexables for unindexed posts.
94 *
95 * @return Indexable[] The created indexables.
96 */
97 public function index() {
98 $query = $this->get_select_query( $this->get_limit() );
99
100 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_select_query returns a prepared query.
101 $post_ids = $this->wpdb->get_col( $query );
102
103 $indexables = [];
104 foreach ( $post_ids as $post_id ) {
105 $indexables[] = $this->repository->find_by_id_and_type( (int) $post_id, 'post' );
106 }
107
108 if ( \count( $indexables ) > 0 ) {
109 \delete_transient( static::UNINDEXED_COUNT_TRANSIENT );
110 \delete_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT );
111 }
112
113 return $indexables;
114 }
115
116 /**
117 * Returns the number of posts that will be indexed in a single indexing pass.
118 *
119 * @return int The limit.
120 */
121 public function get_limit() {
122 /**
123 * Filter 'wpseo_post_indexation_limit' - Allow filtering the amount of posts indexed during each indexing pass.
124 *
125 * @api int The maximum number of posts indexed.
126 */
127 $limit = \apply_filters( 'wpseo_post_indexation_limit', 25 );
128
129 if ( ! \is_int( $limit ) || $limit < 1 ) {
130 $limit = 25;
131 }
132
133 return $limit;
134 }
135
136 /**
137 * Builds a query for counting the number of unindexed posts.
138 *
139 * @return string The prepared query string.
140 */
141 protected function get_count_query() {
142 $indexable_table = Model::get_table_name( 'Indexable' );
143
144 $post_types = $this->get_post_types();
145 $excluded_post_statuses = $this->post_helper->get_excluded_post_statuses();
146 $replacements = \array_merge(
147 $post_types,
148 $excluded_post_statuses
149 );
150
151 $replacements[] = $this->version;
152
153 // Warning: If this query is changed, makes sure to update the query in get_select_query as well.
154 // @phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
155 return $this->wpdb->prepare(
156 "
157 SELECT COUNT(P.ID)
158 FROM {$this->wpdb->posts} AS P
159 WHERE P.post_type IN (" . \implode( ', ', \array_fill( 0, \count( $post_types ), '%s' ) ) . ')
160 AND P.post_status NOT IN (' . \implode( ', ', \array_fill( 0, \count( $excluded_post_statuses ), '%s' ) ) . ")
161 AND P.ID not in (
162 SELECT I.object_id from $indexable_table as I
163 WHERE I.object_type = 'post'
164 AND I.version = %d )",
165 $replacements
166 );
167 }
168
169 /**
170 * Builds a query for selecting the ID's of unindexed posts.
171 *
172 * @param bool $limit The maximum number of post IDs to return.
173 *
174 * @return string The prepared query string.
175 */
176 protected function get_select_query( $limit = false ) {
177 $indexable_table = Model::get_table_name( 'Indexable' );
178
179 $post_types = $this->get_post_types();
180 $excluded_post_statuses = $this->post_helper->get_excluded_post_statuses();
181 $replacements = \array_merge(
182 $post_types,
183 $excluded_post_statuses
184 );
185 $replacements[] = $this->version;
186
187 $limit_query = '';
188 if ( $limit ) {
189 $limit_query = 'LIMIT %d';
190 $replacements[] = $limit;
191 }
192
193 // Warning: If this query is changed, makes sure to update the query in get_count_query as well.
194 // @phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
195 return $this->wpdb->prepare(
196 "
197 SELECT P.ID
198 FROM {$this->wpdb->posts} AS P
199 WHERE P.post_type IN (" . \implode( ', ', \array_fill( 0, \count( $post_types ), '%s' ) ) . ')
200 AND P.post_status NOT IN (' . \implode( ', ', \array_fill( 0, \count( $excluded_post_statuses ), '%s' ) ) . ")
201 AND P.ID not in (
202 SELECT I.object_id from $indexable_table as I
203 WHERE I.object_type = 'post'
204 AND I.version = %d )
205 $limit_query",
206 $replacements
207 );
208 }
209
210 /**
211 * Returns the post types that should be indexed.
212 *
213 * @return array The post types that should be indexed.
214 */
215 protected function get_post_types() {
216 $public_post_types = $this->post_type_helper->get_public_post_types();
217 $excluded_post_types = $this->post_type_helper->get_excluded_post_types_for_indexables();
218
219 // `array_values`, to make sure that the keys are reset.
220 return \array_values( \array_diff( $public_post_types, $excluded_post_types ) );
221 }
222 }
223