Completed
Push — master ( dab69d...577251 )
by David
03:12
created

Wordlift_Entity_Service   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 736
Duplicated Lines 2.58 %

Coupling/Cohesion

Components 2
Dependencies 4

Importance

Changes 0
Metric Value
dl 19
loc 736
rs 5
c 0
b 0
f 0
wmc 56
lcom 2
cbo 4

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A get_instance() 0 4 1
A is_entity() 0 15 3
A get_classification_scope_for() 0 19 4
B is_used() 0 42 4
A is_internal_uri() 0 4 1
A preload_uris() 10 53 2
A reset_uris() 0 5 1
A get_uris() 0 5 1
B get_entity_post_by_uri() 9 78 5
C save_post() 0 24 7
A set_alternative_labels() 0 20 4
A get_alternative_labels() 0 4 1
A get_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 9 1
A add_criterias() 0 12 1
A get_entity_terms() 0 18 1
B create() 0 25 4
A get_related_entities() 0 4 1
A get() 0 11 1
A valid_entity_post_types() 0 7 1

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 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 {@link Wordlift_Relation_Service} instance.
30
	 *
31
	 * @since  3.15.0
32
	 * @access private
33
	 * @var \Wordlift_Relation_Service $relation_service The {@link Wordlift_Relation_Service} instance.
34
	 */
35
	private $relation_service;
36
37
	/**
38
	 * An array of URIs to post ID valid for the current request.
39
	 *
40
	 * @since 3.16.1
41
	 * @var array $uri_to_post An array of URIs to post ID valid for the current request.
42
	 */
43
	private $uri_to_post;
44
45
	/**
46
	 * The entity post type name.
47
	 *
48
	 * @since 3.1.0
49
	 */
50
	const TYPE_NAME = 'entity';
51
52
	/**
53
	 * The alternative label meta key.
54
	 *
55
	 * @since 3.2.0
56
	 */
57
	const ALTERNATIVE_LABEL_META_KEY = '_wl_alt_label';
58
59
	/**
60
	 * The alternative label input template.
61
	 *
62
	 * @since 3.2.0
63
	 */
64
	// TODO: this should be moved to a class that deals with HTML code.
65
	const ALTERNATIVE_LABEL_INPUT_TEMPLATE = '<div class="wl-alternative-label">
66
                <label class="screen-reader-text" id="wl-alternative-label-prompt-text" for="wl-alternative-label">Enter alternative label here</label>
67
                <input name="wl_alternative_label[]" size="30" value="%s" id="wl-alternative-label" type="text">
68
                <button class="button wl-delete-button">%s</button>
69
                </div>';
70
71
	/**
72
	 * A singleton instance of the Entity service.
73
	 *
74
	 * @since  3.2.0
75
	 * @access private
76
	 * @var \Wordlift_Entity_Service $instance A singleton instance of the Entity service.
77
	 */
78
	private static $instance;
79
80
	/**
81
	 * Create a Wordlift_Entity_Service instance.
82
	 *
83
	 * @since 3.2.0
84
	 *
85
	 * @param \Wordlift_UI_Service       $ui_service       The UI service.
86
	 * @param \Wordlift_Relation_Service $relation_service The {@link Wordlift_Relation_Service} instance.
87
	 */
88
	public function __construct( $ui_service, $relation_service ) {
89
90
		$this->log = Wordlift_Log_Service::get_logger( 'Wordlift_Entity_Service' );
91
92
		$this->ui_service       = $ui_service;
93
		$this->relation_service = $relation_service;
94
95
		$this->reset_uris();
96
97
		// Set the singleton instance.
98
		self::$instance = $this;
99
	}
100
101
	/**
102
	 * Get the singleton instance of the Entity service.
103
	 *
104
	 * @since 3.2.0
105
	 * @return \Wordlift_Entity_Service The singleton instance of the Entity service.
106
	 */
107
	public static function get_instance() {
108
109
		return self::$instance;
110
	}
111
112
	/**
113
	 * Determines whether a post is an entity or not. Entity is in this context
114
	 * something which is not an article.
115
	 *
116
	 * @since 3.1.0
117
	 *
118
	 * @param int $post_id A post id.
119
	 *
120
	 * @return bool Return true if the post is an entity otherwise false.
121
	 */
