Skip to content

Commit c681406

Browse files
authored
Merge pull request #1642 from hydephp/simplify-public-navigation-apis
[2.x] Simplify and normalize public navigation APIs
2 parents f7ae4e1 + 89c4890 commit c681406

File tree

7 files changed

+382
-155
lines changed

7 files changed

+382
-155
lines changed

docs/advanced-features/navigation-api.md

Lines changed: 211 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ navigation:
77

88
>warning This article covers advanced information that is only relevant if you want to create custom navigation menus. Instead, you may want to read the [Navigation](navigation) article for a general overview.
99
10+
1011
## Abstract
1112

1213
This article describes the Navigation API introduced in HydePHP v2. Both the main navigation menu and the documentation sidebar bundled with HydePHP are built with this API.
@@ -16,6 +17,7 @@ This article is intended for advanced users, as most users will not need to crea
1617
For this reason, the documentation is very code-driven due to the technical nature of the API.
1718
We'll also be mixing in some practical examples of Blade and PHP code to illustrate how you can use the API in your own projects.
1819

20+
1921
## Overview
2022

2123
The Navigation API consists of a set of classes within the `Hyde\Framework\Features\Navigation` namespace.
@@ -70,7 +72,8 @@ $menu = MainNavigationMenu::get();
7072
$sidebar = DocumentationSidebar::get();
7173
```
7274

73-
>info Developer tip: The menus are only generated *after* the Hyde Kernel is booted. If you are getting BindingResolutionExceptions, ensure that you are not trying to access the menus too early in the application lifecycle. (Consider using the `booted` event.)
75+
>info Developer tip: The menus are only generated *after* the Hyde Kernel is booted. If you are getting BindingResolutionExceptions, ensure that you are not trying to access the menus too early in the application lifecycle. (Consider using the `booted` event.)
76+
7477

7578
## Creating Custom Menus
7679

@@ -141,135 +144,290 @@ The object-oriented nature of the API also makes this perfect for package develo
141144

142145
Here are some general tips to keep in mind when working with the Navigation API:
143146
- You can use the `add` method to add single items or arrays of items. You can also pass an array of items directly to the menu constructor.
144-
- The navigation menu items is stored in a Laravel Collection, and is type safe to support both `NavigationItem` and `NavigationGroup` instances.
147+
- The navigation menu items is stored in a Laravel Collection, and is type safe to support both `NavigationItem` and `NavigationGroup` instances.
145148
- You can also construct NavigationItem instances directly, but the `create` method is a convenient shorthand, and can fill in data from routes, if you use them.
146149
- It's also possible to set an item's priority as the third parameter, but here we don't need it, as they default to the order they are added.
147150

151+
148152
## Class Reference
149153

150154
Below is a reference of the classes and methods available in the Navigation API.
151155

156+
152157
## NavigationMenu
153158

154159
The `NavigationMenu` class represents a navigation menu. It contains a collection of items, which can be either `NavigationItem` or `NavigationGroup` instances.
155160

156-
## NavigationItem
157-
158-
The `NavigationItem` class represents a single item in a navigation menu. It contains information such as the destination link or route, a label, and priority for ordering in the menu.
161+
### Quick Reference
159162

160-
Here is the constructor signature and a quick reference of the methods available on the `NavigationItem` class, and their types. Keep on reading to see more detailed examples and explanations.
163+
Here is a quick reference of the methods available on the NavigationMenu class:
161164

162165
```php
163-
use Hyde\Framework\Features\Navigation\NavigationItem;
166+
use Hyde\Framework\Features\Navigation\NavigationMenu;
164167

165-
$item = new NavigationItem(Route|string $destination, string $label, int $priority = 500);
168+
// Create a new NavigationMenu instance, optionally providing an array of items.
169+
$menu = new NavigationMenu($items = []);
166170

