Passed
Push — master ( ec89a9...5c9d98 )
by Jared
04:02 queued 11s
created

Term   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 453
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 143
c 4
b 0
f 0
dl 0
loc 453
rs 3.28
wmc 64

23 Methods

Rating   Name   Duplication   Size   Complexity  
A __toString() 0 2 1
A __construct() 0 8 3
A init() 0 16 6
A get_term_from_query() 0 10 4
A get_term_meta() 0 4 1
A from() 0 7 2
A link() 0 4 1
C get_term() 0 31 12
A update() 0 4 1
A description() 0 8 2
A get_path() 0 2 1
A title() 0 2 1
A get_meta_field() 0 11 3
A get_edit_url() 0 2 1
A get_children() 0 9 3
A children() 0 2 1
A get_tid() 0 21 6
A path() 0 5 1
A posts() 0 2 1
A get_link() 0 2 1
A edit_link() 0 2 1
B get_posts() 0 42 10
A meta() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like Term 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.

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 Term, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Timber;
4
5
use Timber\Core;
6
use Timber\CoreInterface;
7
8
use Timber\Post;
9
use Timber\Helper;
10
use Timber\URLHelper;
11
12
/**
13
 * Terms: WordPress has got 'em, you want 'em. Categories. Tags. Custom
14
 * Taxonomies. You don't care, you're a fiend. Well let's get this under control:
15
 * @example
16
 * ```php
17
 * //Get a term by its ID
18
 * $context['term'] = new Timber\Term(6);
19
 * //Get a term when on a term archive page
20
 * $context['term_page'] = new Timber\Term();
21
 * //Get a term with a slug
22
 * $context['team'] = new Timber\Term('patriots');
23
 * //Get a team with a slug from a specific taxonomy
24
 * $context['st_louis'] = new Timber\Term('cardinals', 'baseball');
25
 * Timber::render('index.twig', $context);
26
 * ```
27
 * ```twig
28
 * <h2>{{term_page.name}} Archives</h2>
29
 * <h3>Teams</h3>
30
 * <ul>
31
 *     <li>{{st_louis.name}} - {{st_louis.description}}</li>
32
 *     <li>{{team.name}} - {{team.description}}</li>
33
 * </ul>
34
 * ```
35
 * ```html
36
 * <h2>Team Archives</h2>
37
 * <h3>Teams</h3>
38
 * <ul>
39
 *     <li>St. Louis Cardinals - Winner of 11 World Series</li>
40
 *     <li>New England Patriots - Winner of 6 Super Bowls</li>
41
 * </ul>
42
 * ```
43
 */
