• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

timber / timber / 5690057835

pending completion
5690057835

push

github

nlemoine
Merge branch '2.x' of github.com:timber/timber into 2.x-refactor-file-models

# Conflicts:
#	src/Attachment.php
#	src/ExternalImage.php
#	src/FileSize.php
#	src/URLHelper.php

1134 of 1134 new or added lines in 55 files covered. (100.0%)

3923 of 4430 relevant lines covered (88.56%)

59.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.07
/src/Factory/MenuFactory.php
1
<?php
2

3
namespace Timber\Factory;
4

5
use InvalidArgumentException;
6
use Timber\CoreInterface;
7
use Timber\Menu;
8
use WP_Term;
9

10
/**
11
 * Internal API class for instantiating Menus
12
 */
13
class MenuFactory
14
{
15
    /**
16
     * Tries to get a menu by all means available in an order that matches the most common use
17
     * cases.
18
     *
19
     * Will fall back on the first menu found if no parameters are provided. If no menu is found
20
     * with the given parameters, it will return null.
21
     *
22
     * Note that this method has pitfalls and might not be the most performant way to get a menu.
23
     *
24
     * @param mixed $params
25
     * @param array $args
26
     *
27
     * @return Menu|null
28
     */
29
    public function from($params, array $args = []): ?Menu
30
    {
31
        $menu = null;
67✔
32

33
        if (empty($params)) {
67✔
34
            return $this->from_nav_menu_terms($args);
8✔
35
        }
36

37
        // If $params is a numeric slug, we might get the wrong menu
38
        if (\is_numeric($params)) {
59✔
39
            $menu = $this->from_id((int) $params, $args);
43✔
40
        }
41

42
        if (\is_object($params)) {
59✔
43
            $menu = $this->from_object($params, $args);
2✔
44
        }
45

46
        if (!$menu && \is_string($params)) {
59✔
47
            // If $location is the same than some menu slug, we might get the wrong menu
48
            $menu = $this->from_location($params, $args);
15✔
49
            if (!$menu) {
15✔
50
                $menu = $this->from_slug($params, $args);
12✔
51
            }
52
            if (!$menu) {
15✔
53
                $menu = $this->from_name($params, $args);
×
54
            }
55
        }
56

57
        return $menu;
59✔
58
    }
59

60
    /**
61
     * Get a Menu from its location
62
     *
63
     * @param array $args
64
     * @return Menu|null
65
     */
66
    protected function from_nav_menu_terms(array $args = []): ?Menu
67
    {
68
        $menus = \wp_get_nav_menus();
8✔
69
        foreach ($menus as $menu_maybe) {
8✔
70
            $menu_items = \wp_get_nav_menu_items($menu_maybe->term_id, [
7✔
71
                'update_post_term_cache' => false,
7✔
72
            ]);
7✔
73
            if ($menu_items) {
7✔
74
                $menu = $menu_maybe;
7✔
75
                break;
7✔
76
            }
77
        }
78
        return isset($menu) ? $this->from_object($menu, $args) : null;
8✔
79
    }
80

81
    /**
82
     * Get a Menu from its location
83
     *
84
     * @param string $location
85
     * @param array $args
86
     * @return Menu|null
87
     */
88
    public function from_location(string $location, array $args = []): ?Menu
89
    {
90
        $locations = \get_nav_menu_locations();
15✔
91
        if (!isset($locations[$location])) {
15✔
92
            return null;
12✔
93
        }
94

95
        $term = \get_term_by('id', $locations[$location], 'nav_menu');
3✔
96
        if (!$term) {
3✔
97
            return null;
×
98
        }
99

100
        $args['location'] = $location;
3✔
101

102
        return $this->build($term, $args);
3✔
103
    }
104

105
    /**
106
     * Get a Menu by its ID
107
     *
108
     * @internal
109
     */
110
    public function from_id(int $id, array $args = []): ?Menu
111
    {
112
        $term = \get_term_by('id', $id, 'nav_menu');
43✔
113

114
        if (!$term) {
43✔
115
            return null;
2✔
116
        }
117

118
        $args['menu'] = $id;
41✔
119

120
        return $this->build($term, $args);
41✔
121
    }
122

123
    /**
124
     * Get a Menu by its slug
125
     *
126
     * @internal
127
     */
128
    public function from_slug(string $slug, array $args = []): ?Menu
129
    {
130
        $term = \get_term_by('slug', $slug, 'nav_menu');
12✔
131

132
        if (!$term) {
12✔
133
            return null;
×
134
        }
135

136
        $args['menu'] = $slug;
12✔
137

138
        return $this->build($term, $args);
12✔
139
    }
140

141
    /**
142
     * Get a Menu by its name
143
     *
144
     * @internal
145
     */
146
    public function from_name(string $name, array $args = []): ?Menu
147
    {
148
        $term = \get_term_by('name', $name, 'nav_menu');
1✔
149

150
        if (!$term) {
1✔
151
            return null;
×
152
        }
153

154
        $args['menu'] = $name;
1✔
155

156
        return $this->build($term, $args);
1✔
157
    }
158

159
    /**
160
     * Get a menu from object
161
     *
162
     * @internal
163
     */
164
    protected function from_object(object $obj, array $args = []): ?Menu
165
    {
166
        if ($obj instanceof Menu) {
9✔
167
            // We already have a Timber Core object of some kind
168
            return $obj;
×
169
        }
170

171
        if ($obj instanceof WP_Term) {
9✔
172
            $args['menu'] = $obj;
9✔
173
            return $this->build($obj, $args);
9✔
174
        }
175

176
        throw new InvalidArgumentException(\sprintf(
×
177
            'Expected an instance of Timber\CoreInterface or WP_Term, got %s',
×
178
            \get_class($obj)
×
179
        ));
×
180
    }
181

182
    /**
183
     * Get a menu class
184
     *
185
     * @internal
186
     */
187
    protected function get_menu_class($term, $args): string
188
    {
189
        /**
190
         * Filters the class(es) used for different menus.
191
         *
192
         * Read more about this in the documentation for [Menu Class Maps](https://timber.github.io/docs/v2/guides/class-maps/#the-menu-class-map).
193
         *
194
         * The default Menu Class Map will contain class names for locations that map to `Timber\Menu`.
195
         *
196
         * @since 2.0.0
197
         * @example
198
         * ```
199
         * add_filter( 'timber/menu/classmap', function( $classmap ) {
200
         *     $custom_classmap = [
201
         *         'primary'   => MenuPrimary::class,
202
         *         'secondary' => MenuSecondary::class,
203
         *     ];
204
         *
205
         *     return array_merge( $classmap, $custom_classmap );
206
         * } );
207
         * ```
208
         *
209
         * @param array $classmap The menu class(es) to use. An associative array where the key is
210
         *                        the location and the value the name of the class to use for this
211
         *                        menu or a callback that determines the class to use.
212
         */
213
        $classmap = \apply_filters('timber/menu/classmap', []);
64✔
214

215
        $location = $this->get_menu_location($term);
64✔
216

217
        $class = $classmap[$location] ?? null;
64✔
218

219
        // If class is a callable, call it to get the actual class name
220
        if (\is_callable($class)) {
64✔
221
            $class = $class($term, $args);
×
222
        }
223

224
        // Fallback on the default class
225
        $class = $class ?? Menu::class;
64✔
226

227
        /**
228
         * Filters the menu class based on your custom criterias.
229
         *
230
         * Maybe the location is not appropriate in some cases. This filter will allow you to filter the class
231
         * on whatever data is available.
232
         *
233
         * @since 2.0.0
234
         * @example
235
         * ```
236
         * add_filter( 'timber/menu/class', function( $class, $term, $args ) {
237
         *     if ( $args['depth'] === 1 ) {
238
         *         return SingleLevelMenu::class;
239
         *     }
240
         *
241
         *     return MultiLevelMenu::class;
242
         * }, 10, 3 );
243
         * ```
244
         *
245
         * @param string $class The class to use.
246
         * @param WP_Term $term The menu term.
247
         * @param array $args The arguments passed to the menu.
248
         */
249
        $class = \apply_filters('timber/menu/class', $class, $term, $args);
64✔
250

251
        return $class;
64✔
252
    }
253

254
    /**
255
     * Get the menu location
256
     *
257
     * @param WP_Term $term
258
     * @return string|null
259
     */
260
    protected function get_menu_location(WP_Term $term): ?string
261
    {
262
        $locations = \array_flip(\array_filter(\get_nav_menu_locations(), fn ($location) => \is_string($location) || \is_int($location)));
65✔
263
        return $locations[$term->term_id] ?? null;
65✔
264
    }
265

266
    /**
267
     * Build menu
268
     *
269
     * @param WP_Term $term
270
     * @param array $args
271
     * @return CoreInterface
272
     */
273
    protected function build(WP_Term $term, $args): CoreInterface
274
    {
275
        $class = $this->get_menu_class($term, $args);
64✔
276

277
        return $class::build($term, $args);
64✔
278
    }
279
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc