Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ class MainMenu extends \Tatter\Menus\Menu
public function get(): string
{
return $this->builder
->link('/', 'Home')
->link('/about', 'About')
->link(site_url('/'), 'Home')
->link(site_url('/about'), 'About')
->html('<hr>')
->link('/contact', 'Contact')
->link(site_url('/contact'), 'Contact')
->render();
}
}
Expand All @@ -68,6 +68,11 @@ class FruitMenu extends \Tatter\Menus\Menu
}
```

Note: `$builder` is initialized with "set active" to the current URL. You may call `setActive()`
again to remove or change the active menu item. Due to a limitation in `Spatie\Menu` with mixing
relative and absolute URLs you must supply full URL values (e.g. with `site_url()`) to your
`Menu` if you want to use this default "active" URL.

### Deploying

Since `Menu->get()` returns a `string` it can be used in your view or layout files as is.
Expand Down
23 changes: 17 additions & 6 deletions src/Menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

use CodeIgniter\HTTP\URI;
use Spatie\Menu\Menu as BaseMenu;
use Tatter\Menus\Interfaces\MenuInterface;

/**
* Menu Class
Expand All @@ -22,21 +21,33 @@ abstract class Menu
/**
* Initializes the Spatie Menu and
* sets the current URL to be "active".
* Calls any additional functions
* from traits.
*
* @param BaseMenu|null $builder
*/
public function __construct(BaseMenu $builder = null)
{
$this->builder = $builder ?? BaseMenu::new()->setActive(
current_url(true)->getPath(),
(new URI(base_url()))->getPath() ?? '/'
);
$this->builder = $builder ?? BaseMenu::new()->setActive(
current_url(),
(new URI(base_url()))->getPath() ?? '/'
);

foreach (class_uses_recursive($this) as $trait)
{
$method = 'apply' . class_basename($trait);

if (method_exists($this, $method))
{
$this->$method();
}
}
}

/**
* Returns the underlying Spatie Menu.
*/
public function builder(): BaseMenu
public function builder(): BaseMenu
{
return $this->builder;
}
Expand Down
22 changes: 22 additions & 0 deletions src/Traits/Bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php namespace Tatter\Menus\Traits;

use Spatie\Menu\Link;

/**
* Bootstrap Styler Trait
*
* Applies CSS classes & styles
* to make a Bootstrap-style Menu.
*
* @mixin \Tatter\Menus\Menu
*/
trait Bootstrap
{
protected function applyBootstrap(): void
{
$this->builder->registerFilter(function (Link $link) {
$link->addParentClass('nav-item');
$link->addClass('nav-link');
});
}
}
15 changes: 15 additions & 0 deletions tests/_support/MenusTestCase.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<?php namespace Tests\Support;

use CodeIgniter\Config\Factories;
use CodeIgniter\HTTP\URI;
use CodeIgniter\Test\CIUnitTestCase;
use Config\App;
use Config\Services;
use Tatter\Menus\Config\Menus as MenusConfig;
use Tests\Support\Menus\NotMenu;
use Tests\Support\Menus\TestMenu;
Expand All @@ -19,6 +22,18 @@ protected function setUp(): void
{
parent::setUp();

// Use some standard settings that affect URLs
$config = new App();
$config->baseURL = 'http://example.com';
$config->indexPage = '';
Factories::injectMock('config', 'App', $config);

// Set a current URL for checking "active" links
$request = Services::request();
$request->uri = new URI(site_url('current'));
Services::injectMock('request', $request);

// Create some Menu aliases for testing
$config = new MenusConfig();
$config->aliases = [
'test' => TestMenu::class,
Expand Down
16 changes: 5 additions & 11 deletions tests/menu/MenuTest.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<?php namespace Tests\Support;

use CodeIgniter\HTTP\URI;
use Config\Services;
use Spatie\Menu\Menu as BaseMenu;
use Tatter\Menus\Menu;
use Tests\Support\MenusTestCase;
Expand All @@ -21,17 +19,13 @@ protected function setUp(): void
{
parent::setUp();

$request = Services::request();
$request->uri = new URI('http://example.com/bulgar');
Services::injectMock('request', $request);

$this->menu = new class extends Menu {

public function get(): string
{
return $this->builder
->link('/', 'Home')
->link('/bulgar', 'Grain')
->link(site_url('/'), 'Home')
->link(site_url('/current'), 'Grain')
->render();
}
};
Expand All @@ -46,7 +40,7 @@ public function testGetBuilder()

public function testUsesBuilder()
{
$menu = new class(BaseMenu::new()->link('/home', 'asparagus')) extends Menu {
$menu = new class(BaseMenu::new()->link(site_url('/home'), 'asparagus')) extends Menu {

public function get(): string
{
Expand All @@ -56,12 +50,12 @@ public function get(): string

$result = $menu->get();

$this->assertSame('<ul><li><a href="/home">asparagus</a></li></ul>', $result);
$this->assertSame('<ul><li><a href="http://example.com/home">asparagus</a></li></ul>', $result);
}

public function testGetUsesCurrentUrl()
{
$expected = '<ul><li><a href="/">Home</a></li><li class="active exact-active"><a href="/bulgar">Grain</a></li></ul>';
$expected = '<ul><li><a href="http://example.com/">Home</a></li><li class="active exact-active"><a href="http://example.com/current">Grain</a></li></ul>';
$result = $this->menu->get();

$this->assertSame($expected, $result);
Expand Down
29 changes: 29 additions & 0 deletions tests/menu/TraitsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php namespace Tests\Support;

use Tatter\Menus\Menu;
use Tatter\Menus\Traits\Bootstrap;
use Tests\Support\MenusTestCase;

class TraitsTest extends MenusTestCase
{
public function testBootstrapAppliesClasses()
{
$menu = new class extends Menu {

use Bootstrap;

public function get(): string
{
return $this->builder
->link(site_url('/'), 'Home')
->link(site_url('/current'), 'Grain')
->render();
}
};

$result = $menu->get();

$this->assertStringContainsString('li class="nav-item"', $result);
$this->assertStringContainsString('class="nav-link"', $result);
}
}