Completed
Push — master ( 4713b5...48986a )
by David
05:28 queued 15s
created

Wordlift_Entity_Service   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 464
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
dl 0
loc 464
rs 8.295
c 0
b 0
f 0
wmc 42
lcom 1
cbo 3

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A get_instance() 0 4 1
A is_entity() 0 4 1
A get_classification_scope_for() 0 20 4
B is_used() 0 42 4
A is_internal_uri() 0 4 1
B get_entity_post_by_uri() 0 46 4
B save_post() 0 19 5
A set_alternative_labels() 0 20 4
A get_alternative_labels() 0 4 1
A edit_form_before_permalink() 0 21 3
B get_uri() 0 23 6
A get_alternative_label_input() 0 4 1
A count() 0 6 1
B create() 0 25 4
A get_related_entities() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Wordlift_Entity_Service 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 Wordlift_Entity_Service, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Provide entity-related services.
5
 *
6
 * @since 3.1.0
7
 */
8
class Wordlift_Entity_Service {
9
10
	/**
11
	 * The Log service.
12
	 *
13
	 * @since  3.2.0
14
	 * @access private
15
	 * @var \Wordlift_Log_Service $log The Log service.
16
	 */
17
	private $log;
18
19
	/**
20
	 * The UI service.
21
	 *
22
	 * @since  3.2.0
23
	 * @access private
24
	 * @var \Wordlift_UI_Service $ui_service The UI service.
25
	 */
26
	private $ui_service;
27
28
	/**
29
	 * The entity post type name.
30
	 *
31
	 * @since 3.1.0
32
	 */
33
	const TYPE_NAME = 'entity';
34
35
	/**
36
	 * The alternative label meta key.
37
	 *
38
	 * @since 3.2.0
39
	 */
40
	const ALTERNATIVE_LABEL_META_KEY = '_wl_alt_label';
41
42
	/**
43
	 * The alternative label input template.
44
	 *
45
	 * @since 3.2.0
46
	 */
47
	// TODO: this should be moved to a class that deals with HTML code.
48
	const ALTERNATIVE_LABEL_INPUT_TEMPLATE = '<div class="wl-alternative-label">
49
                <label class="screen-reader-text" id="wl-alternative-label-prompt-text" for="wl-alternative-label">Enter alternative label here</label>
50
                <input name="wl_alternative_label[]" size="30" value="%s" id="wl-alternative-label" type="text">
51
                <button class="button wl-delete-button">%s</button>
52
                </div>';
53
54
	/**
55
	 * A singleton instance of the Entity service.
56
	 *
57
	 * @since  3.2.0
58
	 * @access private
59
	 * @var \Wordlift_Entity_Service $instance A singleton instance of the Entity service.
60
	 */
61
	private static $instance;
62
63
	/**
64
	 * Create a Wordlift_Entity_Service instance.
65
	 *
66
	 * @since 3.2.0
67
	 *
68
	 * @param \Wordlift_UI_Service $ui_service The UI service.
69
	 */
70
	public function __construct( $ui_service ) {
71
72
		$this->log = Wordlift_Log_Service::get_logger( 'Wordlift_Entity_Service' );
73
74
		// Set the UI service.
75
		$this->ui_service = $ui_service;
76
77
		// Set the singleton instance.
78
		self::$instance = $this;
79
80
	}
81
82
	/**
83
	 * Get the singleton instance of the Entity service.
84
	 *
85
	 * @since 3.2.0
86
	 * @return \Wordlift_Entity_Service The singleton instance of the Entity service.
87
	 */
88
	public static function get_instance() {
89
90
		return self::$instance;
91
	}
92
93
	/**
94
	 * Determines whether a post is an entity or not.
95
	 *
96
	 * @since 3.1.0
97
	 *
98
	 * @param int $post_id A post id.
99
	 *
100
	 * @return bool Return true if the post is an entity otherwise false.
101
	 */
102
	public function is_entity( $post_id ) {
103
104
		return ( self::TYPE_NAME === get_post_type( $post_id ) );
105
	}
106
107
	/**
108
	 * Get the proper classification scope for a given entity post
109
	 *
110
	 * @since 3.5.0
111
	 *
112
	 * @param integer $post_id An entity post id.
113
	 *
114
	 * @return string Returns an uri.
115
	 */
116
	public function get_classification_scope_for( $post_id ) {
117
118
		if ( false === $this->is_entity( $post_id ) ) {
119
			return null;
120
		}
121
		// Retrieve the entity type
122
		$entity_type_arr = wl_entity_type_taxonomy_get_type( $post_id );
0 ignored issues
show
Deprecated Code introduced by
The function wl_entity_type_taxonomy_get_type() has been deprecated with message: use Wordlift_Entity_Type_Service::get_instance()->get( $post_id )

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

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

Loading history...
123
		$entity_type     = str_replace( 'wl-', '', $entity_type_arr['css_class'] );
124
		// Retrieve classification boxes configuration
125
		$classification_boxes = unserialize( WL_CORE_POST_CLASSIFICATION_BOXES );
126
		foreach ( $classification_boxes as $cb ) {
127
			if ( in_array( $entity_type, $cb['registeredTypes'] ) ) {
128
				return $cb['id'];
129
			}
130
		}
131
132
		// or null
133
		return null;
134
135
	}
136
137
138
	public function is_used( $post_id ) {
139
140
		if ( false === $this->is_entity( $post_id ) ) {
141
			return null;
142
		}
143
		// Retrieve the post
144
		$entity = get_post( $post_id );
145
146
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
147
		// Retrieve Wordlift relation instances table name
148
		$table_name = wl_core_get_relation_instances_table_name();
149
150
		// Check is it's referenced / related to another post / entity
151
		$stmt = $wpdb->prepare(
152
			"SELECT COUNT(*) FROM $table_name WHERE  object_id = %d",
153
			$entity->ID
154
		);
155
156
		// Perform the query
157
		$relation_instances = (int) $wpdb->get_var( $stmt );
158
		// If there is at least one relation instance for the current entity, then it's used
159
		if ( 0 < $relation_instances ) {
160
			return true;
161
		}
162
163
		// Check if the entity uri is used as meta_value
164
		$stmt = $wpdb->prepare(
165
			"SELECT COUNT(*) FROM $wpdb->postmeta WHERE post_id != %d AND meta_value = %s",
166
			$entity->ID,
167
			wl_get_entity_uri( $entity->ID )
0 ignored issues
show
Deprecated Code introduced by
The function wl_get_entity_uri() has been deprecated with message: use Wordlift_Entity_Service::get_instance()->get_uri( $post_id )

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

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

Loading history...
168
		);
169
		// Perform the query
170
		$meta_instances = (int) $wpdb->get_var( $stmt );
171
172
		// If there is at least one meta that refers the current entity uri, then current entity is used
173
		if ( 0 < $meta_instances ) {
174
			return true;
175
		}
176
177
		// If we are here, it means the current entity is not used at the moment
178
		return false;
179
	}
180
181
	/**
182
	 * Determines whether a given uri is an internal uri or not.
183
	 *
184
	 * @since 3.3.2
185
	 *
186
	 * @param int $uri An uri.
187
	 *
188
	 * @return true if the uri internal to the current dataset otherwise false.
189
	 */
190
	public function is_internal_uri( $uri ) {
191
192
		return ( 0 === strrpos( $uri, wl_configuration_get_redlink_dataset_uri() ) );
0 ignored issues
show
Deprecated Code introduced by
The function wl_configuration_get_redlink_dataset_uri() has been deprecated with message: use Wordlift_Configuration_Service::get_instance()->get_dataset_uri();

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

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

Loading history...
193
	}
194
195
	/**
196
	 * Find entity posts by the entity URI. Entity as searched by their entity URI or same as.
197
	 *
198
	 * @since 3.2.0
199
	 *
200
	 * @param string $uri The entity URI.
201
	 *
202
	 * @return WP_Post|null A WP_Post instance or null if not found.
203
	 */
204
	public function get_entity_post_by_uri( $uri ) {
205
206
		// Check if we've been provided with a value otherwise return null.
207
		if ( empty( $uri ) ) {
208
			return null;
209
		}
210
211
		$query_args = array(
212
			'posts_per_page' => 1,
213
			'post_status'    => 'any',
214
			'post_type'      => self::TYPE_NAME,
215
			'meta_query'     => array(
216
				array(
217
					'key'     => WL_ENTITY_URL_META_NAME,
218
					'value'   => $uri,
219
					'compare' => '=',
220
				),
221
			),
222
		);
223
224
		// Only if the current uri is not an internal uri 
225
		// entity search is performed also looking at sameAs values
226
		// This solve issues like https://github.com/insideout10/wordlift-plugin/issues/237
227
		if ( ! $this->is_internal_uri( $uri ) ) {
228
229
			$query_args['meta_query']['relation'] = 'OR';
230
			$query_args['meta_query'][]           = array(
231
				'key'     => Wordlift_Schema_Service::FIELD_SAME_AS,
232
				'value'   => $uri,
233
				'compare' => '=',
234
			);
235
		}
236
237
		$query = new WP_Query( $query_args );
238
239
		// Get the matching entity posts.
240
		$posts = $query->get_posts();
241
242
		// Return null if no post is found.
243
		if ( 0 === count( $posts ) ) {
244
			return null;
245
		}
246
247
		// Return the found post.
248
		return $posts[0];
249
	}
250
251
	/**
252
	 * Fires once a post has been saved. This function uses the $_REQUEST, therefore
253
	 * we check that the post we're saving is the current post.
254
	 *
255
	 * @see   https://github.com/insideout10/wordlift-plugin/issues/363
256
	 *
257
	 * @since 3.2.0
258
	 *
259
	 * @param int     $post_id Post ID.
260
	 * @param WP_Post $post    Post object.
261
	 * @param bool    $update  Whether this is an existing post being updated or not.
262
	 */
263
	public function save_post( $post_id, $post, $update ) {
0 ignored issues
show
Unused Code introduced by
The parameter $post 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...
Unused Code introduced by
The parameter $update 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...
Coding Style introduced by
save_post uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
264
265
		// We're setting the alternative label that have been provided via the UI
266
		// (in fact we're using $_REQUEST), while save_post may be also called
267
		// programmatically by some other function: we need to check therefore if
268
		// the $post_id in the save_post call matches the post id set in the request.
269
		//
270
		// If this is not the current post being saved or if it's not an entity, return.
271
		if ( ! isset( $_REQUEST['post_ID'] ) || $_REQUEST['post_ID'] != $post_id || ! $this->is_entity( $post_id ) ) {
272
			return;
273
		}
274
275
		// Get the alt labels from the request (or empty array).
276
		$alt_labels = isset( $_REQUEST['wl_alternative_label'] ) ? $_REQUEST['wl_alternative_label'] : array();
277
278
		// Set the alternative labels.
279
		$this->set_alternative_labels( $post_id, $alt_labels );
280
281
	}
282
283
	/**
284
	 * Set the alternative labels.
285
	 *
286
	 * @since 3.2.0
287
	 *
288
	 * @param int   $post_id    The post id.
289
	 * @param array $alt_labels An array of labels.
290
	 */
291
	public function set_alternative_labels( $post_id, $alt_labels ) {
292
293
		// Force $alt_labels to be an array
294
		if ( ! is_array( $alt_labels ) ) {
295
			$alt_labels = array( $alt_labels );
296
		}
297
298
		$this->log->debug( "Setting alternative labels [ post id :: $post_id ][ alt labels :: " . implode( ',', $alt_labels ) . " ]" );
299
300
		// Delete all the existing alternate labels.
301
		delete_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY );
302
303
		// Set the alternative labels.
304
		foreach ( $alt_labels as $alt_label ) {
305
			if ( ! empty( $alt_label ) ) {
306
				add_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY, $alt_label );
307
			}
308
		}