122
	public function is_entity( $post_id ) {
123
124
		$terms = wp_get_object_terms( $post_id, Wordlift_Entity_Types_Taxonomy_Service::TAXONOMY_NAME );
125
126
		if ( 0 === count( $terms ) ) {
127
			return false;
128
		}
129
130
		// We don't consider an `article` to be an entity.
131
		if ( 'article' !== $terms[0]->slug ) {
132
			return true;
133
		}
134
135
		return false;
136
	}
137
138
	/**
139
	 * Get the proper classification scope for a given entity post
140
	 *
141
	 * @since 3.5.0
142
	 *
143
	 * @param integer $post_id An entity post id.
144
	 *
145
	 * @param string  $default The default classification scope, `what` if not
146
	 *                         provided.
147
	 *
148
	 * @return string Returns a classification scope (e.g. 'what').
149
	 */
150
	public function get_classification_scope_for( $post_id, $default = WL_WHAT_RELATION ) {
151
152
		if ( false === $this->is_entity( $post_id ) ) {
153
			return $default;
154
		}
155
156
		// Retrieve the entity type
157
		$entity_type_arr = Wordlift_Entity_Type_Service::get_instance()->get( $post_id );
158
		$entity_type     = str_replace( 'wl-', '', $entity_type_arr['css_class'] );
159
		// Retrieve classification boxes configuration
160
		$classification_boxes = unserialize( WL_CORE_POST_CLASSIFICATION_BOXES );
161
		foreach ( $classification_boxes as $cb ) {
162
			if ( in_array( $entity_type, $cb['registeredTypes'] ) ) {
163
				return $cb['id'];
164
			}
165
		}
166
167
		return $default;
168
	}
169
170
	/**
171
	 * Check whether a {@link WP_Post} is used.
172
	 *
173
	 * @param int $post_id The {@link WP_Post}'s id.
174
	 *
175
	 * @return bool|null Null if it's not an entity, otherwise true if it's used.
176
	 */
177
	public function is_used( $post_id ) {
178
179
		if ( false === $this->is_entity( $post_id ) ) {
180
			return null;
181
		}
182
		// Retrieve the post
183
		$entity = get_post( $post_id );
184
185
		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...
186
		// Retrieve Wordlift relation instances table name
187
		$table_name = wl_core_get_relation_instances_table_name();
188
189
		// Check is it's referenced / related to another post / entity
190
		$stmt = $wpdb->prepare(
191
			"SELECT COUNT(*) FROM $table_name WHERE  object_id = %d",
192
			$entity->ID
193
		);
194
195
		// Perform the query
196
		$relation_instances = (int) $wpdb->get_var( $stmt );
197
		// If there is at least one relation instance for the current entity, then it's used
198
		if ( 0 < $relation_instances ) {
199
			return true;
200
		}
201
202
		// Check if the entity uri is used as meta_value
203
		$stmt = $wpdb->prepare(
204
			"SELECT COUNT(*) FROM $wpdb->postmeta WHERE post_id != %d AND meta_value = %s",
205
			$entity->ID,
206
			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...
207
		);
208
		// Perform the query
209
		$meta_instances = (int) $wpdb->get_var( $stmt );
210
211
		// If there is at least one meta that refers the current entity uri, then current entity is used
212
		if ( 0 < $meta_instances ) {
213
			return true;
214
		}
215
216
		// If we are here, it means the current entity is not used at the moment
217
		return false;
218
	}
219
220
	/**
221
	 * Determines whether a given uri is an internal uri or not.
222
	 *
223
	 * @since 3.3.2
224
	 *
225
	 * @param int $uri An uri.
226
	 *
227
	 * @return true if the uri internal to the current dataset otherwise false.
228
	 */
229
	public function is_internal_uri( $uri ) {
230
231
		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...
232
	}
233
234
	/**
235
	 * Preload the provided URIs in the local cache.
236
	 *
237
	 * This function will populate the local `$uri_to_post` array by running a
238
	 * single query with all the URIs and returning the mappings in the array.
239
	 *
240
	 * @since 3.16.1
241
	 *
242
	 * @param array $uris An array of URIs.
243
	 */
