Completed
Push — master ( f0ba99...e1d383 )
by Fulvio
19:06
created

includes/wp-api-menus-v2.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 23 and the first side effect is on line 9.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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
         * @return array
53
         */
54
        public function register_routes() {
55
56
            register_rest_route( self::get_plugin_namespace(), '/menus', array(
57
                array(
58
                    'methods'  => WP_REST_Server::READABLE,
59
                    'callback' => array( $this, 'get_menus' ),
60
                )
61
            ) );
62
63
            register_rest_route( self::get_plugin_namespace(), '/menus/(?P<id>\d+)', array(
64
                array(
65
                    'methods'  => WP_REST_Server::READABLE,
66
                    'callback' => array( $this, 'get_menu' ),
67
                    'args'     => array(
68
                        'context' => array(
69
                        'default' => 'view',
70
                        ),
71
                    ),
72
                )
73
            ) );
74
75
            register_rest_route( self::get_plugin_namespace(), '/menu-locations', array(
76
                array(
77
                    'methods'  => WP_REST_Server::READABLE,
78
                    'callback' => array( $this, 'get_menu_locations' ),
79
                )
80
            ) );
81
82
            register_rest_route( self::get_plugin_namespace(), '/menu-locations/(?P<location>[a-zA-Z0-9_-]+)', array(
83
                array(
84
                    'methods'  => WP_REST_Server::READABLE,
85
                    'callback' => array( $this, 'get_menu_location' ),
86
                )
87
            ) );
88
89
        }
90
91
92
        /**
93
         * Get menus.
94
         *
95
         * @since  1.2.0
96
         * @return array All registered menus
97
         */
98
        public static function get_menus() {
99
100
            $rest_url = trailingslashit( get_rest_url() . self::get_plugin_namespace() . '/menus/' );
101
            $wp_menus = wp_get_nav_menus();
102
103
            $i = 0;
104
            $rest_menus = array();
105 View Code Duplication
            foreach ( $wp_menus as $wp_menu ) :
106
107
                $menu = (array) $wp_menu;
108
109
                $rest_menus[ $i ]                = $menu;
110
                $rest_menus[ $i ]['ID']          = $menu['term_id'];
111
                $rest_menus[ $i ]['name']        = $menu['name'];
112
                $rest_menus[ $i ]['slug']        = $menu['slug'];
113
                $rest_menus[ $i ]['description'] = $menu['description'];
114
                $rest_menus[ $i ]['count']       = $menu['count'];
115
116
                $rest_menus[ $i ]['meta']['links']['collection'] = $rest_url;
117
                $rest_menus[ $i ]['meta']['links']['self']       = $rest_url . $menu['term_id'];
118
119
                $i ++;
120
            endforeach;
121
122
            return apply_filters( 'rest_menus_format_menus', $rest_menus );
123
        }
124
125
126
        /**
127
         * Get a menu.
128
         *
129
         * @since  1.2.0
130
         * @param  $request
131
         * @return array Menu data
132
         */
133
        public function get_menu( $request ) {
134
135
            $id             = (int) $request['id'];
136
            $rest_url       = get_rest_url() . self::get_api_namespace() . '/menus/';
137
            $wp_menu_object = $id ? wp_get_nav_menu_object( $id ) : array();
138
            $wp_menu_items  = $id ? wp_get_nav_menu_items( $id ) : array();
139
140
            $rest_menu = array();
141
142 View Code Duplication
            if ( $wp_menu_object ) :
143
144
                $menu = (array) $wp_menu_object;
145
                $rest_menu['ID']          = abs( $menu['term_id'] );
146
                $rest_menu['name']        = $menu['name'];
147
                $rest_menu['slug']        = $menu['slug'];
148
                $rest_menu['description'] = $menu['description'];
149
                $rest_menu['count']       = abs( $menu['count'] );
150
151
                $rest_menu_items = array();
152
                foreach ( $wp_menu_items as $item_object ) {
153
	                $rest_menu_items[] = $this->format_menu_item( $item_object );
154
                }
155
156
                $rest_menu_items = $this->nested_menu_items($rest_menu_items, 0);
157
158
                $rest_menu['items']                       = $rest_menu_items;
159
                $rest_menu['meta']['links']['collection'] = $rest_url;
160
                $rest_menu['meta']['links']['self']       = $rest_url . $id;
161
162
            endif;
163
164
            return apply_filters( 'rest_menus_format_menu', $rest_menu );
165
        }
166
167
168
        /**
169
         * Handle nested menu items.
170
         *
171
         * Given a flat array of menu items, split them into parent/child items
172
         * and recurse over them to return children nested in their parent.
173
         *
174
         * @since  1.2.0
175
         * @param  $menu_items
176
         * @param  $parent
177
         * @return array
178
         */
179
        private function nested_menu_items( &$menu_items, $parent = null ) {
180
181
            $parents = array();
182
            $children = array();
183
184
            // Separate menu_items into parents & children.
185
            array_map( function( $i ) use ( $parent, &$children, &$parents ){
186
                if ( $i['id'] != $parent && $i['parent'] == $parent ) {
187
                    $parents[] = $i;
188
                } else {
189
                    $children[] = $i;
190
                }
191
            }, $menu_items );
192
193
            foreach ( $parents as &$parent ) {
194
195
                if ( $this->has_children( $children, $parent['id'] ) ) {
196
                    $parent['children'] = $this->nested_menu_items( $children, $parent['id'] );
197
                }
198
            }
199
200
            return $parents;
201
        }
