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