309
310
	}
311
312
	/**
313
	 * Retrieve the alternate labels.
314
	 *
315
	 * @since 3.2.0
316
	 *
317
	 * @param int $post_id Post id.
318
	 *
319
	 * @return mixed An array  of alternative labels.
320
	 */
321
	public function get_alternative_labels( $post_id ) {
322
323
		return get_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY );
324
	}
325
326
	/**
327
	 * Fires before the permalink field in the edit form (this event is available in WP from 4.1.0).
328
	 *
329
	 * @since 3.2.0
330
	 *
331
	 * @param WP_Post $post Post object.
332
	 */
333
	public function edit_form_before_permalink( $post ) {
334
335
		// If it's not an entity, return.
336
		if ( ! $this->is_entity( $post->ID ) ) {
337
			return;
338
		}
339
340
		// Print the input template.
341
		$this->ui_service->print_template( 'wl-tmpl-alternative-label-input', $this->get_alternative_label_input() );
342
343
		// Print all the currently set alternative labels.
344
		foreach ( $this->get_alternative_labels( $post->ID ) as $alt_label ) {
345
346
			echo $this->get_alternative_label_input( $alt_label );
347
348
		};
349
350
		// Print the button.
351
		$this->ui_service->print_button( 'wl-add-alternative-labels-button', __( 'Add more titles', 'wordlift' ) );
352
353
	}
