Association_Field   F
last analyzed

Complexity

Total Complexity 64

Size/Duplication

Total Lines 833
Duplicated Lines 2.88 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 64
lcom 1
cbo 3
dl 24
loc 833
ccs 0
cts 315
cp 0
rs 3.047
c 0
b 0
f 0

36 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A set_value() 0 4 1
A set_value_from_input() 0 11 3
A get_value_string_for_legacy_value() 0 14 3
A value_string_to_property_array() 0 19 5
A value_string_array_to_value_set() 0 24 5
A get_options() 0 52 3
A get_types() 0 3 1
A get_min() 0 3 1
A get_max() 0 3 1
A set_max() 0 4 1
A get_duplicates_allowed() 0 3 1
A get_term_options_sql_select_clause() 0 3 1
A set_types() 0 4 1
A set_min() 0 4 1
A set_items_per_page() 0 4 1
A get_items_per_page() 0 3 1
A set_duplicates_allowed() 0 4 1
A allow_duplicates() 0 3 1
A value_to_json() 0 16 3
A to_json() 0 13 1
A get_post_options_sql() 0 38 1
A get_post_options_sql_select_clause() 0 5 1
A get_term_options_sql() 0 33 1
A get_term_options_sql_clauses() 0 5 1
A get_user_options_sql() 0 26 1
A get_comment_options_sql() 0 28 1
A get_comments_clauses() 0 9 1
A get_thumbnail_by_type() 0 9 2
A get_title_by_type() 0 33 5
A get_item_label() 0 22 4
A get_object_edit_link() 0 26 5
A format_post_option() 0 12 1
A format_term_option() 0 12 1
A format_comment_option() 12 12 1
A format_user_option() 12 12 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Association_Field often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Association_Field, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Carbon_Fields\Field;
4
5
use Carbon_Fields\Value_Set\Value_Set;
6
use WP_Query;
7
use WP_Term_Query;
8
use WP_User_Query;
9
use WP_Comment_Query;
10
11
/**
12
 * Association field class.
13
 * Allows selecting and manually sorting entries from various types:
14
 *  - Posts
15
 *  - Terms
16
 *  - Users
17
 *  - Comments
18
 */
