• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / dd6e866a9e6d253114633104d9e3858d807178ba

19 Jun 2024 10:03AM UTC coverage: 48.628% (-4.3%) from 52.936%
dd6e866a9e6d253114633104d9e3858d807178ba

push

github

web-flow
Merge pull request #21431 from Yoast/21429-update-copy-in-the-introduction-and-consent-modals

Updates the copy for the introduction and consent modals

7441 of 13454 branches covered (55.31%)

Branch coverage included in aggregate %.

0 of 3 new or added lines in 2 files covered. (0.0%)

3718 existing lines in 107 files now uncovered.

25100 of 53464 relevant lines covered (46.95%)

62392.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.15
/src/builders/indexable-term-builder.php
1
<?php
2

3
namespace Yoast\WP\SEO\Builders;
4

5
use Yoast\WP\SEO\Exceptions\Indexable\Invalid_Term_Exception;
6
use Yoast\WP\SEO\Exceptions\Indexable\Term_Not_Built_Exception;
7
use Yoast\WP\SEO\Exceptions\Indexable\Term_Not_Found_Exception;
8
use Yoast\WP\SEO\Helpers\Post_Helper;
9
use Yoast\WP\SEO\Helpers\Taxonomy_Helper;
10
use Yoast\WP\SEO\Models\Indexable;
11
use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions;
12

13
/**
14
 * Term Builder for the indexables.
15
 *
16
 * Formats the term meta to indexable format.
17
 */
