添加模板hook功能
代码优化 添加优先级 添加 header demo add demo 修改产品详情页 修改产品详情页
This commit is contained in:
parent
bd14ecf41b
commit
ed1badd404
|
|
@ -16,10 +16,10 @@ use Beike\Admin\View\Components\Filter;
|
|||
use Beike\Admin\View\Components\Form\Image;
|
||||
use Beike\Admin\View\Components\Form\Input;
|
||||
use Beike\Admin\View\Components\Form\InputLocale;
|
||||
use Beike\Admin\View\Components\Form\Richtext;
|
||||
use Beike\Admin\View\Components\Form\Select;
|
||||
use Beike\Admin\View\Components\Form\SwitchRadio;
|
||||
use Beike\Admin\View\Components\Form\Textarea;
|
||||
use Beike\Admin\View\Components\Form\Richtext;
|
||||
use Beike\Admin\View\Components\Header;
|
||||
use Beike\Admin\View\Components\NoData;
|
||||
use Beike\Admin\View\Components\Sidebar;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Beike\Hook\Facades\Hook;
|
||||
use Beike\Models\AdminUser;
|
||||
use Beike\Models\Currency;
|
||||
use Beike\Models\Customer;
|
||||
|
|
@ -616,7 +617,7 @@ function create_directories($directoryPath)
|
|||
}
|
||||
|
||||
/**
|
||||
* hook filter 埋点
|
||||
* PHP 代码 hook filter 埋点
|
||||
*
|
||||
* @param $hookKey
|
||||
* @param $hookValue
|
||||
|
|
@ -628,7 +629,7 @@ function hook_filter($hookKey, $hookValue): mixed
|
|||
}
|
||||
|
||||
/**
|
||||
* hook action 埋点
|
||||
* PHP 代码 hook action 埋点
|
||||
*
|
||||
* @param $hookKey
|
||||
* @param $hookValue
|
||||
|
|
@ -639,30 +640,42 @@ function hook_action($hookKey, $hookValue)
|
|||
}
|
||||
|
||||
/**
|
||||
* 添加 Filter
|
||||
* 添加 Filter, 执行 PHP 逻辑
|
||||
*
|
||||
* @param $hook
|
||||
* @param $hookKey
|
||||
* @param $callback
|
||||
* @param int $priority
|
||||
* @param int $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
function add_filter($hook, $callback, int $priority = 20, int $arguments = 1)
|
||||
function add_filter($hookKey, $callback, int $priority = 20, int $arguments = 1)
|
||||
{
|
||||
return Eventy::addFilter($hook, $callback, $priority, $arguments);
|
||||
return Eventy::addFilter($hookKey, $callback, $priority, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 Action
|
||||
* 添加 Action, 执行 PHP 逻辑
|
||||
*
|
||||
* @param $hook
|
||||
* @param $hookKey
|
||||
* @param $callback
|
||||
* @param int $priority
|
||||
* @param int $arguments
|
||||
*/
|
||||
function add_action($hook, $callback, int $priority = 20, int $arguments = 1)
|
||||
function add_action($hookKey, $callback, int $priority = 20, int $arguments = 1)
|
||||
{
|
||||
Eventy::addAction($hook, $callback, $priority, $arguments);
|
||||
Eventy::addAction($hookKey, $callback, $priority, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 采用 Hook 修改 Blade 代码
|
||||
*
|
||||
* @param $hookKey
|
||||
* @param $callback
|
||||
* @param int $priority
|
||||
*/
|
||||
function blade_hook($hookKey, $callback, int $priority = 0)
|
||||
{
|
||||
Hook::listen($hookKey, $callback, $priority);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Beike\Hook;
|
||||
|
||||
class Callback
|
||||
{
|
||||
protected $function;
|
||||
|
||||
protected array $parameters = [];
|
||||
|
||||
protected bool $run = true;
|
||||
|
||||
public function __construct($function, $parameters = [])
|
||||
{
|
||||
$this->setCallback($function, $parameters);
|
||||
}
|
||||
|
||||
public function setCallback($function, $parameters)
|
||||
{
|
||||
$this->function = $function;
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
public function call($parameters = null)
|
||||
{
|
||||
if ($this->run) {
|
||||
$this->run = false;
|
||||
|
||||
return call_user_func_array($this->function, ($parameters ?: $this->parameters));
|
||||
}
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->run = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Beike\Hook\Console;
|
||||
|
||||
use Beike\Hook\Facades\Hook;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class HookListeners extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'hook:list';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'List all hook listeners';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$list = Hook::getListeners();
|
||||
$array = [];
|
||||
|
||||
foreach ($list as $hook => $lister) {
|
||||
foreach ($lister as $key => $element) {
|
||||
$array[] = [
|
||||
$key,
|
||||
$hook,
|
||||
$element['caller']['class'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$headers = ['Sort', 'Hook name', 'Listener class'];
|
||||
|
||||
$this->table($headers, $array);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Beike\Hook\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
class Hook extends Facade
|
||||
{
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return 'Hook';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
<?php
|
||||
/**
|
||||
* Inspired by https://github.com/esemve/Hook
|
||||
*/
|
||||
|
||||
namespace Beike\Hook;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class Hook
|
||||
{
|
||||
protected array $watch = [];
|
||||
|
||||
protected array $stop = [];
|
||||
|
||||
protected array $mock = [];
|
||||
|
||||
protected bool $testing = false;
|
||||
|
||||
/**
|
||||
* Return the hook answer.
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
* @param array $params
|
||||
* @param callable|null $callback
|
||||
* @param string $htmlContent content wrapped by hook
|
||||
*
|
||||
* @return null|void
|
||||
*/
|
||||
public function get(string $hook, array $params = [], callable $callback = null, string $htmlContent = '')
|
||||
{
|
||||
$callbackObject = $this->createCallbackObject($callback, $params);
|
||||
|
||||
$output = $this->returnMockIfDebugModeAndMockExists($hook);
|
||||
if ($output) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$output = $this->run($hook, $params, $callbackObject, $htmlContent);
|
||||
|
||||
if (! $output) {
|
||||
$output = $callbackObject->call();
|
||||
}
|
||||
|
||||
unset($callbackObject);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all another hook running.
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
*/
|
||||
public function stop(string $hook)
|
||||
{
|
||||
$this->stop[$hook] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to hook.
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
* @param $priority
|
||||
* @param $function
|
||||
*/
|
||||
public function listen(string $hook, $function, $priority = null)
|
||||
{
|
||||
$caller = debug_backtrace(null, 3)[2];
|
||||
|
||||
if (in_array(Arr::get($caller, 'function'), ['include', 'require'])) {
|
||||
$caller = debug_backtrace(null, 4)[3];
|
||||
}
|
||||
|
||||
if (empty($this->watch[$hook])) {
|
||||
$this->watch[$hook] = [];
|
||||
}
|
||||
|
||||
if (! is_numeric($priority)) {
|
||||
$priority = null;
|
||||
}
|
||||
|
||||
$this->watch[$hook][$priority] = [
|
||||
'function' => $function,
|
||||
'caller' => [
|
||||
//'file' => $caller['file'],
|
||||
//'line' => $caller['line'],
|
||||
'class' => Arr::get($caller, 'class'),
|
||||
],
|
||||
];
|
||||
|
||||
ksort($this->watch[$hook]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all registered hooks.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHooks(): array
|
||||
{
|
||||
$hookNames = (array_keys($this->watch));
|
||||
ksort($hookNames);
|
||||
|
||||
return $hookNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all listeners for hook.
|
||||
*
|
||||
* @param string $hook
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getEvents(string $hook): array
|
||||
{
|
||||
$output = [];
|
||||
|
||||
foreach ($this->watch[$hook] as $key => $value) {
|
||||
$output[$key] = $value['caller'];
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing.
|
||||
*
|
||||
* @param string $name Hook name
|
||||
* @param mixed $return Answer
|
||||
*/
|
||||
public function mock(string $name, mixed $return)
|
||||
{
|
||||
$this->testing = true;
|
||||
$this->mock[$name] = ['return' => $return];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mock value.
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
*/
|
||||
protected function returnMockIfDebugModeAndMockExists(string $hook)
|
||||
{
|
||||
if ($this->testing) {
|
||||
if (array_key_exists($hook, $this->mock)) {
|
||||
$output = $this->mock[$hook]['return'];
|
||||
unset($this->mock[$hook]);
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new callback object.
|
||||
*
|
||||
* @param callable $callback function
|
||||
* @param array $params parameters
|
||||
*
|
||||
* @return Callback
|
||||
*/
|
||||
protected function createCallbackObject(callable $callback, array $params): Callback
|
||||
{
|
||||
return new Callback($callback, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run hook events.
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
* @param array $params Parameters
|
||||
* @param Callback $callback Callback object
|
||||
* @param string|null $output html wrapped by hook
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function run(string $hook, array $params, Callback $callback, string $output = null): mixed
|
||||
{
|
||||
array_unshift($params, $output);
|
||||
array_unshift($params, $callback);
|
||||
|
||||
if (array_key_exists($hook, $this->watch)) {
|
||||
if (is_array($this->watch[$hook])) {
|
||||
foreach ($this->watch[$hook] as $function) {
|
||||
if (! empty($this->stop[$hook])) {
|
||||
unset($this->stop[$hook]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$output = call_user_func_array($function['function'], $params);
|
||||
$params[1] = $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the listeners.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getListeners(): array
|
||||
{
|
||||
return $this->watch;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Beike\Hook;
|
||||
|
||||
use Beike\Hook\Console\HookListeners;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class HookServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
$this->bootDirectives();
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
$this->commands([
|
||||
HookListeners::class,
|
||||
]);
|
||||
|
||||
$this->app->singleton('Hook', function () {
|
||||
return new Hook();
|
||||
});
|
||||
}
|
||||
|
||||
protected function bootDirectives()
|
||||
{
|
||||
Blade::directive('hook', function ($parameter) {
|
||||
$parameter = trim($parameter, '()');
|
||||
$parameters = explode(',', $parameter);
|
||||
|
||||
$name = trim($parameters[0], "'");
|
||||
|
||||
// $parameters[1] => bool => is this wrapper component?
|
||||
if (!isset($parameters[1])) {
|
||||
return ' <'.'?php
|
||||
|
||||
$__definedVars = (get_defined_vars()["__data"]);
|
||||
if (empty($__definedVars))
|
||||
{
|
||||
$__definedVars = [];
|
||||
}
|
||||
$output = \Hook::get("' . $name . '",["data"=>$__definedVars],function($data) { return null; });
|
||||
if ($output)
|
||||
echo $output;
|
||||
?'.'>';
|
||||
} else {
|
||||
return ' <'.'?php
|
||||
$__hook_name="'. $name .'";
|
||||
ob_start();
|
||||
?'.'>';
|
||||
}
|
||||
});
|
||||
|
||||
Blade::directive('endhook', function ($parameter) {
|
||||
return ' <'.'?php
|
||||
$__definedVars = (get_defined_vars()["__data"]);
|
||||
if (empty($__definedVars))
|
||||
{
|
||||
$__definedVars = [];
|
||||
}
|
||||
$__hook_content = ob_get_clean();
|
||||
$output = \Hook::get("$__hook_name",["data"=>$__definedVars],function($data) { return null; },$__hook_content);
|
||||
unset($__hook_name);
|
||||
unset($__hook_content);
|
||||
if ($output)
|
||||
echo $output;
|
||||
?'.'>';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -180,6 +180,7 @@ return [
|
|||
Beike\Shop\Providers\ShopServiceProvider::class,
|
||||
Beike\Shop\Providers\PluginServiceProvider::class,
|
||||
Beike\Installer\Providers\InstallerServiceProvider::class,
|
||||
Beike\Hook\HookServiceProvider::class,
|
||||
|
||||
],
|
||||
|
||||
|
|
@ -235,6 +236,7 @@ return [
|
|||
'URL' => Illuminate\Support\Facades\URL::class,
|
||||
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||
'View' => Illuminate\Support\Facades\View::class,
|
||||
'Hook' => Beike\Hook\Facades\Hook::class,
|
||||
|
||||
],
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@
|
|||
},
|
||||
"exclude": [
|
||||
"database",
|
||||
"plugins"
|
||||
"plugins",
|
||||
"beike/Hook"
|
||||
],
|
||||
"notName": [
|
||||
"server.php"
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ class Bootstrap
|
|||
public function boot()
|
||||
{
|
||||
$this->addLatestProducts();
|
||||
|
||||
// $this->modifyHeader();
|
||||
// $this->modifyProductDetail();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -31,4 +34,44 @@ class Bootstrap
|
|||
return $data;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改前台全局 header 模板
|
||||
*/
|
||||
private function modifyHeader()
|
||||
{
|
||||
blade_hook('header.top.currency', function ($callback, $output, $data) {
|
||||
return $output . '货币后';
|
||||
});
|
||||
|
||||
blade_hook('header.top.language', function ($callback, $output, $data) {
|
||||
return $output . '语言后';
|
||||
});
|
||||
|
||||
blade_hook('header.top.telephone', function ($callback, $output, $data) {
|
||||
return '电话前' . $output;
|
||||
});
|
||||
|
||||
blade_hook('header.menu.logo', function ($callback, $output, $data) {
|
||||
return $output . 'logo后';
|
||||
});
|
||||
|
||||
blade_hook('header.menu.icon', function ($callback, $output, $data) {
|
||||
$view = view('LatestProducts::header_icon')->render();
|
||||
return $output . $view;
|
||||
});
|
||||
}
|
||||
|
||||
private function modifyProductDetail()
|
||||
{
|
||||
blade_hook('product.detail.brand', function ($callback, $output, $data) {
|
||||
return $output . '<div class="d-flex"><span class="title text-muted">Brand 2:</span>品牌 2</div>';
|
||||
});
|
||||
|
||||
blade_hook('product.detail.buy.after', function ($callback, $output, $data) {
|
||||
$view = view('LatestProducts::product_button')->render();
|
||||
return $output . $view;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<li class="nav-item">
|
||||
<a href="https://beikeshop.com" class="nav-link"><i class="iconfont">图标后</i></a>
|
||||
</li>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<button class="btn btn-dark ms-3 fw-bold">
|
||||
<i class="bi bi-bag-fill me-1"></i>DIY
|
||||
</button>
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
<div class="top-wrap">
|
||||
<div class="container d-flex justify-content-between align-items-center">
|
||||
<div class="left d-flex align-items-center">
|
||||
@hook('header.top.currency', true)
|
||||
<div class="dropdown">
|
||||
<a class="btn dropdown-toggle ps-0" href="javascript:void(0)" role="button" id="currency-dropdown" data-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
|
|
@ -33,6 +34,9 @@
|
|||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endhook
|
||||
|
||||
@hook('header.top.language', true)
|
||||
<div class="dropdown">
|
||||
<a class="btn dropdown-toggle" href="javascript:void(0)" role="button" id="language-dropdown" data-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
|
|
@ -47,11 +51,14 @@
|
|||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endhook
|
||||
</div>
|
||||
|
||||
@if (system_setting('base.telephone', ''))
|
||||
<div class="right nav">
|
||||
@hook('header.top.telephone', true)
|
||||
<span class="px-2"><i class="bi bi-telephone-forward me-2"></i> {{ system_setting('base.telephone') }}</span>
|
||||
@endhook
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
|
@ -59,9 +66,11 @@
|
|||
|
||||
<div class="header-content d-none d-lg-block py-3">
|
||||
<div class="container navbar-expand-lg">
|
||||
@hook('header.menu.logo', true)
|
||||
<div class="logo"><a href="{{ shop_route('home.index') }}">
|
||||
<img src="{{ image_origin(system_setting('base.logo')) }}" class="img-fluid"></a>
|
||||
</div>
|
||||
@endhook
|
||||
<div class="menu-wrap">
|
||||
@if (!is_mobile())
|
||||
@include('shared.menu-pc')
|
||||
|
|
@ -69,6 +78,7 @@
|
|||
</div>
|
||||
<div class="right-btn">
|
||||
<ul class="navbar-nav flex-row">
|
||||
@hook('header.menu.icon', true)
|
||||
<li class="nav-item"><a href="#offcanvas-search-top" data-bs-toggle="offcanvas"
|
||||
aria-controls="offcanvasExample" class="nav-link"><i class="iconfont"></i></a></li>
|
||||
<li class="nav-item"><a href="{{ shop_route('account.wishlist.index') }}" class="nav-link"><i
|
||||
|
|
@ -100,6 +110,7 @@
|
|||
@endauth
|
||||
</ul>
|
||||
</li>
|
||||
@endhook
|
||||
<li class="nav-item">
|
||||
<a class="nav-link position-relative" {{ !equal_route('shop.carts.index') ? 'data-bs-toggle=offcanvas' : '' }}
|
||||
href="{{ !equal_route('shop.carts.index') ? '#offcanvas-right-cart' : 'javascript:void(0);' }}" role="button"
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="stock-and-sku mb-4">
|
||||
@hook('product.detail.quantity', true)
|
||||
<div class="d-flex">
|
||||
<span class="title text-muted">{{ __('product.quantity') }}:</span>
|
||||
<div :class="product.quantity > 0 ? 'text-success' : 'text-secondary'">
|
||||
|
|
@ -67,14 +68,24 @@
|
|||
<template v-else>{{ __('shop/products.out_stock') }}</template>
|
||||
</div>
|
||||
</div>
|
||||
@endhook
|
||||
|
||||
@if ($product['brand_id'])
|
||||
@hook('product.detail.brand', true)
|
||||
<div class="d-flex">
|
||||
<span class="title text-muted">{{ __('product.brand') }}:</span>
|
||||
<a href="{{ shop_route('brands.show', $product['brand_id']) }}">{{ $product['brand_name'] }}</a>
|
||||
</div>
|
||||
@endhook
|
||||
@endif
|
||||
|
||||
@hook('product.detail.sku', true)
|
||||
<div class="d-flex"><span class="title text-muted">SKU:</span>@{{ product.sku }}</div>
|
||||
@endhook
|
||||
|
||||
@hook('product.detail.model', true)
|
||||
<div class="d-flex" v-if="product.model"><span class="title text-muted">{{ __('shop/products.model') }}:</span> @{{ product.model }}</div>
|
||||
@endhook
|
||||
</div>
|
||||
@if (0)
|
||||
<div class="rating-wrap d-flex">
|
||||
|
|
@ -106,6 +117,7 @@
|
|||
|
||||
@if ($product['active'])
|
||||
<div class="quantity-btns">
|
||||
@hook('product.detail.buy.before')
|
||||
<div class="quantity-wrap">
|
||||
<input type="text" class="form-control" :disabled="!product.quantity" onkeyup="this.value=this.value.replace(/\D/g,'')" v-model="quantity" name="quantity">
|
||||
<div class="right">
|
||||
|
|
@ -125,6 +137,7 @@
|
|||
@click="addCart(true, this)"
|
||||
><i class="bi bi-bag-fill me-1"></i>{{ __('shop/products.buy_now') }}
|
||||
</button>
|
||||
@hook('product.detail.buy.after')
|
||||
</div>
|
||||
<div class="add-wishlist">
|
||||
<button class="btn btn-link ps-0 text-secondary" data-in-wishlist="{{ $product['in_wishlist'] }}" onclick="bk.addWishlist('{{ $product['id'] }}', this)">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<ul class="navbar-nav mx-auto">
|
||||
@hook('header.menu.before')
|
||||
@foreach ($menu_content as $menu)
|
||||
@if ($menu['name'])
|
||||
<li
|
||||
|
|
@ -56,4 +57,5 @@
|
|||
</li>
|
||||
@endif
|
||||
@endforeach
|
||||
</ul>
|
||||
@hook('header.menu.after')
|
||||
</ul>
|
||||
|
|
|
|||
Loading…
Reference in New Issue