202
203
204
        /**
205
         * Check if a collection of menu items contains an item that is the parent id of 'id'.
206
         *
207
         * @since  1.2.0
208
         * @param  array $items
209
         * @param  int $id
210
         * @return array
211
         */
212
        private function has_children( $items, $id ) {
213
            return array_filter( $items, function( $i ) use ( $id ) {
214
                return $i['parent'] == $id;
215
            } );
216
        }
217
218
219
        /**
220
         * Get menu locations.
221
         *
222
         * @since 1.2.0
223
         * @param  $request
224
         * @return array All registered menus locations
225
         */
226 View Code Duplication
        public static function get_menu_locations( $request ) {
227
228
            $locations        = get_nav_menu_locations();
229
            $registered_menus = get_registered_nav_menus();
230
	        $rest_url         = get_rest_url() . self::get_api_namespace() . '/menu-locations/';
231
            $rest_menus       = array();
232
233
            if ( $locations && $registered_menus ) :
234
235
                foreach ( $registered_menus as $slug => $label ) :
236
237
	                // Sanity check
238
	                if ( ! isset( $locations[ $slug ] ) ) {
239
		                continue;
240
	                }
241
242
	                $rest_menus[ $slug ]['ID']                          = $locations[ $slug ];
243
                    $rest_menus[ $slug ]['label']                       = $label;
244
                    $rest_menus[ $slug ]['meta']['links']['collection'] = $rest_url;
245
                    $rest_menus[ $slug ]['meta']['links']['self']       = $rest_url . $slug;
246
247
                endforeach;
248
249
            endif;
250
251
            return $rest_menus;
252
        }
253
254
255
        /**
256
         * Get menu for location.
257
         *
258
         * @since 1.2.0
259
         * @param  $request
260
         * @return array The menu for the corresponding location
261
         */
262
        public function get_menu_location( $request ) {
263
264
            $params     = $request->get_params();
265
            $location   = $params['location'];
266
            $locations  = get_nav_menu_locations();
267
268
            if ( ! isset( $locations[ $location ] ) ) {
269
	            return array();
270
            }
271
272
            $wp_menu = wp_get_nav_menu_object( $locations[ $location ] );
273
            $menu_items = wp_get_nav_menu_items( $wp_menu->term_id );
274
275
			/**
276
			 * wp_get_nav_menu_items() outputs a list that's already sequenced correctly.
277
			 * So the easiest thing to do is to reverse the list and then build our tree
278
			 * from the ground up
279
			 */
280
			$rev_items = array_reverse ( $menu_items );
281
			$rev_menu = array();
282
			$cache = array();
283
			foreach ( $rev_items as $item ) :
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
				// Pickup my children
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
					// Wait for parent to pick me up
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
				} else {
316
					array_push( $rev_menu, $formatted );
317
				}
318
			endforeach;
319
			return array_reverse ( $rev_menu );
320
        }
321
322
323
        /**
324
         * Returns all child nav_menu_items under a specific parent.
325
         *
326
         * @since   1.2.0
327
         * @param int   $parent_id      The parent nav_menu_item ID
328
         * @param array $nav_menu_items Navigation menu items
329
         * @param bool  $depth          Gives all children or direct children only
330
         * @return  array   returns filtered array of nav_menu_items
331
         */
332 View Code Duplication
        public function get_nav_menu_item_children( $parent_id, $nav_menu_items, $depth = true ) {
333
334
            $nav_menu_item_list = array();
335
336
            foreach ( (array) $nav_menu_items as $nav_menu_item ) :
337
338
                if ( $nav_menu_item->menu_item_parent == $parent_id ) :
339
340
                    $nav_menu_item_list[] = $this->format_menu_item( $nav_menu_item, true, $nav_menu_items );
341
342
                    if ( $depth ) {
343
                        if ( $children = $this->get_nav_menu_item_children( $nav_menu_item->ID, $nav_menu_items ) ) {
344
                            $nav_menu_item_list = array_merge( $nav_menu_item_list, $children );
345
                        }
346
                    }
347
348
                endif;
349
350
            endforeach;
351
352
            return $nav_menu_item_list;
353
        }
354
355
356
        /**
357
         * Format a menu item for REST API consumption.
358
         *
359
         * @since  1.2.0
360
         * @param  object|array $menu_item  The menu item
361
         * @param  bool         $children   Get menu item children (default false)
362
         * @param  array        $menu       The menu the item belongs to (used when $children is set to true)
363
         * @return array   a formatted menu item for REST
364
         */
365 View Code Duplication
        public function format_menu_item( $menu_item, $children = false, $menu = array() ) {
366
367
            $item = (array) $menu_item;
368
369
            $menu_item = array(
370
                'id'          => abs( $item['ID'] ),
371
                'order'       => (int) $item['menu_order'],
372
                'parent'      => abs( $item['menu_item_parent'] ),
373
                'title'       => $item['title'],
374
                'url'         => $item['url'],
375
                'attr'        => $item['attr_title'],
376
                'target'      => $item['target'],
377
                'classes'     => implode( ' ', $item['classes'] ),
378
                'xfn'         => $item['xfn'],
379
                'description' => $item['description'],
380
                'object_id'   => abs( $item['object_id'] ),
381
                'object'      => $item['object'],
382
                'type'        => $item['type'],
383
                'type_label'  => $item['type_label'],
384
            );
385
386
            if ( $children === true && ! empty( $menu ) ) {
387
	            $menu_item['children'] = $this->get_nav_menu_item_children( $item['ID'], $menu );
388
            }
389
390
            return apply_filters( 'rest_menus_format_menu_item', $menu_item );
391
        }
392
393
394
    }
395
396
397
endif;
398