Completed
Push — master ( 0688b4...aad3f4 )
by Joro
49:06 queued 39:48
created

Block_Container   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 456
Duplicated Lines 7.46 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 34
loc 456
rs 6.96
c 0
b 0
f 0
wmc 53
lcom 1
cbo 4

29 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 7 7 2
A init() 0 3 1
A is_valid_save() 0 5 1
A save() 0 4 1
A get_environment_for_request() 0 3 1
A is_valid_attach_for_request() 0 3 1
A get_environment_for_object() 0 3 1
A is_valid_attach_for_object() 0 3 1
A attach() 0 5 1
A attach_block_category() 0 9 3
A set_description() 0 5 1
A set_category() 0 7 2
A set_icon() 0 5 1
A set_keywords() 0 5 1
A set_style_handle() 0 9 2
A set_style() 0 3 1
A set_editor_style() 0 3 1
A set_preview_mode() 0 8 2
A set_mode() 0 11 2
A set_parent() 3 9 5
A set_inner_blocks() 0 5 1
A set_inner_blocks_position() 9 9 2
A set_inner_blocks_template() 3 9 3
A set_inner_blocks_template_lock() 9 9 3
A set_allowed_inner_blocks() 3 19 5
A set_render_callback() 0 5 1
A render_block() 0 13 1
A get_block_type_name() 0 3 1
A register_block() 0 29 5

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 Block_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 Block_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\Helper\Helper;
7
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
8
9
class Block_Container extends Container {
10
	/**
11
	 * {@inheritDoc}
12
	 */
13
	public $settings = array(
14
		'mode' => 'edit',
15
		'preview' => true,
16
		'parent' => null,
17
		'icon' => 'block-default',
18
		'inner_blocks' => array(
19
			'enabled' => false,
20
			'position' => 'above',
21
			'template' => null,
22
			'template_lock' => null,
23
			'allowed_blocks' => null,
24
		),
25
		'category' => array(
26
			'slug' => 'common',
27
		),
28
	);
29
30
	/**
31
	 * Mode map for settings
32
	 *
33
	 * @see set_mode()
34
	 * @var array
35
	 */
36
	protected $mode_map = array(
37
		'both' => array(
38
			'mode' => 'edit',
39
			'preview' => true,
40
		),
41
		'edit' => array(
42
			'mode' => 'edit',
43
			'preview' => false,
44
		),
45
		'preview' => array(
46
			'mode' => 'preview',
47
			'preview' => false,
48
		),
49
	);
50
51
	/***
52
	 * Block type render callback.
53
	 *
54
	 * @var callable
55
	 */
56
	protected $render_callback;
57
58
	/**
59
	 * {@inheritDoc}
60
	 */
61 View Code Duplication
	public function __construct( $id, $title, $type, $condition_collection, $condition_translator ) {
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...
62
		parent::__construct( $id, $title, $type, $condition_collection, $condition_translator );
63
64
		if ( ! $this->get_datastore() ) {
65
			$this->set_datastore( Datastore::make( 'empty' ), $this->has_default_datastore() );
66
		}
67
	}
68
69
	/**
70
	 * {@inheritDoc}
71
	 */
72
	public function init() {
73
		add_action( 'init', array( $this, '_attach' ) );
74
	}
75
76
	/**
77
	 * {@inheritDoc}
78
	 */
79
	public function is_valid_save() {
80
		// Return false because Gutenberg
81
		// will handle saving.
82
		return false;
83
	}
84
85
	/**
86
	 * {@inheritDoc}
87
	 */
88
	public function save( $data = null ) {
89
		// Nothing to do here because
90
		// the data is saved by Gutenberg.
91
	}
92
93
	/**
94
	 * {@inheritDoc}
95
	 */
96
	protected function get_environment_for_request() {
97
		return array();
98
	}
99
100
	/**
101
	 * {@inheritDoc}
102
	 */
103
	public function is_valid_attach_for_request() {
104
		return function_exists( 'register_block_type' );
105
	}
106
107
	/**
108
	 * {@inheritDoc}
109
	 */
110
	protected function get_environment_for_object( $object_id ) {
111
		return array();
112
	}
113
114
	/**
115
	 * {@inheritDoc}
116
	 */
117
	public function is_valid_attach_for_object( $object_id = null ) {
118
		return function_exists( 'register_block_type' );
119
	}
120
121
	/**
122
	 * {@inheritDoc}
123
	 */
124
	public function attach() {
125
		add_filter( 'block_categories', array( $this, 'attach_block_category' ), 10, 2 );
126
127
		$this->register_block();
128
	}
129
130
	/**
131
	 * Attach the category of the block type.
132
	 *
133
	 * @param  array $categories
134
	 * @return array
135
	 */
136
	public function attach_block_category( $categories ) {
137
		foreach ( $categories as $category ) {
138
			if ( $category[ 'slug' ] === $this->settings[ 'category' ][ 'slug' ] ) {
139
				return $categories;
140
			}
141
		}
142
143
		return array_merge( $categories, array( $this->settings[ 'category' ] ) );
144
	}
145
146
	/**
147
	 * Set the description of the block type.
148
	 *
149
	 * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-registration/#description-optional
150
	 *
151
	 * @param  string $description
152
	 * @return Block_Container
153
	 */
154
	public function set_description( $description ) {
155
		$this->settings[ 'description' ] = $description;
156
157
		return $this;
158
	}
159
160
	/**
161
	 * Set the category of the block type.
162
	 *
163
	 * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-registration/#category
164
	 *
165
	 * @param  string $slug
166
	 * @param  string $title
167
	 * @param  string $icon
168
	 * @return Block_Container
169
	 */
170
	public function set_category( $slug, $title = null, $icon = null ) {
171
		$this->settings[ 'category' ][ 'slug' ] = $slug;
172
		$this->settings[ 'category' ][ 'icon' ] = $icon;
173
		$this->settings[ 'category' ][ 'title' ] = $title ?: Helper::normalize_label( $slug );
174
175
		return $this;
176
	}
177
178
	/**
179
	 * Set the icon of the block type.
180
	 *
181
	 * @see https://developer.wordpress.org/resource/dashicons
182
	 * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-registration/#icon-optional
183
	 *
184
	 * @param  string $icon
185
	 * @return Block_Container
186
	 */
187
	public function set_icon( $icon ) {
188
		$this->settings[ 'icon' ] = $icon;
189
190
		return $this;
191
	}
192
193
	/**
194
	 * Set the keywords of the block type.
195
	 *
196
	 * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-registration/#keywords-optional
197
	 *
198
	 * @param  array $keywords
199
	 * @return Block_Container
200
	 */
201
	public function set_keywords( $keywords = array() ) {
202
		$this->settings[ 'keywords' ] = array_slice( $keywords, 0, 3 );
203
204
		return $this;
205
	}
206
207
	/**
208
	 * Set a style handle.
209
	 *
210
	 * @param  string $key
211
	 * @param  string $handle
212
	 * @return Block_Container
213
	 */
214
	protected function set_style_handle( $key, $handle ) {
215
		if ( ! wp_style_is( $handle ) ) {
216
			throw new \Exception( __( "Style '$handle' is not enqueued.", 'crb' ) );
217
		}
218
219
		$this->settings[ $key ] = $handle;
220
221
		return $this;
222
	}
223
224
	/**
225
	 * Set the style of the block type.
226
	 *
227
	 * @param  string $handle
228
	 * @return Block_Container
229
	 */
230
	public function set_style( $handle ) {
231
		return $this->set_style_handle( 'style', $handle );
232
	}
233
234
	/**
235
	 * Set the editor style of the block type.
236
	 *
237
	 * @param  string $handle
238
	 * @return Block_Container
239
	 */
240
	public function set_editor_style( $handle ) {
241
		return $this->set_style_handle( 'editor_style', $handle );
242
	}
243
244
	/**
245
	 * Set whether the preview mode is available for the block type.
246
	 *
247
	 * @param  boolean $preview
248
	 * @return Block_Container
249
	 */
250
	public function set_preview_mode( $preview = true ) {
251
		_deprecated_function( __FUNCTION__, '3.0', 'set_mode()' );
252
253
		$mode = $preview ? 'both' : 'edit';
254
		$this->set_mode( $mode );
255
256
		return $this;
257
	}
258
259
	/**
260
	 * Set the mode for the block type.
261
	 *
262
	 * @param  string $mode
263
	 * @return Block_Container
264
	 */
265
	public function set_mode( $mode ) {
266
		$modes = array_keys( $this->mode_map );
267
		if ( ! in_array( $mode, $modes ) ) {
268
			Incorrect_Syntax_Exception::raise( 'The mode must be one of the following: ' . implode( ', ', $modes ) );
269
		}
270
271
		$this->settings[ 'mode' ] = $this->mode_map[ $mode ][ 'mode' ];
272
		$this->settings[ 'preview' ] = $this->mode_map[ $mode ][ 'preview' ];
273
274
		return $this;
275
	}
276
277
	/**
278
	 * Set the parent block(s) in which the block type can be inserted.
279
	 *
280
	 * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/block-api/block-registration/#parent-optional
281
	 *
282
	 * @param  string|string[]|null $parent
283
	 * @return Block_Container
284
	 */
285
	public function set_parent( $parent = null ) {
286 View Code Duplication
		if ( ! is_array( $parent ) && ! is_string( $parent ) && ! is_null( $parent ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
287
			throw new \Exception( __( "The parent must be 'array', 'string' or 'null'.", 'crb' ) );
288
		}
289
290
		$this->settings[ 'parent' ] = is_string( $parent ) ? array( $parent ) : $parent;
291
292
		return $this;
293
	}
294
295
	/**
296
	 * Set whether the inner blocks are available for the block type.
297
	 *
298
	 * @param  boolean $inner_blocks
299
	 * @return Block_Container
300
	 */
301
	public function set_inner_blocks( $inner_blocks = true ) {
302
		$this->settings[ 'inner_blocks' ][ 'enabled' ] = $inner_blocks;
303
304
		return $this;
305
	}
306
307
	/**
308
	 * Set the position of the inner blocks to be rendered
309
	 * above or below the fields.
310
	 *
311
	 * @param  string $position
312
	 * @return Block_Container
313
	 */
314 View Code Duplication
	public function set_inner_blocks_position( $position = 'above' ) {
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...
315
		if ( ! in_array( $position, [ 'above', 'below' ] ) ) {
316
			throw new \Exception( __( "The position of inner blocks must be 'above' or 'below'.", 'crb' ) );
317
		}
318
319
		$this->settings[ 'inner_blocks' ][ 'position' ] = $position;
320
321
		return $this;
322
	}
323
324
	/**
325
	 * Set the default template that should be rendered in inner blocks.
326
	 *
327
	 * @see https://github.com/WordPress/gutenberg/tree/master/packages/editor/src/components/inner-blocks#template
328
	 *
329
	 * @param  array[]|null $template
330
	 * @return Block_Container
331
	 */
332
	public function set_inner_blocks_template( $template = null ) {
333 View Code Duplication
		if ( ! is_array( $template ) && ! is_null( $template ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
334
			throw new \Exception( __( "The template must be an 'array' or 'null'.", 'crb' ) );
335
		}
336
337
		$this->settings[ 'inner_blocks' ][ 'template' ] = $template;
338
339
		return $this;
340
	}
341
342
	/**
343
	 * Set the lock mode used by template of inner blocks.
344
	 *
345
	 * @see https://github.com/WordPress/gutenberg/tree/master/packages/editor/src/components/inner-blocks#templatelock
346
	 *
347
	 * @param  string|boolean|null $lock
348
	 * @return Block_Container
349
	 */
350 View Code Duplication
	public function set_inner_blocks_template_lock( $lock = null ) {
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...
351
		if ( is_string( $lock ) && ! in_array( $lock, [ 'all', 'insert' ] ) ) {
352
			throw new \Exception( __( "The template lock must be 'all', 'insert', 'false' or 'null'.", 'crb' ) );
353
		}
354
355
		$this->settings[ 'inner_blocks' ][ 'template_lock' ] = $lock;
356
357
		return $this;
358
	}
359
360
	/**
361
	 * Set the list of allowed blocks that can be inserted.
362
	 *
363
	 * @see https://github.com/WordPress/gutenberg/tree/master/packages/editor/src/components/inner-blocks#allowedblocks
364
	 *
365
	 * @param  string[]|null $blocks
366
	 * @return Block_Container
367
	 */
368
	public function set_allowed_inner_blocks( $blocks = null ) {
369 View Code Duplication
		if ( ! is_array( $blocks ) && ! is_null( $blocks ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
370
			throw new \Exception( __( "The allowed blocks must be an 'array' or 'null'.", 'crb' ) );
371
		}
372
373
		if ( is_array( $blocks ) ) {
374
			$this->settings[ 'inner_blocks' ][ 'allowed_blocks' ] = array_map( function ( $block ) {
375
				if ( $block instanceof self ) {
376
					return $block->get_block_type_name();
377
				}
378
379
				return $block;
380
			}, $blocks );
381
		} else {
382
			$this->settings[ 'inner_blocks' ][ 'allowed_blocks' ] = $blocks;
383
		}
384
385
		return $this;
386
	}
387
388
	/**
389
	 * Set the render callback of the block type.
390
	 *
391
	 * @see https://wordpress.org/gutenberg/handbook/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks/
392
	 *
393
	 * @param  callable $render_callback
394
	 * @return Block_Container
395
	 */
396
	public function set_render_callback( $render_callback ) {
397
		$this->render_callback = $render_callback;
398
399
		return $this;
400
	}
401
402
	/**
403
	 * Render the block type.
404
	 *
405
	 * @param  array  $attributes
406
	 * @param  string $content
407
	 * @return string
408
	 */
409
	public function render_block( $attributes, $content ) {
410
		$fields = $attributes['data'];
411
412
		// Unset the "data" property because we
413
		// pass it as separate argument to the callback.
414
		unset($attributes['data']);
415
416
		ob_start();
417
418
		call_user_func( $this->render_callback , $fields, $attributes, $content );
419
420
		return ob_get_clean();
421
	}
422
423
	/**
424
	 * Returns the block type name, e.g. "carbon-fields/testimonial"
425
	 */
426
	private function get_block_type_name() {
427
		return str_replace( 'carbon-fields-container-', 'carbon-fields/', str_replace( '_', '-', $this->id ) );
428
	}
429
430
	/**
431
	 * Register the block type.
432
	 *
433
	 * @return void
434
	 */
435
	protected function register_block() {
436
		if ( is_null( $this->render_callback ) ) {
437
			throw new \Exception( __( "'render_callback' is required for the blocks.", 'crb' ) );
438
		}
439
440
		if ( ! is_callable( $this->render_callback ) ) {
441
			throw new \Exception( __( "'render_callback' must be a callable.", 'crb' ) );
442
		}
443
444
		$style = isset( $this->settings[ 'style' ] ) ? $this->settings[ 'style' ] : null;
445
		$editor_style = isset( $this->settings[ 'editor_style' ] ) ? $this->settings[ 'editor_style' ] : null;
446
		$attributes = array_reduce( $this->get_fields(), function( $attributes, $field ) {
447
			$attributes[ 'data' ][ 'default' ][ $field->get_base_name() ] = $field->get_default_value();
448
449
			return $attributes;
450
		}, array(
451
			'data' => array(
452
				'type' => 'object',
453
				'default' => array(),
454
			),
455
		) );
456
457
		register_block_type( $this->get_block_type_name(), array(
458
			'style' => $style,
459
			'editor_style' => $editor_style,
460
			'attributes' => $attributes,
461
			'render_callback' => array( $this, 'render_block' ),
462
		) );
463
	}
464
}
465