Completed
Push — master ( 5db201...585add )
by David
08:20
created

Wordlift_Relation_Service::fields()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 1
dl 0
loc 5
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
			"
94
			SELECT p.$actual_fields
95
			FROM {$this->relation_table} r
96
			INNER JOIN $wpdb->posts p
97
				ON p.id = r.subject_id
98
			"
99
			// Add the status clause.
100
			. self::and_status( $status )
101
			. self::inner_join_is_article()
102
			. self::where_object_id( $objects )
103
			// Since `object_id` can be an article ID we need to exclude it from
104
			// the results.
105
			. self::and_article_not_in( array_merge( $excludes, (array) $object_id ) )
106
			. self::and_article_in( $include )
107
			. " AND p.post_type IN ( 'post', 'page', 'entity' ) "
108
			. self::and_predicate( $predicate )
109
			. self::limit( $limit );
110
111
		return '*' === $actual_fields ? $wpdb->get_results( $sql ) : $wpdb->get_col( $sql );
112
	}
113
114
	/**
115
	 * Add the limit clause if specified.
116
	 *
117
	 * @since 3.15.0
118
	 *
119
	 * @param null|int $limit The maximum number of results.
120
	 *
121
	 * @return string The limit clause (empty if no limit has been specified).
122
	 */
123
	private static function limit( $limit = null ) {
124
125
		if ( null === $limit ) {
126
			return '';
127
		}
128
129
		return "LIMIT $limit";
130
	}
131
132
	/**
133
	 * Map the provided ids into entities (i.e. return the id if it's an entity
134
	 * or get the entities if it's a post).
135
	 *
136
	 * @since 3.15.0
137
	 *
138
	 * @param int|array $object_id An array of posts/entities' ids.
139
	 *
140
	 * @return array An array of entities' ids.
141
	 */
142
	private function article_id_to_entity_id( $object_id ) {
143
144
		$entity_service = Wordlift_Entity_Service::get_instance();
145
146
		$relation_service = $this;
147
148
		return array_reduce( (array) $object_id, function ( $carry, $item ) use ( $entity_service, $relation_service ) {
149
			if ( $entity_service->is_entity( $item ) ) {
150
				return array_merge( $carry, (array) $item );
151
			}
152
153
			return array_merge( $carry, $relation_service->get_objects( $item, 'ids' ) );
154
		}, array() );
155
156
	}
157
158
	/**
159
	 * Add the WHERE clause.
160
	 *
161
	 * @since 3.15.0
162
	 *
163
	 * @param int|array $object_id An array of {@link WP_Post}s' ids.
164
	 *
165
	 * @return string The WHERE clause.
166
	 */
167
	private static function where_object_id( $object_id ) {
168
169
		return ' WHERE r.object_id IN ( ' . implode( ',', wp_parse_id_list( (array) $object_id ) ) . ' )';
170
	}
171
172
	/**
173
	 * Add the exclude clause.
174
	 *
175
	 * @since 3.15.0
176
	 *
177
	 * @param int|array $exclude An array of {@link WP_Post}s' ids to exclude.
178
	 *
179
	 * @return string The exclude clause.
180
	 */
181
	private static function and_article_not_in( $exclude ) {
182
183
		return ' AND NOT p.ID IN ( ' . implode( ',', wp_parse_id_list( (array) $exclude ) ) . ' )';
184
	}
185
186
	/**
187
	 * Add the include clause.
188
	 *
189
	 * @since 3.15.0
190
	 *
191
	 * @param null|int|array $include An array of {@link WP_Post}s' ids.
192
	 *
193
	 * @return string An empty string if $include is null otherwise the include
194
	 *                clause.
195
	 */
196
	private static function and_article_in( $include = null ) {
197
198
		if ( null === $include ) {
199
			return '';
200
		}
201
202
		return ' AND p.ID IN ( ' . implode( ',', wp_parse_id_list( (array) $include ) ) . ' )';
203
	}
204
205
	/**
206
	 * Get the entities' {@link WP_Post}s' ids referencing the specified {@link WP_Post}.
207
	 *
208
	 * @since 3.15.0
209
	 *
210
	 * @param int         $object_id The object {@link WP_Post}'s id.
211
	 * @param string      $fields    The fields to return, 'ids' to only return ids or
212
	 *                               '*' to return all fields, by default '*'.
213
	 * @param null|string $status    The status, by default all.
214
	 *
215
	 * @return array|object|null Database query results
216
	 */
217 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...
218
		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...
219
220
		// The output fields.
221
		$actual_fields = self::fields( $fields );
222
223
		$sql = $wpdb->prepare(
224
			"
225
			SELECT p.$actual_fields
226
			FROM {$this->relation_table} r
227
			INNER JOIN $wpdb->posts p
228
				ON p.id = r.subject_id
229
			"
230
			// Add the status clause.
231
			. self::and_status( $status )
232
			. self::inner_join_is_not_article() .
233
			"
234
			WHERE r.object_id = %d
235
				AND p.post_type IN ( 'post', 'page', 'entity' )
236
			"
237
			,
238
			$object_id
239
		);
