Completed
Push — development ( 6b3261...c8c20d )
by
unknown
03:24
created

Post_Meta_Container   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 419
Duplicated Lines 10.02 %

Coupling/Cohesion

Components 4
Dependencies 4

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 42
loc 419
ccs 0
cts 124
cp 0
rs 5.1724
c 0
b 0
f 0
wmc 57
lcom 4
cbo 4

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 7 7 2
A init() 0 14 3
B is_valid_save() 0 18 5
C get_environment_for_request() 0 24 8
A is_valid_attach_for_request() 0 14 4
A get_environment_for_object() 0 10 1
A is_valid_attach_for_object() 9 9 2
A attach() 0 16 2
A add_postbox_classes() 0 4 1
A render() 0 3 1
A set_post_id() 11 11 3
A get_post_type_visibility() 15 15 3
A show_on_page() 0 14 4
A show_on_page_children() 0 6 2
A show_on_template() 0 10 3
A hide_on_template() 0 5 2
A show_on_level() 0 4 1
A show_on_post_format() 0 5 2
A show_on_post_type() 0 5 2
A show_on_category() 0 8 1
A show_on_taxonomy_term() 0 8 1
A set_context() 0 4 1
A set_priority() 0 4 1
A save() 0 13 2

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

1
<?php
2
3
namespace Carbon_Fields\Container;
4
5
use Carbon_Fields\Datastore\Datastore;
6
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
7
8
/**
9
 * Field container designed to extend WordPress custom fields functionality,
10
 * providing easier user interface to add, edit and delete text, media files,
11
 * location information and more.
12
 */
13
class Post_Meta_Container extends Container {
14
	/**
15
	 * ID of the post the container is working with
16
	 *
17
	 * @see init()
18
	 * @var int
19
	 */
20
	protected $post_id;
21
22
	/**
23
	 * List of default container settings
24
	 *
25
	 * @see init()
26
	 * @var array
27
	 */
28
	public $settings = array(
29
		'meta_box_context' => 'normal',
30
		'meta_box_priority' => 'high',
31
	);
32
33
	/**
34
	 * {@inheritDoc}
35
	 */
36 View Code Duplication
	public function __construct( $id, $title, $type, $condition_collection, $condition_translator ) {
37
		parent::__construct( $id, $title, $type, $condition_collection, $condition_translator );
38
39
		if ( ! $this->get_datastore() ) {
40
			$this->set_datastore( Datastore::make( 'post_meta' ), $this->has_default_datastore() );
41
		}
42
	}
43
44
	/**
45
	 * Create DataStore instance, set post ID to operate with (if such exists).
46
	 * Bind attach() and save() to the appropriate WordPress actions.
47
	 */
48
	public function init() {
49
		$input = stripslashes_deep( $_GET );
50
		$request_post_id = isset( $input['post'] ) ? intval( $input['post'] ) : 0;
51
		if ( $request_post_id > 0 ) {
52
			$this->set_post_id( $request_post_id );
53
		}
54
55
		add_action( 'admin_init', array( $this, '_attach' ) );
56
		add_action( 'save_post', array( $this, '_save' ) );
57
58
		// support for attachments
59
		add_action( 'add_attachment', array( $this, '_save' ) );
60
		add_action( 'edit_attachment', array( $this, '_save' ) );
61
	}
62
63
	/**
64
	 * Checks whether the current save request is valid
65
	 * Possible errors are triggering save() for autosave requests
66
	 * or performing post save outside of the post edit page (like Quick Edit)
67
	 *
68
	 * @return bool
69
	 */
70
	public function is_valid_save() {
71
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
72
			return false;
73
		}
74
75
		if ( ! $this->verified_nonce_in_request() ) {
76
			return false;
77
		}
78
79
		$params = func_get_args();
80
		$post_id = $params[0];
81
		$post_type = get_post_type( $post_id );
82
		if ( $post_type === 'revision' ) {
83
			return false;
84
		}
85
86
		return $this->is_valid_attach_for_object( $post_id );
87
	}
88
89
	/**
90
	 * Perform save operation after successful is_valid_save() check.
91
	 * The call is propagated to all fields in the container.
92
	 *
93
	 * @param int $post_id ID of the post against which save() is ran
94
	 */
95
	public function save( $post_id = null ) {
96
		// Unhook action to garantee single save
97
		remove_action( 'save_post', array( $this, '_save' ) );
98
99
		$this->set_post_id( $post_id );
100
101
		foreach ( $this->fields as $field ) {
102
			$field->set_value_from_input( stripslashes_deep( $_POST ) );
103
			$field->save();
104
		}
105
106
		do_action( 'carbon_fields_post_meta_container_saved', $post_id, $this );
107
	}
108
109
	/**
110
	 * Get environment array for page request (in admin)
111
	 *
112
	 * @return array
113
	 */
