PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.0
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.0
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 / importing / aioseo-posts-importing-action.php

aioseo-posts-importing-action.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.0, at src/actions/importing/aioseo-posts-importing-action.php

320 lines 9.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\Importing;
4
5 use wpdb;
6 use Yoast\WP\SEO\Conditionals\AIOSEO_V4_Importer_Conditional;
7 use Yoast\WP\SEO\Helpers\Indexable_To_Postmeta_Helper;
8 use Yoast\WP\SEO\Helpers\Options_Helper;
9 use Yoast\WP\SEO\Helpers\Wpdb_Helper;
10 use Yoast\WP\SEO\Models\Indexable;
11 use Yoast\WP\SEO\Repositories\Indexable_Repository;
12 use Yoast\WP\SEO\Services\Importing\Aioseo_Replacevar_Handler;
13
14 /**
15 * Importing action for AIOSEO post data.
16 *
17 * @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
18 */
19 class Aioseo_Posts_Importing_Action extends Abstract_Importing_Action {
20
21 use Import_Cursor_Manager_Trait;
22
23 /**
24 * The plugin of the action.
25 */
26 const PLUGIN = 'aioseo';
27
28 /**
29 * The type of the action.
30 */
31 const TYPE = 'posts';
32
33 /**
34 * The map of aioseo to yoast meta.
35 *
36 * @var array
37 */
38 protected $aioseo_to_yoast_map = [
39 'title' => 'title',
40 'description' => 'description',
41 'og_title' => 'open_graph_title',
42 'og_description' => 'open_graph_description',
43 'twitter_title' => 'twitter_title',
44 'twitter_description' => 'twitter_description',
45 ];
46
47 /**
48 * Represents the indexables repository.
49 *
50 * @var Indexable_Repository
51 */
52 protected $indexable_repository;
53
54 /**
55 * The WordPress database instance.
56 *
57 * @var wpdb
58 */
59 protected $wpdb;
60
61 /**
62 * The indexable_to_postmeta helper.
63 *
64 * @var Indexable_To_Postmeta_Helper
65 */
66 protected $indexable_to_postmeta;
67
68 /**
69 * The wpdb helper.
70 *
71 * @var Wpdb_Helper
72 */
73 protected $wpdb_helper;
74
75 /**
76 * The replacevar handler.
77 *
78 * @var Aioseo_Replacevar_Handler
79 */
80 protected $replacevar_handler;
81
82 /**
83 * Class constructor.
84 *
85 * @param Indexable_Repository $indexable_repository The indexables repository.
86 * @param wpdb $wpdb The WordPress database instance.
87 * @param Indexable_To_Postmeta_Helper $indexable_to_postmeta The indexable_to_postmeta helper.
88 * @param Options_Helper $options The options helper.
89 * @param Wpdb_Helper $wpdb_helper The wpdb_helper helper.
90 * @param Aioseo_Replacevar_Handler $replacevar_handler The replacevar handler.
91 */
92 public function __construct(
93 Indexable_Repository $indexable_repository,
94 wpdb $wpdb,
95 Indexable_To_Postmeta_Helper $indexable_to_postmeta,
96 Options_Helper $options,
97 Wpdb_Helper $wpdb_helper,
98 Aioseo_Replacevar_Handler $replacevar_handler ) {
99 parent::__construct( $options );
100
101 $this->indexable_repository = $indexable_repository;
102 $this->wpdb = $wpdb;
103 $this->indexable_to_postmeta = $indexable_to_postmeta;
104 $this->wpdb_helper = $wpdb_helper;
105 $this->replacevar_handler = $replacevar_handler;
106 }
107
108 /**
109 * Retrieves the AIOSEO table name along with the db prefix.
110 *
111 * @return string The AIOSEO table name along with the db prefix.
112 */
113 protected function get_table() {
114 return $this->wpdb->prefix . 'aioseo_posts';
115 }
116
117 /**
118 * Determines if the AIOSEO database table exists.
119 *
120 * @return bool True if the table is found.
121 */
122 protected function aioseo_exists() {
123 return $this->wpdb_helper->table_exists( $this->get_table() ) === true;
124 }
125
126 /**
127 * Returns whether the AISOEO post importing action is enabled.
128 *
129 * @return bool True if the AISOEO post importing action is enabled.
130 */
131 public function is_enabled() {
132 $aioseo_importer_conditional = \YoastSEO()->classes->get( AIOSEO_V4_Importer_Conditional::class );
133
134 return $aioseo_importer_conditional->is_met();
135 }
136
137 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- Reason: They are already prepared.
138
139 /**
140 * Returns the total number of unimported objects.
141 *
142 * @return int The total number of unimported objects.
143 */
144 public function get_total_unindexed() {
145 if ( ! $this->aioseo_exists() ) {
146 return 0;
147 }
148
149 $limit = false;
150 $just_detect = true;
151 $indexables_to_create = $this->wpdb->get_col( $this->query( $limit, $just_detect ) );
152
153 $number_of_indexables_to_create = \count( $indexables_to_create );
154 $completed = $number_of_indexables_to_create === 0;
155 $this->set_completed( $completed );
156
157 return $number_of_indexables_to_create;
158 }
159
160 /**
161 * Returns the limited number of unimported objects.
162 *
163 * @param int $limit The maximum number of unimported objects to be returned.
164 *
165 * @return int|false The limited number of unindexed posts. False if the query fails.
166 */
167 public function get_limited_unindexed_count( $limit ) {
168 if ( ! $this->aioseo_exists() ) {
169 return 0;
170 }
171
172 $just_detect = true;
173 $indexables_to_create = $this->wpdb->get_col( $this->query( $limit, $just_detect ) );
174
175 $number_of_indexables_to_create = \count( $indexables_to_create );
176 $completed = $number_of_indexables_to_create === 0;
177 $this->set_completed( $completed );
178
179 return $number_of_indexables_to_create;
180 }
181
182 /**
183 * Imports AIOSEO meta data and creates the respective Yoast indexables and postmeta.
184 *
185 * @todo: Replace the replace vars with Yoast ones.
186 *
187 * @return Indexable[]|false An array of created indexables or false if aioseo data was not found.
188 */
189 public function index() {
190 if ( ! $this->aioseo_exists() ) {
191 return false;
192 }
193
194 $limit = $this->get_limit();
195 $aioseo_indexables = $this->wpdb->get_results( $this->query( $limit ), ARRAY_A );
196 $created_indexables = [];
197
198 $completed = \count( $aioseo_indexables ) === 0;
199 $this->set_completed( $completed );
200
201 $last_indexed_aioseo_id = 0;
202 foreach ( $aioseo_indexables as $aioseo_indexable ) {
203 $last_indexed_aioseo_id = $aioseo_indexable['id'];
204
205 $indexable = $this->indexable_repository->find_by_id_and_type( $aioseo_indexable['post_id'], 'post' );
206
207 // Let's ensure that the current post id represents something that we want to index (eg. *not* shop_order).
208 if ( ! \is_a( $indexable, 'Yoast\WP\SEO\Models\Indexable' ) ) {
209 continue;
210 }
211
212 $indexable = $this->map( $indexable, $aioseo_indexable );
213 $indexable->save();
214
215 // To ensure that indexables can be rebuild after a reset, we have to store the data in the postmeta table too.
216 $this->indexable_to_postmeta->map_to_postmeta( $indexable );
217
218 $last_indexed_aioseo_id = $aioseo_indexable['id'];
219
220 $created_indexables[] = $indexable;
221 }
222
223 $cursor_id = $this->get_cursor_id();
224 $this->set_cursor( $this->options, $cursor_id, $last_indexed_aioseo_id );
225
226 return $created_indexables;
227 }
228
229 // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
230
231 /**
232 * Maps AIOSEO meta data to Yoast meta data.
233 *
234 * @param Indexable $indexable The Yoast indexable.
235 * @param array $aioseo_indexable The AIOSEO indexable.
236 *
237 * @return Indexable The created indexables.
238 */
239 public function map( $indexable, $aioseo_indexable ) {
240 // Do not overwrite any existing values.
241 foreach ( $this->aioseo_to_yoast_map as $aioseo_key => $yoast_key ) {
242 if ( ! empty( $indexable->{$yoast_key} ) ) {
243 continue;
244 }
245
246 if ( ! empty( $aioseo_indexable[ $aioseo_key ] ) ) {
247 $indexable->{$yoast_key} = $this->replacevar_handler->transform( $aioseo_indexable[ $aioseo_key ] );
248 }
249 }
250
251 return $indexable;
252 }
253
254 /**
255 * Returns the number of objects that will be imported in a single importing pass.
256 *
257 * @return int The limit.
258 */
259 public function get_limit() {
260 /**
261 * Filter 'wpseo_aioseo_post_indexation_limit' - Allow filtering the number of posts indexed during each indexing pass.
262 *
263 * @api int The maximum number of posts indexed.
264 */
265 $limit = \apply_filters( 'wpseo_aioseo_post_indexation_limit', 25 );
266
267 if ( ! \is_int( $limit ) || $limit < 1 ) {
268 $limit = 25;
269 }
270
271 return $limit;
272 }
273
274 /**
275 * Creates a query for gathering AiOSEO data from the database.
276 *
277 * @param int $limit The maximum number of unimported objects to be returned.
278 * @param bool $just_detect Whether we want to just detect if there are unimported objects. If false, we want to actually import them too.
279 *
280 * @return string The query to use for importing or counting the number of items to import.
281 */
282 public function query( $limit = false, $just_detect = false ) {
283 $table = $this->get_table();
284
285 $select_statement = 'id';
286 if ( ! $just_detect ) {
287 // If we want to import too, we need the actual needed data from AIOSEO indexables.
288 $needed_data = \array_keys( $this->aioseo_to_yoast_map );
289 \array_push( $needed_data, 'id', 'post_id' );
290
291 $select_statement = \implode( ', ', $needed_data );
292 }
293
294 $cursor_id = $this->get_cursor_id();
295 $cursor = $this->get_cursor( $this->options, $cursor_id );
296
297 /**
298 * Filter 'wpseo_aioseo_post_cursor' - Allow filtering the value of the aioseo post import cursor.
299 *
300 * @api int The value of the aioseo post import cursor.
301 */
302 $cursor = \apply_filters( 'wpseo_aioseo_post_import_cursor', $cursor );
303
304 $replacements = [ $cursor ];
305
306 $limit_statement = '';
307 if ( ! empty( $limit ) ) {
308 $replacements[] = $limit;
309 $limit_statement = ' LIMIT %d';
310 }
311
312 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: There is no unescaped user input.
313 return $this->wpdb->prepare(
314 "SELECT {$select_statement} FROM {$table} WHERE id > %d ORDER BY id{$limit_statement}",
315 $replacements
316 );
317 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
318 }
319 }
320