167-
$item->getLink(): string; // Returns the resolved link route or destination URL.
168-
$item->getLabel(): string; // Returns the label of the item.
169-
$item->isActive(): bool; // Returns true if the item is the page being rendered.
170-
$item->getPriority(): int; // Returns the priority of the item.
171-
$item->getPage(): ?HydePage; // Returns the underlying Page instance, if there is one.
171+
// Add a single item or an array of items to the menu.
172+
$menu->add(new NavigationItem());
173+
$menu->add([new NavigationItem()]);
174+
175+
// Get all items in the menu as a sorted Collection.
176+
$menu->getItems(): Collection<NavigationItem|NavigationGroup>
172177
```
173178

174-
### Creating Navigation Items
179+
### Creating Navigation Menus
175180

176-
There are several ways to create navigation items using the `NavigationItem` class.
181+
You can create a new NavigationMenu instance by simply calling the constructor, optionally providing an array of items.
177182

178-
#### Direct instantiation
183+
```php
184+
use Hyde\Framework\Features\Navigation\NavigationMenu;
179185

180-
You can create instances directly by passing either a URL or a Route instance to the constructor.
186+
$menu = new NavigationMenu($items = []);
187+
```
181188

182-
The first parameter is the destination, the second is the label, and the third optional parameter is the priority.
189+
Here is how to provide an array or Collection of `NavigationItem` and/or `NavigationGroup` instances directly to the constructor.
183190

184191
```php
185-
$item = new NavigationItem('index', 'Home');
186-
$item = new NavigationItem(Routes::get('index'), 'Home');
187-
$item = new NavigationItem('https://example.com', 'External Link');
192+
use Hyde\Framework\Features\Navigation\NavigationMenu;
193+
use Hyde\Framework\Features\Navigation\NavigationItem;
194+
use Hyde\Framework\Features\Navigation\NavigationGroup;
195+
196+
$menu = new NavigationMenu([
197+
new NavigationItem('index.html', 'Home'),
198+
new NavigationItem('posts.html', 'Blog'),
199+
new NavigationGroup('About', [
200+
new NavigationItem('about.html', 'About Us'),
201+
new NavigationItem('team.html', 'Our Team'),
202+
]),
203+
]);
204+
```
205+
206+
### Adding Items to the Menu
207+
208+
You can also add items to the menu after it has been created by using the `add` method which can take a single item or an array of items, and can be fluently chained.
209+
210+
```php
211+
$menu = (new NavigationMenu())
212+
->add(new NavigationItem('contact.html', 'Contact Us'))
213+
->add([
214+
new NavigationItem('privacy.html', 'Privacy Policy'),
215+
new NavigationItem('terms.html', 'Terms of Service'),
216+
]);
188217
```
189218

190-
#### Setting Priority
219+
### Accessing Items in the Menu
191220

192-
You can set the priority of the item, which determines its position in the menu.
221+
You can access all items in the menu by calling the `getItems` method, which will return a `Collection` of all items in the menu.
193222

194223
```php
195-
$item = new NavigationItem(Routes::get('index'), 'Home', 25);
224+
$items = $menu->getItems();
196225
```
197226

198-
#### Static Creation Method
227+
The items will automatically be sorted by their priority, with lower numbers coming first, defaulting to the order they were added if no priority is set.
199228

200-
You can use the static `create` method, which can automatically fill in the label and priority from a Route.
229+
## NavigationItem
230+
231+
The `NavigationItem` class is an abstraction for a navigation menu item containing useful information like the destination, label, and priority.
232+
233+
### Quick Reference
234+
235+
Here is a quick reference of the methods available on the `NavigationItem` class:
201236

202237
```php
203-
$item = NavigationItem::create(Routes::get('index'));
238+
use Hyde\Framework\Features\Navigation\NavigationItem;
239+
240+
// Create a new NavigationItem instance.
241+
$item = NavigationItem::create($destination, $label, $priority): NavigationItem;
242+
$item = new NavigationItem($destination, $label, $priority); // Same as above.
243+
244+
// Get the link of the item.
245+
$item->getLink(): string;
246+
247+
// Get the label of the item.
248+
$item->getLabel(): string;
249+
250+
// Get the priority of the item.
251+
$item->getPriority(): int;
252+
253+
// Check if the item is active. (Only works when the destination is a route)
254+
$item->isActive(): bool;
255+
```
256+
257+
### Blade Example
258+
259+
Here is an example of how you can put it all together in a Blade template:
260+
261+
```blade
262+
<a href="{{ $item->getLink() }}" @class(['active' => $item->isActive()])>
263+
{{ $item->getLabel() }}
264+
</a>
204265
```
205266

206-
You can also pass a custom label and priority to the `create` method to override the defaults.
267+
This will output an anchor tag with the correct link and label, and if the item is active, it will add an `active` class to the tag.
268+
269+
### Creating Navigation Items
270+
271+
There are two syntaxes for creating `NavigationItem` instances, you can use a standard constructor or the static create method.
272+
Both options provide the exact same signature and functionality, so it's just a matter of preference which one you use.
273+
274+
The constructors take three parameters: the destination, the label, and the optional priority.
275+
The destination can be a `Route` instance, a route key string, or an external URL.
207276

208277
```php
209-
$item = NavigationItem::create(Routes::get('index'), 'Custom Label', 50);
278+
use Hyde\Framework\Features\Navigation\NavigationItem;
279+
280+
$item = new NavigationItem($destination, $label, $priority);
281+
$item = NavigationItem::create($destination, $label, $priority);
210282
```
211283

212-
#### Using Route Keys and URLs
284+
#### Using Routes
213285

214-
The `create` method works with route keys and URLs.
286+
Using the HydePHP routing system is the recommended way to create navigation items leading to pages within your project,
287+
as they will automatically have links resolved to the correct URL, and Hyde can check if the items are active.
288+
Additionally, Hyde will use the page data as the label and priority defaults unless you override them.
289+
290+
You can create routed navigation items by providing either a `Route` instance or a route key string as the destination.
215291

216292
```php
217-
$item = NavigationItem::create('index');
218-
$item = NavigationItem::create('https://example.com');
293+
// Using a route key string.
294+
new NavigationItem('index');
295+
296+
// Using the Routes facade to get a Route instance.
297+
new NavigationItem(Routes::get('index'));
298+
299+
// Setting the label and/or priorities will override inferred data.
300+
new NavigationItem(Routes::get('index'), 'Custom Label', 25);
219301
```
220302

221-
Unless you pass a custom label to URL items, the label will be the URL itself.
303+
Using a route key is more concise, but will not provide type safety as it will be treated as a link if the route does not exist,
304+
whereas providing an invalid route key to the `Routes` facade will throw an exception. It's up to you which one you prefer.
305+
306+
#### Using External URLs
307+
308+
You can also create navigation items that link to external URLs by providing a full URL as the destination.
309+
310+
If you do not set a label for links, the label will default to the URL, and if you do not set a priority, it will default to `500`.
222311

223312
```php
224-
$item = NavigationItem::create('https://example.com');
313+
// This will lead directly to the link, and use it as the label with a priority of 500.
314+
new NavigationItem('https://example.com');
315+
316+
// You can also set a custom label and priority to override the defaults.
317+
new NavigationItem('https://example.com', 'External Link', 25);
225318
```
226319

227-
### Navigation Item Methods
320+
While it is discouraged to use external URLs for internal pages, as Hyde won't be able to resolve relative links or check active states,
321+
they are excellent for any time you want an absolute link to an external site or resource.
228322

229-
### Getting Label and Link
323+
Note that Hyde will not validate or modify the URL, so you are responsible for ensuring it's correct.
324+
325+
### Accessing the resolved links
326+
327+
The `getLink` method is designed to return a link that can be used in the `href` attribute of an anchor tag.
328+
329+
If the destination is a route, the link will be resolved to the correct URL, using relative paths if possible. It will also respect the pretty URL setting.
230330

231331
```php
232-
$item = NavigationItem::create('index');
332+
$item = new NavigationItem(Routes::get('index'));
333+
$item->getLink(); // Outputs 'index.html'
233334

