PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.4
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.4
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 / llms-txt / infrastructure / content / automatic-post-collection.php

automatic-post-collection.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.4, at src/llms-txt/infrastructure/content/automatic-post-collection.php

216 lines 7.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
4 namespace Yoast\WP\SEO\Llms_Txt\Infrastructure\Content;
5
6 use WP_Post;
7 use Yoast\WP\SEO\Helpers\Indexable_Helper;
8 use Yoast\WP\SEO\Helpers\Options_Helper;
9 use Yoast\WP\SEO\Llms_Txt\Domain\Content\Post_Collection_Interface;
10 use Yoast\WP\SEO\Llms_Txt\Domain\Content_Types\Content_Type_Entry;
11 use Yoast\WP\SEO\Repositories\Indexable_Repository;
12 use Yoast\WP\SEO\Surfaces\Meta_Surface;
13
14 /**
15 * The class that handles the automatic post collection. Based on either indexables or WP_Query.
16 *
17 * @makePublic
18 */
19 class Automatic_Post_Collection implements Post_Collection_Interface {
20
21 /**
22 * The options helper.
23 *
24 * @var Options_Helper
25 */
26 private $options_helper;
27
28 /**
29 * The indexable repository.
30 *
31 * @var Indexable_Repository
32 */
33 private $indexable_repository;
34
35 /**
36 * The meta surface.
37 *
38 * @var Meta_Surface
39 */
40 private $meta;
41
42 /**
43 * The indexable helper.
44 *
45 * @var Indexable_Helper
46 */
47 private $indexable_helper;
48
49 /**
50 * Constructs the class.
51 *
52 * @param Options_Helper $options_helper The options helper.
53 * @param Indexable_Repository $indexable_repository The indexable repository.
54 * @param Meta_Surface $meta The meta surface.
55 * @param Indexable_Helper $indexable_helper The indexable helper.
56 */
57 public function __construct(
58 Options_Helper $options_helper,
59 Indexable_Repository $indexable_repository,
60 Meta_Surface $meta,
61 Indexable_Helper $indexable_helper
62 ) {
63 $this->options_helper = $options_helper;
64 $this->indexable_repository = $indexable_repository;
65 $this->meta = $meta;
66 $this->indexable_helper = $indexable_helper;
67 }
68
69 /**
70 * Gets the posts that are relevant for the LLMs.txt.
71 *
72 * @param string $post_type The post type.
73 * @param int $limit The maximum number of posts to return.
74 *
75 * @return array<int, array<Content_Type_Entry>> The posts that are relevant for the LLMs.txt.
76 */
77 public function get_posts( string $post_type, int $limit ): array {
78 $posts = $this->get_recent_cornerstone_content( $post_type, $limit );
79
80 if ( \count( $posts ) >= $limit ) {
81 return $posts;
82 }
83
84 $recent_posts = $this->get_recent_posts( $post_type, $limit );
85 foreach ( $recent_posts as $recent_post ) {
86 // If the post is already in the list because it's cornerstone, don't add it again.
87 if ( isset( $posts[ $recent_post->get_id() ] ) ) {
88 continue;
89 }
90
91 $posts[ $recent_post->get_id() ] = $recent_post;
92
93 if ( \count( $posts ) >= $limit ) {
94 break;
95 }
96 }
97
98 return $posts;
99 }
100
101 /**
102 * Gets the most recently modified cornerstone content.
103 *
104 * @param string $post_type The post type.
105 * @param int $limit The maximum number of posts to return.
106 *
107 * @return array<int, array<Content_Type_Entry>> The most recently modified cornerstone content.
108 */
109 private function get_recent_cornerstone_content( string $post_type, int $limit ): array {
110 if ( ! $this->options_helper->get( 'enable_cornerstone_content' ) ) {
111 return [];
112 }
113
114 $cornerstone_limit = ( \is_post_type_hierarchical( $post_type ) ) ? null : $limit;
115 $cornerstones = $this->indexable_repository->get_recent_cornerstone_for_post_type( $post_type, $cornerstone_limit );
116
117 $recent_cornerstone_posts = [];
118 foreach ( $cornerstones as $cornerstone ) {
119 $cornerstone_meta = $this->meta->for_indexable( $cornerstone );
120 if ( $cornerstone_meta->post instanceof WP_Post ) {
121 $recent_cornerstone_posts[ $cornerstone_meta->post->ID ] = Content_Type_Entry::from_meta( $cornerstone_meta );
122 }
123 }
124
125 return $recent_cornerstone_posts;
126 }
127
128 /**
129 * Gets the most recently modified posts.
130 *
131 * @param string $post_type The post type.
132 * @param int $limit The maximum number of posts to return.
133 * @param string $search_filter Optional. The search filter to apply to the query.
134 * @param bool $disable_excluding_old_posts Optional. Whether to disable excluding posts older than one year.
135 *
136 * @return array<Content_Type_Entry> The most recently modified posts.
137 */
138 public function get_recent_posts( string $post_type, int $limit, string $search_filter = '', bool $disable_excluding_old_posts = false ): array {
139 $exclude_older_than_one_year = false;
140
141 if ( $post_type === 'post' && ! $disable_excluding_old_posts ) {
142 $exclude_older_than_one_year = true;
143 }
144
145 if ( $this->indexable_helper->should_index_indexables() ) {
146 return $this->get_recently_modified_posts_indexables( $post_type, $limit, $exclude_older_than_one_year, $search_filter );
147 }
148
149 return $this->get_recently_modified_posts_wp_query( $post_type, $limit, $exclude_older_than_one_year, $search_filter );
150 }
151
152 /**
153 * Returns most recently modified posts of a post type, using indexables.
154 *
155 * @param string $post_type The post type.
156 * @param int $limit The maximum number of posts to return.
157 * @param bool $exclude_older_than_one_year Whether to exclude posts older than one year.
158 * @param string $search_filter Optional. The search filter to apply to the query.
159 *
160 * @return array<Content_Type_Entry> The most recently modified posts.
161 */
162 private function get_recently_modified_posts_indexables( string $post_type, int $limit, bool $exclude_older_than_one_year, string $search_filter = '' ): array {
163 $posts = [];
164 $recently_modified_indexables = $this->indexable_repository->get_recently_modified_posts( $post_type, $limit, $exclude_older_than_one_year, $search_filter );
165
166 foreach ( $recently_modified_indexables as $indexable ) {
167 $indexable_meta = $this->meta->for_indexable( $indexable );
168 if ( $indexable_meta->post instanceof WP_Post ) {
169 $posts[] = Content_Type_Entry::from_meta( $indexable_meta );
170 }
171 }
172
173 return $posts;
174 }
175
176 /**
177 * Returns most recently modified posts of a post type, using WP_Query.
178 *
179 * @param string $post_type The post type.
180 * @param int $limit The maximum number of posts to return.
181 * @param bool $exclude_older_than_one_year Whether to exclude posts older than one year.
182 * @param string $search_filter Optional. The search filter to apply to the query.
183 *
184 * @return array<WP_Post> The most recently modified posts.
185 */
186 private function get_recently_modified_posts_wp_query( string $post_type, int $limit, bool $exclude_older_than_one_year, string $search_filter = '' ): array {
187 $args = [
188 'post_type' => $post_type,
189 'posts_per_page' => $limit,
190 'post_status' => 'publish',
191 'orderby' => 'modified',
192 'order' => 'DESC',
193 'has_password' => false,
194 ];
195
196 if ( $exclude_older_than_one_year === true ) {
197 $args['date_query'] = [
198 [
199 'after' => '12 months ago',
200 ],
201 ];
202 }
203
204 if ( $search_filter !== '' ) {
205 $args['s'] = $search_filter;
206 }
207
208 $posts = [];
209 foreach ( \get_posts( $args ) as $post ) {
210 $posts[] = Content_Type_Entry::from_post( $post, \get_permalink( $post->ID ) );
211 }
212
213 return $posts;
214 }
215 }
216