These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * WP REST API Menu routes |
||
4 | * |
||
5 | * @package WP_API_Menus |
||
6 | */ |
||
7 | |||
8 | if ( ! defined( 'ABSPATH' ) ) { |
||
9 | exit; // Exit if accessed directly |
||
10 | } |
||
11 | |||
12 | if ( ! class_exists( 'WP_REST_Menus' ) ) : |
||
13 | |||
14 | |||
15 | /** |
||
16 | * WP REST Menus class. |
||
17 | * |
||
18 | * WP API Menus support for WP API v2. |
||
19 | * |
||
20 | * @package WP_API_Menus |
||
21 | * @since 1.2.0 |
||
22 | */ |
||
23 | class WP_REST_Menus { |
||
24 | |||
25 | |||
26 | /** |
||
27 | * Get WP API namespace. |
||
28 | * |
||
29 | * @since 1.2.0 |
||
30 | * @return string |
||
31 | */ |
||
32 | public static function get_api_namespace() { |
||
33 | return 'wp/v2'; |
||
34 | } |
||
35 | |||
36 | |||
37 | /** |
||
38 | * Get WP API Menus namespace. |
||
39 | * |
||
40 | * @since 1.2.1 |
||
41 | * @return string |
||
42 | */ |
||
43 | public static function get_plugin_namespace() { |
||
44 | return 'wp-api-menus/v2'; |
||
45 | } |
||
46 | |||
47 | |||
48 | /** |
||
49 | * Register menu routes for WP API v2. |
||
50 | * |
||
51 | * @since 1.2.0 |
||
52 | */ |
||
53 | public function register_routes() { |
||
54 | |||
55 | register_rest_route( self::get_plugin_namespace(), '/menus', array( |
||
56 | array( |
||
57 | 'methods' => WP_REST_Server::READABLE, |
||
58 | 'callback' => array( $this, 'get_menus' ), |
||
59 | ) |
||
60 | ) ); |
||
61 | |||
62 | register_rest_route( self::get_plugin_namespace(), '/menus/(?P<id>\d+)', array( |
||
63 | array( |
||
64 | 'methods' => WP_REST_Server::READABLE, |
||
65 | 'callback' => array( $this, 'get_menu' ), |
||
66 | 'args' => array( |
||
67 | 'context' => array( |
||
68 | 'default' => 'view', |
||
69 | ), |
||
70 | ), |
||
71 | ) |
||
72 | ) ); |
||
73 | |||
74 | register_rest_route( self::get_plugin_namespace(), '/menu-locations', array( |
||
75 | array( |
||
76 | 'methods' => WP_REST_Server::READABLE, |
||
77 | 'callback' => array( $this, 'get_menu_locations' ), |
||
78 | ) |
||
79 | ) ); |
||
80 | |||
81 | register_rest_route( self::get_plugin_namespace(), '/menu-locations/(?P<location>[a-zA-Z0-9_-]+)', array( |
||
82 | array( |
||
83 | 'methods' => WP_REST_Server::READABLE, |
||
84 | 'callback' => array( $this, 'get_menu_location' ), |
||
85 | ) |
||
86 | ) ); |
||
87 | } |
||
88 | |||
89 | |||
90 | /** |
||
91 | * Get menus. |
||
92 | * |
||
93 | * @since 1.2.0 |
||
94 | * @return array All registered menus |
||
95 | */ |
||
96 | public static function get_menus() { |
||
97 | |||
98 | $rest_url = trailingslashit( get_rest_url() . self::get_plugin_namespace() . '/menus/' ); |
||
99 | $wp_menus = wp_get_nav_menus(); |
||
100 | |||
101 | $i = 0; |
||
102 | $rest_menus = array(); |
||
103 | View Code Duplication | foreach ( $wp_menus as $wp_menu ) : |
|
104 | |||
105 | $menu = (array) $wp_menu; |
||
106 | |||
107 | $rest_menus[ $i ] = $menu; |
||
108 | $rest_menus[ $i ]['ID'] = $menu['term_id']; |
||
109 | $rest_menus[ $i ]['name'] = $menu['name']; |
||
110 | $rest_menus[ $i ]['slug'] = $menu['slug']; |
||
111 | $rest_menus[ $i ]['description'] = $menu['description']; |
||
112 | $rest_menus[ $i ]['count'] = $menu['count']; |
||
113 | |||
114 | $rest_menus[ $i ]['meta']['links']['collection'] = $rest_url; |
||
115 | $rest_menus[ $i ]['meta']['links']['self'] = $rest_url . $menu['term_id']; |
||
116 | |||
117 | $i ++; |
||
118 | endforeach; |
||
119 | |||
120 | return apply_filters( 'rest_menus_format_menus', $rest_menus ); |
||
121 | } |
||
122 | |||
123 | |||
124 | /** |
||
125 | * Get a menu. |
||
126 | * |
||
127 | * @since 1.2.0 |
||
128 | * @param $request |
||
129 | * @return array Menu data |
||
130 | */ |
||
131 | public function get_menu( $request ) { |
||
132 | |||
133 | $id = (int) $request['id']; |
||
134 | $rest_url = get_rest_url() . self::get_api_namespace() . '/menus/'; |
||
135 | $wp_menu_object = $id ? wp_get_nav_menu_object( $id ) : array(); |
||
136 | $wp_menu_items = $id ? wp_get_nav_menu_items( $id ) : array(); |
||
137 | |||
138 | $rest_menu = array(); |
||
139 | |||
140 | View Code Duplication | if ( $wp_menu_object ) : |
|
141 | |||
142 | $menu = (array) $wp_menu_object; |
||
143 | $rest_menu['ID'] = abs( $menu['term_id'] ); |
||
144 | $rest_menu['name'] = $menu['name']; |
||
145 | $rest_menu['slug'] = $menu['slug']; |
||
146 | $rest_menu['description'] = $menu['description']; |
||
147 | $rest_menu['count'] = abs( $menu['count'] ); |
||
148 | |||
149 | $rest_menu_items = array(); |
||
150 | foreach ( $wp_menu_items as $item_object ) { |
||
151 | $rest_menu_items[] = $this->format_menu_item( $item_object ); |
||
152 | } |
||
153 | |||
154 | $rest_menu_items = $this->nested_menu_items($rest_menu_items, 0); |
||
155 | |||
156 | $rest_menu['items'] = $rest_menu_items; |
||
157 | $rest_menu['meta']['links']['collection'] = $rest_url; |
||
158 | $rest_menu['meta']['links']['self'] = $rest_url . $id; |
||
159 | |||
160 | endif; |
||
161 | |||
162 | return apply_filters( 'rest_menus_format_menu', $rest_menu ); |
||
163 | } |
||
164 | |||
165 | |||
166 | /** |
||
167 | * Handle nested menu items. |
||
168 | * |
||
169 | * Given a flat array of menu items, split them into parent/child items |
||
170 | * and recurse over them to return children nested in their parent. |
||
171 | * |
||
172 | * @since 1.2.0 |
||
173 | * @param $menu_items |
||
174 | * @param $parent |
||
175 | * @return array |
||
176 | */ |
||
177 | private function nested_menu_items( &$menu_items, $parent = null ) { |
||
178 | |||
179 | $parents = array(); |
||
180 | $children = array(); |
||
181 | |||
182 | // Separate menu_items into parents & children. |
||
183 | array_map( function( $i ) use ( $parent, &$children, &$parents ){ |
||
184 | if ( $i['id'] != $parent && $i['parent'] == $parent ) { |
||
185 | $parents[] = $i; |
||
186 | } else { |
||
187 | $children[] = $i; |
||
188 | } |
||
189 | }, $menu_items ); |
||
190 | |||
191 | foreach ( $parents as &$parent ) { |
||
192 | |||
193 | if ( $this->has_children( $children, $parent['id'] ) ) { |
||
194 | $parent['children'] = $this->nested_menu_items( $children, $parent['id'] ); |
||
195 | } |
||
196 | } |
||
197 | |||
198 | return $parents; |
||
199 | } |
||
200 | |||
201 | |||
202 | /** |
||
203 | * Check if a collection of menu items contains an item that is the parent id of 'id'. |
||
204 | * |
||
205 | * @since 1.2.0 |
||
206 | * @param array $items |
||
207 | * @param int $id |
||
208 | * @return array |
||
209 | */ |
||
210 | private function has_children( $items, $id ) { |
||
211 | return array_filter( $items, function( $i ) use ( $id ) { |
||
212 | return $i['parent'] == $id; |
||
213 | } ); |
||
214 | } |
||
215 | |||
216 | |||
217 | /** |
||
218 | * Get menu locations. |
||
219 | * |
||
220 | * @since 1.2.0 |
||
221 | * @param $request |
||
222 | * @return array All registered menus locations |
||
223 | */ |
||
224 | View Code Duplication | public static function get_menu_locations( $request ) { |
|
225 | |||
226 | $locations = get_nav_menu_locations(); |
||
227 | $registered_menus = get_registered_nav_menus(); |
||
228 | $rest_url = get_rest_url() . self::get_api_namespace() . '/menu-locations/'; |
||
229 | $rest_menus = array(); |
||
230 | |||
231 | if ( $locations && $registered_menus ) : |
||
232 | |||
233 | foreach ( $registered_menus as $slug => $label ) : |
||
234 | |||
235 | // Sanity check |
||
236 | if ( ! isset( $locations[ $slug ] ) ) { |
||
237 | continue; |
||
238 | } |
||
239 | |||
240 | $rest_menus[ $slug ]['ID'] = $locations[ $slug ]; |
||
241 | $rest_menus[ $slug ]['label'] = $label; |
||
242 | $rest_menus[ $slug ]['meta']['links']['collection'] = $rest_url; |
||
243 | $rest_menus[ $slug ]['meta']['links']['self'] = $rest_url . $slug; |
||
244 | |||
245 | endforeach; |
||
246 | |||
247 | endif; |
||
248 | |||
249 | return $rest_menus; |
||
250 | } |
||
251 | |||
252 | |||
253 | /** |
||
254 | * Get menu for location. |
||
255 | * |
||
256 | * @since 1.2.0 |
||
257 | * @param $request |
||
258 | * @return array The menu for the corresponding location |
||
259 | */ |
||
260 | public function get_menu_location( $request ) { |
||
261 | |||
262 | $params = $request->get_params(); |
||
263 | $location = $params['location']; |
||
264 | $locations = get_nav_menu_locations(); |
||
265 | |||
266 | if ( ! isset( $locations[ $location ] ) ) { |
||
267 | return array(); |
||
268 | } |
||
269 | |||
270 | $wp_menu = wp_get_nav_menu_object( $locations[ $location ] ); |
||
271 | $menu_items = wp_get_nav_menu_items( $wp_menu->term_id ); |
||
272 | |||
273 | /** |
||
274 | * wp_get_nav_menu_items() outputs a list that's already sequenced correctly. |
||
275 | * So the easiest thing to do is to reverse the list and then build our tree |
||
276 | * from the ground up |
||
277 | */ |
||
278 | $rev_items = array_reverse ( $menu_items ); |
||
279 | $rev_menu = array(); |
||
280 | $cache = array(); |
||
281 | |||
282 | foreach ( $rev_items as $item ) : |
||
283 | |||
284 | $formatted = array( |
||
285 | 'ID' => abs( $item->ID ), |
||
286 | 'order' => (int) $item->menu_order, |
||
287 | 'parent' => abs( $item->menu_item_parent ), |
||
288 | 'title' => $item->title, |
||
289 | 'url' => $item->url, |
||
290 | 'attr' => $item->attr_title, |
||
291 | 'target' => $item->target, |
||
292 | 'classes' => implode( ' ', $item->classes ), |
||
293 | 'xfn' => $item->xfn, |
||
294 | 'description' => $item->description, |
||
295 | 'object_id' => abs( $item->object_id ), |
||
296 | 'object' => $item->object, |
||
297 | 'type' => $item->type, |
||
298 | 'type_label' => $item->type_label, |
||
299 | 'children' => array(), |
||
300 | ); |
||
301 | |||
302 | if ( array_key_exists( $item->ID , $cache ) ) { |
||
303 | $formatted['children'] = array_reverse( $cache[ $item->ID ] ); |
||
304 | } |
||
305 | |||
306 | $formatted = apply_filters( 'rest_menus_format_menu_item', $formatted ); |
||
307 | |||
308 | if ( $item->menu_item_parent != 0 ) { |
||
309 | |||
310 | if ( array_key_exists( $item->menu_item_parent , $cache ) ) { |
||
311 | array_push( $cache[ $item->menu_item_parent ], $formatted ); |
||
312 | } else { |
||
313 | $cache[ $item->menu_item_parent ] = array( $formatted, ); |
||
314 | } |
||
315 | |||
316 | } else { |
||
317 | |||
318 | array_push( $rev_menu, $formatted ); |
||
319 | } |
||
320 | |||
321 | endforeach; |
||
322 | |||
323 | return array_reverse ( $rev_menu ); |
||
324 | } |
||
325 | |||
326 | |||
327 | /** |
||
328 | * Returns all child nav_menu_items under a specific parent. |
||
329 | * |
||
330 | * @since 1.2.0 |
||
331 | * @param int $parent_id The parent nav_menu_item ID |
||
332 | * @param array $nav_menu_items Navigation menu items |
||
333 | * @param bool $depth Gives all children or direct children only |
||
334 | * @return array returns filtered array of nav_menu_items |
||
335 | */ |
||
336 | View Code Duplication | public function get_nav_menu_item_children( $parent_id, $nav_menu_items, $depth = true ) { |
|
337 | |||
338 | $nav_menu_item_list = array(); |
||
339 | |||
340 | foreach ( (array) $nav_menu_items as $nav_menu_item ) : |
||
341 | |||
342 | if ( $nav_menu_item->menu_item_parent == $parent_id ) : |
||
343 | |||
344 | $nav_menu_item_list[] = $this->format_menu_item( $nav_menu_item, true, $nav_menu_items ); |
||
345 | |||
346 | if ( $depth ) { |
||
347 | if ( $children = $this->get_nav_menu_item_children( $nav_menu_item->ID, $nav_menu_items ) ) { |
||
348 | $nav_menu_item_list = array_merge( $nav_menu_item_list, $children ); |
||
349 | } |
||
350 | } |
||
351 | |||
352 | endif; |
||
353 | |||
354 | endforeach; |
||
355 | |||
356 | return $nav_menu_item_list; |
||
357 | } |
||
358 | |||
359 | |||
360 | /** |
||
361 | * Format a menu item for REST API consumption. |
||
362 | * |
||
363 | * @since 1.2.0 |
||
364 | * @param object|array $menu_item The menu item |
||
365 | * @param bool $children Get menu item children (default false) |
||
366 | * @param array $menu The menu the item belongs to (used when $children is set to true) |
||
367 | * @return array a formatted menu item for REST |
||
368 | */ |
||
369 | View Code Duplication | public function format_menu_item( $menu_item, $children = false, $menu = array() ) { |
|
0 ignored issues
–
show
|
|||
370 | |||
371 | $item = (array) $menu_item; |
||
372 | |||
373 | $menu_item = array( |
||
374 | 'id' => abs( $item['ID'] ), |
||
375 | 'order' => (int) $item['menu_order'], |
||
376 | 'parent' => abs( $item['menu_item_parent'] ), |
||
377 | 'title' => $item['title'], |
||
378 | 'url' => $item['url'], |
||
379 | 'attr' => $item['attr_title'], |
||
380 | 'target' => $item['target'], |
||
381 | 'classes' => implode( ' ', $item['classes'] ), |
||
382 | 'xfn' => $item['xfn'], |
||
383 | 'description' => $item['description'], |
||
384 | 'object_id' => abs( $item['object_id'] ), |
||
385 | 'object' => $item['object'], |
||
386 | 'object_slug' => get_post( $item['object_id'] )->post_name, |
||
387 | 'type' => $item['type'], |
||
388 | 'type_label' => $item['type_label'], |
||
389 | ); |
||
390 | |||
391 | if ( $children === true && ! empty( $menu ) ) { |
||
392 | $menu_item['children'] = $this->get_nav_menu_item_children( $item['ID'], $menu ); |
||
393 | } |
||
394 | |||
395 | return apply_filters( 'rest_menus_format_menu_item', $menu_item ); |
||
396 | } |
||
397 | |||
398 | |||
399 | } |
||
400 | |||
401 | |||
402 | endif; |
||
403 |
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.