Completed
Push — develop ( bce0b6...c6ac88 )
by David
03:08
created

Wordlift_Entity_Service   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 471
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
dl 0
loc 471
rs 8.3396
c 0
b 0
f 0
wmc 44
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 47 4
C save_post() 0 25 7
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, entity search is
225
		// performed also looking at sameAs values.
226
		//
227
		// This solve issues like https://github.com/insideout10/wordlift-plugin/issues/237
228
		if ( ! $this->is_internal_uri( $uri ) ) {
229
230
			$query_args['meta_query']['relation'] = 'OR';
231
			$query_args['meta_query'][]           = array(
232
				'key'     => Wordlift_Schema_Service::FIELD_SAME_AS,
233
				'value'   => $uri,
234
				'compare' => '=',
235
			);
236
		}
237
238
		$query = new WP_Query( $query_args );
239
240
		// Get the matching entity posts.
241
		$posts = $query->get_posts();
242
243
		// Return null if no post is found.
244
		if ( 0 === count( $posts ) ) {
245
			return null;
246
		}
247
248
		// Return the found post.
249
		return $posts[0];
250
	}
251
252
	/**
253
	 * Fires once a post has been saved. This function uses the $_REQUEST, therefore
254
	 * we check that the post we're saving is the current post.
255
	 *
256
	 * @see   https://github.com/insideout10/wordlift-plugin/issues/363
257
	 *
258
	 * @since 3.2.0
259
	 *
260
	 * @param int     $post_id Post ID.
261
	 * @param WP_Post $post    Post object.
262
	 * @param bool    $update  Whether this is an existing post being updated or not.
263
	 */
264
	public function save_post( $post_id, $post, $update ) {
0 ignored issues
show
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...
265
266
		// Avoid doing anything if post is autosave or a revision.
267
268
		if ( wp_is_post_autosave( $post ) || wp_is_post_revision( $post ) ) {
269
			return;
270
		}
271
272
		// We're setting the alternative label that have been provided via the UI
273
		// (in fact we're using $_REQUEST), while save_post may be also called
274
		// programmatically by some other function: we need to check therefore if
275
		// the $post_id in the save_post call matches the post id set in the request.
276
		//
277
		// If this is not the current post being saved or if it's not an entity, return.
278
		if ( ! isset( $_REQUEST['post_ID'] ) || $_REQUEST['post_ID'] != $post_id || ! $this->is_entity( $post_id ) ) {
279
			return;
280
		}
281
282
		// Get the alt labels from the request (or empty array).
283
		$alt_labels = isset( $_REQUEST['wl_alternative_label'] ) ? $_REQUEST['wl_alternative_label'] : array();
284
285
		// Set the alternative labels.
286
		$this->set_alternative_labels( $post_id, $alt_labels );
287
288
	}
289
290
	/**
291
	 * Set the alternative labels.
292
	 *
293
	 * @since 3.2.0
294
	 *
295
	 * @param int   $post_id    The post id.
296
	 * @param array $alt_labels An array of labels.
297
	 */
298
	public function set_alternative_labels( $post_id, $alt_labels ) {
299
300
		// Force $alt_labels to be an array
301
		if ( ! is_array( $alt_labels ) ) {
302
			$alt_labels = array( $alt_labels );
303
		}
304
305
		$this->log->debug( "Setting alternative labels [ post id :: $post_id ][ alt labels :: " . implode( ',', $alt_labels ) . " ]" );
306
307
		// Delete all the existing alternate labels.
308
		delete_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY );
309
310
		// Set the alternative labels.
311
		foreach ( $alt_labels as $alt_label ) {
312
			if ( ! empty( $alt_label ) ) {
313
				add_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY, $alt_label );
314
			}
315
		}
316
317
	}
318
319
	/**
320
	 * Retrieve the alternate labels.
321
	 *
322
	 * @since 3.2.0
323
	 *
324
	 * @param int $post_id Post id.
325
	 *
326
	 * @return mixed An array  of alternative labels.
327
	 */
328
	public function get_alternative_labels( $post_id ) {
329
330
		return get_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY );
331
	}
332
333
	/**
334
	 * Fires before the permalink field in the edit form (this event is available in WP from 4.1.0).
335
	 *
336
	 * @since 3.2.0
337
	 *
338
	 * @param WP_Post $post Post object.
339
	 */