244
	public function preload_uris( $uris ) {
245
246
		$that          = $this;
247
		$external_uris = array_filter( $uris, function ( $item ) use ( $that ) {
248
			return ! $that->is_internal_uri( $item );
249
		} );
250
251
		$query_args = array(
252
			// See https://github.com/insideout10/wordlift-plugin/issues/654.
253
			'ignore_sticky_posts' => 1,
254
			'post_status'         => 'any',
255
			'post_type'           => Wordlift_Entity_Service::valid_entity_post_types(),
256
			'meta_query'          => array(
257
				array(
258
					'key'     => WL_ENTITY_URL_META_NAME,
259
					'value'   => $uris,
260
					'compare' => 'IN',
261
				),
262
			),
263
		);
264
265
		// Only if the current uri is not an internal uri, entity search is
266
		// performed also looking at sameAs values.
267
		//
268
		// This solve issues like https://github.com/insideout10/wordlift-plugin/issues/237
269 View Code Duplication
		if ( 0 < count( $external_uris ) ) {
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...
270
271
			$query_args['meta_query']['relation'] = 'OR';
272
			$query_args['meta_query'][]           = array(
273
				'key'     => Wordlift_Schema_Service::FIELD_SAME_AS,
274
				'value'   => $external_uris,
275
				'compare' => 'IN',
276
			);
277
278
		}
279
280
		// Get the posts.
281
		$posts = get_posts( $query_args );
282
283
		// Populate the array. We reinitialize the array on purpose because
284
		// we don't want these data to long live.
285
		$this->uri_to_post = array_reduce( $posts, function ( $carry, $item ) use ( $that ) {
0 ignored issues
show
Documentation Bug introduced by
It seems like array_reduce($posts, fun...D), $item); }, array()) of type * is incompatible with the declared type array of property $uri_to_post.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
286
			return $carry
287
				   // Get the URI related to the post and fill them with the item id.
288
				   + array_fill_keys( $that->get_uris( $item->ID ), $item );
289
		}, array() );
290
291
		// Add the not found URIs.
292
		$this->uri_to_post += array_fill_keys( $uris, null );
293
294
		$this->log->debug( count( $this->uri_to_post ) . " URI(s) preloaded." );
295
296
	}
297
298
	/**
299
	 * Reset the URI to post local cache.
300
	 *
301
	 * @since 3.16.1
302
	 */
303
	public function reset_uris() {
304
305
		$this->uri_to_post = array();
306
307
	}
308
309
	/**
310
	 * Get all the URIs (item id and same as) related to a post.
311
	 *
312
	 * @since 3.16.1
313
	 *
314
	 * @param int $post_id The {@link WP_Post) id.
315
	 *
316
	 * @return array An array of URIs.
317
	 */
318
	public function get_uris( $post_id ) {
319
320
		return get_post_meta( $post_id, WL_ENTITY_URL_META_NAME )
321
			   + get_post_meta( $post_id, Wordlift_Schema_Service::FIELD_SAME_AS );
322
	}
323
324
325
	/**
326
	 * Find entity posts by the entity URI. Entity as searched by their entity URI or same as.
327
	 *
328
	 * @since 3.2.0
329
	 *
330
	 * @param string $uri The entity URI.
331
	 *
332
	 * @return WP_Post|null A WP_Post instance or null if not found.
333
	 */