354
355
	/**
356
	 * Get the URI for the entity with the specified post id.
357
	 *
358
	 * @since 3.6.0
359
	 *
360
	 * @param int $post_id The entity post id.
361
	 *
362
	 * @return null|string The entity URI or NULL if not found or the dataset URI is not configured.
363
	 */
364
	public function get_uri( $post_id ) {
365
366
		// If a null is given, nothing to do
367
		if ( null == $post_id ) {
368
			return null;
369
		}
370
371
		$uri = get_post_meta( $post_id, WL_ENTITY_URL_META_NAME, true );
372
373
		// If the dataset uri is not properly configured, null is returned
374
		if ( '' === wl_configuration_get_redlink_dataset_uri() ) {
0 ignored issues
show
Deprecated Code introduced by
The function wl_configuration_get_redlink_dataset_uri() has been deprecated with message: use Wordlift_Configuration_Service::get_instance()->get_dataset_uri();

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

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

Loading history...
375
			return null;
376
		}
377
378
		// Set the URI if it isn't set yet.
379
		$post_status = get_post_status( $post_id );
380
		if ( empty( $uri ) && 'auto-draft' !== $post_status && 'revision' !== $post_status ) {
381
			$uri = wl_build_entity_uri( $post_id );
382
			wl_set_entity_uri( $post_id, $uri );
383
		}
384
385
		return $uri;
386
	}