340
	public function edit_form_before_permalink( $post ) {
341
342
		// If it's not an entity, return.
343
		if ( ! $this->is_entity( $post->ID ) ) {
344
			return;
345
		}
346
347
		// Print the input template.
348
		$this->ui_service->print_template( 'wl-tmpl-alternative-label-input', $this->get_alternative_label_input() );
349
350
		// Print all the currently set alternative labels.
351
		foreach ( $this->get_alternative_labels( $post->ID ) as $alt_label ) {
352
353
			echo $this->get_alternative_label_input( $alt_label );
354
355
		};
356
357
		// Print the button.
358
		$this->ui_service->print_button( 'wl-add-alternative-labels-button', __( 'Add more titles', 'wordlift' ) );
359
360
	}
361
362
	/**
363
	 * Get the URI for the entity with the specified post id.
364
	 *
365
	 * @since 3.6.0
366
	 *
367
	 * @param int $post_id The entity post id.
368
	 *
369
	 * @return null|string The entity URI or NULL if not found or the dataset URI is not configured.
370
	 */
371
	public function get_uri( $post_id ) {
372
373
		// If a null is given, nothing to do
374
		if ( null == $post_id ) {
375
			return null;
376
		}
377
378
		$uri = get_post_meta( $post_id, WL_ENTITY_URL_META_NAME, true );
379
380
		// If the dataset uri is not properly configured, null is returned
381
		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...
382
			return null;
383
		}
384
385
		// Set the URI if it isn't set yet.
386
		$post_status = get_post_status( $post_id );
387
		if ( empty( $uri ) && 'auto-draft' !== $post_status && 'revision' !== $post_status ) {
388
			$uri = wl_build_entity_uri( $post_id );
389
			wl_set_entity_uri( $post_id, $uri );
390
		}
391
392
		return $uri;
393
	}
394
395
396
	/**
397
	 * Get the alternative label input HTML code.
398
	 *
399
	 * @since 3.2.0
400
	 *
401
	 * @param string $value The input value.
402
	 *
403
	 * @return string The input HTML code.
404
	 */
405
	private function get_alternative_label_input( $value = '' ) {
406
407
		return sprintf( self::ALTERNATIVE_LABEL_INPUT_TEMPLATE, esc_attr( $value ), __( 'Delete', 'wordlift' ) );
408
	}
409
410
	/**
411
	 * Get the number of entity posts published in this blog.
412
	 *
413
	 * @since 3.6.0
414
	 *
415
	 * @return int The number of published entity posts.
416
	 */
417
	public function count() {
418
419
		$count = wp_count_posts( self::TYPE_NAME );
420
421
		return $count->publish;
422
	}
423
424
	/**
425
	 * Create a new entity.
426
	 *
427
	 * @since 3.9.0
428
	 *
429
	 * @param string $name     The entity name.
430
	 * @param string $type_uri The entity's type URI.
431
	 * @param null   $logo     The entity logo id (or NULL if none).
432
	 * @param string $status   The post status, by default 'publish'.
433
	 *
434
	 * @return int|WP_Error The entity post id or a {@link WP_Error} in case the `wp_insert_post` call fails.
435
	 */
436
	public function create( $name, $type_uri, $logo = null, $status = 'publish' ) {
437
438
		// Create an entity for the publisher.
439
		$post_id = wp_insert_post( array(
440
			'post_type'    => self::TYPE_NAME,
441
			'post_title'   => $name,
442
			'post_status'  => $status,
443
			'post_content' => '',
444
		) );
445
446
		// Return the error if any.
447
		if ( is_wp_error( $post_id ) ) {
448
			return $post_id;
449
		}
450
451
		// Set the entity logo.
452
		if ( $logo && is_numeric( $logo ) ) {
453
			set_post_thumbnail( $post_id, $logo );
454
		}
455
456
		// Set the entity type.
457
		Wordlift_Entity_Type_Service::get_instance()->set( $post_id, $type_uri );
458
459
		return $post_id;
460
	}
461
462
	/**
463
	 * Get the entities related to the one with the specified id. By default only
464
	 * published entities will be returned.
465
	 *
466
	 * @since 3.10.0
467
	 *
468
	 * @param int    $id          The post id.
469
	 * @param string $post_status The target post status (default = publish).
470
	 *
471
	 * @return array An array of post ids.
472
	 */
473
	public function get_related_entities( $id, $post_status = 'publish' ) {
474
475
		return wl_core_inner_get_related_entities( 'post_ids', $id, null, $post_status );
476
	}
477
478
}
479