Completed
Push — milestone/2.0 ( 9cc217...20a31b )
by
unknown
02:47
created

Association_Field::get_term_options()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 16
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 27
rs 8.8571
1
<?php
2
3
namespace Carbon_Fields\Field;
4
5
use Carbon_Fields\App;
6
use Carbon_Fields\Value_Set\Value_Set;
7
8
/**
9
 * Association field class.
10
 * Allows selecting and manually sorting entries from various types:
11
 *  - Posts
12
 *  - Terms
13
 *  - Users
14
 *  - Comments
15
 */
16
class Association_Field extends Relationship_Field {
17
	
18
	/**
19
	 * WP_Toolset instance
20
	 * @var Carbon_Fields\Toolset\WP_Toolset
21
	 */
22
	protected $wp_toolset;
23
24
	/**
25
	 * Types of entries to associate with.
26
	 * @var array
27
	 */
28
	protected $types = array(
29
		array(
30
			'type' => 'post',
31
			'post_type' => 'post',
32
		),
33
	);
34
35
	/**
36
	 * Create a field from a certain type with the specified label.
37
	 * @param string $name  Field name
38
	 * @param string $label Field label
39
	 */
40
	protected function __construct( $name, $label ) {
41
		$this->wp_toolset = App::resolve( 'wp_toolset' );
42
		$this->value = new Value_Set( Value_Set::TYPE_VALUE_SET, array( 'type' => '', 'subtype' => '', 'object_id' => 0 ) );
43
		Field::__construct( $name, $label );
44
	}
45
46
	/**
47
	 * Alias for $this->value()->set( $value );
48
	 **/
49
	public function set_value( $value ) {
50
		$value = $this->value_string_array_to_value_set( $value );
51
		parent::set_value( $value );
52
	}
53
54
	/**
55
	 * Convert a colo:separated:string into it's expected components
56
	 * Used for backwards compatibility to CF 1.5
57
	 * 
58
	 * @param string $value_string
59
	 * @return array
60
	 */
61
	protected function value_string_to_property_array( $value_string ) {
62
		$value_pieces = explode( ':', $value_string );
63
		$property_array = array(
64
			Value_Set::VALUE_PROPERTY => $value_string,
65
			'type' => $value_pieces[0],
66
			'subtype' => $value_pieces[1],
67
			'object_id' => $value_pieces[2],
68
		);
69
		return $property_array;
70
	}
71
72
	/**
73
	 * Convert a colon:separated:string into it's expected components
74
	 * Used for backwards compatibility to CF 1.5
75
	 * 
76
	 * @param array $value_string_array
77
	 * @return array<array>
78
	 */
79
	protected function value_string_array_to_value_set( $value_string_array ) {
80
		$value_set = array();
81
		foreach ( $value_string_array as $raw_value_entry ) {
82
			$value_string = $raw_value_entry;
83
84
			if ( is_array( $raw_value_entry ) ) {
85
				if ( isset( $raw_value_entry['type'] ) ) {
86
					// array is already in suitable format
87
					$value_set[] = $raw_value_entry;
88
					continue;
89
				}
90
				$value_string = $raw_value_entry[ Value_Set::VALUE_PROPERTY ];
91
			}
92
93
			$property_array = $this->value_string_to_property_array( $value_string );
94
			$value_set[] = $property_array;
95
		}
96
		return $value_set;
97
	}
98
99
	/**
100
	 * Used to get the title of an item.
101
	 *
102
	 * Can be overriden or extended by the `carbon_association_title` filter.
103
	 *
104
	 * @param int $id The database ID of the item.
105
	 * @param string $type Item type (post, term, user, comment, or a custom one).
106
	 * @param string $subtype The subtype - "page", "post", "category", etc.
107
	 * @return string $title The title of the item.
108
	 */
109
	protected function get_title_by_type( $id, $type, $subtype = '' ) {
110
		$title = '';
111
112
		$method = 'get_' . $type . '_title';
113
		$callable = array( $this->wp_toolset, $method );
114
		if ( is_callable( $callable ) ) {
115
			$title = call_user_func( $callable, $id, $subtype );
116
		}
117
118
		if ( $type === 'comment' ) {
119
			$max = apply_filters( 'carbon_association_comment_length', 30, $this->get_name() );
120
			if ( strlen( $title ) > $max ) {
121
				$title = substr( $title, 0, $max ) . '...';
122
			}
123
		}
124
125
		/**
126
		 * Filter the title of the association item.
127
		 *
128
		 * @param string $title   The unfiltered item title.
129
		 * @param string $name    Name of the association field.
130
		 * @param int    $id      The database ID of the item.
131
		 * @param string $type    Item type (post, term, user, comment, or a custom one).
132
		 * @param string $subtype Subtype - "page", "post", "category", etc.
133
		 */
134
		$title = apply_filters( 'carbon_association_title', $title, $this->get_name(), $id, $type, $subtype );
135
136
		if ( ! $title ) {
137
			$title = '(no title) - ID: ' . $id;
138
		}
139
140
		return $title;
141
	}
142
143
	/**
144
	 * Used to get the label of an item.
145
	 *
146
	 * Can be overriden or extended by the `carbon_association_item_label` filter.
147
	 *
148
	 * @param int     $id      The database ID of the item.
149
	 * @param string  $type    Item type (post, term, user, comment, or a custom one).
150
	 * @param string  $subtype Subtype - "page", "post", "category", etc.
151
	 * @return string $label The label of the item.
152
	 */
153
	protected function get_item_label( $id, $type, $subtype = '' ) {
154
		$label = $subtype ? $subtype : $type;
155
156
		if ( $type === 'post' ) {
157
			$post_type_object = get_post_type_object( $subtype );
158
			$label = $post_type_object->labels->singular_name;
159
		} elseif ( $type === 'term' ) {
160
			$taxonomy_object = get_taxonomy( $subtype );
161
			$label = $taxonomy_object->labels->singular_name;
162
		}
163
164
		/**
165
		 * Filter the label of the association item.
166
		 *
167
		 * @param string $label   The unfiltered item label.
168
		 * @param string $name    Name of the association field.
169
		 * @param int    $id      The database ID of the item.
170
		 * @param string $type    Item type (post, term, user, comment, or a custom one).
171
		 * @param string $subtype Subtype - "page", "post", "category", etc.
172
		 */
173
		return apply_filters( 'carbon_association_item_label', $label, $this->get_name(), $id, $type, $subtype );
174
	}
175
176
	/**
177
	 * Get post options
178
	 *
179
	 * @return array $options
180
	 */
181
	protected function get_post_options( $type ) {
182
		/**
183
		 * Filter the default query when fetching posts for a particular field.
184
		 *
185
		 * @param array $args The parameters, passed to get_posts().
186
		 */
187
		$filter_name = 'carbon_association_options_' . $this->get_name() . '_' . $type['type'] . '_' . $type['post_type'];
188
		$args = apply_filters( $filter_name, array(
189
			'post_type' => $type['post_type'],
190
			'posts_per_page' => -1,
191
			'fields' => 'ids',
192
			'suppress_filters' => false,
193
		) );
194
195
		// fetch and prepare posts as association items
196
		$posts = get_posts( $args );
197
		foreach ( $posts as &$p ) {
198
			$p = array(
199
				'id' => $p,
200
				'title' => $this->get_title_by_type( $p, $type['type'], $type['post_type'] ),
201
				'type' => $type['type'],
202
				'subtype' => $type['post_type'],
203
				'label' => $this->get_item_label( $p, $type['type'], $type['post_type'] ),
204
				'is_trashed' => ( get_post_status( $p ) == 'trash' ),
205
				'edit_link' => $this->get_object_edit_link( $type, $p ),
206
			);
207
		}
208
		return $posts;
209
	}
210
211
	/**
212
	 * Get term options
213
	 *
214
	 * @return array $options
215
	 */
216
	protected function get_term_options( $type ) {
217
		/**
218
		 * Filter the default parameters when fetching terms for a particular field.
219
		 *
220
		 * @param array $args The parameters, passed to get_terms().
221
		 */
222
		$filter_name = 'carbon_association_options_' . $this->get_name() . '_' . $type['type'] . '_' . $type['taxonomy'];
223
		$args = apply_filters( $filter_name, array(
224
			'hide_empty' => 0,
225
			'fields' => 'id=>name',
226
		) );
227
228
		// fetch and prepare terms as association items
229
		$terms = get_terms( $type['taxonomy'], $args );
230
		foreach ( $terms as $term_id => &$term ) {
231
			$term = array(
232
				'id' => $term_id,
233
				'title' => $term,
234
				'type' => $type['type'],
235
				'subtype' => $type['taxonomy'],
236
				'label' => $this->get_item_label( $term_id, $type['type'], $type['taxonomy'] ),
237
				'is_trashed' => false,
238
				'edit_link' => $this->get_object_edit_link( $type, $term_id ),
239
			);
240
		}
241
		return $terms;
242
	}
243
244
	/**
245
	 * Get user options
246
	 *
247
	 * @return array $options
248
	 */
249 View Code Duplication
	protected function get_user_options( $type ) {
1 ignored issue
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...
250
		/**
251
		 * Filter the default parameters when fetching users for a particular field.
252
		 *
253
		 * @param array $args The parameters, passed to get_users().
254
		 */
255
		$filter_name = 'carbon_association_options_' . $this->get_name() . '_' . $type['type'];
256
		$args = apply_filters( $filter_name, array(
257
			'fields' => 'ID',
258
		) );
259
260
		// fetch and prepare users as association items
261
		$users = get_users( $args );
262
		foreach ( $users as &$u ) {
263
			$u = array(
264
				'id' => $u,
265
				'title' => $this->get_title_by_type( $u, $type['type'] ),
266
				'type' => $type['type'],
267
				'subtype' => 'user',
268
				'label' => $this->get_item_label( $u, $type['type'] ),
269
				'is_trashed' => false,
270
				'edit_link' => $this->get_object_edit_link( $type, $u ),
271
			);
272
		}
273
		return $users;
274
	}
275
276
	/**
277
	 * Get comment options
278
	 *
279
	 * @return array $options
280
	 */
281 View Code Duplication
	protected function get_comment_options( $type ) {
1 ignored issue
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...
282
		/**
283
		 * Filter the default parameters when fetching comments for a particular field.
284
		 *
285
		 * @param array $args The parameters, passed to get_comments().
286
		 */
287
		$filter_name = 'carbon_association_options_' . $this->get_name() . '_' . $type['type'];
288
		$args = apply_filters( $filter_name, array(
289
			'fields' => 'ids',
290
		) );
291
292
		// fetch and prepare comments as association items
293
		$comments = get_comments( $args );
294
		foreach ( $comments as &$c ) {
295
			$c = array(
296
				'id' => $c,
297
				'title' => $this->get_title_by_type( $c, $type['type'] ),
298
				'type' => $type['type'],
299
				'subtype' => 'comment',
300
				'label' => $this->get_item_label( $c, $type['type'] ),
301
				'is_trashed' => false,
302
				'edit_link' => $this->get_object_edit_link( $type, $c ),
303
			);
304
		}
305
		return $comments;
306
	}
307
308
	/**
309
	 * Generate the item options.
310
	 *
311
	 * @return array $options The selectable options of the association field.
312
	 */
313
	public function get_options() {
314
		$options = array();
315
316
		foreach ( $this->types as $type ) {
317
			$method = 'get_' . $type['type'] . '_options';
318
			$callable = array( $this, $method );
319
			if ( is_callable( $callable ) ) {
320
				$options = array_merge( $options, call_user_func( $callable, $type ) );
321
			}
322
		}
323
324
		/**
325
		 * Filter the final list of options, available to a certain association field.
326
		 *
327
		 * @param array $options Unfiltered options items.
328
		 * @param string $name Name of the association field.
329
		 */
330
		$options = apply_filters( 'carbon_association_options', $options, $this->get_name() );
331
332
		return $options;
333
	}
334
335
	/**
336
	 * Retrieve the edit link of a particular object.
337
	 *
338
	 * @param  string $type Object type.
339
	 * @param  int $id      ID of the object.
340
	 * @return string       URL of the edit link.
341
	 */
342
	protected function get_object_edit_link( $type, $id ) {
343
		switch ( $type['type'] ) {
344
345
			case 'post':
346
				$edit_link = get_edit_post_link( $id );
347
				break;
348
349
			case 'term':
350
				$edit_link = get_edit_term_link( $id, $type['taxonomy'], $type['type'] );
351
				break;
352
353
			case 'comment':
354
				$edit_link = get_edit_comment_link( $id );
355
				break;
356
357
			case 'user':
358
				$edit_link = get_edit_user_link( $id );
359
				break;
360
361
			default:
362
				$edit_link = false;
363
364
		}
365
366
		return $edit_link;
367
	}
368
369
	/**
370
	 * Modify the types.
371
	 * @param array $types New types
372
	 */
373
	public function set_types( $types ) {
374
		$this->types = $types;
375
		return $this;
376
	}
377
378
	/**
379
	 * Converts the field values into a usable associative array.
380
	 *
381
	 * The association data is saved in the database in the following format:
382
	 * 	array (
383
	 *		0 => 'post:page:4',
384
	 *		1 => 'term:category:2',
385
	 *		2 => 'user:user:1',
386
	 * 	)
387
	 * where the value of each array item contains:
388
	 * 	- Type of data (post, term, user or comment)
389
	 * 	- Subtype of data (the particular post type or taxonomy)
390
	 * 	- ID of the item (the database ID of the item)
391
	 */
392
	protected function value_to_json() {
393
		$value_set = $this->get_value();
394
		$value = array();
395
		foreach ( $value_set as $value_set_entry ) {
396
			$item = array(
397
				'type' => $value_set_entry['type'],
398
				'subtype' => $value_set_entry['subtype'],
399
				'id' => $value_set_entry['object_id'],
400
				'title' => $this->get_title_by_type( $value_set_entry['object_id'], $value_set_entry['type'], $value_set_entry['subtype'] ),
401
				'label' => $this->get_item_label( $value_set_entry['object_id'], $value_set_entry['type'], $value_set_entry['subtype'] ),
402
				'is_trashed' => ( $value_set_entry['type'] == 'post' && get_post_status( $value_set_entry['object_id'] ) === 'trash' ),
403
			);
404
			$value[] = $item;
405
		}
406
		return $value;
407
	}
408
409
	/**
410
	 * Convert the field data into JSON representation.
411
	 * @param  bool $load Whether to load data from the datastore.
412
	 * @return mixed      The JSON field data.
413
	 */
414
	public function to_json( $load ) {
415
		$field_data = Field::to_json( $load );
416
417
		$field_data = array_merge( $field_data, array(
418
			'value' => $this->value_to_json(),
419
			'options' => $this->get_options(),
420
			'max' => $this->max,
421
			'allow_duplicates' => $this->allow_duplicates,
422
		) );
423
424
		$i = 0;
425
		foreach ( $field_data['value'] as $key => $value ) {
426
			$field_data['value'][ $key ]['fieldIndex'] = $i;
427
			$i++;
428
		}
429
		$field_data['nextfieldIndex'] = $i;
430
431
		return $field_data;
432
	}
433
434
	/**
435
	 * Serves as a backbone template for the association items.
436
	 * Used for both the selected and the selectable options.
437
	 *
438
	 * @param bool $display_input Whether to display the selected item input field.
439
	 */
440
	public function item_template( $display_input = true ) {
441
		?>
442
		<li>
443
			<span class="mobile-handle dashicons-before dashicons-menu"></span>
444
			<a href="#" data-item-id="{{{ item.id }}}" data-item-title="{{{ item.title }}}" data-item-type="{{{ item.type }}}" data-item-subtype="{{{ item.subtype }}}" data-item-label="{{{ item.label }}}" data-value="{{{ item.type }}}:{{{ item.subtype }}}:{{{ item.id }}}">
445
				<# if ( item.edit_link ) { #>
446
					<em class="edit-link dashicons-before dashicons-edit" data-href="{{{ item.edit_link }}}"><?php _e( 'Edit', \Carbon_Fields\TEXT_DOMAIN ); ?></em>
447
				<# } #>
448
				<em>{{{ item.label }}}</em>
449
				<span class="dashicons-before dashicons-plus-alt"></span>
450
				{{{ item.title }}}
451
				<# if (item.is_trashed) { #>
452
					<i class="trashed dashicons-before dashicons-trash"></i>
453
				<# } #>
454
			</a>
455
			<?php if ( $display_input ) :  ?>
456
				<input type="hidden" name="{{{ name }}}[{{{ item.fieldIndex }}}]" value="{{{ item.type }}}:{{{ item.subtype }}}:{{{ item.id }}}" />
457
			<?php endif; ?>
458
		</li>
459
		<?php
460
	}
461
}
462