18
class Indexable_Term_Builder {
19

20
        use Indexable_Social_Image_Trait;
21

22
        /**
23
         * Holds the taxonomy helper instance.
24
         *
25
         * @var Taxonomy_Helper
26
         */
27
        protected $taxonomy_helper;
28

29
        /**
30
         * The latest version of the Indexable_Term_Builder.
31
         *
32
         * @var int
33
         */
34
        protected $version;
35

36
        /**
37
         * Holds the taxonomy helper instance.
38
         *
39
         * @var Post_Helper
40
         */
41
        protected $post_helper;
42

43
        /**
44
         * Indexable_Term_Builder constructor.
45
         *
46
         * @param Taxonomy_Helper            $taxonomy_helper The taxonomy helper.
47
         * @param Indexable_Builder_Versions $versions        The latest version of each Indexable Builder.
48
         * @param Post_Helper                $post_helper     The post helper.
49
         */
50
        public function __construct(
30✔
51
                Taxonomy_Helper $taxonomy_helper,
52
                Indexable_Builder_Versions $versions,
53
                Post_Helper $post_helper
54
        ) {
15✔
55
                $this->taxonomy_helper = $taxonomy_helper;
30✔
56
                $this->version         = $versions->get_latest_version_for_type( 'term' );
30✔
57
                $this->post_helper     = $post_helper;
30✔
58
        }
15✔
59

60
        /**
61
         * Formats the data.
62
         *
63
         * @param int       $term_id   ID of the term to save data for.
64
         * @param Indexable $indexable The indexable to format.
65
         *
66
         * @return bool|Indexable The extended indexable. False when unable to build.
67
         *
68
         * @throws Invalid_Term_Exception   When the term is invalid.
69
         * @throws Term_Not_Built_Exception When the term is not viewable.
70
         * @throws Term_Not_Found_Exception When the term is not found.
71
         */
72
        public function build( $term_id, $indexable ) {
8✔
73
                $term = \get_term( $term_id );
8✔
74

75
                if ( $term === null ) {
8✔
76
                        throw new Term_Not_Found_Exception();
2✔
77
                }
78

79
                if ( \is_wp_error( $term ) ) {
6✔
80
                        throw new Invalid_Term_Exception( $term->get_error_message() );
2✔
81
                }
82

83
                $indexable_taxonomies = $this->taxonomy_helper->get_indexable_taxonomies();
4✔
84
                if ( ! \in_array( $term->taxonomy, $indexable_taxonomies, true ) ) {
4✔
UNCOV
85
                        throw Term_Not_Built_Exception::because_not_indexable( $term_id );
×
86
                }
87

88
                $term_link = \get_term_link( $term, $term->taxonomy );
4✔
89

90
                if ( \is_wp_error( $term_link ) ) {
4✔
91
                        throw new Invalid_Term_Exception( $term_link->get_error_message() );
2✔
92
                }
93

94
                $term_meta = $this->taxonomy_helper->get_term_meta( $term );
2✔
95

96
                $indexable->object_id       = $term_id;
2✔
97
                $indexable->object_type     = 'term';
2✔
98
                $indexable->object_sub_type = $term->taxonomy;
2✔
99
                $indexable->permalink       = $term_link;
2✔
100
                $indexable->blog_id         = \get_current_blog_id();
2✔
101

102
                $indexable->primary_focus_keyword_score = $this->get_keyword_score(
2✔
103
                        $this->get_meta_value( 'wpseo_focuskw', $term_meta ),
2✔
104
                        $this->get_meta_value( 'wpseo_linkdex', $term_meta )
2✔
105
                );
1✔
106

107
                $indexable->is_robots_noindex = $this->get_noindex_value( $this->get_meta_value( 'wpseo_noindex', $term_meta ) );
2✔
108
                $indexable->is_public         = ( $indexable->is_robots_noindex === null ) ? null : ! $indexable->is_robots_noindex;
2✔
109

110
                $this->reset_social_images( $indexable );
2✔
111

112
                foreach ( $this->get_indexable_lookup() as $meta_key => $indexable_key ) {
2✔
113
                        $indexable->{$indexable_key} = $this->get_meta_value( $meta_key, $term_meta );
2✔
114
                }
115

116
                if ( empty( $indexable->breadcrumb_title ) ) {
2✔
117
                        $indexable->breadcrumb_title = $term->name;
2✔
118
                }
119

120
                $this->handle_social_images( $indexable );
2✔
121

122
                $indexable->is_cornerstone = $this->get_meta_value( 'wpseo_is_cornerstone', $term_meta );
2✔
123

124
                // Not implemented yet.
125
                $indexable->is_robots_nofollow     = null;
2✔
126
                $indexable->is_robots_noarchive    = null;
2✔
127
                $indexable->is_robots_noimageindex = null;
2✔
128
                $indexable->is_robots_nosnippet    = null;
2✔
129

130
                $timestamps                      = $this->get_object_timestamps( $term_id, $term->taxonomy );
2✔
131
                $indexable->object_published_at  = $timestamps->published_at;
2✔
132
                $indexable->object_last_modified = $timestamps->last_modified;
2✔
133

134
                $indexable->version = $this->version;
2✔
135

136
                return $indexable;
2✔
137
        }
138

139
        /**
140
         * Converts the meta noindex value to the indexable value.
141
         *
142
         * @param string $meta_value Term meta to base the value on.
143
         *
144
         * @return bool|null
145
         */
146
        protected function get_noindex_value( $meta_value ) {
8✔
147
                if ( $meta_value === 'noindex' ) {
8✔
148
                        return true;
4✔
149
                }
150

151
                if ( $meta_value === 'index' ) {
4✔
152
                        return false;
2✔
153
                }
154

155
                return null;
2✔
156
        }
157

158
        /**
159
         * Determines the focus keyword score.
160
         *
161
         * @param string $keyword The focus keyword that is set.
162
         * @param int    $score   The score saved on the meta data.
163
         *
164
         * @return int|null Score to use.
165
         */
166
        protected function get_keyword_score( $keyword, $score ) {
6✔
167
                if ( empty( $keyword ) ) {
6✔
168
                        return null;
2✔
169
                }
170

171
                return $score;
4✔
172
        }
173

174
        /**
175
         * Retrieves the lookup table.
176
         *
177
         * @return array Lookup table for the indexable fields.
178
         */
179
        protected function get_indexable_lookup() {
2✔
180
                return [
1✔
181
                        'wpseo_canonical'                => 'canonical',
2✔
182
                        'wpseo_focuskw'                  => 'primary_focus_keyword',
1✔
183
                        'wpseo_title'                    => 'title',
1✔
184
                        'wpseo_desc'                     => 'description',
1✔
185
                        'wpseo_content_score'            => 'readability_score',
1✔
186
                        'wpseo_inclusive_language_score' => 'inclusive_language_score',
1✔
187
                        'wpseo_bctitle'                  => 'breadcrumb_title',
1✔
188
                        'wpseo_opengraph-title'          => 'open_graph_title',
1✔
189
                        'wpseo_opengraph-description'    => 'open_graph_description',
1✔
190
                        'wpseo_opengraph-image'          => 'open_graph_image',
1✔
191
                        'wpseo_opengraph-image-id'       => 'open_graph_image_id',
1✔
192
                        'wpseo_twitter-title'            => 'twitter_title',
1✔
193
                        'wpseo_twitter-description'      => 'twitter_description',
1✔
194
                        'wpseo_twitter-image'            => 'twitter_image',
1✔
195
                        'wpseo_twitter-image-id'         => 'twitter_image_id',
1✔
196
                ];
1✔
197
        }
198

199
        /**
200
         * Retrieves a meta value from the given meta data.
201
         *
202
         * @param string $meta_key  The key to extract.
203
         * @param array  $term_meta The meta data.
204
         *
205
         * @return string|null The meta value.
206
         */
207
        protected function get_meta_value( $meta_key, $term_meta ) {
8✔
208
                if ( ! $term_meta || ! \array_key_exists( $meta_key, $term_meta ) ) {
8✔
209
                        return null;
4✔
210
                }
211

212
                $value = $term_meta[ $meta_key ];
6✔
213
                if ( \is_string( $value ) && $value === '' ) {
6✔
214
                        return null;
2✔
215
                }
216

217
                return $value;
4✔
218
        }
219

220
        /**
221
         * Finds an alternative image for the social image.
222
         *
223
         * @param Indexable $indexable The indexable.
224
         *
225
         * @return array|bool False when not found, array with data when found.
226
         */
227
        protected function find_alternative_image( Indexable $indexable ) {
4✔
228
                $content_image = $this->image->get_term_content_image( $indexable->object_id );
4✔
229
                if ( $content_image ) {
4✔
230
                        return [
1✔
231
                                'image'  => $content_image,
2✔
232
                                'source' => 'first-content-image',
2✔
233
                        ];
1✔
234
                }
235

236
                return false;
2✔
237
        }
238

239
        /**
240
         * Returns the timestamps for a given term.
241
         *
242
         * @param int    $term_id  The term ID.
243
         * @param string $taxonomy The taxonomy.
244
         *
245
         * @return object An object with last_modified and published_at timestamps.
246
         */
247
        protected function get_object_timestamps( $term_id, $taxonomy ) {
2✔
248
                global $wpdb;
2✔
249
                $post_statuses = $this->post_helper->get_public_post_statuses();
2✔
250

251
                $replacements   = [];
2✔
252
                $replacements[] = 'post_modified_gmt';
2✔
253
                $replacements[] = 'post_date_gmt';
2✔
254
                $replacements[] = $wpdb->posts;
2✔
255
                $replacements[] = $wpdb->term_relationships;
2✔
256
                $replacements[] = 'object_id';
2✔
257
                $replacements[] = 'ID';
2✔
258
                $replacements[] = $wpdb->term_taxonomy;
2✔
259
                $replacements[] = 'term_taxonomy_id';
2✔
260
                $replacements[] = 'term_taxonomy_id';
2✔
261
                $replacements[] = 'taxonomy';
2✔
262
                $replacements[] = $taxonomy;
2✔
263
                $replacements[] = 'term_id';
2✔
264
                $replacements[] = $term_id;
2✔
265
                $replacements[] = 'post_status';
2✔
266
                $replacements   = \array_merge( $replacements, $post_statuses );
2✔
267
                $replacements[] = 'post_password';
2✔
268

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(
2✔
273
                        $wpdb->prepare(
2✔
274
                                '
1✔
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' ) ) . ")
2✔
284
                                AND                p.%i = ''
285
                        ",
1✔
286
                                $replacements
2✔
287
                        )
1✔
288
                );
1✔
289
                //phpcs:enable
290
        }
291
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc