|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* Services: Entity Uri Service |
|
4
|
|
|
* |
|
5
|
|
|
* Provides access to entities' URIs, i.e. URIs stored as `entity_url` (the main |
|
6
|
|
|
* entity item ID) or as `same_as`. |
|
7
|
|
|
* |
|
8
|
|
|
* @since 3.16.3 |
|
9
|
|
|
* @package Wordlift |
|
10
|
|
|
* @subpackage Wordlift/includes |
|
11
|
|
|
*/ |
|
12
|
|
|
|
|
13
|
|
|
/** |
|
14
|
|
|
* Define the {@link Wordlift_Entity_Uri_Service} class. |
|
15
|
|
|
* |
|
16
|
|
|
* @since 3.16.3 |
|
17
|
|
|
*/ |
|
18
|
|
|
class Wordlift_Entity_Uri_Service { |
|
19
|
|
|
|
|
20
|
|
|
/** |
|
21
|
|
|
* Holds the {@link Wordlift_Entity_Uri_Service} instance. |
|
22
|
|
|
* |
|
23
|
|
|
* @since 3.21.5 |
|
24
|
|
|
* @access private |
|
25
|
|
|
* @var Wordlift_Entity_Uri_Service $instance The {@link Wordlift_Entity_Uri_Service} singleton. |
|
26
|
|
|
*/ |
|
27
|
|
|
private static $instance; |
|
28
|
|
|
|
|
29
|
|
|
/** |
|
30
|
|
|
* A {@link Wordlift_Log_Service} instance. |
|
31
|
|
|
* |
|
32
|
|
|
* @since 3.16.3 |
|
33
|
|
|
* @access private |
|
34
|
|
|
* @var \Wordlift_Log_Service $log A {@link Wordlift_Log_Service} instance. |
|
35
|
|
|
*/ |
|
36
|
|
|
private $log; |
|
37
|
|
|
|
|
38
|
|
|
/** |
|
39
|
|
|
* The {@link Wordlift_Configuration_Service} instance. |
|
40
|
|
|
* |
|
41
|
|
|
* @since 3.16.3 |
|
42
|
|
|
* @access private |
|
43
|
|
|
* @var \Wordlift_Configuration_Service $configuration_service The {@link Wordlift_Configuration_Service} instance. |
|
44
|
|
|
*/ |
|
45
|
|
|
private $configuration_service; |
|
46
|
|
|
|
|
47
|
|
|
/** |
|
48
|
|
|
* An array of URIs to post ID valid for the current request. |
|
49
|
|
|
* |
|
50
|
|
|
* @since 3.16.3 |
|
51
|
|
|
* @access private |
|
52
|
|
|
* @var array $uri_to_post An array of URIs to post ID valid for the current request. |
|
53
|
|
|
*/ |
|
54
|
|
|
protected $uri_to_post; |
|
55
|
|
|
|
|
56
|
|
|
/** |
|
57
|
|
|
* Create a {@link Wordlift_Entity_Uri_Service} instance. |
|
58
|
|
|
* |
|
59
|
|
|
* @param \Wordlift_Configuration_Service $configuration_service The {@link Wordlift_Configuration_Service} instance. |
|
60
|
|
|
* |
|
61
|
|
|
* @since 3.16.3 |
|
62
|
|
|
* |
|
63
|
|
|
*/ |
|
64
|
|
|
public function __construct( $configuration_service ) { |
|
65
|
|
|
|
|
66
|
|
|
$this->log = Wordlift_Log_Service::get_logger( get_class() ); |
|
67
|
|
|
|
|
68
|
|
|
$this->configuration_service = $configuration_service; |
|
69
|
|
|
|
|
70
|
|
|
// Add a filter to the `rest_post_dispatch` filter to add the wl_entity_url meta as `wl:entity_url`. |
|
71
|
|
|
add_filter( 'rest_post_dispatch', array( $this, 'rest_post_dispatch' ) ); |
|
72
|
|
|
|
|
73
|
|
|
self::$instance = $this; |
|
74
|
|
|
|
|
75
|
|
|
} |
|
76
|
|
|
|
|
77
|
|
|
/** |
|
78
|
|
|
* Get the singleton. |
|
79
|
|
|
* |
|
80
|
|
|
* @return Wordlift_Entity_Uri_Service The singleton instance. |
|
81
|
|
|
* @since 3.21.5 |
|
82
|
|
|
*/ |
|
83
|
|
|
public static function get_instance() { |
|
84
|
|
|
|
|
85
|
|
|
return self::$instance; |
|
86
|
|
|
} |
|
87
|
|
|
|
|
88
|
|
|
/** |
|
89
|
|
|
* Preload the provided URIs in the local cache. |
|
90
|
|
|
* |
|
91
|
|
|
* This function will populate the local `$uri_to_post` array by running a |
|
92
|
|
|
* single query with all the URIs and returning the mappings in the array. |
|
93
|
|
|
* |
|
94
|
|
|
* @param array $uris An array of URIs. |
|
95
|
|
|
* |
|
96
|
|
|
* @since 3.16.3 |
|
97
|
|
|
* |
|
98
|
|
|
*/ |
|
99
|
|
|
public function preload_uris( $uris ) { |
|
100
|
|
|
|
|
101
|
|
|
// Bail out if there are no URIs. |
|
102
|
|
|
if ( 0 === count( $uris ) ) { |
|
103
|
|
|
return; |
|
104
|
|
|
} |
|
105
|
|
|
|
|
106
|
|
|
$this->log->trace( 'Preloading ' . count( $uris ) . ' URI(s)...' ); |
|
107
|
|
|
|
|
108
|
|
|
$that = $this; |
|
109
|
|
|
$external_uris = array_filter( $uris, function ( $item ) use ( $that ) { |
|
110
|
|
|
return ! $that->is_internal( $item ); |
|
111
|
|
|
} ); |
|
112
|
|
|
|
|
113
|
|
|
$query_args = array( |
|
114
|
|
|
// See https://github.com/insideout10/wordlift-plugin/issues/654. |
|
115
|
|
|
'ignore_sticky_posts' => 1, |
|
116
|
|
|
'cache_results' => false, |
|
117
|
|
|
'numberposts' => - 1, |
|
118
|
|
|
'post_status' => 'any', |
|
119
|
|
|
'post_type' => Wordlift_Entity_Service::valid_entity_post_types(), |
|
120
|
|
|
'meta_query' => array( |
|
121
|
|
|
array( |
|
122
|
|
|
'key' => WL_ENTITY_URL_META_NAME, |
|
123
|
|
|
'value' => $uris, |
|
124
|
|
|
'compare' => 'IN', |
|
125
|
|
|
), |
|
126
|
|
|
), |
|
127
|
|
|
); |
|
128
|
|
|
|
|
129
|
|
|
// Only if the current uri is not an internal uri, entity search is |
|
130
|
|
|
// performed also looking at sameAs values. |
|
131
|
|
|
// |
|
132
|
|
|
// This solve issues like https://github.com/insideout10/wordlift-plugin/issues/237 |
|
133
|
|
View Code Duplication |
if ( 0 < count( $external_uris ) ) { |
|
|
|
|
|
|
134
|
|
|
|
|
135
|
|
|
$query_args['meta_query']['relation'] = 'OR'; |
|
136
|
|
|
$query_args['meta_query'][] = array( |
|
137
|
|
|
'key' => Wordlift_Schema_Service::FIELD_SAME_AS, |
|
138
|
|
|
'value' => $external_uris, |
|
139
|
|
|
'compare' => 'IN', |
|
140
|
|
|
); |
|
141
|
|
|
|
|
142
|
|
|
} |
|
143
|
|
|
|
|
144
|
|
|
// Get the posts. |
|
145
|
|
|
$posts = get_posts( $query_args ); |
|
146
|
|
|
|
|
147
|
|
|
// Populate the array. We reinitialize the array on purpose because |
|
148
|
|
|
// we don't want these data to long live. |
|
149
|
|
|
$this->uri_to_post = array_reduce( $posts, function ( $carry, $item ) use ( $that ) { |
|
|
|
|
|
|
150
|
|
|
$uris = array_merge( |
|
151
|
|
|
get_post_meta( $item->ID, WL_ENTITY_URL_META_NAME ), |
|
152
|
|
|
get_post_meta( $item->ID, Wordlift_Schema_Service::FIELD_SAME_AS ) |
|
153
|
|
|
); |
|
154
|
|
|
|
|
155
|
|
|
return $carry |
|
156
|
|
|
// Get the URI related to the post and fill them with the item id. |
|
157
|
|
|
+ array_fill_keys( $uris, $item ); |
|
158
|
|
|
}, array() ); |
|
159
|
|
|
|
|
160
|
|
|
// Add the not found URIs. |
|
161
|
|
|
$this->uri_to_post += array_fill_keys( $uris, null ); |
|
162
|
|
|
|
|
163
|
|
|
$this->log->debug( count( $this->uri_to_post ) . " URI(s) preloaded." ); |
|
164
|
|
|
|
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
/** |
|
168
|
|
|
* Reset the URI to post local cache. |
|
169
|
|
|
* |
|
170
|
|
|
* @since 3.16.3 |
|
171
|
|
|
*/ |
|
172
|
|
|
public function reset_uris() { |
|
173
|
|
|
|
|
174
|
|
|
$this->uri_to_post = array(); |
|
175
|
|
|
|
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
/** |
|
179
|
|
|
* Find entity posts by the entity URI. Entity as searched by their entity URI or same as. |
|
180
|
|
|
* |
|
181
|
|
|
* @param string $uri The entity URI. |
|
182
|
|
|
* |
|
183
|
|
|
* @return WP_Post|null A WP_Post instance or null if not found. |
|
184
|
|
|
* @since 3.2.0 |
|
185
|
|
|
* |
|
186
|
|
|
*/ |
|
187
|
|
|
public function get_entity( $uri ) { |
|
188
|
|
|
|
|
189
|
|
|
$this->log->trace( "Getting an entity post for URI $uri..." ); |
|
190
|
|
|
|
|
191
|
|
|
// Check if we've been provided with a value otherwise return null. |
|
192
|
|
|
if ( empty( $uri ) ) { |
|
193
|
|
|
return null; |
|
194
|
|
|
} |
|
195
|
|
|
|
|
196
|
|
|
$this->log->debug( "Querying post for $uri..." ); |
|
197
|
|
|
|
|
198
|
|
|
$query_args = array( |
|
199
|
|
|
// See https://github.com/insideout10/wordlift-plugin/issues/654. |
|
200
|
|
|
'ignore_sticky_posts' => 1, |
|
201
|
|
|
'posts_per_page' => 1, |
|
202
|
|
|
'post_status' => 'any', |
|
203
|
|
|
'post_type' => Wordlift_Entity_Service::valid_entity_post_types(), |
|
204
|
|
|
'meta_query' => array( |
|
205
|
|
|
array( |
|
206
|
|
|
'key' => WL_ENTITY_URL_META_NAME, |
|
207
|
|
|
'value' => $uri, |
|
208
|
|
|
'compare' => '=', |
|
209
|
|
|
), |
|
210
|
|
|
), |
|
211
|
|
|
); |
|
212
|
|
|
|
|
213
|
|
|
// Only if the current uri is not an internal uri, entity search is |
|
214
|
|
|
// performed also looking at sameAs values. |
|
215
|
|
|
// |
|
216
|
|
|
// This solve issues like https://github.com/insideout10/wordlift-plugin/issues/237 |
|
217
|
|
View Code Duplication |
if ( ! $this->is_internal( $uri ) ) { |
|
|
|
|
|
|
218
|
|
|
|
|
219
|
|
|
$query_args['meta_query']['relation'] = 'OR'; |
|
220
|
|
|
$query_args['meta_query'][] = array( |
|
221
|
|
|
'key' => Wordlift_Schema_Service::FIELD_SAME_AS, |
|
222
|
|
|
'value' => $uri, |
|
223
|
|
|
'compare' => '=', |
|
224
|
|
|
); |
|
225
|
|
|
} |
|
226
|
|
|
|
|
227
|
|
|
$posts = get_posts( $query_args ); |
|
228
|
|
|
|
|
229
|
|
|
// Attempt to find post by URI (only for local entity URLs) |
|
230
|
|
|
if ( empty( $posts ) ) { |
|
231
|
|
|
|
|
232
|
|
|
$this->log->debug( "Finding post by $uri..." ); |
|
233
|
|
|
$postid = url_to_postid( $uri ); |
|
234
|
|
|
if ( $postid !== 0 ) { |
|
235
|
|
|
$this->log->trace( "Found post $postid by URL" ); |
|
236
|
|
|
|
|
237
|
|
|
return get_post( $postid ); |
|
238
|
|
|
} |
|
239
|
|
|
|
|
240
|
|
|
} |
|
241
|
|
|
|
|
242
|
|
|
// Return null if no post is found. |
|
243
|
|
|
if ( empty( $posts ) ) { |
|
244
|
|
|
$this->log->warn( "No post for URI $uri." ); |
|
245
|
|
|
|
|
246
|
|
|
return null; |
|
247
|
|
|
} |
|
248
|
|
|
|
|
249
|
|
|
// Return the found post. |
|
250
|
|
|
return current( $posts ); |
|
251
|
|
|
} |
|
252
|
|
|
|
|
253
|
|
|
/** |
|
254
|
|
|
* Determines whether a given uri is an internal uri or not. |
|
255
|
|
|
* |
|
256
|
|
|
* @param string $uri An uri. |
|
257
|
|
|
* |
|
258
|
|
|
* @return true if the uri internal to the current dataset otherwise false. |
|
259
|
|
|
* @since 3.16.3 |
|
260
|
|
|
* |
|
261
|
|
|
*/ |
|
262
|
|
|
public function is_internal( $uri ) { |
|
263
|
|
|
|
|
264
|
|
|
return ( 0 === strrpos( $uri, (string) $this->configuration_service->get_dataset_uri() ) ); |
|
265
|
|
|
} |
|
266
|
|
|
|
|
267
|
|
|
/** |
|
268
|
|
|
* Hook to `rest_post_dispatch` to alter the response and add the `wl_entity_url` post meta as `wl:entity_url`. |
|
269
|
|
|
* |
|
270
|
|
|
* We're using this filter instead of the well known `register_meta` / `register_rest_field` because we still need |
|
271
|
|
|
* to provide full compatibility with WordPress 4.4+. |
|
272
|
|
|
* |
|
273
|
|
|
* @param WP_HTTP_Response $result Result to send to the client. Usually a WP_REST_Response. |
|
274
|
|
|
* |
|
275
|
|
|
* @return WP_HTTP_Response The result to send to the client. |
|
276
|
|
|
* |
|
277
|
|
|
* @since 3.23.0 |
|
278
|
|
|
*/ |
|
279
|
|
|
public function rest_post_dispatch( $result ) { |
|
280
|
|
|
|
|
281
|
|
|
// Get a reference to the actual data. |
|
282
|
|
|
$data = &$result->data; |
|
283
|
|
|
|
|
284
|
|
|
// Bail out if we don't have the required parameters, or if the type is not a valid entity. |
|
285
|
|
|
if ( ! is_array( $data ) || ! isset( $data['id'] ) || ! isset( $data['type'] ) |
|
286
|
|
|
|| ! Wordlift_Entity_Type_Service::is_valid_entity_post_type( $data['type'] ) ) { |
|
287
|
|
|
return $result; |
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
|
|
// Add the `wl:entity_url`. |
|
291
|
|
|
$data['wl:entity_url'] = Wordlift_Entity_Service::get_instance()->get_uri( $data['id'] ); |
|
292
|
|
|
|
|
293
|
|
|
return $result; |
|
294
|
|
|
} |
|
295
|
|
|
|
|
296
|
|
|
} |
|
297
|
|
|
|
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.