234-
// Get the label of the item.
235-
$item->getLabel(); // Returns 'Home'
335+
$item = new NavigationItem('https://example.com');
336+
$item->getLink(); // Outputs 'https://example.com'
337+
```
236338

237-
// Get the link of the item.
238-
$item->getLink(); // Returns 'index.html'
339+
**Tip:** The item instances automatically turns into the resolved link when cast to a string. Perfect for your Blade templates!
340+
341+
```blade
342+
<a href="{{ $item }}">{{ $item->getLabel() }}</a>
239343
```
240344

241-
You can also get the link by casting the item to a string.
345+
### Accessing the label
346+
347+
The `getLabel` method returns the label of the item. This is the text that will be displayed in the navigation menu.
242348

243349
```php
244-
(string) $item; // Returns 'index.html'
350+
$item = new NavigationItem('index', 'Home');
351+
$item->getLabel(); // Outputs 'Home'
245352
```
246353

247-
### Getting Priority
354+
### Accessing the priority
355+
356+
The `getPriority` method returns the priority of the item. This is a number that determines the order in which the items are displayed in the menu, where lower numbers come first.
248357

249358
```php
250-
$item->getPriority(); // Returns 0
359+
$item = new NavigationItem('index', 'Home', 25);
360+
$item->getPriority(); // Outputs 25
251361
```
252362

253-
### Getting Page Instance
363+
### Checking if the item is active
254364

255-
You can get the underlying Page instance of the item, if it exists.
365+
The `isActive` method checks if the item is active (by comparing it to the Hyde page being compiled at the moment). This is useful for highlighting the current page in the navigation menu.
256366

257367
```php
258-
$item->getPage(); // Returns instance of BladePage or null
368+
$item = new NavigationItem('index');
369+
$item->isActive(); // Outputs true if the item is the current page, otherwise false.
259370
```
260371

261-
### Checking Active State
372+
<!--
373+
Generated by Copilot, kinda cool, maybe something to implement?
374+
375+
### Advanced Usage
376+
377+
#### Customizing the Active State
262378
263-
You can check if the item is active (i.e., the current page being rendered).
379+
By default, the `isActive` method will check if the item's destination matches the current page being compiled.
380+
381+
However, you can also provide a custom callback to the method to determine if the item is active.
264382
265383
```php
266-
$item->isActive(); // Returns false
384+
$item = new NavigationItem('index');
385+
$item->isActive(fn($item) => $item->getLink() === 'index.html');
267386
```
268387
269-
This concludes the documentation for the `NavigationItem` class.
388+
This is useful if you want to check for a specific query parameter, or if you want to check if the item is active based on a more complex condition.
389+
-->
270390

271391

272392
## NavigationGroup
273393

274394
The `NavigationGroup` class represents a group of items in a navigation menu. It contains a label, priority, and a collection of navigation items.
275395
This class is often used to create submenus or dropdowns in a navigation menu.
396+
397+
The `NavigationGroup` class extends the `NavigationMenu` class, and thus inherits the same base methods and functionality,
398+
while also having shared methods with the `NavigationItem` class to render the groups in a Blade view.
399+
400+
### Quick Reference
401+
402+
Here is a quick reference of the methods available on the `NavigationGroup` class:
403+
404+
```php
405+
use Hyde\Framework\Features\Navigation\NavigationGroup;
406+
407+
// Create a new NavigationGroup instance.
408+
$group = new NavigationGroup($label, $items = [], $priority = 500);
409+
410+
// Add a single item or an array of items to the group.
411+
$group->add(new NavigationItem());
412+
$group->add([new NavigationItem()]);
413+
414+
// Get all items in the group as a Collection sorted by priority.
415+
$group->getItems(): Collection<NavigationItem|NavigationGroup>
416+
417+
// Get the label of the group.
418+
$group->getLabel(): string;
419+
420+
// Get the priority of the group.
421+
$group->getPriority(): int;
422+
423+
// Get the group key, which is a normalized kebab-case version of the label.
424+
$group->getGroupKey(): string;
425+
```
426+
427+
As the `NavigationGroup` class extends the `NavigationMenu` class, please see the `NavigationMenu` section for detailed information of the methods available.
428+
429+
### Usage Scenarios
430+
431+
HydePHP uses the `NavigationGroup` class to create dropdowns in the main navigation menu and the category groups in the documentation sidebar.
432+
433+
In your own custom menus, you can use this class for the same types of functionality, and you can even nest groups within groups to create complex navigation structures.

0 commit comments

Comments
 (0)