Completed
Pull Request — master (#62)
by
unknown
01:53
created

WP_REST_Menus::get_api_namespace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
                    'args'     => array(
60
                        'include' => array(
61
                            'required' => false,
62
                            'validate_callback' => function($val){
63
                                $list = explode(',',$val);
64
                                if(!is_array( $list))
65
                                    return new WP_Error( 'rest_invalid_param', 'include should be an array of menu ID numbers', array( 'status' => 500 ) );
66
                                foreach($list as $id){
67
                                    if(!is_nav_menu($id))
68
                                        return new WP_Error( 'rest_invalid_param', $id.' is not a nav menu', array( 'status' => 500 ) );
69
                                }
70
                                return true;
71
                            }
72
                        ),
73
                        'include_menu_items' => array(
74
                            'required' => false
75
                        ),
76
                    ),
77
                    'permission_callback' => '__return_true',
78
                )
79
            ) );
80
81
            register_rest_route( self::get_plugin_namespace(), '/menus/(?P<id>\d+)', array(
82
                array(
83
                    'methods'  => WP_REST_Server::READABLE,
84
                    'callback' => array( $this, 'get_menu' ),
85
                    'permission_callback' => '__return_true',
86
                    'args'     => array(
87
                        'context' => array(
88
                        'default' => 'view',
89
                        ),
90
                    ),
91
                )
92
            ) );
93
94
            register_rest_route( self::get_plugin_namespace(), '/menu-locations', array(
95
                array(
96
                    'methods'  => WP_REST_Server::READABLE,
97
                    'callback' => array( $this, 'get_menu_locations' ),
98
                    'permission_callback' => '__return_true',
99
                )
100
            ) );
101
102
            register_rest_route( self::get_plugin_namespace(), '/menu-locations/(?P<location>[a-zA-Z0-9_-]+)', array(
103
                array(
104
                    'methods'  => WP_REST_Server::READABLE,
105
                    'callback' => array( $this, 'get_menu_location' ),
106
                    'permission_callback' => '__return_true',
107
                )
108
            ) );
109
        }
110
111
112
        /**
113
         * Get menus.
114
         *
115
         * @since  1.2.0
116
         * @return array All registered menus
117
         */