334
	public function get_entity_post_by_uri( $uri ) {
335
336
		$this->log->trace( "Getting an entity post for URI $uri..." );
337
338
		// Check if we've been provided with a value otherwise return null.
339
		if ( empty( $uri ) ) {
340
			return null;
341
		}
342
343
		// If the URI is cached, return the cached post.
344
		if ( array_key_exists( $uri, $this->uri_to_post ) ) {
345
			$this->log->debug( "Returning cached post for $uri..." );
346
347
			return $this->uri_to_post[ $uri ];
348
		}
349
350
		$this->log->debug( "Querying post for $uri..." );
351
352
		$query_args = array(
353
			// See https://github.com/insideout10/wordlift-plugin/issues/654.
354
			'ignore_sticky_posts' => 1,
355
			'posts_per_page'      => 1,
356
			'post_status'         => 'any',
357
			'post_type'           => Wordlift_Entity_Service::valid_entity_post_types(),
358
			'meta_query'          => array(
359
				array(
360
					'key'     => WL_ENTITY_URL_META_NAME,
361
					'value'   => $uri,
362
					'compare' => '=',
363
				),
364
			),
365
			// The following query can be avoided, it's superfluous since
366
			// we're looking for a post with a specific Entity URI. This query
367
			// may in fact consume up to 50% of the timing necessary to load a
368
			// page.
369
			//
370
			// See https://github.com/insideout10/wordlift-plugin/issues/674.
371
			//			'tax_query'           => array(
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
372
			//				'relation' => 'AND',
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
373
			//				array(
374
			//					'taxonomy' => Wordlift_Entity_Types_Taxonomy_Service::TAXONOMY_NAME,
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
375
			//					'operator' => 'EXISTS',
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
376
			//				),
377
			//				array(
378
			//					'taxonomy' => Wordlift_Entity_Types_Taxonomy_Service::TAXONOMY_NAME,
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
379
			//					'field'    => 'slug',
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
380
			//					'terms'    => 'article',
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
381
			//					'operator' => 'NOT IN',
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
382
			//				),
383
			//			),
384
		);
385
386
		// Only if the current uri is not an internal uri, entity search is
387
		// performed also looking at sameAs values.
388
		//
389
		// This solve issues like https://github.com/insideout10/wordlift-plugin/issues/237
390 View Code Duplication
		if ( ! $this->is_internal_uri( $uri ) ) {
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...
391
392
			$query_args['meta_query']['relation'] = 'OR';
393
			$query_args['meta_query'][]           = array(
394
				'key'     => Wordlift_Schema_Service::FIELD_SAME_AS,
395
				'value'   => $uri,
396
				'compare' => '=',
397
			);
398
		}
399
400
		$posts = get_posts( $query_args );
401
402
		// Return null if no post is found.
403
		if ( 0 === count( $posts ) ) {
404
			$this->log->warn( "No post for URI $uri." );
405
406
			return null;
407
		}
408
409
		// Return the found post.
410
		return current( $posts );
411
	}
412
413
	/**
414
	 * Fires once a post has been saved. This function uses the $_REQUEST, therefore
415
	 * we check that the post we're saving is the current post.
416
	 *
417
	 * @see   https://github.com/insideout10/wordlift-plugin/issues/363
418
	 *
419
	 * @since 3.2.0
420
	 *
421
	 * @param int     $post_id Post ID.
422
	 * @param WP_Post $post    Post object.
423
	 * @param bool    $update  Whether this is an existing post being updated or not.
424
	 */
425
	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...
426
427
		// Avoid doing anything if post is autosave or a revision.
428
		if ( wp_is_post_autosave( $post ) || wp_is_post_revision( $post ) ) {
429
			return;
430
		}
431
432
		// We're setting the alternative label that have been provided via the UI
433
		// (in fact we're using $_REQUEST), while save_post may be also called
434
		// programmatically by some other function: we need to check therefore if
435
		// the $post_id in the save_post call matches the post id set in the request.
436
		//
437
		// If this is not the current post being saved or if it's not an entity, return.
438
		if ( ! isset( $_REQUEST['post_ID'] ) || $_REQUEST['post_ID'] != $post_id || ! $this->is_entity( $post_id ) ) {
439
			return;
440
		}
441
442
		// Get the alt labels from the request (or empty array).
443
		$alt_labels = isset( $_REQUEST['wl_alternative_label'] ) ? $_REQUEST['wl_alternative_label'] : array();
444
445
		// Set the alternative labels.
446
		$this->set_alternative_labels( $post_id, $alt_labels );
447
448
	}
449
450
	/**
451
	 * Set the alternative labels.
452
	 *
453
	 * @since 3.2.0
454
	 *
455
	 * @param int   $post_id    The post id.
456
	 * @param array $alt_labels An array of labels.
457
	 */
458
	public function set_alternative_labels( $post_id, $alt_labels ) {
459
460
		// Force $alt_labels to be an array
461
		if ( ! is_array( $alt_labels ) ) {
462
			$alt_labels = array( $alt_labels );
463
		}
464
465
		$this->log->debug( "Setting alternative labels [ post id :: $post_id ][ alt labels :: " . implode( ',', $alt_labels ) . " ]" );
466
467
		// Delete all the existing alternate labels.
468
		delete_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY );
469
470
		// Set the alternative labels.
471
		foreach ( $alt_labels as $alt_label ) {
472
			if ( ! empty( $alt_label ) ) {
473
				add_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY, $alt_label );
474
			}
475
		}
476
477
	}