240
241
		return '*' === $actual_fields ? $wpdb->get_results( $sql ) : $wpdb->get_col( $sql );
242
	}
243
244
	/**
245
	 * Get the entities referenced by the specified {@link WP_Post}.
246
	 *
247
	 * @since 3.15.0
248
	 *
249
	 * @param int         $subject_id The {@link WP_Post}'s id.
250
	 * @param string      $fields     The fields to return, 'ids' to only return ids or
251
	 *                                '*' to return all fields, by default '*'.
252
	 * @param null|string $predicate  The predicate (who|what|...), by default all.
253
	 * @param null|string $status     The status, by default all.
254
	 *
255
	 * @return array|object|null Database query results
256
	 */
257 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...
258
		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...
259
260
		// The output fields.
261
		$actual_fields = self::fields( $fields );
262
263
		$sql = $wpdb->prepare(
264
			"
265
			SELECT p.$actual_fields
266
			FROM {$this->relation_table} r
267
			INNER JOIN $wpdb->posts p
268
				ON p.id = r.object_id
269
			"
270
			// Add the status clause.
271
			. self::and_status( $status )
272
			. self::inner_join_is_not_article()
273
			. "
274
			WHERE r.subject_id = %d
275
				AND p.post_type IN ( 'post', 'page', 'entity' )
276
			"
277
			. self::and_predicate( $predicate )
278
			,
279
			$subject_id
280
		);
281
282
		return '*' === $actual_fields ? $wpdb->get_results( $sql ) : $wpdb->get_col( $sql );
283
	}
284
285
	/**
286
	 * Add the `post_status` clause.
287
	 *
288
	 * @since 3.15.0
289
	 *
290
	 * @param null|string|array $status The status values.
291
	 *
292
	 * @return string An empty string if $status is null, otherwise the status clause.
293
	 */
294 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...
295
296
		if ( null === $status ) {
297
			return '';
298
		}
299
300
		return " AND p.post_status IN ('" . implode( "', '", array_map( 'esc_sql', (array) $status ) ) . "')";
301
	}
302
303
	/**
304
	 * Add the `predicate` clause.
305
	 *
306
	 * @since 3.15.0
307
	 *
308
	 * @param null|string|array $predicate An array of predicates.
309
	 *
310
	 * @return string An empty string if $predicate is null otherwise the predicate
311
	 *                clause.
312
	 */
313 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...
314
315
		if ( null === $predicate ) {
316
			return '';
317
		}
318
319
		return " AND r.predicate IN ('" . implode( "', '", array_map( 'esc_sql', (array) $predicate ) ) . "')";
320
	}
321
322
	/**
323
	 * The select fields.
324
	 *
325
	 * @since 3.15.0
326
	 *
327
	 * @param string $fields Either 'ids' or '*', by default '*'.
328
	 *
329
	 * @return string The `id` field if `ids` otherwise `*`.
330
	 */
331
	private static function fields( $fields = '*' ) {
332
333
		// The output fields.
334
		return 'ids' === $fields ? 'id' : '*';
335
	}
336
337
	/**
338
	 * The inner join clause for articles.
339
	 *
340
	 * @since 3.15.0
341
	 *
342
	 * @return string The articles inner join clause.
343
	 */
344
	private static function inner_join_is_article() {
345
		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...
346
347
		return $wpdb->prepare(
348
			"
349
			INNER JOIN $wpdb->term_relationships tr
350
			 ON p.id = tr.object_id
351
			INNER JOIN $wpdb->term_taxonomy tt
352
			 ON tt.term_taxonomy_id = tr.term_taxonomy_id
353
			  AND tt.taxonomy = %s
354
			INNER JOIN $wpdb->terms t
355
			 ON t.term_id = tt.term_id
356
			  AND t.slug = %s
357
			",
358
			'wl_entity_type',
359
			'article'
360
		);
361
	}
362
363
	/**
364
	 * The inner join clause for non-articles.
365
	 *
366
	 * @since 3.15.0
367
	 *
368
	 * @return string The non-articles inner join clause.
369
	 */
370
	private static function inner_join_is_not_article() {
371
		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...
372
373
		return $wpdb->prepare(
374
			"
375
			INNER JOIN $wpdb->term_relationships tr
376
			 ON p.id = tr.object_id
377
			INNER JOIN $wpdb->term_taxonomy tt
378
			 ON tt.term_taxonomy_id = tr.term_taxonomy_id
379
			  AND tt.taxonomy = %s
380
			INNER JOIN $wpdb->terms t
381
			 ON t.term_id = tt.term_id
382
			  AND NOT t.slug = %s
383
			",
384
			'wl_entity_type',
385
			'article'
386
		);
387
	}
388
389
}
390