PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.3
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.3
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 / generators / breadcrumbs-generator.php

breadcrumbs-generator.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.3, at src/generators/breadcrumbs-generator.php

389 lines 11.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\Generators;
4
5 use Yoast\WP\SEO\Context\Meta_Tags_Context;
6 use Yoast\WP\SEO\Helpers\Current_Page_Helper;
7 use Yoast\WP\SEO\Helpers\Options_Helper;
8 use Yoast\WP\SEO\Helpers\Pagination_Helper;
9 use Yoast\WP\SEO\Helpers\Post_Type_Helper;
10 use Yoast\WP\SEO\Helpers\Url_Helper;
11 use Yoast\WP\SEO\Models\Indexable;
12 use Yoast\WP\SEO\Repositories\Indexable_Repository;
13
14 /**
15 * Represents the generator class for the breadcrumbs.
16 */
17 class Breadcrumbs_Generator implements Generator_Interface {
18
19 /**
20 * The indexable repository.
21 *
22 * @var Indexable_Repository
23 */
24 private $repository;
25
26 /**
27 * The options helper.
28 *
29 * @var Options_Helper
30 */
31 private $options;
32
33 /**
34 * The current page helper.
35 *
36 * @var Current_Page_Helper
37 */
38 private $current_page_helper;
39
40 /**
41 * The post type helper.
42 *
43 * @var Post_Type_Helper
44 */
45 private $post_type_helper;
46
47 /**
48 * The URL helper.
49 *
50 * @var Url_Helper
51 */
52 private $url_helper;
53
54 /**
55 * The pagination helper.
56 *
57 * @var Pagination_Helper
58 */
59 private $pagination_helper;
60
61 /**
62 * Breadcrumbs_Generator constructor.
63 *
64 * @param Indexable_Repository $repository The repository.
65 * @param Options_Helper $options The options helper.
66 * @param Current_Page_Helper $current_page_helper The current page helper.
67 * @param Post_Type_Helper $post_type_helper The post type helper.
68 * @param Url_Helper $url_helper The URL helper.
69 * @param Pagination_Helper $pagination_helper The pagination helper.
70 */
71 public function __construct(
72 Indexable_Repository $repository,
73 Options_Helper $options,
74 Current_Page_Helper $current_page_helper,
75 Post_Type_Helper $post_type_helper,
76 Url_Helper $url_helper,
77 Pagination_Helper $pagination_helper
78 ) {
79 $this->repository = $repository;
80 $this->options = $options;
81 $this->current_page_helper = $current_page_helper;
82 $this->post_type_helper = $post_type_helper;
83 $this->url_helper = $url_helper;
84 $this->pagination_helper = $pagination_helper;
85 }
86
87 /**
88 * Generates the breadcrumbs.
89 *
90 * @param Meta_Tags_Context $context The meta tags context.
91 *
92 * @return array An array of associative arrays that each have a 'text' and a 'url'.
93 */
94 public function generate( Meta_Tags_Context $context ) {
95 $static_ancestors = [];
96 $breadcrumbs_home = $this->options->get( 'breadcrumbs-home' );
97 if ( $breadcrumbs_home !== '' && ! \in_array( $this->current_page_helper->get_page_type(), [ 'Home_Page', 'Static_Home_Page' ], true ) ) {
98 $front_page_id = $this->current_page_helper->get_front_page_id();
99 if ( $front_page_id === 0 ) {
100 $static_ancestors[] = $this->repository->find_for_home_page();
101 }
102 else {
103 $static_ancestor = $this->repository->find_by_id_and_type( $front_page_id, 'post' );
104 if ( $static_ancestor->post_status !== 'unindexed' ) {
105 $static_ancestors[] = $static_ancestor;
106 }
107 }
108 }
109 $page_for_posts = \get_option( 'page_for_posts' );
110 if ( $this->should_have_blog_crumb( $page_for_posts, $context ) ) {
111 $static_ancestor = $this->repository->find_by_id_and_type( $page_for_posts, 'post' );
112 if ( $static_ancestor->post_status !== 'unindexed' ) {
113 $static_ancestors[] = $static_ancestor;
114 }
115 }
116 if (
117 $context->indexable->object_type === 'post'
118 && $context->indexable->object_sub_type !== 'post'
119 && $context->indexable->object_sub_type !== 'page'
120 && $this->post_type_helper->has_archive( $context->indexable->object_sub_type )
121 ) {
122 $static_ancestors[] = $this->repository->find_for_post_type_archive( $context->indexable->object_sub_type );
123 }
124 if ( $context->indexable->object_type === 'term' ) {
125 $parent = $this->get_taxonomy_post_type_parent( $context->indexable->object_sub_type );
126 if ( $parent && $parent !== 'post' && $this->post_type_helper->has_archive( $parent ) ) {
127 $static_ancestors[] = $this->repository->find_for_post_type_archive( $parent );
128 }
129 }
130
131 // Get all ancestors of the indexable and append itself to get all indexables in the full crumb.
132 $indexables = $this->repository->get_ancestors( $context->indexable );
133 $indexables[] = $context->indexable;
134
135 if ( ! empty( $static_ancestors ) ) {
136 \array_unshift( $indexables, ...$static_ancestors );
137 }
138
139 $indexables = \apply_filters( 'wpseo_breadcrumb_indexables', $indexables, $context );
140
141 $callback = function ( Indexable $ancestor ) {
142 $crumb = [
143 'url' => $ancestor->permalink,
144 'text' => $ancestor->breadcrumb_title,
145 ];
146 switch ( $ancestor->object_type ) {
147 case 'post':
148 $crumb = $this->get_post_crumb( $crumb, $ancestor );
149 break;
150 case 'post-type-archive':
151 $crumb = $this->get_post_type_archive_crumb( $crumb, $ancestor );
152 break;
153 case 'term':
154 $crumb = $this->get_term_crumb( $crumb, $ancestor );
155 break;
156 case 'system-page':
157 $crumb = $this->get_system_page_crumb( $crumb, $ancestor );
158 break;
159 case 'user':
160 $crumb = $this->get_user_crumb( $crumb, $ancestor );
161 break;
162 case 'date-archive':
163 $crumb = $this->get_date_archive_crumb( $crumb );
164 break;
165 }
166 return $crumb;
167 };
168 $crumbs = \array_map( $callback, $indexables );
169
170 if ( $breadcrumbs_home !== '' ) {
171 $crumbs[0]['text'] = $breadcrumbs_home;
172 }
173
174 $crumbs = $this->add_paged_crumb( $crumbs, $context->indexable );
175
176 /**
177 * Filter: 'wpseo_breadcrumb_links' - Allow the developer to filter the Yoast SEO breadcrumb links, add to them, change order, etc.
178 *
179 * @api array $crumbs The crumbs array.
180 */
181 $crumbs = \apply_filters( 'wpseo_breadcrumb_links', $crumbs );
182
183 $filter_callback = static function( $link_info, $index ) use ( $crumbs ) {
184 /**
185 * Filter: 'wpseo_breadcrumb_single_link_info' - Allow developers to filter the Yoast SEO Breadcrumb link information.
186 *
187 * @api array $link_info The breadcrumb link information.
188 *
189 * @param int $index The index of the breadcrumb in the list.
190 * @param array $crumbs The complete list of breadcrumbs.
191 */
192 return \apply_filters( 'wpseo_breadcrumb_single_link_info', $link_info, $index, $crumbs );
193 };
194 return \array_map( $filter_callback, $crumbs, \array_keys( $crumbs ) );
195 }
196
197 /**
198 * Returns the modified post crumb.
199 *
200 * @param array $crumb The crumb.
201 * @param Indexable $ancestor The indexable.
202 *
203 * @return array The crumb.
204 */
205 private function get_post_crumb( $crumb, $ancestor ) {
206 $crumb['id'] = $ancestor->object_id;
207
208 return $crumb;
209 }
210
211 /**
212 * Returns the modified post type crumb.
213 *
214 * @param array $crumb The crumb.
215 * @param Indexable $ancestor The indexable.
216 *
217 * @return array The crumb.
218 */
219 private function get_post_type_archive_crumb( $crumb, $ancestor ) {
220 $crumb['ptarchive'] = $ancestor->object_sub_type;
221
222 return $crumb;
223 }
224
225 /**
226 * Returns the modified term crumb.
227 *
228 * @param array $crumb The crumb.
229 * @param Indexable $ancestor The indexable.
230 *
231 * @return array The crumb.
232 */
233 private function get_term_crumb( $crumb, $ancestor ) {
234 $crumb['term_id'] = $ancestor->object_id;
235
236 return $crumb;
237 }
238
239 /**
240 * Returns the modified system page crumb.
241 *
242 * @param array $crumb The crumb.
243 * @param Indexable $ancestor The indexable.
244 *
245 * @return array The crumb.
246 */
247 private function get_system_page_crumb( $crumb, $ancestor ) {
248 if ( $ancestor->object_sub_type === 'search-result' ) {
249 $crumb['text'] = $this->options->get( 'breadcrumbs-searchprefix' ) . ' ' . \esc_html( \get_search_query() );
250 $crumb['url'] = \get_search_link();
251 }
252 elseif ( $ancestor->object_sub_type === '404' ) {
253 $crumb['text'] = $this->options->get( 'breadcrumbs-404crumb' );
254 }
255
256 return $crumb;
257 }
258
259 /**
260 * Returns the modified user crumb.
261 *
262 * @param array $crumb The crumb.
263 * @param Indexable $ancestor The indexable.
264 *
265 * @return array The crumb.
266 */
267 private function get_user_crumb( $crumb, $ancestor ) {
268 $display_name = \get_the_author_meta( 'display_name', $ancestor->object_id );
269 $crumb['text'] = $this->options->get( 'breadcrumbs-archiveprefix' ) . ' ' . $display_name;
270
271 return $crumb;
272 }
273
274 /**
275 * Returns the modified date archive crumb.
276 *
277 * @param array $crumb The crumb.
278 *
279 * @return array The crumb.
280 */
281 protected function get_date_archive_crumb( $crumb ) {
282 $home_url = $this->url_helper->home();
283 $prefix = $this->options->get( 'breadcrumbs-archiveprefix' );
284
285 if ( \is_day() ) {
286 $day = \esc_html( \get_the_date() );
287 $crumb['url'] = $home_url . \get_the_date( 'Y/m/d' ) . '/';
288 $crumb['text'] = $prefix . ' ' . $day;
289 }
290 elseif ( \is_month() ) {
291 $month = \esc_html( \trim( \single_month_title( ' ', false ) ) );
292 $crumb['url'] = $home_url . \get_the_date( 'Y/m' ) . '/';
293 $crumb['text'] = $prefix . ' ' . $month;
294 }
295 elseif ( \is_year() ) {
296 $year = \get_the_date( 'Y' );
297 $crumb['url'] = $home_url . $year . '/';
298 $crumb['text'] = $prefix . ' ' . $year;
299 }
300
301 return $crumb;
302 }
303
304 /**
305 * Returns whether or not a blog crumb should be added.
306 *
307 * @param int $page_for_posts The page for posts ID.
308 * @param Meta_Tags_Context $context The meta tags context.
309 *
310 * @return bool Whether or not a blog crumb should be added.
311 */
312 protected function should_have_blog_crumb( $page_for_posts, $context ) {
313 // When there is no page configured as blog page.
314 if ( \get_option( 'show_on_front' ) !== 'page' || ! $page_for_posts ) {
315 return false;
316 }
317
318 if ( $context->indexable->object_type === 'term' ) {
319 $parent = $this->get_taxonomy_post_type_parent( $context->indexable->object_sub_type );
320 return $parent === 'post';
321 }
322
323 if ( $this->options->get( 'breadcrumbs-display-blog-page' ) !== true ) {
324 return false;
325 }
326
327 // When the current page is the home page, searchpage or isn't a singular post.
328 if ( \is_home() || \is_search() || ! \is_singular( 'post' ) ) {
329 return false;
330 }
331
332 return true;
333 }
334
335 /**
336 * Returns the post type parent of a given taxonomy.
337 *
338 * @param string $taxonomy The taxonomy.
339 *
340 * @return string|false The parent if it exists, false otherwise.
341 */
342 protected function get_taxonomy_post_type_parent( $taxonomy ) {
343 $parent = $this->options->get( 'taxonomy-' . $taxonomy . '-ptparent' );
344
345 if ( empty( $parent ) || (string) $parent === '0' ) {
346 return false;
347 }
348
349 return $parent;
350 }
351
352 /**
353 * Adds a crumb for the current page, if we're on an archive page or paginated post.
354 *
355 * @param array $crumbs The array of breadcrumbs.
356 * @param Indexable $current_indexable The current indexable.
357 *
358 * @return array The breadcrumbs.
359 */
360 protected function add_paged_crumb( array $crumbs, $current_indexable ) {
361 $is_simple_page = $this->current_page_helper->is_simple_page();
362
363 // If we're not on a paged page do nothing.
364 if ( ! $is_simple_page && ! $this->current_page_helper->is_paged() ) {
365 return $crumbs;
366 }
367
368 // If we're not on a paginated post do nothing.
369 if ( $is_simple_page && $current_indexable->number_of_pages === null ) {
370 return $crumbs;
371 }
372
373 $current_page_number = $this->pagination_helper->get_current_page_number();
374 if ( $current_page_number <= 1 ) {
375 return $crumbs;
376 }
377
378 $crumbs[] = [
379 'text' => \sprintf(
380 /* translators: %s expands to the current page number */
381 \__( 'Page %s', 'wordpress-seo' ),
382 $current_page_number
383 ),
384 ];
385
386 return $crumbs;
387 }
388 }
389