478
479
	/**
480
	 * Retrieve the alternate labels.
481
	 *
482
	 * @since 3.2.0
483
	 *
484
	 * @param int $post_id Post id.
485
	 *
486
	 * @return mixed An array  of alternative labels.
487
	 */
488
	public function get_alternative_labels( $post_id ) {
489
490
		return get_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY );
491
	}
492
493
	/**
494
	 * Retrieve the labels for an entity, i.e. the title + the synonyms.
495
	 *
496
	 * @since 3.12.0
497
	 *
498
	 * @param int $post_id The entity {@link WP_Post} id.
499
	 *
500
	 * @return array An array with the entity title and labels.
501
	 */
502
	public function get_labels( $post_id ) {
503
504
		return array_merge( (array) get_the_title( $post_id ), $this->get_alternative_labels( $post_id ) );
505
	}
506
507
	/**
508
	 * Fires before the permalink field in the edit form (this event is available in WP from 4.1.0).
509
	 *
510
	 * @since 3.2.0
511
	 *
512
	 * @param WP_Post $post Post object.
513
	 */
514
	public function edit_form_before_permalink( $post ) {
515
516
		// If it's not an entity, return.
517
		if ( ! $this->is_entity( $post->ID ) ) {
518
			return;
519
		}
520
521
		// Print the input template.
522
		$this->ui_service->print_template( 'wl-tmpl-alternative-label-input', $this->get_alternative_label_input() );
523
524
		// Print all the currently set alternative labels.
525
		foreach ( $this->get_alternative_labels( $post->ID ) as $alt_label ) {
526
527
			echo $this->get_alternative_label_input( $alt_label );
528
529
		};
530
531
		// Print the button.
532
		$this->ui_service->print_button( 'wl-add-alternative-labels-button', __( 'Add more titles', 'wordlift' ) );
533
534
	}
535
536
	/**
537
	 * Get the URI for the entity with the specified post id.
538
	 *
539
	 * @since 3.6.0
540
	 *
541
	 * @param int $post_id The entity post id.
542
	 *
543
	 * @return null|string The entity URI or NULL if not found or the dataset URI is not configured.
544
	 */
545
	public function get_uri( $post_id ) {
546
547
		// If a null is given, nothing to do
548
		if ( null == $post_id ) {
549
			return null;
550
		}
551
552
		$uri = get_post_meta( $post_id, WL_ENTITY_URL_META_NAME, true );
553
554
		// If the dataset uri is not properly configured, null is returned
555
		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...
556
			return null;
557
		}
558
559
		// Set the URI if it isn't set yet.
560
		$post_status = get_post_status( $post_id );
561
		if ( empty( $uri ) && 'auto-draft' !== $post_status && 'revision' !== $post_status ) {
562
			$uri = wl_build_entity_uri( $post_id );
563
			wl_set_entity_uri( $post_id, $uri );
564
		}
565
566
		return $uri;
567
	}
568
569
570
	/**
571
	 * Get the alternative label input HTML code.
572
	 *
573
	 * @since 3.2.0
574
	 *
575
	 * @param string $value The input value.
576
	 *
577
	 * @return string The input HTML code.
578
	 */
579
	private function get_alternative_label_input( $value = '' ) {
580
581
		return sprintf( self::ALTERNATIVE_LABEL_INPUT_TEMPLATE, esc_attr( $value ), __( 'Delete', 'wordlift' ) );
582
	}
583
584
	/**
585
	 * Get the number of entity posts published in this blog.
586
	 *
587
	 * @since 3.6.0
588
	 *
589
	 * @return int The number of published entity posts.
590
	 */
591
	public function count() {
592
593
		$posts = get_posts( $this->add_criterias( array(
594
			'post_status' => 'any',
595
			'numberposts' => - 1,
596
		) ) );
597
598
		return count( $posts );
599
	}
600
601
	/**
602
	 * Add the entity filtering criterias to the arguments for a `get_posts`
603
	 * call.
604
	 *
605
	 * @since 3.15.0
606
	 *
607
	 * @param array $args The arguments for a `get_posts` call.
608
	 *
609
	 * @return array The arguments for a `get_posts` call.
610
	 */
611
	public static function add_criterias( $args ) {
612
613
		return $args + array(
614
				'post_type' => Wordlift_Entity_Service::valid_entity_post_types(),
615
				'tax_query' => array(
616
					array(
617
						'taxonomy' => Wordlift_Entity_Types_Taxonomy_Service::TAXONOMY_NAME,
618
						'terms'    => self::get_entity_terms(),
619
					),
620
				),
621
			);
622
	}