19
class Association_Field extends Field {
20
	/**
21
	 * WP_Toolset instance for WP data loading
22
	 *
23
	 * @var \Carbon_Fields\Toolset\WP_Toolset
24
	 */
25
	protected $wp_toolset;
26
27
	/**
28
	 * Min number of selected items allowed. -1 for no limit
29
	 *
30
	 * @var integer
31
	 */
32
	protected $min = -1;
33
34
	/**
35
	 * Max number of selected items allowed. -1 for no limit
36
	 *
37
	 * @var integer
38
	 */
39
	protected $max = -1;
40
41
	/**
42
	 * Max items per page. -1 for no limit
43
	 *
44
	 * @var integer
45
	 */
46
	protected $items_per_page = 20;
47
48
	/**
49
	 * Allow items to be added multiple times
50
	 *
51
	 * @var boolean
52
	 */
53
	protected $duplicates_allowed = false;
54
55
	/**
56
	 * Default field value
57
	 *
58
	 * @var array
59
	 */
60
	protected $default_value = array();
61
62
	/**
63
	 * Types of entries to associate with.
64
	 * @var array
65
	 */
66
	protected $types = array(
67
		array(
68
			'type' => 'post',
69
			'post_type' => 'post',
70
		),
71
	);
72
73
	/**
74
	 * Create a field from a certain type with the specified label.
75
	 *
76
	 * @param string $type  Field type
77
	 * @param string $name  Field name
78
	 * @param string $label Field label
79
	 */
80
	public function __construct( $type, $name, $label ) {
81
		$this->wp_toolset = \Carbon_Fields\Carbon_Fields::resolve( 'wp_toolset' );
82
		$this->set_value_set( new Value_Set( Value_Set::TYPE_VALUE_SET, array( 'type' => '', 'subtype' => '', 'id' => 0 ) ) );
83
		parent::__construct( $type, $name, $label );
84
	}
85
86
	/**
87
	 * {@inheritDoc}
88
	 */
89
	public function set_value_from_input( $input ) {
90
		$value = array();
91
		if ( isset( $input[ $this->get_name() ] ) ) {
92
			$value = stripslashes_deep( $input[ $this->get_name() ] );
93
			if ( is_array( $value ) ) {
94
				$value = array_values( $value );
95
			}
96
		}
97
		$this->set_value( $value );
98
		return $this;
99
	}
100
101
	/**
102
	 * {@inheritDoc}
103
	 */
104
	public function set_value( $value ) {
105
		$value = $this->value_string_array_to_value_set( $value );
106
		return parent::set_value( $value );
107
	}
108
109
	/**
110
	 * Get value string for legacy value
111
	 *
112
	 * @param string $legacy_value
113
	 * @return string
114
	 */
115
	protected function get_value_string_for_legacy_value( $legacy_value ) {
116
		$entry_type = 'post';
117
		$entry_subtype = 'post';
118
119
		// attempt to find a suitable type that is registered to this field as post type is not stored for legacy data
120
		foreach ( $this->types as $type ) {
121
			if ( $type['type'] === $entry_type ) {
122
				$entry_subtype = $type['post_type'];
123
				break;
124
			}
125
		}
126
127
		return $entry_type . ':' . $entry_subtype . ':' . $legacy_value;
128
	}
129
130
	/**
131
	 * Convert a colon:separated:string into its expected components
132
	 * Used for backwards compatibility to CF 1.5
133
	 *
134
	 * @param string $value_string
135
	 * @return array
136
	 */
137
	protected function value_string_to_property_array( $value_string ) {
138
		if ( is_numeric( $value_string ) ) {
139
			// we are dealing with legacy data that only contains a post ID
140
			$value_string = $this->get_value_string_for_legacy_value( $value_string );
141
		}
142
143
		$value_pieces = explode( ':', $value_string );
144
		$type = isset( $value_pieces[0] ) ? $value_pieces[0] : 'post';
145
		$subtype = isset( $value_pieces[1] ) ? $value_pieces[1] : 'post';
146
		$id = isset( $value_pieces[2] ) ? $value_pieces[2] : 0;
147
148
		$property_array = array(
149
			Value_Set::VALUE_PROPERTY => $value_string,
150
			'type' => $type,
151
			'subtype' => $subtype,
152
			'id' => intval( $id ),
153
		);
154
		return $property_array;
155
	}
156
157
	/**
158
	 * Convert a colon:separated:string into its expected components
159
	 * Used for backwards compatibility to CF 1.5
160
	 *
161
	 * @param array $value_string_array
162
	 * @return array<array>
163
	 */
164
	protected function value_string_array_to_value_set( $value_string_array ) {
165
		$value_set = array();
166
		foreach ( $value_string_array as $raw_value_entry ) {
167
			$value_string = $raw_value_entry;
168
169
			if ( is_array( $raw_value_entry ) ) {
170
				if ( isset( $raw_value_entry['type'] ) ) {
171
					// array is already in suitable format
172
					$value_set[] = $raw_value_entry;
173
					continue;
174
				}
175
				$value_string = $raw_value_entry[ Value_Set::VALUE_PROPERTY ];
176
			}
177
			$value_string = trim( $value_string );
178
			if ( empty( $value_string ) ) {
179
				continue;
180
			}
181
182
			$property_array = $this->value_string_to_property_array( $value_string );
183
			$value_set[] = $property_array;
184
		}
185
186
		return $value_set;
187
	}
188
189
	/**
190
	 * Generate the item options.
191
	 *
192
	 * @access public
193
	 *
194
	 * @param  array $args
195
	 * @return array $options The selectable options of the association field.
196
	 */
197
	public function get_options( $args = array() ) {
198
		global $wpdb;
199
200
		$args = wp_parse_args( $args, array(
201
			'page' => 1,
202
			'term' => '',
203
		) );
204
205
		$sql_queries = array();
206
207
		foreach ( $this->types as $type ) {
208
			$type_args = array_merge( $type, array(
209
				'term' => $args['term'],
210
			) );
211
212
			$callback = "get_{$type['type']}_options_sql";
213
214
			$sql_statement = $this->$callback( $type_args );
215
216
			$sql_queries[] = $sql_statement;
217
		}
218
219
		$sql_queries = implode( " UNION ", $sql_queries );
220
221
		$per_page = $this->get_items_per_page();
222
		$offset   = ($args['page'] - 1) * $per_page;
223
224
		$sql_queries .= " ORDER BY `title` ASC LIMIT {$per_page} OFFSET {$offset}";
225
226
		$results = $wpdb->get_results( $sql_queries );
227
228
		$options = array();
229
230
		foreach ( $results as $result ) {
231
			$callback = "format_{$result->type}_option";
232
233
			$options[] = $this->$callback( $result );
234
		}
235
236
		/**
237
		 * Filter the final list of options, available to a certain association field.
238
		 *
239
		 * @param array  $options Unfiltered options items.
240
		 * @param string $name Name of the association field.
241
		 */
242
		$options = apply_filters( 'carbon_fields_association_field_options', $options, $this->get_base_name() );
243
244
		return array(
245
			'total_options' => $wpdb->get_var( "SELECT COUNT(*) FROM (" . preg_replace( '~(LIMIT .*)$~', '', $sql_queries ) . ") as t" ),
246
			'options'       => $options,
247
		);
248
	}
249
250
	/**
251
	 * Get the types.
252
	 *
253
	 * @access public
254
	 *
255
	 * @return array
256
	 */
257
	public function get_types() {
258
		return $this->types;
259
	}
260
261
	/**
262
	 * Modify the types.
263
	 *
264
	 * @param  array $types New types
265
	 * @return self  $this
266
	 */
267
	public function set_types( $types ) {
268
		$this->types = $types;
269
		return $this;
270
	}
271
272
	/**
273
	 * Get the minimum allowed number of selected entries.
274
	 *
275
	 * @return int
276
	 */
277
	public function get_min() {
278
		return $this->min;
279
	}
280
281
	/**
282
	 * Set the minimum allowed number of selected entries.
283
	 *
284
	 * @param  int   $min
285
	 * @return self  $this
286
	 */
287
	public function set_min( $min ) {
288
		$this->min = intval( $min );
289
		return $this;
290
	}
291
292
	/**
293
	 * Get the maximum allowed number of selected entries.
294
	 *
295
	 * @return int
296
	 */
297
	public function get_max() {
298
		return $this->max;
299
	}
300
301
	/**
302
	 * Set the maximum allowed number of selected entries.
303
	 *
304
	 * @param  int   $max
305
	 * @return self  $this
306
	 */
307
	public function set_max( $max ) {
308
		$this->max = intval( $max );
309
		return $this;
310
	}
311
312
	/**
313
	 * Set the items per page.
314
	 *
315
	 * @param  int   $items_per_page
316
	 * @return self  $this
317
	 */
318
	public function set_items_per_page( $items_per_page ) {
319
		$this->items_per_page = intval( $items_per_page );
320
		return $this;
321
	}
322
323
	/**
324
	 * Get the items per page.
325
	 *
326
	 * @return int
327
	 */
328
	public function get_items_per_page() {
329
		return $this->items_per_page;
330
	}
331
332
	/**
333
	 * Get whether entry duplicates are allowed.
334
	 *
335
	 * @return boolean
336
	 */
337
	public function get_duplicates_allowed() {
338
		return $this->duplicates_allowed;
339
	}
340
341
	/**
342
	 * Set whether entry duplicates are allowed.
343
	 *
344
	 * @param  boolean $allowed
345
	 * @return self    $this
346
	 */
347
	public function set_duplicates_allowed( $allowed ) {
348
		$this->duplicates_allowed = $allowed;
349
		return $this;
350
	}
351
352
	/**
353
	 * Specify whether to allow each entry to be selected multiple times.
354
	 * Backwards-compatibility alias.
355
	 *
356
	 * @param  boolean $allow
357
	 * @return self    $this
358
	 */
359
	public function allow_duplicates( $allow = true ) {
360
		return $this->set_duplicates_allowed( $allow );
361
	}
362
363
	/**
364
	 * Converts the field values into a usable associative array.
365
	 *
366
	 * The association data is saved in the database in the following format:
367
	 * 	array (
368
	 *		0 => 'post:page:4',
369
	 *		1 => 'term:category:2',
370
	 *		2 => 'user:user:1',
371
	 * 	)
372
	 * where the value of each array item contains:
373
	 * 	- Type of data (post, term, user or comment)
374
	 * 	- Subtype of data (the particular post type or taxonomy)
375
	 * 	- ID of the item (the database ID of the item)
376
	 */
377
	protected function value_to_json() {
378
		$value_set = $this->get_value();
379
		$value = array();
380
		foreach ( $value_set as $entry ) {
381
			$item = array(
382
				'type' => $entry['type'],
383
				'subtype' => $entry['subtype'],
384
				'id' => intval( $entry['id'] ),
385
				'title' => $this->get_title_by_type( $entry['id'], $entry['type'], $entry['subtype'] ),
386
				'label' => $this->get_item_label( $entry['id'], $entry['type'], $entry['subtype'] ),
387
				'is_trashed' => ( $entry['type'] == 'post' && get_post_status( $entry['id'] ) === 'trash' ),
388
			);
389
			$value[] = $item;
390
		}
391
		return $value;
392
	}
393
394
	/**
395
	 * Convert the field data into JSON representation.
396
	 * @param  bool $load Whether to load data from the datastore.
397
	 * @return mixed      The JSON field data.
398
	 */
399
	public function to_json( $load ) {
400
		$field_data = parent::to_json( $load );
401
402
		$field_data = array_merge( $field_data, array(
403
			'value'              => $this->value_to_json(),
404
			'options'            => $this->get_options(),
405
			'min'                => $this->get_min(),
406
			'max'                => $this->get_max(),
407
			'duplicates_allowed' => $this->duplicates_allowed,
408
		) );
409
410
		return $field_data;
411
	}
412
413
	/**
414
	 * Helper method to prepare the SQL needed to search for options of type 'post'.
415
	 *
416
	 * Creates a 'fake' WP_Query with only one result in order to catch the SQL
417
	 * that it will construct in order to support all of the WP_Query arguments.
418
	 *
419
	 * @access public
420
	 *
421
	 * @param  array  $args
422
	 * @return string
423
	 */
424
	public function get_post_options_sql( $args = array() ) {
425
		$type        = $args['type'];
426
		$post_type   = $args['post_type'];
427
		$search_term = $args['term'];
428
429
		unset( $args['type'], $args['post_type'], $args['term'] );
430
431
		/**
432
		 * Filter the default query when fetching posts for a particular field.
433
		 *
434
		 * @param array $args The parameters, passed to WP_Query::__construct().
435
		 */
436
		$filter_name = 'carbon_fields_association_field_options_' . $this->get_base_name() . '_' . $type . '_' . $post_type;
437
438
		$args = apply_filters( $filter_name, array(
439
			'post_type'        => $post_type,
440
			'posts_per_page'   => 1,
441
			'fields'           => 'ids',
442
			'suppress_filters' => false,
443
			's'                => $search_term,
444
		) );
445
446
		add_filter( 'posts_fields_request', array( $this, 'get_post_options_sql_select_clause' ) );
447
448
		add_filter( 'posts_groupby_request', '__return_empty_string' );
449
		add_filter( 'posts_orderby_request', '__return_empty_string' );
450
		add_filter( 'post_limits_request', '__return_empty_string' );
451
452
		$posts_query = new WP_Query( $args );
453
454
		remove_filter( 'posts_fields_request', array( $this, 'get_post_options_sql_select_clause' ) );
455
456
		remove_filter( 'posts_groupby_request', '__return_empty_string' );
457
		remove_filter( 'posts_orderby_request', '__return_empty_string' );
458
		remove_filter( 'post_limits_request', '__return_empty_string' );
459
460
		return $posts_query->request;
461
	}
462
463
	/**
464
	 * Modify the "SELECT" columns for the WP_Query.
465
	 *
466
	 * @access public
467
	 *
468
	 * @param  string $fields
469
	 * @return string
470
	 */
471
	public function get_post_options_sql_select_clause( $fields ) {
472
		global $wpdb;
473
474
		return $fields . " , `{$wpdb->posts}`.`post_title` AS `title`, 'post' AS `type`, `{$wpdb->posts}`.`post_type` AS `subtype` ";
475
	}
476
477
	/**
478
	 * Helper method to prepare the SQL needed to search for options of type 'term'.
479
	 *
480
	 * Creates a 'fake' WP_Term_Query with only one result in order to catch the SQL
481
	 * that it will construct in order to support all of the WP_Term_Query arguments.
482
	 *
483
	 * @access public
484
	 *
485
	 * @param  array  $args
486
	 * @return string
487
	 */
488
	public function get_term_options_sql( $args = array() ) {
489
		$type        = $args['type'];
490
		$taxonomy    = $args['taxonomy'];
491
		$search_term = $args['term'];
492
493
		unset( $args['type'], $args['taxonomy'], $args['term'] );
494
495
		/**
496
		 * Filter the default parameters when fetching terms for a particular field.
497
		 *
498
		 * @param array $args The parameters, passed to WP_Term_Query::__construct().
499
		 */
500
		$filter_name = 'carbon_fields_association_field_options_' . $this->get_base_name() . '_' . $type . '_' . $taxonomy;
501
502
		$args = apply_filters( $filter_name, array(
503
			'hide_empty'             => 0,
504
			'taxonomy'               => $taxonomy,
505
			'fields'                 => 'count',
506
			'number'                 => 1,
507
			'search'                 => $search_term,
508
			'update_term_meta_cache' => false,
509
		) );
510
511
		add_filter( 'get_terms_fields', array( $this, 'get_term_options_sql_select_clause' ) );
512
		add_filter( 'terms_clauses', array( $this, 'get_term_options_sql_clauses' ) );
513
514
		$terms_query = new WP_Term_Query( $args );
515
516
		remove_filter( 'get_terms_fields', array( $this, 'get_term_options_sql_select_clause' ) );
517
		remove_filter( 'terms_clauses', array( $this, 'get_term_options_sql_clauses' ) );
518
519
		return $terms_query->request;
520
	}
521
522
	/**
523
	 * Modify the "SELECT" columns for the WP_Term_Query.
524
	 *
525
	 * @access public
526
	 *
527
	 * @param  array  $fields
528
	 * @return array
529
	 */
530
	public function get_term_options_sql_select_clause( $fields ) {
0 ignored issues
show
Unused Code introduced by
The parameter $fields is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
531
		return array( '`t`.`term_id` AS `ID`', '`t`.`name` AS `title`', '\'term\' as `type`', '`tt`.`taxonomy` AS `subtype`' );
532
	}
533
534
	/**
535
	 * Modify the clauses for the SQL request of the WP_Term_Query.
536
	 *
537
	 * @access public
538
	 *
539
	 * @param  array  $clauses
540
	 * @return array
541
	 */
542
	public function get_term_options_sql_clauses( $clauses ) {
543
		unset( $clauses['orderby'], $clauses['order'], $clauses['limits'] );
544
545
		return $clauses;
546
	}
547
548
	/**
549
	 * Helper method to prepare the SQL needed to search for options of type 'user'.
550
	 *
551
	 * Creates a 'fake' WP_User_Query with only one result in order to catch the SQL
552
	 * that it will construct in order to support all of the WP_User_Query arguments.
553
	 *
554
	 * @access public
555
	 *
556
	 * @param  array  $args
557
	 * @return string
558
	 */
559
	public function get_user_options_sql( $args = array() ) {
560
		global $wpdb;
561
562
		$type        = $args['type'];
563
		$search_term = $args['term'];
564
565
		unset( $args['type'], $args['term'], $args['subtype'] );
566
567
		/**
568
		 * Filter the default parameters when fetching terms for a particular field.
569
		 *
570
		 * @param array $args The parameters, passed to WP_User_Query::__construct().
571
		 */
572
		$filter_name = 'carbon_fields_association_field_options_' . $this->get_base_name() . '_' . $type;
573
574
		$args = apply_filters( $filter_name, array(
575
			'fields' => 'ID',
576
			'number' => 1,
577
			'search' => $search_term,
578
		) );
579
580
		$users_query = new WP_User_Query;
581
		$users_query->prepare_query( $args );
582
583
		return "SELECT `{$wpdb->users}`.`ID`, '' AS `title`, 'user' AS `type`, 'user' AS `subtype` {$users_query->query_from} {$users_query->query_where}";
584
	}
585
586
	/**
587
	 * Helper method to prepare the SQL needed to search for options of type 'comment'.
588
	 *
589
	 * Creates a 'fake' WP_Comment_Query with only one result in order to catch the SQL
590
	 * that it will construct in order to support all of the WP_Comment_Query arguments.
591
	 *
592
	 * @access public
593
	 *
594
	 * @param  array  $args
595
	 * @return string
596
	 */
597
	public function get_comment_options_sql( $args = array() ) {
598
		$type        = $args['type'];
599
		$search_term = $args['term'];
600
601
		unset( $args['type'], $args['term'], $args['subtype'] );
602
603
		/**
604
		 * Filter the default parameters when fetching comments for a particular field.
605
		 *
606
		 * @param array $args The parameters, passed to get_comments().
607
		 */
608
		$filter_name = 'carbon_fields_association_field_options_' . $this->get_base_name() . '_' . $type;
609
610
		$args = apply_filters( $filter_name, array(
611
			'fields' => 'ids',
612
			'number' => 1,
613
			'search' => $search_term,
614
		) );
615
616
		add_filter( 'comments_clauses', array( $this, 'get_comments_clauses' ) );
617
618
		$comments_query = new WP_Comment_Query;
619
		$comments_query->query( $args );
620
621
		remove_filter( 'comments_clauses', array( $this, 'get_comments_clauses' ) );
622
623
		return $comments_query->request;
624
	}
625
626
	/**
627
	 * Modify the "SELECT" columns and the clauses for the SQL request
628
	 * performed by the WP_Comment_Query.
629
	 *
630
	 * @access public
631
	 *
632
	 * @param  array  $clauses
633
	 * @return array
634
	 */
635
	public function get_comments_clauses( $clauses ) {
636
		global $wpdb;
637
638
		$clauses['fields'] = " {$wpdb->comments}.`comment_ID` AS `ID`, '' AS `title`, 'comment' AS `type`, 'comment' AS `subtype` ";
639
640
		unset( $clauses['orderby'], $clauses['limits'], $clauses['groupby'] );
641
642
		return $clauses;
643
	}
644
645
		/**
646
	 * Used to get the thumbnail of an item.
647
	 *
648
	 * Can be overriden or extended by the `carbon_fields_association_field_option_thumbnail` filter.
649
	 *
650
	 * @param int $id The database ID of the item.
651
	 * @param string $type Item type (post, term, user, comment, or a custom one).
652
	 * @param string $subtype The subtype - "page", "post", "category", etc.
653
	 * @return string $title The title of the item.
654
	 */
655
	public function get_thumbnail_by_type( $id, $type, $subtype = '' ) {
656
		$thumbnail_url = '';
657
658
		if ( $type === 'post' ) {
659
			$thumbnail_url = get_the_post_thumbnail_url( $id, 'thumbnail' );
660
		}
661
662
		return apply_filters( 'carbon_fields_association_field_option_thumbnail', $thumbnail_url, $id, $type, $subtype );
663
	}
664
665
	/**
666
	 * Used to get the title of an item.
667
	 *
668
	 * Can be overriden or extended by the `carbon_association_title` filter.
669
	 *
670
	 * @param int $id The database ID of the item.
671
	 * @param string $type Item type (post, term, user, comment, or a custom one).
672
	 * @param string $subtype The subtype - "page", "post", "category", etc.
673
	 * @return string $title The title of the item.
674
	 */
675
	public function get_title_by_type( $id, $type, $subtype = '' ) {
676
		$title = '';
677
678
		$method = 'get_' . $type . '_title';
679
		$callable = array( $this->wp_toolset, $method );
680
		if ( is_callable( $callable ) ) {
681
			$title = call_user_func( $callable, $id, $subtype );
682
		}
683
684
		if ( $type === 'comment' ) {
685
			$max = apply_filters( 'carbon_fields_association_field_comment_length', 30, $this->get_base_name() );
686
			if ( strlen( $title ) > $max ) {
687
				$title = substr( $title, 0, $max ) . '...';
688
			}
689
		}
690
691
		/**
692
		 * Filter the title of the association item.
693
		 *
694
		 * @param string $title   The unfiltered item title.
695
		 * @param string $name    Name of the association field.
696
		 * @param int    $id      The database ID of the item.
697
		 * @param string $type    Item type (post, term, user, comment, or a custom one).
698
		 * @param string $subtype Subtype - "page", "post", "category", etc.
699
		 */
700
		$title = apply_filters( 'carbon_fields_association_field_title', $title, $this->get_base_name(), $id, $type, $subtype );
701
702
		if ( ! $title ) {
703
			$title = '(no title) - ID: ' . $id;
704
		}
705
706
		return $title;
707
	}
708
709
	/**
710
	 * Used to get the label of an item.
711
	 *
712
	 * Can be overriden or extended by the `carbon_association_item_label` filter.
713
	 *
714
	 * @param int     $id      The database ID of the item.
715
	 * @param string  $type    Item type (post, term, user, comment, or a custom one).
716
	 * @param string  $subtype Subtype - "page", "post", "category", etc.
717
	 * @return string $label The label of the item.
718
	 */
719
	public function get_item_label( $id, $type, $subtype = '' ) {
720
		$label = $subtype ? $subtype : $type;
721
722
		if ( $type === 'post' ) {
723
			$post_type_object = get_post_type_object( $subtype );
724
			$label = $post_type_object->labels->singular_name;
725
		} elseif ( $type === 'term' ) {
726
			$taxonomy_object = get_taxonomy( $subtype );
727
			$label = $taxonomy_object->labels->singular_name;
728
		}
729
730
		/**
731
		 * Filter the label of the association item.
732
		 *
733
		 * @param string $label   The unfiltered item label.
734
		 * @param string $name    Name of the association field.
735
		 * @param int    $id      The database ID of the item.
736
		 * @param string $type    Item type (post, term, user, comment, or a custom one).
737
		 * @param string $subtype Subtype - "page", "post", "category", etc.
738
		 */
739
		return apply_filters( 'carbon_fields_association_field_item_label', $label, $this->get_base_name(), $id, $type, $subtype );
740
	}
741
742
	/**
743
	 * Retrieve the edit link of a particular object.
744
	 *
745
	 * @param  array $type Object type.
746
	 * @param  int $id      ID of the object.
747
	 * @return string       URL of the edit link.
748
	 */
749
	protected function get_object_edit_link( $type, $id ) {
750
		switch ( $type['type'] ) {
751
752
			case 'post':
753
				$edit_link = get_edit_post_link( $id, '' );
754
				break;
755
756
			case 'term':
757
				$edit_link = get_edit_term_link( $id, '', $type['type'] );
758
				break;
759
760
			case 'comment':
761
				$edit_link = get_edit_comment_link( $id );
762
				break;
763
764
			case 'user':
765
				$edit_link = get_edit_user_link( $id );
766
				break;
767
768
			default:
769
				$edit_link = false;
770
771
		}
772
773
		return $edit_link;
774
	}
775
776
	/**
777
	 * Prepares an option of type 'post' for JS usage.
778
	 *
779
	 * @param  \stdClass  $data
780
	 * @return array
781
	 */
782
	public function format_post_option( $data ) {
783
		return array(
784
			'id'         => intval( $data->ID ),
785
			'title'      => $this->get_title_by_type( $data->ID, $data->type, $data->subtype ),
786
			'thumbnail'  => get_the_post_thumbnail_url( $data->ID, 'thumbnail' ),
787
			'type'       => $data->type,
788
			'subtype'    => $data->subtype,
789
			'label'      => $this->get_item_label( $data->ID, $data->type, $data->subtype ),
790
			'is_trashed' => ( get_post_status( $data->ID ) == 'trash' ),
791
			'edit_link'  => $this->get_object_edit_link( get_object_vars( $data ), $data->ID ),
792
		);
793
	}
794
795
	/**
796
	 * Prepares an option of type 'term' for JS usage.
797
	 *
798
	 * @param  \stdClass  $data
799
	 * @return array
800
	 */
801
	public function format_term_option( $data ) {
802
		return array(
803
			'id'         => intval( $data->ID ),
804
			'title'      => $this->get_title_by_type( $data->ID, $data->type, $data->subtype ),
805
			'thumbnail'  => '',
806
			'type'       => $data->type,
807
			'subtype'    => $data->subtype,
808
			'label'      => $this->get_item_label( $data->ID, $data->type, $data->subtype ),
809
			'is_trashed' => false,
810
			'edit_link'  => $this->get_object_edit_link( get_object_vars( $data ), $data->ID ),
811
		);
812
	}
813
814
	/**
815
	 * Prepares an option of type 'comment' for JS usage.
816
	 *
817
	 * @param  \stdClass  $data
818
	 * @return array
819
	 */
820 View Code Duplication
	public function format_comment_option( $data ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
821
		return array(
822
			'id'         => intval( $data->ID ),
823
			'title'      => $this->get_title_by_type( $data->ID, 'comment' ),
824
			'thumbnail'  => '',
825
			'type'       => 'comment',
826
			'subtype'    => 'comment',
827
			'label'      => $this->get_item_label( $data->ID, 'comment' ),
828
			'is_trashed' => false,
829
			'edit_link'  => $this->get_object_edit_link( get_object_vars( $data ), $data->ID ),
830
		);
831
	}
832
833
	/**
834
	 * Prepares an option of type 'user' for JS usage.
835
	 *
836
	 * @param  \stdClass  $data
837
	 * @return array
838
	 */
839 View Code Duplication
	public function format_user_option( $data ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
840
		return array(
841
			'id'         => intval( $data->ID ),
842
			'title'      => $this->get_title_by_type( $data->ID, 'user' ),
843
			'thumbnail'  => get_avatar_url( $data->ID, array( 'size' => 150 ) ),
844
			'type'       => 'user',
845
			'subtype'    => 'user',
846
			'label'      => $this->get_item_label( $data->ID, 'user' ),
847
			'is_trashed' => false,
848
			'edit_link'  => $this->get_object_edit_link( get_object_vars( $data ), $data->ID ),
849
		);
850
	}
851
}
852