PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.5
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
← All changes | src/builders/indexable-author-builder.php +77 -23 18.228.5 View file →
@@ -1,10 +1,11 @@
1 1 <?php
2 2
3 3 namespace Yoast\WP\SEO\Builders;
4 4
5 -use wpdb;
5 +use Yoast\WP\SEO\Exceptions\Indexable\Author_Not_Built_Exception;
6 6 use Yoast\WP\SEO\Helpers\Author_Archive_Helper;
7 +use Yoast\WP\SEO\Helpers\Options_Helper;
7 8 use Yoast\WP\SEO\Helpers\Post_Helper;
8 9 use Yoast\WP\SEO\Models\Indexable;
9 10 use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions;
10 11
@@ -31,20 +32,20 @@
31 32 */
32 33 protected $version;
33 34
34 35 /**
35 - * Holds the taxonomy helper instance.
36 + * Holds the options helper instance.
36 37 *
37 - * @var Post_Helper
38 + * @var Options_Helper
38 39 */
39 - protected $post_helper;
40 + protected $options_helper;
40 41
41 42 /**
42 - * The WPDB instance.
43 + * Holds the taxonomy helper instance.
43 44 *
44 - * @var wpdb
45 + * @var Post_Helper
45 46 */
46 - protected $wpdb;
47 + protected $post_helper;
47 48
48 49 /**
49 50 * Indexable_Author_Builder constructor.
50 51 *
@@ -49,21 +50,21 @@
49 50 * Indexable_Author_Builder constructor.
50 51 *
51 52 * @param Author_Archive_Helper $author_archive The author archive helper.
52 53 * @param Indexable_Builder_Versions $versions The Indexable version manager.
54 + * @param Options_Helper $options_helper The options helper.
53 55 * @param Post_Helper $post_helper The post helper.
54 - * @param wpdb $wpdb The WPDB instance.
55 56 */
56 57 public function __construct(
57 58 Author_Archive_Helper $author_archive,
58 59 Indexable_Builder_Versions $versions,
59 - Post_Helper $post_helper,
60 - wpdb $wpdb
60 + Options_Helper $options_helper,
61 + Post_Helper $post_helper
61 62 ) {
62 63 $this->author_archive = $author_archive;
63 64 $this->version = $versions->get_latest_version_for_type( 'user' );
65 + $this->options_helper = $options_helper;
64 66 $this->post_helper = $post_helper;
65 - $this->wpdb = $wpdb;
66 67 }
67 68
68 69 /**
69 70 * Formats the data.
@@ -71,10 +72,17 @@
71 72 * @param int $user_id The user to retrieve the indexable for.
72 73 * @param Indexable $indexable The indexable to format.
73 74 *
74 75 * @return Indexable The extended indexable.
76 + *
77 + * @throws Author_Not_Built_Exception When author is not built.
75 78 */
76 79 public function build( $user_id, Indexable $indexable ) {
80 + $exception = $this->check_if_user_should_be_indexed( $user_id );
81 + if ( $exception ) {
82 + throw $exception;
83 + }
84 +
77 85 $meta_data = $this->get_meta_data( $user_id );
78 86
79 87 $indexable->object_id = $user_id;
80 88 $indexable->object_type = 'user';
@@ -154,9 +162,9 @@
154 162 $indexable->object_id,
155 163 [
156 164 'size' => 500,
157 165 'scheme' => 'https',
158 - ]
166 + ],
159 167 );
160 168 if ( $gravatar_image ) {
161 169 return [
162 170 'image' => $gravatar_image,
@@ -169,25 +177,71 @@
169 177
170 178 /**
171 179 * Returns the timestamps for a given author.
172 180 *
173 - * @param int $author_id The author ID.
181 + * @param int $author_id The author ID.
174 182 *
175 183 * @return object An object with last_modified and published_at timestamps.
176 184 */
177 185 protected function get_object_timestamps( $author_id ) {
186 + global $wpdb;
178 187 $post_statuses = $this->post_helper->get_public_post_statuses();
179 188
180 - $sql = "
181 - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at
182 - FROM {$this->wpdb->posts} AS p
183 - WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ")
184 - AND p.post_password = ''
185 - AND p.post_author = %d
186 - ";
189 + $replacements = [];
190 + $replacements[] = 'post_modified_gmt';
191 + $replacements[] = 'post_date_gmt';
192 + $replacements[] = $wpdb->posts;
193 + $replacements[] = 'post_status';
194 + $replacements = \array_merge( $replacements, $post_statuses );
195 + $replacements[] = 'post_password';
196 + $replacements[] = 'post_author';
197 + $replacements[] = $author_id;
187 198
188 - $replacements = \array_merge( $post_statuses, [ $author_id ] );
199 + //phpcs:disable WordPress.DB.PreparedSQLPlaceholders -- %i placeholder is still not recognized.
200 + //phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery -- Reason: Most performant way.
201 + //phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: No relevant caches.
202 + return $wpdb->get_row(
203 + $wpdb->prepare(
204 + '
205 + SELECT MAX(p.%i) AS last_modified, MIN(p.%i) AS published_at
206 + FROM %i AS p
207 + WHERE p.%i IN (' . \implode( ', ', \array_fill( 0, \count( $post_statuses ), '%s' ) ) . ")
208 + AND p.%i = ''
209 + AND p.%i = %d
210 + ",
211 + $replacements,
212 + ),
213 + );
214 + //phpcs:enable
215 + }
189 216
190 - // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- We are using wpdb prepare.
191 - return $this->wpdb->get_row( $this->wpdb->prepare( $sql, $replacements ) );
217 + /**
218 + * Checks if the user should be indexed.
219 + * Returns an exception with an appropriate message if not.
220 + *
221 + * @param string $user_id The user id.
222 + *
223 + * @return Author_Not_Built_Exception|null The exception if it should not be indexed, or `null` if it should.
224 + */
225 + protected function check_if_user_should_be_indexed( $user_id ) {
226 + $exception = null;
227 +
228 + if ( $this->author_archive->are_disabled() ) {
229 + $exception = Author_Not_Built_Exception::author_archives_are_disabled( $user_id );
230 + }
231 + // We will check if the author has public posts the WP way, instead of the indexable way, to make sure we get proper results even if SEO optimization is not run.
232 + // In case the user has no public posts, we check if the user should be indexed anyway.
233 + elseif ( $this->options_helper->get( 'noindex-author-noposts-wpseo', false ) === true && $this->author_archive->author_has_public_posts_wp( $user_id ) === false ) {
234 + $exception = Author_Not_Built_Exception::author_archives_are_not_indexed_for_users_without_posts( $user_id );
235 + }
236 +
237 + /**
238 + * Filter: Include or exclude a user from being build and saved as an indexable.
239 + * Return an `Author_Not_Built_Exception` when the indexable should not be build, with an appropriate message telling why it should not be built.
240 + * Return `null` if the indexable should be build.
241 + *
242 + * @param Author_Not_Built_Exception|null $exception An exception if the indexable is not being built, `null` if the indexable should be built.
243 + * @param string $user_id The ID of the user that should or should not be excluded.
244 + */
245 + return \apply_filters( 'wpseo_should_build_and_save_user_indexable', $exception, $user_id );
192 246 }
193 247 }