Laravel Nova is a great tool! There are many packages that will do similar to the customization I will introduce here.
As a dev, less package is always better if the required customization is simple.
So, This is what we will customize
- Add an icon to each navigation
- Add method to define return the sort value so we can sort
- Both navigation and group use the defined sort value for sorting
If you open a default navigation.blade.php
it looks something like this
@if (count(\Laravel\Nova\Nova::resourcesForNavigation(request())))
<h3 class="flex items-center font-normal text-white mb-6 text-base no-underline">
<svg class="sidebar-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path fill="var(--sidebar-icon)" d="M3 1h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2H3c-1.1045695 0-2-.8954305-2-2V3c0-1.1045695.8954305-2 2-2zm0 2v4h4V3H3zm10-2h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2h-4c-1.1045695 0-2-.8954305-2-2V3c0-1.1045695.8954305-2 2-2zm0 2v4h4V3h-4zM3 11h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2H3c-1.1045695 0-2-.8954305-2-2v-4c0-1.1045695.8954305-2 2-2zm0 2v4h4v-4H3zm10-2h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2h-4c-1.1045695 0-2-.8954305-2-2v-4c0-1.1045695.8954305-2 2-2zm0 2v4h4v-4h-4z"
/>
</svg>
<span class="sidebar-label">{{ __('Resources') }}</span>
</h3>
@foreach($navigation as $group => $resources)
@if (count($groups) > 1)
<h4 class="ml-8 mb-4 text-xs text-white-50% uppercase tracking-wide">{{ $group }}</h4>
@endif
<ul class="list-reset mb-8">
@foreach($resources as $resource)
<li class="leading-tight mb-4 ml-8 text-sm">
<router-link :to="{
name: 'index',
params: {
resourceName: '{{ $resource::uriKey() }}'
}
}" class="text-white text-justify no-underline dim" dusk="{{ $resource::uriKey() }}-resource-link">
{{ $resource::label() }}
</router-link>
</li>
@endforeach
</ul>
@endforeach
@endif
- Add an icon to each navigation
Let's look at the part where each link is render
@foreach($resources as $resource)
<li class="leading-tight mb-4 ml-8 text-sm">
<router-link :to="{
name: 'index',
params: {
resourceName: '{{ $resource::uriKey() }}'
}
}" class="text-white text-justify no-underline dim" dusk="{{ $resource::uriKey() }}-resource-link">
{{ $resource::label() }}
</router-link>
</li>
@endforeach
If you dd($resource)
it's a fully FQN of your resources. And this on each, it's calling static method to resource class to get the label
{{ $resource::label() }}
We are going to add a new icon()
method to the resource.
/**
* The icon of the resource.
*
* @return string
*/
public static function icon()
{
return view('nova::icons.icon-customer')->render();
}
in the icon-customer.blade.php
file we only have an SVG.
<svg width="20" height="20" class="sidebar-icon" fill="var(--sidebar-icon)" viewBox="0 0 25 26" xmlns="http://www.w3.org/2000/svg"><path d="M18.501 15.62a7.161 7.161 0 003.409-6.095c0-3.949-3.202-7.161-7.137-7.161-3.935 0-7.137 3.212-7.137 7.161a7.161 7.161 0 003.408 6.095c-4.406.992-7.499 3.702-7.499 6.98 0 2.684 7.058 3.4 11.228 3.4S26 25.284 26 22.6c0-3.278-3.092-5.988-7.499-6.98zM9.106 9.525c0-3.134 2.542-5.684 5.667-5.684 3.124 0 5.666 2.55 5.666 5.684 0 3.135-2.542 5.684-5.666 5.684-3.125 0-5.667-2.55-5.667-5.684zm5.667 14.998c-5.994 0-9.582-1.27-9.757-1.927.005-3.259 4.38-5.91 9.757-5.91 5.376 0 9.75 2.65 9.756 5.909-.17.656-3.758 1.928-9.756 1.928z" /><path d="M18.501 15.62a7.161 7.161 0 003.409-6.095c0-3.949-3.202-7.161-7.137-7.161-3.935 0-7.137 3.212-7.137 7.161a7.161 7.161 0 003.408 6.095c-4.406.992-7.499 3.702-7.499 6.98 0 2.684 7.058 3.4 11.228 3.4S26 25.284 26 22.6c0-3.278-3.092-5.988-7.499-6.98zM9.106 9.525c0-3.134 2.542-5.684 5.667-5.684 3.124 0 5.666 2.55 5.666 5.684 0 3.135-2.542 5.684-5.666 5.684-3.125 0-5.667-2.55-5.667-5.684zm5.667 14.998c-5.994 0-9.582-1.27-9.757-1.927.005-3.259 4.38-5.91 9.757-5.91 5.376 0 9.75 2.65 9.756 5.909-.17.656-3.758 1.928-9.756 1.928z" /><path d="M8.169 12.236c-3.92.379-6.927 2.459-6.93 4.961.1.383 1.552 1.01 4.08 1.368a6.948 6.948 0 00-.903 1.134C2.029 19.296 0 18.534 0 17.2c0-2.787 2.604-5.09 6.315-5.933a6.1 6.1 0 01-2.87-5.18C3.444 2.73 6.14 0 9.454 0c1.94 0 3.667.936 4.767 2.385-.49.037-.967.125-1.425.258a4.727 4.727 0 00-3.342-1.387c-2.632 0-4.772 2.167-4.772 4.832a4.839 4.839 0 003.031 4.498 7.09 7.09 0 00.455 1.65zM18.595 15.56l-.094.06.128.03a4.757 4.757 0 00-.034-.09z" /></svg>
Now, update the navigation to render the icon if the method exists or just show the default icon.
@if(method_exists($resource, 'icon'))
{!! $resource::icon() !!}
@else
<svg class="sidebar-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path fill="var(--sidebar-icon)" d="M3 1h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2H3c-1.1045695 0-2-.8954305-2-2V3c0-1.1045695.8954305-2 2-2zm0 2v4h4V3H3zm10-2h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2h-4c-1.1045695 0-2-.8954305-2-2V3c0-1.1045695.8954305-2 2-2zm0 2v4h4V3h-4zM3 11h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2H3c-1.1045695 0-2-.8954305-2-2v-4c0-1.1045695.8954305-2 2-2zm0 2v4h4v-4H3zm10-2h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2h-4c-1.1045695 0-2-.8954305-2-2v-4c0-1.1045695.8954305-2 2-2zm0 2v4h4v-4h-4z"/>
</svg>
@endif
<span class="sidebar-label">{{ $resource::label() }}</span>
- Add method to define return the sort value
To sort the navigation, it can be done at the
navigation.blade.php
but let's dive a little bit deeper into how Nova got those resources lists from. if you openvendor/laravel/nova/src/Tools/ResourceManager.php
, you will find this:
/**
* Build the view that renders the navigation links for the tool.
*
* @return \Illuminate\View\View
*/
public function renderNavigation()
{
$request = request();
$groups = Nova::groups($request);
$navigation = Nova::groupedResourcesForNavigation($request);
return view('nova::resources.navigation', [
'navigation' => $navigation,
'groups' => $groups,
]);
}
if you start tracing you ended up on the
/**
* Get the sorting strategy to use for Nova resources.
*
* @return array
*/
public static function sortResourcesWith()
{
return static::$sortCallback ?? function ($resource) {
return $resource::label();
};
}
If you notice, there is a $sortCallback
function which you can register when Nova boots. So let's do that.
so in my NovaServiceProvider.php
in the boot()
func, we gonna add:
Nova::sortResourcesBy(function ($resource) {
return method_exists($resource, 'sortOrder') ?
$resource::sortOrder() : $resource::label();
});
So, we will look for the sortOrder
method in our resource, if it doesn't it will fall back to the original label
based sort.
That's all!!
- Both navigation and group use the defined sort value
For the group to order, I just utilize the sortorder
of resource.
@php
// dump($navigation);
$orderNav = $navigation->sortBy(function($resources){
// dump($resources->first());
return $resources->first()::sortOrder();
});
@endphp
@foreach($orderNav as $group => $resources)
...
@endforeach
I ended up only showing the icon for the group and here is my navigation.blade.php
. just FYI: not cleaned up/optimized
@if (count(Nova::availableResources(request())))
<ul class="sidemenu">
@php
$orderNav = $navigation->sortBy(function($resources){
return $resources->first()::sortOrder();
});
@endphp
@foreach($orderNav as $group => $resources)
@if (count($groups) > 1)
<li class="sidebar-dropdown mb-2">
<input id="{{$group}}" class="toogle-group" type="checkbox" />
<label for="{{$group}}" data-toggle="dropdown" class="flex items-center font-normal text-white mb-4 text-base no-underline dim">
@if(method_exists($resources->first(), 'icon'))
{!! $resources->first()::icon() !!}
@endif
{{ $group }}
<svg class="text-gray-300 ml-auto h-5 w-5 transform group-hover:text-gray-400 transition-transform ease-in-out duration-150 focus:ring-indigo-500" viewBox="0 0 20 20" aria-hidden="true">
<path d="M6 6L14 10L6 14V6Z" fill="currentColor" />
</svg>
</label>
<ul class="dropdown-menu">
@foreach($resources as $resource)
<li>
<router-link :to="{
name: 'index',
params: {
resourceName: '{{ $resource::uriKey() }}'
}
}" class="flex items-center font-normal text-white mb-4 text-base no-underline dim">
<span class="sidebar-label">{{ $resource::label() }}</span>
</router-link>
</li>
@endforeach
</ul>
</li>
@else
@foreach($resources as $resource)
<li class="sidebar-dropdown">
<router-link :to="{
name: 'index',
params: {
resourceName: '{{ $resource::uriKey() }}'
}
}" class="flex items-center font-normal text-white mb-6 text-base no-underline dim">
@if(property_exists($resource, 'icon'))
{!! $resource::$icon !!}
@elseif(method_exists($resource, 'icon'))
{!! $resource::icon() !!}
@else
<svg class="sidebar-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path fill="var(--sidebar-icon)" d="M3 1h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2H3c-1.1045695 0-2-.8954305-2-2V3c0-1.1045695.8954305-2 2-2zm0 2v4h4V3H3zm10-2h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2h-4c-1.1045695 0-2-.8954305-2-2V3c0-1.1045695.8954305-2 2-2zm0 2v4h4V3h-4zM3 11h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2H3c-1.1045695 0-2-.8954305-2-2v-4c0-1.1045695.8954305-2 2-2zm0 2v4h4v-4H3zm10-2h4c1.1045695 0 2 .8954305 2 2v4c0 1.1045695-.8954305 2-2 2h-4c-1.1045695 0-2-.8954305-2-2v-4c0-1.1045695.8954305-2 2-2zm0 2v4h4v-4h-4z"
/>
</svg>
@endif
<span class="sidebar-label">{{ $resource::label() }}</span>
</router-link>
</li>
@endforeach
@endif
@endforeach
</ul>
@endif
Top comments (0)