114
	protected function get_environment_for_request() {
115
		global $pagenow;
116
117
		$input = stripslashes_deep( $_GET );
118
		$request_post_type = isset( $input['post_type'] ) ? $input['post_type'] : '';
119
		$post_type = '';
120
121
		if ( $this->post_id ) {
122
			$post_type = get_post_type( $this->post_id );
123
		} elseif ( ! empty( $request_post_type ) ) {
124
			$post_type = $request_post_type;
125
		} elseif ( $pagenow === 'post-new.php' ) {
126
			$post_type = 'post';
127
		}
128
129
		$post = get_post( $this->post_id );
130
		$post = $post ? $post : null;
131
		$environment = array(
132
			'post_id' => $post ? $post->ID : 0,
133
			'post_type' => $post ? $post->post_type : $post_type,
134
			'post' => $post,
135
		);
136
		return $environment;
137
	}
138
139
	/**
140
	 * Perform checks whether the container should be attached during the current request
141
	 *
142
	 * @return bool True if the container is allowed to be attached
143
	 */
144
	public function is_valid_attach_for_request() {
145
		global $pagenow;
146
147
		if ( $pagenow !== 'post.php' && $pagenow !== 'post-new.php' ) {
148
			return false;
149
		}
150
151
		$environment = $this->get_environment_for_request();
152
		if ( ! $environment['post_type'] ) {
153
			return false;
154
		}
155
156
		return $this->static_conditions_pass();
157
	}
158
159
	/**
160
	 * Get environment array for object id
161
	 *
162
	 * @return array
163
	 */
164
	protected function get_environment_for_object( $object_id ) {
165
		$post = get_post( intval( $object_id ) );
166
167
		$environment = array(
168
			'post_id' => $post->ID,
169
			'post' => $post,
170
			'post_type' => get_post_type( $post->ID ),
171
		);
172
		return $environment;
173
	}
174
175
	/**
176
	 * Check container attachment rules against object id
177
	 *
178
	 * @param int $object_id
179
	 * @return bool
180
	 */
181 View Code Duplication
	public function is_valid_attach_for_object( $object_id = null ) {
182
		$post = get_post( intval( $object_id ) );
183
184
		if ( ! $post ) {
185
			return false;
186
		}
187
188
		return $this->all_conditions_pass( intval( $post->ID ) );
189
	}
190
191
	/**
192
	 * Add meta box for each of the container post types
193
	 */
194
	public function attach() {
195
		$this->post_types = $this->get_post_type_visibility();
196
197
		foreach ( $this->post_types as $post_type ) {
198
			add_meta_box(
199
				$this->id,
200
				$this->title,
201
				array( $this, 'render' ),
202
				$post_type,
203
				$this->settings['meta_box_context'],
204
				$this->settings['meta_box_priority']
205
			);
206
207
			add_filter( "postbox_classes_{$post_type}_{$this->id}", array( $this, 'add_postbox_classes' ) );
208
		}
209
	}
210
211
	/**
212
	 * Classes to add to the post meta box
213
	 */
214
	public function add_postbox_classes( $classes ) {
215
		$classes[] = 'carbon-box';
216
		return $classes;
217
	}
218
219
	/**
220
	 * Output the container markup
221
	 */
222
	public function render() {
223
		include \Carbon_Fields\DIR . '/templates/Container/post_meta.php';
224
	}
225
226
	/**
227
	 * Set the post ID the container will operate with.
228
	 *
229
	 * @param int $post_id
230
	 */
231 View Code Duplication
	protected function set_post_id( $post_id ) {
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...
232
		$this->post_id = $post_id;
233
		$this->get_datastore()->set_object_id( $post_id );
234
235
		foreach ( $this->fields as $field ) {
236
			$datastore = $field->get_datastore();
237
			if ( $datastore->get_object_id() === 0 ) {
238
				$datastore->set_object_id( $post_id );
239
			}
240
		}
241
	}
242
243
	/**
244
	 * Get array of post types this container can appear on conditionally
245
	 *
246
	 * @return array<string>
247
	 */
248 View Code Duplication
	public function get_post_type_visibility() {
249
		$all_post_types = get_post_types();
250
		$filtered_collection = $this->condition_collection->filter( array( 'post_type' ) );
251
252
		$shown_on = array();
253
		foreach ( $all_post_types as $post_type ) {
254
			$environment = array(
255
				'post_type' => $post_type,
256
			);
257
			if ( $filtered_collection->is_fulfilled( $environment ) ) {
258
				$shown_on[] = $post_type;
259
			}
260
		}
261
		return $shown_on;
262
	}
263
264
	/**
265
	 * COMMON USAGE METHODS
266
	 */
267
268
	/**
269
	 * Show the container only on particular page referenced by it's path.
270
	 *
271
	 * @deprecated
272
	 * @param int|string $page page ID or page path
273
	 * @return object $this
274
	 */
275
	public function show_on_page( $page ) {
276
		$page_id = absint( $page );
277
278
		if ( $page_id && $page_id == $page ) {
279
			$page_obj = get_post( $page_id );
280
		} else {
281
			$page_obj = get_page_by_path( $page );
282
		}
283
		$page_id = ( $page_obj ) ? $page_obj->ID : -1;
284
285
		$this->where( 'post_id', '=', $page_id );
286
287
		return $this;
288
	}
