Completed
Push — develop ( f65bd6...158ffc )
by David
08:55
created

Wordlift_Relation_Service::and_status()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 8
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 8
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Services: Relation Service.
4
 *
5
 * The Relation Service provides helpful function to get related posts/entities.
6
 *
7
 * @since      3.15.0
8
 * @package    Wordlift
9
 * @subpackage Wordlift/includes
10
 */
11
12
/**
13
 * Define the {@link Wordlift_Relation_Service} class.
14
 *
15
 * @since      3.15.0
16
 * @package    Wordlift
17
 * @subpackage Wordlift/includes
18
 */
19
class Wordlift_Relation_Service {
20
21
	/**
22
	 * The singleton instance.
23
	 *
24
	 * @since  3.15.0
25
	 * @access private
26
	 * @var \Wordlift_Relation_Service $instance The singleton instance.
27
	 */
28
	private static $instance;
29
30
	/**
31
	 * The relation table name in MySQL, set during instantiation.
32
	 *
33
	 * @since  3.15.0
34
	 * @access private
35
	 * @var string $relation_table The relation table name.
36
	 */
37
	private $relation_table;
38
39
	/**
40
	 * Create a {@link Wordlift_Relation_Service} instance.
41
	 *
42
	 * @since 3.15.0
43
	 */
44
	public function __construct() {
45
		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...
46
47
		// The relations table.
48
		$this->relation_table = "{$wpdb->prefix}wl_relation_instances";
49
50
		self::$instance = $this;
51
52
	}
53
54
	/**
55
	 * Get the singleton instance.
56
	 *
57
	 * @since  3.15.0
58
	 * @access public
59
	 * @return \Wordlift_Relation_Service The {@link Wordlift_Relation_Service}
60
	 *                                    singleton instance.
61
	 */
62
	public static function get_instance() {
63
64
		return self::$instance;
65
	}
66
67
	/**
68
	 * Get the articles referencing the specified entity {@link WP_Post}.
69
	 *
70
	 * @since 3.15.0
71
	 *
72
	 * @param int|array   $object_id The entity {@link WP_Post}'s id.
73
	 * @param string      $fields    The fields to return, 'ids' to only return ids or
74
	 *                               '*' to return all fields, by default '*'.
75
	 * @param null|string $predicate The predicate (who|what|...), by default all.
76
	 * @param null|string $status    The status, by default all.
77
	 * @param array       $excludes  An array of ids to exclude from the results.
78
	 * @param null|int    $limit     The maximum number of results, by default
79
	 *                               no limit.
80
	 * @param null|array  $include   The {@link WP_Post}s' ids to include.
81
	 *
82
	 * @return array|object|null Database query results
83
	 */
84
	public function get_article_subjects( $object_id, $fields = '*', $predicate = null, $status = null, $excludes = array(), $limit = null, $include = null ) {
85
		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...
86
87
		// The output fields.
88
		$actual_fields = self::fields( $fields );
89
90
		$objects = $this->article_id_to_entity_id( $object_id );
91
92
		$sql = "
93
			SELECT p.$actual_fields
94
			FROM {$this->relation_table} r
95
			INNER JOIN $wpdb->posts p
96
				ON p.id = r.subject_id
97
			"
98
		       // Add the status clause.
99
		       . self::and_status( $status )
100
		       . self::inner_join_is_article()
101
		       . self::where_object_id( $objects )
102
		       // Since `object_id` can be an article ID we need to exclude it from
103
		       // the results.
104
		       . self::and_article_not_in( array_merge( $excludes, (array) $object_id ) )
105
		       . self::and_article_in( $include )
106
		       . " AND p.post_type IN ( 'post', 'page', 'entity' ) "
107
		       . self::and_predicate( $predicate )
108
		       . self::limit( $limit );
109
110
		return '*' === $actual_fields ? $wpdb->get_results( $sql ) : $wpdb->get_col( $sql );
111
	}
112
113
	/**
114
	 * Add the limit clause if specified.
115
	 *
116
	 * @since 3.15.0
117
	 *
118
	 * @param null|int $limit The maximum number of results.
119
	 *
120
	 * @return string The limit clause (empty if no limit has been specified).
121
	 */
122
	private static function limit( $limit = null ) {
123
124
		if ( null === $limit ) {
125
			return '';
126
		}
127
128
		return "LIMIT $limit";
129
	}
130
131
	/**
132
	 * Map the provided ids into entities (i.e. return the id if it's an entity
133
	 * or get the entities if it's a post).
134
	 *
135
	 * @since 3.15.0
136
	 *
137
	 * @param int|array $object_id An array of posts/entities' ids.
138
	 *
139
	 * @return array An array of entities' ids.
140
	 */
141
	private function article_id_to_entity_id( $object_id ) {
142
143
		$entity_service = Wordlift_Entity_Service::get_instance();
144
145
		$relation_service = $this;
146
147
		return array_reduce( (array) $object_id, function ( $carry, $item ) use ( $entity_service, $relation_service ) {
148
			if ( $entity_service->is_entity( $item ) ) {
149
				return array_merge( $carry, (array) $item );
150
			}
151
152
			return array_merge( $carry, $relation_service->get_objects( $item, 'ids' ) );
153
		}, array() );
154
155
	}
156
157
	/**
158
	 * Add the WHERE clause.
159
	 *
160
	 * @since 3.15.0
161
	 *
162
	 * @param int|array $object_id An array of {@link WP_Post}s' ids.
163
	 *
164
	 * @return string The WHERE clause.
165
	 */
166
	private static function where_object_id( $object_id ) {
167
168
		return ' WHERE r.object_id IN ( ' . implode( ',', wp_parse_id_list( (array) $object_id ) ) . ' )';
169
	}
170
171
	/**
172
	 * Add the exclude clause.
173
	 *
174
	 * @since 3.15.0
175
	 *
176
	 * @param int|array $exclude An array of {@link WP_Post}s' ids to exclude.
177
	 *
178
	 * @return string The exclude clause.
179
	 */
180
	private static function and_article_not_in( $exclude ) {
181
182
		return ' AND NOT p.ID IN ( ' . implode( ',', wp_parse_id_list( (array) $exclude ) ) . ' )';
183
	}
184
185
	/**
186
	 * Add the include clause.
187
	 *
188
	 * @since 3.15.0
189
	 *
190
	 * @param null|int|array $include An array of {@link WP_Post}s' ids.
191
	 *
192
	 * @return string An empty string if $include is null otherwise the include
193
	 *                clause.
194
	 */
195
	private static function and_article_in( $include = null ) {
196
197
		if ( null === $include ) {
198
			return '';
199
		}
200
201
		return ' AND p.ID IN ( ' . implode( ',', wp_parse_id_list( (array) $include ) ) . ' )';
202
	}
203
204
	/**
205
	 * Get the entities' {@link WP_Post}s' ids referencing the specified {@link WP_Post}.
206
	 *
207
	 * @since 3.15.0
208
	 *
209
	 * @param int         $object_id The object {@link WP_Post}'s id.
210
	 * @param string      $fields    The fields to return, 'ids' to only return ids or
211
	 *                               '*' to return all fields, by default '*'.
212
	 * @param null|string $status    The status, by default all.
213
	 *
214
	 * @return array|object|null Database query results
215
	 */
216 View Code Duplication
	public function get_non_article_subjects( $object_id, $fields = '*', $status = 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...
217
		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...
218
219
		// The output fields.
220
		$actual_fields = self::fields( $fields );
221
222
		$sql = $wpdb->prepare(
223
			"
224
			SELECT p.$actual_fields
225
			FROM {$this->relation_table} r
226
			INNER JOIN $wpdb->posts p
227
				ON p.id = r.subject_id
228
			"
229
			// Add the status clause.
230
			. self::and_status( $status )
231
			. self::inner_join_is_not_article() .
232
			"
233
			WHERE r.object_id = %d
234
				AND p.post_type IN ( 'post', 'page', 'entity' )
235
			"
236
			,
237
			$object_id
238
		);
239
240
		return '*' === $actual_fields ? $wpdb->get_results( $sql ) : $wpdb->get_col( $sql );
241
	}
242
243
	/**
244
	 * Get the entities referenced by the specified {@link WP_Post}.
245
	 *
246
	 * @since 3.15.0
247
	 *
248
	 * @param int         $subject_id The {@link WP_Post}'s id.
249
	 * @param string      $fields     The fields to return, 'ids' to only return ids or
250
	 *                                '*' to return all fields, by default '*'.
251
	 * @param null|string $predicate  The predicate (who|what|...), by default all.
252
	 * @param null|string $status     The status, by default all.
253
	 *
254
	 * @return array|object|null Database query results
255
	 */
256 View Code Duplication
	public function get_objects( $subject_id, $fields = '*', $predicate = null, $status = 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...
257
		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...
258
259
		// The output fields.
260
		$actual_fields = self::fields( $fields );
261
262
		$sql = $wpdb->prepare(
263
			"
264
			SELECT p.$actual_fields
265
			FROM {$this->relation_table} r
266
			INNER JOIN $wpdb->posts p
267
				ON p.id = r.object_id
268
			"
269
			// Add the status clause.
270
			. self::and_status( $status )
271
			. self::inner_join_is_not_article()
272
			. "
273
			WHERE r.subject_id = %d
274
				AND p.post_type IN ( 'post', 'page', 'entity' )
275
			"
276
			. self::and_predicate( $predicate )
277
			,
278
			$subject_id
279
		);
280
281
		return '*' === $actual_fields ? $wpdb->get_results( $sql ) : $wpdb->get_col( $sql );
282
	}
283
284
	/**
285
	 * Add the `post_status` clause.
286
	 *
287
	 * @since 3.15.0
288
	 *
289
	 * @param null|string|array $status The status values.
290
	 *
291
	 * @return string An empty string if $status is null, otherwise the status clause.
292
	 */
293 View Code Duplication
	private static function and_status( $status = 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...
294
295
		if ( null === $status ) {
296
			return '';
297
		}
298
299
		return " AND p.post_status IN ('" . implode( "', '", array_map( 'esc_sql', (array) $status ) ) . "')";
300
	}
301
302
	/**
303
	 * Add the `predicate` clause.
304
	 *
305
	 * @since 3.15.0
306
	 *
307
	 * @param null|string|array $predicate An array of predicates.
308
	 *
309
	 * @return string An empty string if $predicate is null otherwise the predicate
310
	 *                clause.
311
	 */
312 View Code Duplication
	private static function and_predicate( $predicate = 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...
313
314
		if ( null === $predicate ) {
315
			return '';
316
		}
317
318
		return " AND r.predicate IN ('" . implode( "', '", array_map( 'esc_sql', (array) $predicate ) ) . "')";
319
	}
320
321
	/**
322
	 * The select fields.
323
	 *
324
	 * @since 3.15.0
325
	 *
326
	 * @param string $fields Either 'ids' or '*', by default '*'.
327
	 *
328
	 * @return string The `id` field if `ids` otherwise `*`.
329
	 */
330
	private static function fields( $fields = '*' ) {
331
332
		// The output fields.
333
		return 'ids' === $fields ? 'id' : '*';
334
	}
335
336
	/**
337
	 * The inner join clause for articles.
338
	 *
339
	 * @since 3.15.0
340
	 *
341
	 * @return string The articles inner join clause.
342
	 */
343
	private static function inner_join_is_article() {
344
		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...
345
346
		return $wpdb->prepare(
347
			"
348
			INNER JOIN $wpdb->term_relationships tr
349
			 ON p.id = tr.object_id
350
			INNER JOIN $wpdb->term_taxonomy tt
351
			 ON tt.term_taxonomy_id = tr.term_taxonomy_id
352
			  AND tt.taxonomy = %s
353
			INNER JOIN $wpdb->terms t
354
			 ON t.term_id = tt.term_id
355
			  AND t.slug = %s
356
			",
357
			'wl_entity_type',
358
			'article'
359
		);
360
	}
361
362
	/**
363
	 * The inner join clause for non-articles.
364
	 *
365
	 * @since 3.15.0
366
	 *
367
	 * @return string The non-articles inner join clause.
368
	 */
369
	private static function inner_join_is_not_article() {
370
		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...
371
372
		return $wpdb->prepare(
373
			"
374
			INNER JOIN $wpdb->term_relationships tr
375
			 ON p.id = tr.object_id
376
			INNER JOIN $wpdb->term_taxonomy tt
377
			 ON tt.term_taxonomy_id = tr.term_taxonomy_id
378
			  AND tt.taxonomy = %s
379
			INNER JOIN $wpdb->terms t
380
			 ON t.term_id = tt.term_id
381
			  AND NOT t.slug = %s
382
			",
383
			'wl_entity_type',
384
			'article'
385
		);
386
	}
387
388
}
389