PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / trunk
Pods – Custom Content Types and Fields vtrunk
trunk 1.14.8 2.7.31.3 2.8.23.3 2.9.19.3 3.0.10.3 3.1.4.1 3.2.0 3.2.1 3.2.1.1 3.2.2 3.2.4 3.2.5 3.2.6 3.2.7 3.2.7.1 3.2.8 3.2.8.1 3.2.8.2 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9
pods / classes / PodsTermSplitting.php
pods / classes Last commit date
cli 4 months ago fields 4 months ago widgets 4 months ago Pods.php 4 months ago PodsAPI.php 4 months ago PodsAdmin.php 4 months ago PodsArray.php 4 months ago PodsComponent.php 4 months ago PodsComponents.php 4 months ago PodsData.php 4 months ago PodsField.php 4 months ago PodsForm.php 4 months ago PodsI18n.php 4 months ago PodsInit.php 4 months ago PodsMeta.php 4 months ago PodsMigrate.php 4 months ago PodsRESTFields.php 4 months ago PodsRESTHandlers.php 4 months ago PodsTermSplitting.php 4 months ago PodsUI.php 4 months ago PodsView.php 4 months ago
PodsTermSplitting.php
511 lines
1 <?php
2
3 // Don't load directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 die( '-1' );
6 }
7
8 /**
9 * @package Pods
10 */
11 class Pods_Term_Splitting {
12
13 /** @var int ID of the formerly shared term */
14 private $term_id;
15
16 /** @var int ID of the new term created for the $term_taxonomy_id */
17 private $new_term_id;
18
19 /** @var string Taxonomy for the split term */
20 private $taxonomy;
21
22 /** @var string */
23 private $progress_option_name;
24
25 /** @var array */
26 private $previous_progress = [];
27
28 /**
29 * @param int $term_id ID of the formerly shared term.
30 * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
31 * @param string $taxonomy Taxonomy for the split term.
32 */
33 public function __construct( $term_id, $new_term_id, $taxonomy ) {
34 $this->term_id = $term_id;
35 $this->new_term_id = $new_term_id;
36 $this->taxonomy = $taxonomy;
37
38 $this->progress_option_name = "_pods_term_split_{$term_id}_{$taxonomy}";
39 }
40
41 /**
42 *
43 */
44 public function split_shared_term() {
45 // Stash any previous progress
46 $this->previous_progress = $this->get_progress();
47
48 if ( empty( $this->previous_progress ) ) {
49 $this->append_progress( 'started' );
50 $this->append_progress( "new term ID: {$this->new_term_id}" );
51 }
52
53 // Get the Pod information if the taxonomy is a Pod
54 $taxonomy_pod = $this->get_pod_info();
55
56 // Is the taxonomy a Pod?
57 if ( is_array( $taxonomy_pod ) || $taxonomy_pod instanceof Pods\Whatsit ) {
58 $this->update_podsrel_taxonomy( $taxonomy_pod['id'] );
59
60 // Update the Pods table if the taxonomy is a table based Pod
61 if ( 'table' === $taxonomy_pod['storage'] ) {
62 $this->update_pod_table( $taxonomy_pod['pod_table'] );
63 }
64 }
65
66 // Track down all fields related to the target taxonomy and update stored term IDs as necessary
67 $this->update_relationships_to_term();
68
69 // Clean up
70 $this->delete_progress();
71 }
72
73 /**
74 * Return the Pod information for the specified taxonomy, or null if the taxonomy isn't a Pod
75 *
76 * @return array|bool|mixed|null
77 */
78 private function get_pod_info() {
79 $pod_info = null;
80
81 try {
82 $api = pods_api();
83
84 if ( $api->pod_exists( [ 'name' => $this->taxonomy ] ) ) {
85 // Load the taxonomy Pod
86 $params = [
87 'name' => $this->taxonomy,
88 ];
89
90 $pod_info = $api->load_pod( $params, false );
91 }
92 } catch ( Exception $exception ) {
93 pods_debug_log( $exception );
94 }
95
96 return $pod_info;
97 }
98
99 /**
100 * @param int $pod_id
101 */
102 private function update_podsrel_taxonomy( $pod_id ) {
103 /** @var wpdb $wpdb */
104 global $wpdb;
105
106 $task = "update_podsrel_taxonomy_{$pod_id}";
107
108 if ( ! $this->have_done( $task ) ) {
109 if ( pods_podsrel_enabled( null, __METHOD__ ) ) {
110 // UPDATE {$wpdb->prefix}podsrel SET item_id = {$new_term_id} WHERE pod_id = {$pod_id} AND item_id = {$term_id}
111 $table = "{$wpdb->prefix}podsrel";
112
113 $data = [
114 'item_id' => $this->new_term_id,
115 ];
116
117 $where = [
118 'pod_id' => $pod_id,
119 'item_id' => $this->term_id,
120 ];
121
122 $format = '%d';
123 $where_format = '%d';
124
125 $wpdb->update( $table, $data, $where, $format, $where_format );
126 }
127
128 /**
129 * Allow hooking into the term splitting process for taxonomy.
130 *
131 * @since 2.8.0
132 *
133 * @param int $pod_id The pod ID for the taxonomy.
134 * @param int $term_id The current term ID being split.
135 * @param int $new_term_id The new term ID.
136 * @param string $task The task being done.
137 */
138 do_action( 'pods_term_splitting_update_taxonomy', $pod_id, $this->term_id, $this->new_term_id, $task );
139
140 $this->append_progress( $task );
141 }
142 }
143
144 /**
145 * @param string $pod_table
146 */
147 private function update_pod_table( $pod_table ) {
148 /** @var wpdb $wpdb */
149 global $wpdb;
150
151 $task = "update_pod_table_{$pod_table}";
152
153 if ( ! $this->have_done( $task ) ) {
154 // Prime the values and update
155 $data = [
156 'id' => $this->new_term_id,
157 ];
158
159 $where = [
160 'id' => $this->term_id,
161 ];
162
163 $format = '%d';
164 $where_format = '%d';
165
166 $wpdb->update( $pod_table, $data, $where, $format, $where_format );
167
168 $this->append_progress( $task );
169 }
170 }
171
172 /**
173 * Track down all fields related to the target taxonomy and update stored term IDs as necessary
174 */
175 private function update_relationships_to_term() {
176 // Loop through all Pods
177 try {
178 $all_pods = pods_api()->load_pods();
179 } catch ( Exception $exception ) {
180 pods_debug_log( $exception );
181
182 return;
183 }
184
185 if ( ! is_array( $all_pods ) ) {
186 return;
187 }
188
189 foreach ( $all_pods as $this_pod_id => $this_pod ) {
190 // Loop through all fields in this Pod
191 foreach ( $this_pod['fields'] as $this_field_name => $this_field ) {
192 // Ignore everything except relationship fields to this taxonomy
193 if ( 'pick' !== $this_field['type'] || 'taxonomy' !== $this_field['pick_object'] || $this->taxonomy !== $this_field['pick_val'] ) {
194 continue;
195 }
196
197 // Update the term ID in podsrel everywhere it is the value for this field
198 $this->update_podsrel_related_term( $this_field['id'] );
199
200 // Fix-up any special-case relationships that store term IDs in their own meta table and/or serialized
201 switch ( $this_pod['type'] ) {
202 case 'post_type':
203 $this->update_postmeta( $this_pod['name'], $this_field_name );
204 break;
205
206 case 'comment':
207 $this->update_commentmeta( $this_field_name );
208 break;
209
210 case 'user':
211 $this->update_usermeta( $this_field_name );
212 break;
213
214 case 'settings':
215 $this->update_setting_meta( $this_pod['name'], $this_field_name );
216 break;
217 }
218 }//end foreach
219 }//end foreach
220
221 }
222
223 /**
224 * @param int $field_id
225 */
226 private function update_podsrel_related_term( $field_id ) {
227 /** @var wpdb $wpdb */
228 global $wpdb;
229
230 $task = "update_podsrel_related_term_{$field_id}";
231
232 if ( ! $this->have_done( $task ) ) {
233 if ( pods_podsrel_enabled( null, __METHOD__ ) ) {
234 // UPDATE {$wpdb->prefix}podsrel SET related_item_id = {$new_term_id} WHERE field_id = {$field_id} AND related_item_id = {$term_id}
235 $table = "{$wpdb->prefix}podsrel";
236
237 $data = [
238 'related_item_id' => $this->new_term_id,
239 ];
240
241 $where = [
242 'field_id' => $field_id,
243 'related_item_id' => $this->term_id,
244 ];
245
246 $format = '%d';
247 $where_format = '%d';
248
249 $wpdb->update( $table, $data, $where, $format, $where_format );
250 }
251
252 /**
253 * Allow hooking into the term splitting process for taxonomy by related term.
254 *
255 * @since 2.8.0
256 *
257 * @param int $field_id The field ID for the relationship.
258 * @param int $term_id The current term ID being split.
259 * @param int $new_term_id The new term ID.
260 * @param string $task The task being done.
261 */
262 do_action( 'pods_term_splitting_update_related_term', $field_id, $this->term_id, $this->new_term_id, $task );
263
264 $this->append_progress( $task );
265 }
266 }
267
268 /**
269 * Called for all fields related to the target taxonomy that are in a post_type
270 *
271 * @param string $pod_name
272 * @param string $field_name
273 */
274 private function update_postmeta( $pod_name, $field_name ) {
275 /** @var wpdb $wpdb */
276 global $wpdb;
277
278 // Fix up the unserialized data
279 $task = "update_postmeta_{$pod_name}_{$field_name}_unserialized";
280 if ( ! $this->have_done( $task ) ) {
281 $wpdb->query( $wpdb->prepare( "
282 UPDATE
283 {$wpdb->postmeta} AS meta
284 LEFT JOIN {$wpdb->posts} AS t
285 ON meta.post_id = t.ID
286 SET
287 meta_value = %s
288 WHERE
289 meta_key = %s
290 AND meta_value = %s
291 AND t.post_type = %s
292 ", $this->new_term_id, $field_name, $this->term_id, $pod_name ) );
293
294 $this->append_progress( $task );
295 }//end if
296
297 // Fix up the serialized data
298 $task = "update_postmeta_{$pod_name}_{$field_name}_serialized";
299 if ( ! $this->have_done( $task ) ) {
300 $meta_key = sprintf( '_pods_%s', $field_name );
301 $target_serialized = sprintf( ';i:%s;', $this->term_id );
302 $replace_serialized = sprintf( ';i:%s;', $this->new_term_id );
303
304 $wpdb->query( $wpdb->prepare( "
305 UPDATE
306 {$wpdb->postmeta} AS meta
307 LEFT JOIN {$wpdb->posts} AS t
308 ON meta.post_id = t.ID
309 SET
310 meta.meta_value = REPLACE( meta.meta_value, %s, %s )
311 WHERE
312 meta.meta_key = %s
313 AND t.post_type = %s
314 AND meta_value LIKE %s
315 ", $target_serialized, $replace_serialized, $meta_key, $pod_name, '%' . pods_sanitize_like( $target_serialized ) . '%' ) );
316
317 $this->append_progress( $task );
318 }//end if
319
320 }
321
322 /**
323 * Called for all fields related to the target taxonomy that are in a comment Pod
324 *
325 * @param string $field_name
326 */
327 private function update_commentmeta( $field_name ) {
328 /** @var wpdb $wpdb */
329 global $wpdb;
330
331 // Fix up the unserialized data
332 $task = "update_commentmeta_{$field_name}_unserialized";
333 if ( ! $this->have_done( $task ) ) {
334 $table = $wpdb->commentmeta;
335 $data = [ 'meta_value' => $this->new_term_id ]; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
336 $where = [
337 'meta_key' => $field_name, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
338 'meta_value' => $this->term_id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
339 ];
340 $format = '%s';
341 $where_format = [ '%s', '%s' ];
342 $wpdb->update( $table, $data, $where, $format, $where_format );
343
344 $this->append_progress( $task );
345 }
346
347 // Fix up the serialized data
348 $task = "update_commentmeta_{$field_name}_serialized";
349 if ( ! $this->have_done( $task ) ) {
350 $meta_key = sprintf( '_pods_%s', $field_name );
351 $target_serialized = sprintf( ';i:%s;', $this->term_id );
352 $replace_serialized = sprintf( ';i:%s;', $this->new_term_id );
353
354 $wpdb->query( $wpdb->prepare( "
355 UPDATE
356 {$wpdb->commentmeta}
357 SET
358 meta_value = REPLACE( meta_value, %s, %s )
359 WHERE
360 meta_key = %s
361 AND meta_value LIKE %s
362 ", $target_serialized, $replace_serialized, $meta_key, '%' . pods_sanitize_like( $target_serialized ) . '%' ) );
363
364 $this->append_progress( $task );
365 }//end if
366
367 }
368
369 /**
370 * Called for all fields related to the target taxonomy that are in a user Pod
371 *
372 * @param string $field_name
373 */
374 private function update_usermeta( $field_name ) {
375 /** @var wpdb $wpdb */
376 global $wpdb;
377
378 // Fix up the unserialized data
379 $task = "update_usermeta_{$field_name}_unserialized";
380 if ( ! $this->have_done( $task ) ) {
381 $table = $wpdb->usermeta;
382 $data = [ 'meta_value' => $this->new_term_id ]; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
383 $where = [
384 'meta_key' => $field_name, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
385 'meta_value' => $this->term_id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
386 ];
387 $format = '%s';
388 $where_format = [ '%s', '%s' ];
389 $wpdb->update( $table, $data, $where, $format, $where_format );
390
391 $this->append_progress( $task );
392 }
393
394 // Fix up the serialized data
395 $task = "update_usermeta_{$field_name}_serialized";
396 if ( ! $this->have_done( $task ) ) {
397 $meta_key = sprintf( '_pods_%s', $field_name );
398 $target_serialized = sprintf( ';i:%s;', $this->term_id );
399 $replace_serialized = sprintf( ';i:%s;', $this->new_term_id );
400
401 $wpdb->query( $wpdb->prepare( "
402 UPDATE
403 {$wpdb->usermeta}
404 SET
405 meta_value = REPLACE( meta_value, %s, %s )
406 WHERE
407 meta_key = %s
408 AND meta_value LIKE %s
409 ", $target_serialized, $replace_serialized, $meta_key, '%' . pods_sanitize_like( $target_serialized ) . '%' ) );
410
411 $this->append_progress( $task );
412 }//end if
413
414 }
415
416 /**
417 * Called for all fields related to the target taxonomy that are in a user Pod
418 *
419 * @param string $pod_name
420 * @param string $field_name
421 */
422 private function update_setting_meta( $pod_name, $field_name ) {
423 /** @var wpdb $wpdb */
424 global $wpdb;
425
426 $option_name = "{$pod_name}_{$field_name}";
427
428 // Fix up the unserialized data
429 $task = "update_setting_meta_{$pod_name}_{$field_name}_unserialized";
430 if ( ! $this->have_done( $task ) ) {
431 // UPDATE {$wpdb->options} SET option_value = '{$new_term_id}' WHERE option_name = '{$pod_name}_{$field_name}' AND option_value = '{$term_id}'
432 $table = $wpdb->options;
433 $data = [ 'option_value' => $this->new_term_id ];
434 $where = [
435 'option_name' => $option_name,
436 'option_value' => $this->term_id,
437 ];
438 $format = '%s';
439 $where_format = [ '%s', '%s' ];
440 $wpdb->update( $table, $data, $where, $format, $where_format );
441
442 $this->append_progress( $task );
443 }
444
445 // Fix up the serialized data
446 $task = "update_setting_meta_{$pod_name}_{$field_name}_serialized";
447 if ( ! $this->have_done( $task ) ) {
448 $target_serialized = sprintf( ';i:%s;', $this->term_id );
449 $replace_serialized = sprintf( ';i:%s;', $this->new_term_id );
450
451 $wpdb->query( $wpdb->prepare( "
452 UPDATE
453 {$wpdb->options}
454 SET
455 option_value = REPLACE( option_value, %s, %s )
456 WHERE
457 option_name = %s
458 AND option_value LIKE %s
459 ", $target_serialized, $replace_serialized, $option_name, '%' . pods_sanitize_like( $target_serialized ) . '%' ) );
460
461 $this->append_progress( $task );
462 }//end if
463
464 }
465
466 /**
467 * @param string $task_name
468 *
469 * @return bool
470 */
471 private function have_done( $task_name ) {
472 return in_array( $task_name, $this->previous_progress, true );
473 }
474
475 /**
476 * @return array
477 */
478 private function get_progress() {
479 return get_option( $this->progress_option_name, [] );
480 }
481
482 /**
483 * @param $data
484 */
485 private function append_progress( $data ) {
486 // Get the current progress array
487 $current_progress = $this->get_progress();
488 if ( ! is_array( $current_progress ) ) {
489 $current_progress = [];
490 }
491
492 // Tack on the new data
493 $updated_progress = array_merge( $current_progress, [ $data ] );
494
495 // Note: we don't want autoload set and you cannot specify autoload via update_option
496 if ( ! empty( $current_progress ) && is_array( $current_progress ) ) {
497 update_option( $this->progress_option_name, $updated_progress );
498 } else {
499 add_option( $this->progress_option_name, $updated_progress, '', false );
500 }
501 }
502
503 /**
504 *
505 */
506 private function delete_progress() {
507 delete_option( $this->progress_option_name );
508 }
509
510 }
511