Completed
Pull Request — master (#27)
by
unknown
02:25
created

WP_REST_Menus::has_children()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 1 Features 1
Metric Value
cc 1
eloc 3
c 6
b 1
f 1
nc 1
nop 2
dl 0
loc 5
rs 9.4285
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 {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

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