118
        public static function get_menus($request =false) {
119
            if(!$request)
120
                return [];
121
122
            $params = $request->get_params();
0 ignored issues
show
Bug introduced by
The method get_params cannot be called on $request (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
123
            
124
            $query_args = [];
125
            if(isset($params['include']))
126
                $query_args['include'] = explode(',',$params['include']);
127
128
            $rest_url = trailingslashit( get_rest_url() . self::get_plugin_namespace() . '/menus/' );
0 ignored issues
show
Unused Code introduced by
$rest_url is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
129
            $wp_menus = wp_get_nav_menus($query_args);
130
131
            // check if we should also include the actual menu; default to false
132
            $include_items= (isset($params['include_menu_items']) && ($params['include_menu_items'] == "true")) ? true : false;
133
            
134
            $i = 0;
135
            $rest_menus = array();
136
            foreach ( $wp_menus as $wp_menu ) :
137
138
                $rest_menus[ $i ] = array_merge((array)$wp_menu,(new WP_REST_Menus)->get_menu(['id'=>$wp_menu->term_id],$include_items));
139
                $i ++;
140
            endforeach;
141
142
            return apply_filters( 'rest_menus_format_menus', $rest_menus );
143
        }
144
145
146
        /**
147
         * Get a menu.
148
         *
149
         * @since  1.2.0
150
         * @param  $request
151
         * @return array Menu data
152
         */
153
        public function get_menu( $request,$include_items=true ) {
154
155
            $id             = (int) $request['id'];
156
            $rest_url       = get_rest_url() . self::get_api_namespace() . '/menus/';
157
            $wp_menu_object = $id ? wp_get_nav_menu_object( $id ) : array();            
158
159
            $rest_menu = array();
160
161
            if ( $wp_menu_object ) :
162
163
                $menu = (array) $wp_menu_object;
164
                $rest_menu['ID']          = abs( $menu['term_id'] );
165
                $rest_menu['name']        = $menu['name'];
166
                $rest_menu['slug']        = $menu['slug'];
167
                $rest_menu['description'] = $menu['description'];
168
                $rest_menu['count']       = abs( $menu['count'] );
169
170
171
                if($include_items){
172
                    $wp_menu_items  = $id ? wp_get_nav_menu_items( $id ) : array();
173
174
                    $rest_menu_items = array();
175
                    foreach ( $wp_menu_items as $item_object ) {
176
                    
177
	                    $rest_menu_items[] = $this->format_menu_item( $item_object );
178
                    }
179
180
                
181
                    $rest_menu_items = $this->nested_menu_items($rest_menu_items, 0);
182
183
                    $rest_menu['items']                       = $rest_menu_items;
184
                }
185
186
                // wp_die(print_r($rest_menu));
187
                $rest_menu['meta']['links']['collection'] = $rest_url;
188
                $rest_menu['meta']['links']['self']       = $rest_url . $id;
189
190
            endif;
191
192
            return apply_filters( 'rest_menus_format_menu', $rest_menu );
193
        }
194
195
196
        /**
197
         * Handle nested menu items.
198
         *
199
         * Given a flat array of menu items, split them into parent/child items
200
         * and recurse over them to return children nested in their parent.
201
         *
202
         * @since  1.2.0
203
         * @param  $menu_items
204
         * @param  $parent
205
         * @return array
206
         */
207
        private function nested_menu_items( &$menu_items, $parent = null ) {
208
209
            $parents = array();
210
            $children = array();
211
212
            // Separate menu_items into parents & children.
213
            array_map( function( $i ) use ( $parent, &$children, &$parents ){
214
                if ( $i['id'] != $parent && $i['parent'] == $parent ) {
215
                    $parents[] = $i;
216
                } else {
217
                    $children[] = $i;
218
                }
219
            }, $menu_items );
220
221
            foreach ( $parents as &$parent ) {
222
223
                if ( $this->has_children( $children, $parent['id'] ) ) {
224
                    $parent['children'] = $this->nested_menu_items( $children, $parent['id'] );
225
                }
226
            }
227
228
            return $parents;
229
        }
230
231
232
        /**
233
         * Check if a collection of menu items contains an item that is the parent id of 'id'.
234
         *
235
         * @since  1.2.0
236
         * @param  array $items
237
         * @param  int $id
238
         * @return array
239
         */
240
        private function has_children( $items, $id ) {
241
            return array_filter( $items, function( $i ) use ( $id ) {
242
                return $i['parent'] == $id;
243
            } );
244
        }
245
246
247
        /**
248
         * Get menu locations.
249
         *
250
         * @since 1.2.0
251
         * @param  $request
252
         * @return array All registered menus locations
253
         */
254 View Code Duplication
        public static function get_menu_locations( $request ) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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...
255
256
            $locations        = get_nav_menu_locations();
257
            $registered_menus = get_registered_nav_menus();
258
	        $rest_url         = get_rest_url() . self::get_api_namespace() . '/menu-locations/';
259
            $rest_menus       = array();
260
261
            if ( $locations && $registered_menus ) :
262
263
                foreach ( $registered_menus as $slug => $label ) :
264
265
	                // Sanity check
266
	                if ( ! isset( $locations[ $slug ] ) ) {
267
		                continue;
268
	                }
269
270
	                $rest_menus[ $slug ]['ID']                          = $locations[ $slug ];
271
                    $rest_menus[ $slug ]['label']                       = $label;
272
                    $rest_menus[ $slug ]['meta']['links']['collection'] = $rest_url;
273
                    $rest_menus[ $slug ]['meta']['links']['self']       = $rest_url . $slug;
274
275
                endforeach;
276
277
            endif;
278
279
            return $rest_menus;
280
        }
281
282
283
        /**
284
         * Get menu for location.
285
         *
286
         * @since 1.2.0
287
         * @param  $request
288
         * @return array The menu for the corresponding location
289
         */