44
class Term extends Core implements CoreInterface {
45
46
	public $PostClass = 'Timber\Post';
47
	public $TermClass = 'Term';
48
49
	public $object_type = 'term';
50
	public static $representation = 'term';
51
52
	public $_children;
53
	/**
54
	 * @api
55
	 * @var string the human-friendly name of the term (ex: French Cuisine)
56
	 */
57
	public $name;
58
	/**
59
	 * @api
60
	 * @var string the WordPress taxonomy slug (ex: `post_tag` or `actors`)
61
	 */
62
	public $taxonomy;
63
64
	/**
65
	 * @param int $tid
66
	 * @param string $tax
67
	 */
68
	public function __construct( $tid = null, $tax = '' ) {
69
		if ( $tid === null ) {
70
			$tid = $this->get_term_from_query();
71
		}
72
		if ( strlen($tax) ) {
73
			$this->taxonomy = $tax;
74
		}
75
		$this->init($tid);
76
	}
77
78
	/**
79
	 * @return string
80
	 */
81
	public function __toString() {
82
		return $this->name;
83
	}
84
85
	/**
86
	 * @param $tid
87
	 * @param $taxonomy
88
	 *
89
	 * @return static
90
	 */
91
	public static function from( $tid, $taxonomy ) {
92
		if ( is_array($tid) ) {
93
			return array_map( function($term) use ($taxonomy) {
94
				return new static($term, $taxonomy);
95
			}, $tid);
96
		}
97
		return new static($tid, $taxonomy);
98
	}
99
100
101
	/* Setup
102
	===================== */
103
104
	/**
105
	 * @internal
106
	 * @return integer
107
	 */
108
	protected function get_term_from_query() {
109
		global $wp_query;
110
		if ( isset($wp_query->queried_object) ) {
111
			$qo = $wp_query->queried_object;
112
			if ( isset($qo->term_id) ) {
113
				return $qo->term_id;
114
			}
115
		}
116
		if ( isset($wp_query->tax_query->queries[0]['terms'][0]) ) {
117
			return $wp_query->tax_query->queries[0]['terms'][0];
118
		}
119
	}
120
121
	/**
122
	 * @internal
123
	 * @param int $tid
124
	 */
125
	protected function init( $tid ) {
126
		$term = $this->get_term($tid);
127
		if ( isset($term->id) ) {
128
			$term->ID = $term->id;
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on WP_Error.
Loading history...
Bug introduced by
The property ID does not seem to exist on WP_Term.
Loading history...
129
		} else if ( isset($term->term_id) ) {
130
			$term->ID = $term->term_id;
131
		} else if ( is_string($tid) ) {
0 ignored issues
show
introduced by
The condition is_string($tid) is always false.
Loading history...
132
			//echo 'bad call using '.$tid;
133
			//Helper::error_log(debug_backtrace());
134
		}
135
		if ( isset($term->ID) ) {
136
			$term->id = $term->ID;
0 ignored issues
show
Bug introduced by
The property id does not seem to exist on WP_Error.
Loading history...
Bug introduced by
The property id does not seem to exist on WP_Term.
Loading history...
137
			$this->import($term);
138
			if ( isset($term->term_id) ) {
139
				$custom = $this->get_term_meta($term->term_id);
140
				$this->import($custom);
141
			}
142
		}
143
	}
144
145
	/**
146
	 * @internal
147
	 * @param int $tid
148
	 * @return array
149
	 */
150
	protected function get_term_meta( $tid ) {
151
		$customs = array();
152
		$customs = apply_filters('timber_term_get_meta', $customs, $tid, $this);
153
		return apply_filters('timber/term/meta', $customs, $tid, $this);
154
	}
155
156
	/**
157
	 * @internal
158
	 * @param int|object|array $tid
159
	 * @return mixed
160
	 */
161
	protected function get_term( $tid ) {
162
		if ( is_object($tid) || is_array($tid) ) {
163
			return $tid;
164
		}
165
		$tid = self::get_tid($tid);
166
167
		if ( is_array($tid) ) {
0 ignored issues
show
introduced by
The condition is_array($tid) is always false.
Loading history...
168
			//there's more than one matching $term_id, let's figure out which is correct
169
			if ( isset($this->taxonomy) && strlen($this->taxonomy) ) {
170
				foreach( $tid as $term_id ) {
171
					$maybe_term = get_term($term_id, $this->taxonomy);
172
					if ( $maybe_term ) {
173
						return $maybe_term;
174
					}
175
				}
176
			}
177
			$tid = $tid[0];
178
		}
179
180
		if ( isset($this->taxonomy) && strlen($this->taxonomy) ) {
181
			return get_term($tid, $this->taxonomy);
182
		} else {
183
			global $wpdb;
184
			$query = $wpdb->prepare("SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d LIMIT 1", $tid);
185
			$tax = $wpdb->get_var($query);
186
			if ( isset($tax) && strlen($tax) ) {
187
				$this->taxonomy = $tax;
188
				return get_term($tid, $tax);
189
			}
190
		}
191
		return null;
192
	}
193
194
	/**
195
	 * @internal
196
	 * @param int $tid
197
	 * @return int|array
198
	 */
199
	protected static function get_tid( $tid ) {
200
		global $wpdb;
201
		if ( is_numeric($tid) ) {
0 ignored issues
show
introduced by
The condition is_numeric($tid) is always true.
Loading history...
202
			return $tid;
203
		}
204
		if ( gettype($tid) == 'object' ) {
205
			$tid = $tid->term_id;
206
		}
207
		if ( is_numeric($tid) ) {
208
			$query = $wpdb->prepare("SELECT term_id FROM $wpdb->terms WHERE term_id = %d", $tid);
209
		} else {
210
			$query = $wpdb->prepare("SELECT term_id FROM $wpdb->terms WHERE slug = %s", $tid);
211
		}
212
		$result = $wpdb->get_col($query);
213
		if ( $result ) {
214
			if ( count($result) == 1) {
215
				return $result[0];
216
			}
217
			return $result;
218
		}
219
		return 0;
220
	}
221
222
	/* Public methods
223
	===================== */
224
225
	/**
226
	 * @internal
227
	 * @return string
228
	 */
229
	public function get_edit_url() {
230
		return get_edit_term_link($this->ID, $this->taxonomy);
231
	}
232
233
	/**
234
	 * @internal
235
	 * @param string $field_name
236
	 * @return string
237
	 */
238
	public function get_meta_field( $field_name ) {
239
		if ( !isset($this->$field_name) ) {
240
			$field_value = get_term_meta($this->ID, $field_name, true);
241
			if ( !$field_value ) {
242
				$field_value = apply_filters('timber_term_get_meta_field', '', $this->ID, $field_name, $this);
243
				$field_value = apply_filters('timber/term/meta/field', $field_value, $this->ID, $field_name, $this);
244
			}
245
			$this->$field_name = $field_value;
246
247
		}
248
		return $this->$field_name;
249
	}
250
251
	/**
252
	 * @internal
253
	 * @deprecated since 1.0
254
	 * @return string
255
	 */
256
	public function get_path() {
257
		return $this->path();
258
	}
259
260
	/**
261
	 * @internal
262
	 * @deprecated since 1.0
263
	 * @return string
264
	 */
265
	public function get_link() {
266
		return $this->link();
267
	}
268
269
	/**
270
	 * Get posts that have the current term assigned.
271
	 *
272
	 * @internal
273
	 * @param int|array $numberposts_or_args Optional. Either the number of posts or an array of
274
	 *                                       arguments for the post query that this method is going.
275
	 *                                       to perform. Default `10`.
276
	 * @param string    $post_type_or_class  Optional. Either the post type to get or the name of
277
	 *                                       post class to use for the returned posts. Default
278
	 *                                       `any`.
279
	 * @param string    $post_class          Optional. The name of the post class to use for the
280
	 *                                       returned posts. Default `Timber\Post`.
281
	 * @return array|bool|null
282
	 */
283
	public function get_posts( $numberposts_or_args = 10, $post_type_or_class = 'any', $post_class = '' ) {
284
		if ( !strlen($post_class) ) {
285
				$post_class = $this->PostClass;
286
			}
287
		$default_tax_query = array(array(
288
			'field' => 'term_id',
289
			'terms' => $this->ID,
290
			'taxonomy' => $this->taxonomy,
291
		));
292
		if ( is_string( $numberposts_or_args) && strstr( $numberposts_or_args, '=') ) {
0 ignored issues
show
introduced by
The condition is_string($numberposts_or_args) is always false.
Loading history...
293
			$args     = $numberposts_or_args;
294
			$new_args = array();
295
			parse_str($args, $new_args);
296
			$args = $new_args;
297
			$args['tax_query'] = $default_tax_query;
298
			if ( !isset($args['post_type']) ) {
299
				$args['post_type'] = 'any';
300
			}
301
			if ( class_exists($post_type_or_class) ) {
302
				$post_class = $post_type_or_class;
303
			}
304
		} else if ( is_array( $numberposts_or_args) ) {
305
			//they sent us an array already baked
306
			$args = $numberposts_or_args;
307
			if ( !isset($args['tax_query']) ) {
308
				$args['tax_query'] = $default_tax_query;
309
			}
310
			if ( class_exists($post_type_or_class) ) {
311
				$post_class = $post_type_or_class;
312
			}
313
			if ( !isset($args['post_type']) ) {
314
				$args['post_type'] = 'any';
315
			}
316
		} else {
317
			$args = array(
318
				'numberposts' => $numberposts_or_args,
319
				'tax_query'   => $default_tax_query,
320
				'post_type'   => $post_type_or_class
321
			);
322
		}
323
324
		return Timber::get_posts($args, $post_class);
325
	}
326
327
	/**
328
	 * @internal
329
	 * @return array
330
	 */
331
	public function get_children() {
332
		if ( !isset($this->_children) ) {
333
			$children = get_term_children($this->ID, $this->taxonomy);
334
			foreach ( $children as &$child ) {
335
				$child = new Term($child);
336
			}
337
			$this->_children = $children;
338
		}
339
		return $this->_children;
340
	}
341
342
	/**
343
	 *
344
	 *
345
	 * @param string  $key
346
	 * @param mixed   $value
347
	 */
348
	public function update( $key, $value ) {
349
		$value = apply_filters('timber_term_set_meta', $value, $key, $this->ID, $this);
350
		$value = apply_filters('timber/term/meta/set', $value, $key, $this->ID, $this);
351
		$this->$key = $value;
352
	}
353
354
	/* Alias
355
	====================== */
356
357
	/**
358
	 * @api
359
	 * @return array
360
	 */
361
	public function children() {
362
		return $this->get_children();
363
	}
364
365
	/**
366
	 * @api
367
	 * @return string
368
	 */
369
	public function description() {
370
		$prefix = '<p>';
371
		$desc = term_description($this->ID, $this->taxonomy);
372
		if ( substr($desc, 0, strlen($prefix)) == $prefix ) {
373
			$desc = substr($desc, strlen($prefix));
374
		}
375
		$desc = preg_replace('/'.preg_quote('</p>', '/').'$/', '', $desc);
376
		return trim($desc);
377
	}
378
379
	/**
380
	 * @api
381
	 * @return string
382
	 */
383
	public function edit_link() {
384
		return $this->get_edit_url();
385
	}
386
387
388
	/**
389
	 * Returns a full link to the term archive page like
390
	 * `http://example.com/category/news`
391
	 * @api
392
	 * @example
393
	 * ```twig
394
	 * See all posts in: <a href="{{ term.link }}">{{ term.name }}</a>
395
	 * ```
396
	 * @return string
397
	 */
398
	public function link() {
399
		$link = get_term_link($this);
400
		$link = apply_filters('timber_term_link', $link, $this);
401
		return apply_filters('timber/term/link', $link, $this);
402
	}
403
404
	/**
405
	 * Retrieves and outputs meta information stored with a term. This will use
406
	 * both data stored under (old) ACF hacks and new (WP 4.6+) where term meta
407
	 * has its own table. If retrieving a special ACF field (repeater, etc.) you
408
	 * can use the output immediately in Twig — no further processing is
409
	 * required.
410
	 * @api
411
	 * @param string $field_name
412
	 * @example
413
	 * ```twig
414
	 * <div class="location-info">
415
	 *   <h2>{{ term.name }}</h2>
416
	 *   <p>{{ term.meta('address') }}</p>
417
	 * </div>
418
	 * ```
419
	 * @return string
420
	 */
421
	public function meta( $field_name ) {
422
		return $this->get_meta_field($field_name);
423
	}
424
425
	/**
426
	 * Returns a relative link (path) to the term archive page like
427
	 * `/category/news`
428
	 * @api
429
	 * @example
430
	 * ```twig
431
	 * See all posts in: <a href="{{ term.path }}">{{ term.name }}</a>
432
	 * ```
433
	 * @return string
434
	 */
435
	public function path() {
436
		$link = $this->get_link();
0 ignored issues
show
Deprecated Code introduced by
The function Timber\Term::get_link() has been deprecated: since 1.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

436
		$link = /** @scrutinizer ignore-deprecated */ $this->get_link();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
437
		$rel = URLHelper::get_rel_url($link, true);
438
		$rel = apply_filters('timber_term_path', $rel, $this);
439
		return apply_filters('timber/term/path', $rel, $this);
440
	}
441
442
	/**
443
	 * Gets posts that have the current term assigned.
444
	 *
445
	 * @api
446
	 * @example
447
	 * ```twig
448
	 * <h4>Recent posts in {{ term.name }}</h4>
449
	 *
450
	 * <ul>
451
	 * {% for post in term.posts(3, 'post') %}
452
	 *     <li>
453
	 *         <a href="{{ post.link }}">{{ post.title }}</a>
454
	 *     </li>
455
	 * {% endfor %}
456
	 * </ul>
457
	 * ```
458
	 *
459
	 * If you need more control over the query that is going to be performed, you can pass your
460
	 * custom query arguments in the first parameter.
461
	 *
462
	 * ```twig
463
	 * <h4>Our branches in {{ region.name }}</h4>
464
	 *
465
	 * <ul>
466
	 * {% for branch in region.posts({
467
	 *     posts_per_page: -1,
468
	 *     orderby: 'menu_order'
469
	 * }, 'branch', 'Branch') %}
470
	 *     <li>
471
	 *         <a href="{{ branch.link }}">{{ branch.title }}</a>
472
	 *     </li>
473
	 * {% endfor %}
474
	 * </ul>
475
	 * ```
476
	 *
477
	 * @param int|array $numberposts_or_args Optional. Either the number of posts or an array of
478
	 *                                       arguments for the post query that this method is going.
479
	 *                                       to perform. Default `10`.
480
	 * @param string $post_type_or_class     Optional. Either the post type to get or the name of
481
	 *                                       post class to use for the returned posts. Default
482
	 *                                       `any`.
483
	 * @param string $post_class             Optional. The name of the post class to use for the
484
	 *                                       returned posts. Default `Timber\Post`.
485
	 * @return array|bool|null
486
	 */
487
	public function posts( $numberposts_or_args = 10, $post_type_or_class = 'any', $post_class = '' ) {
488
		return $this->get_posts($numberposts_or_args, $post_type_or_class, $post_class);
489
	}
490
491
	/**
492
	 * @api
493
	 * @return string
494
	 */
495
	public function title() {
496
		return $this->name;
497
	}
498
}
499