289
290
	/**
291
	 * Show the container only on pages whose parent is referenced by $parent_page_path.
292
	 *
293
	 * @deprecated
294
	 * @param string $parent_page_path
295
	 * @return object $this
296
	 */
297
	public function show_on_page_children( $parent_page_path ) {
298
		$page = get_page_by_path( $parent_page_path );
299
		$page_id = ( $page ) ? $page->ID : -1;
300
		$this->where( 'post_parent_id', '=', $page_id );
301
		return $this;
302
	}
303
304
	/**
305
	 * Show the container only on pages whose template has filename $template_path.
306
	 *
307
	 * @deprecated
308
	 * @param string|array $template_path
309
	 * @return object $this
310
	 */
311
	public function show_on_template( $template_path ) {
312
		// Backwards compatibility where only pages support templates
313
		if ( version_compare( get_bloginfo( 'version' ), '4.7', '<' ) ) {
314
			$this->show_on_post_type( 'page' );
315
		}
316
317
		$template_paths = is_array( $template_path ) ? $template_path : array( $template_path );
318
		$this->where( 'post_template', 'IN', $template_paths );
319
		return $this;
320
	}
321
322
	/**
323
	 * Hide the container from pages whose template has filename $template_path.
324
	 *
325
	 * @deprecated
326
	 * @param string|array $template_path
327
	 * @return object $this
328
	 */
329
	public function hide_on_template( $template_path ) {
330
		$template_paths = is_array( $template_path ) ? $template_path : array( $template_path );
331
		$this->where( 'post_template', 'NOT IN', $template_paths );
332
		return $this;
333
	}
334
335
	/**
336
	 * Show the container only on hierarchical posts of level $level.
337
	 * Levels start from 1 (top level post)
338
	 *
339
	 * @deprecated
340
	 * @param int $level
341
	 * @return object $this
342
	 */
343
	public function show_on_level( $level ) {
344
		$this->where( 'post_level', '=', intval( $level ) );
345
		return $this;
346
	}
347
348
	/**
349
	 * Show the container only on posts from the specified format.
350
	 * Learn more about {@link http://codex.wordpress.org/Post_Formats Post Formats (Codex)}
351
	 *
352
	 * @deprecated
353
	 * @param string|array $post_format Name of the format as listed on Codex
354
	 * @return object $this
355
	 */
356
	public function show_on_post_format( $post_format ) {
357
		$post_formats = is_array( $post_format ) ? $post_format : array( $post_format );
358
		$this->where( 'post_format', 'IN', $post_formats );
359
		return $this;
360
	}
361
362
	/**
363
	 * Show the container only on posts from the specified type(s).
364
	 *
365
	 * @deprecated
366
	 * @param string|array $post_types
367
	 * @return object $this
368
	 */
369
	public function show_on_post_type( $post_types ) {
370
		$post_types = is_array( $post_types ) ? $post_types : array( $post_types );
371
		$this->where( 'post_type', 'IN', $post_types );
372
		return $this;
373
	}
374
375
	/**
376
	 * Show the container only on posts from the specified category.
377
	 *
378
	 * @see show_on_taxonomy_term()
379
	 *
380
	 * @deprecated
381
	 * @param string $category_slug
382
	 * @return object $this
383
	 */
384
	public function show_on_category( $category_slug ) {
385
		$this->where( 'post_term', '=', array(
386
			'value' => $category_slug,
387
			'field' => 'slug',
388
			'taxonomy' => 'category',
389
		) );
390
		return $this;
391
	}
392
393
	/**
394
	 * Show the container only on posts which have term $term_slug from the $taxonomy_slug taxonomy.
395
	 *
396
	 * @deprecated
397
	 * @param string $taxonomy_slug
398
	 * @param string $term_slug
399
	 * @return object $this
400
	 */
401
	public function show_on_taxonomy_term( $term_slug, $taxonomy_slug ) {
402
		$this->where( 'post_term', '=', array(
403
			'value' => $term_slug,
404
			'field' => 'slug',
405
			'taxonomy' => $taxonomy_slug,
406
		) );
407
		return $this;
408
	}
409
410
	/**
411
	 * Sets the meta box container context
412
	 *
413
	 * @see https://codex.wordpress.org/Function_Reference/add_meta_box
414
	 * @param string $context ('normal', 'advanced', 'side' or the custom `carbon_fields_after_title`)
415
	 */
416
	public function set_context( $context ) {
417
		$this->settings['meta_box_context'] = $context;
418
		return $this;
419
	}
420
421
	/**
422
	 * Sets the meta box container priority
423
	 *
424
	 * @see https://codex.wordpress.org/Function_Reference/add_meta_box
425
	 * @param string $priority ('high', 'core', 'default' or 'low')
426
	 */
427
	public function set_priority( $priority ) {
428
		$this->settings['meta_box_priority'] = $priority;
429
		return $this;
430
	}
431
}
432