290
        public function get_menu_location( $request ) {
291
292
            $params     = $request->get_params();
293
            $location   = $params['location'];
294
            $locations  = get_nav_menu_locations();
295
296
            if ( ! isset( $locations[ $location ] ) ) {
297
	            return array();
298
            }
299
300
            $wp_menu = wp_get_nav_menu_object( $locations[ $location ] );
301
            $menu_items = wp_get_nav_menu_items( $wp_menu->term_id );
302
			/**
303
			 * wp_get_nav_menu_items() outputs a list that's already sequenced correctly.
304
			 * So the easiest thing to do is to reverse the list and then build our tree
305
			 * from the ground up
306
			 */
307
			$rev_items = array_reverse ( $menu_items );
308
			$rev_menu  = array();
309
			$cache     = array();
310
311
			foreach ( $rev_items as $item ) :
312
				$formatted = array(
313
					'ID'          => abs( $item->ID ),
314
					'order'       => (int) $item->menu_order,
315
					'parent'      => abs( $item->menu_item_parent ),
316
					'title'       => $item->title,
317
					'url'         => $item->url,
318
					'attr'        => $item->attr_title,
319
					'target'      => $item->target,
320
					'classes'     => implode( ' ', $item->classes ),
321
					'xfn'         => $item->xfn,
322
					'description' => $item->description,
323
					'object_id'   => abs( $item->object_id ),
324
					'object'      => $item->object,
325
					'type'        => $item->type,
326
                    'type_label'  => $item->type_label,
327
					'children'    => array(),
328
				);
329
330
				if ( array_key_exists( $item->ID , $cache ) ) {
331
					$formatted['children'] = array_reverse( $cache[ $item->ID ] );
332
				}
333
334
            	$formatted = apply_filters( 'rest_menus_format_menu_item', $formatted );
335
336
				if ( $item->menu_item_parent != 0 ) {
337
338
					if ( array_key_exists( $item->menu_item_parent , $cache ) ) {
339
						array_push( $cache[ $item->menu_item_parent ], $formatted );
340
					} else {
341
						$cache[ $item->menu_item_parent ] = array( $formatted, );
342
					}
343
344
				} else {
345
346
					array_push( $rev_menu, $formatted );
347
				}
348
349
			endforeach;
350
351
			return array_reverse ( $rev_menu );
352
        }
353
354
355
        /**
356
         * Returns all child nav_menu_items under a specific parent.
357
         *
358
         * @since   1.2.0
359
         * @param int   $parent_id      The parent nav_menu_item ID
360
         * @param array $nav_menu_items Navigation menu items
361
         * @param bool  $depth          Gives all children or direct children only
362
         * @return array	returns filtered array of nav_menu_items
363
         */
364 View Code Duplication
        public function get_nav_menu_item_children( $parent_id, $nav_menu_items, $depth = true ) {
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...
365
366
            $nav_menu_item_list = array();
367
368
            foreach ( (array) $nav_menu_items as $nav_menu_item ) :
369
370
                if ( $nav_menu_item->menu_item_parent == $parent_id ) :
371
372
                    $nav_menu_item_list[] = $this->format_menu_item( $nav_menu_item, true, $nav_menu_items );
373
374
                    if ( $depth ) {
375
                        if ( $children = $this->get_nav_menu_item_children( $nav_menu_item->ID, $nav_menu_items ) ) {
376
                            $nav_menu_item_list = array_merge( $nav_menu_item_list, $children );
377
                        }
378
                    }
379
380
                endif;
381
382
            endforeach;
383
384
            return $nav_menu_item_list;
385
        }
386
387
388
        /**
389
         * Format a menu item for REST API consumption.
390
         *
391
         * @since  1.2.0
392
         * @param  object|array $menu_item  The menu item
393
         * @param  bool         $children   Get menu item children (default false)
394
         * @param  array        $menu       The menu the item belongs to (used when $children is set to true)
395
         * @return array	a formatted menu item for REST
396
         */
397
        public function format_menu_item( $menu_item, $children = false, $menu = array() ) {
398
399
            $item = (array) $menu_item;
400
            $id = (int)$item['ID'];
0 ignored issues
show
Unused Code introduced by
$id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
401
            $menu_item = array(
402
                'id'          => abs( $item['ID'] ),
403
                'order'       => (int) $item['menu_order'],
404
                'parent'      => abs( $item['menu_item_parent'] ),
405
                'title'       => $item['title'],
406
                'url'         => $item['url'],
407
                'attr'        => $item['attr_title'],
408
                'target'      => $item['target'],
409
                'classes'     => implode( ' ', $item['classes'] ),
410
                'xfn'         => $item['xfn'],
411
                'description' => $item['description'],
412
                'object_id'   => abs( $item['object_id'] ),
413
                'object'      => $item['object'],
414
                'object_slug' => $item['post_name'],
415
                'type'        => $item['type'],
416
                'type_label'  => $item['type_label']
417
            );
418
419 View Code Duplication
            if ( $children === true && ! empty( $menu ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
420
	            $menu_item['children'] = $this->get_nav_menu_item_children( $item['ID'], $menu );
421
            }
422
423
            return apply_filters( 'rest_menus_format_menu_item', $menu_item );
424
        }
425
426
427
    }
428
429
430
endif;
431