387
388
389
	/**
390
	 * Get the alternative label input HTML code.
391
	 *
392
	 * @since 3.2.0
393
	 *
394
	 * @param string $value The input value.
395
	 *
396
	 * @return string The input HTML code.
397
	 */
398
	private function get_alternative_label_input( $value = '' ) {
399
400
		return sprintf( self::ALTERNATIVE_LABEL_INPUT_TEMPLATE, esc_attr( $value ), __( 'Delete', 'wordlift' ) );
401
	}
402
403
	/**
404
	 * Get the number of entity posts published in this blog.
405
	 *
406
	 * @since 3.6.0
407
	 *
408
	 * @return int The number of published entity posts.
409
	 */
410
	public function count() {
411
412
		$count = wp_count_posts( self::TYPE_NAME );
413
414
		return $count->publish;
415
	}
416
417
	/**
418
	 * Create a new entity.
419
	 *
420
	 * @since 3.9.0
421
	 *
422
	 * @param string $name     The entity name.
423
	 * @param string $type_uri The entity's type URI.
424
	 * @param null   $logo     The entity logo id (or NULL if none).
425
	 * @param string $status   The post status, by default 'publish'.
426
	 *
427
	 * @return int|WP_Error The entity post id or a {@link WP_Error} in case the `wp_insert_post` call fails.
428
	 */
429
	public function create( $name, $type_uri, $logo = null, $status = 'publish' ) {
430
431
		// Create an entity for the publisher.
432
		$post_id = wp_insert_post( array(
433
			'post_type'    => self::TYPE_NAME,
434
			'post_title'   => $name,
435
			'post_status'  => $status,
436
			'post_content' => '',
437
		) );
438
439
		// Return the error if any.
440
		if ( is_wp_error( $post_id ) ) {
441
			return $post_id;
442
		}
443
444
		// Set the entity logo.
445
		if ( $logo && is_numeric( $logo ) ) {
446
			set_post_thumbnail( $post_id, $logo );
447
		}
448
449
		// Set the entity type.
450
		Wordlift_Entity_Type_Service::get_instance()->set( $post_id, $type_uri );
451
452
		return $post_id;
453
	}
454
455
	/**
456
	 * Get the entities related to the one with the specified id. By default only
457
	 * published entities will be returned.
458
	 *
459
	 * @since 3.10.0
460
	 *
461
	 * @param int    $id          The post id.
462
	 * @param string $post_status The target post status (default = publish).
463
	 *
464
	 * @return array An array of post ids.
465
	 */
466
	public function get_related_entities( $id, $post_status = 'publish' ) {
467
468
		return wl_core_inner_get_related_entities( 'post_ids', $id, null, $post_status );
469
	}
470
471
}
472