623
624
	/**
625
	 * Get the entity terms IDs which represent an entity.
626
	 *
627
	 * @since 3.15.0
628
	 *
629
	 * @return array An array of terms' ids.
630
	 */
631
	public static function get_entity_terms() {
632
633
		$terms = get_terms( Wordlift_Entity_Types_Taxonomy_Service::TAXONOMY_NAME, array(
634
			'hide_empty' => false,
635
			// Because of #334 (and the AAM plugin) we changed fields from 'id=>slug' to 'all'.
636
			// An issue has been opened with the AAM plugin author as well.
637
			//
638
			// see https://github.com/insideout10/wordlift-plugin/issues/334
639
			// see https://wordpress.org/support/topic/idslug-not-working-anymore?replies=1#post-8806863
640
			'fields'     => 'all',
641
		) );
642
643
		return array_map( function ( $term ) {
644
			return $term->term_id;
645
		}, array_filter( $terms, function ( $term ) {
646
			return 'article' !== $term->slug;
647
		} ) );
648
	}
649
650
	/**
651
	 * Create a new entity.
652
	 *
653
	 * @since 3.9.0
654
	 *
655
	 * @param string $name     The entity name.
656
	 * @param string $type_uri The entity's type URI.
657
	 * @param null   $logo     The entity logo id (or NULL if none).
658
	 * @param string $status   The post status, by default 'publish'.
659
	 *
660
	 * @return int|WP_Error The entity post id or a {@link WP_Error} in case the `wp_insert_post` call fails.
661
	 */
662
	public function create( $name, $type_uri, $logo = null, $status = 'publish' ) {
663
664
		// Create an entity for the publisher.
665
		$post_id = wp_insert_post( array(
666
			'post_type'    => self::TYPE_NAME,
667
			'post_title'   => $name,
668
			'post_status'  => $status,
669
			'post_content' => '',
670
		) );
671
672
		// Return the error if any.
673
		if ( is_wp_error( $post_id ) ) {
674
			return $post_id;
675
		}
676
677
		// Set the entity logo.
678
		if ( $logo && is_numeric( $logo ) ) {
679
			set_post_thumbnail( $post_id, $logo );
680
		}
681
682
		// Set the entity type.
683
		Wordlift_Entity_Type_Service::get_instance()->set( $post_id, $type_uri );
684
685
		return $post_id;
686
	}
687
688
	/**
689
	 * Get the entities related to the one with the specified id. By default only
690
	 * published entities will be returned.
691
	 *
692
	 * @since 3.10.0
693
	 *
694
	 * @param int    $id          The post id.
695
	 * @param string $post_status The target post status (default = publish).
696
	 *
697
	 * @return array An array of post ids.
698
	 */
699
	public function get_related_entities( $id, $post_status = 'publish' ) {
700
701
		return $this->relation_service->get_objects( $id, 'ids', null, $post_status );
702
	}
703
704
	/**
705
	 * Get the list of entities.
706
	 *
707
	 * @since 3.12.2
708
	 *
709
	 * @param array $params Custom parameters for WordPress' own {@link get_posts} function.
710
	 *
711
	 * @return array An array of entity posts.
712
	 */
713
	public function get( $params = array() ) {
714
715
		// Set the defaults.
716
		$defaults = array( 'post_type' => 'entity' );
717
718
		// Merge the defaults with the provided parameters.
719
		$args = wp_parse_args( $params, $defaults );
720
721
		// Call the `get_posts` function.
722
		return get_posts( $args );
723
	}
724
725
	/**
726
	 * The list of post type names which can be used for entities
727
	 *
728
	 * Criteria is that the post type is public. The list of valid post types
729
	 * can be overridden with a filter.
730
	 *
731
	 * @since 3.15.0
732
	 *
733
	 * @return array Array containing the names of the valid post types.
734
	 */
735
	static function valid_entity_post_types() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
736
737
		// Ignore builtins in the call to avoid getting attachments.
738
		$post_types = array( 'post', 'page', self::TYPE_NAME );
739
740
		return apply_filters( 'wl_valid_entity_post_types', $post_types );
741
	}
742
743
}
744