Completed
Pull Request — master (#30)
by
unknown
02:54
created

WP_REST_Menus   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 392
Duplicated Lines 19.64 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 19
Bugs 3 Features 6
Metric Value
c 19
b 3
f 6
dl 77
loc 392
rs 8.6
wmc 37
lcom 1
cbo 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A get_api_namespace() 0 3 1
A get_plugin_namespace() 0 3 1
B register_routes() 0 42 1
B get_menus() 0 38 4
B nested_menu_items() 0 23 5
A has_children() 0 5 1
B get_menu_locations() 27 27 5
B get_menu_location() 0 59 6
B get_nav_menu_item_children() 22 22 5
B format_menu_item() 28 28 3
B get_menu() 0 33 5

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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