Skip to content

Commit

Permalink
feat: item could be added by a route
Browse files Browse the repository at this point in the history
 - combined the adding methods into a general add method
  • Loading branch information
red-freak committed Oct 6, 2023
1 parent 011ca49 commit e32b1f1
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 16 deletions.
6 changes: 6 additions & 0 deletions src/Facades/Menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@

/**
* @method static MenuDto add(string $menuName, array $menuData = [])
* @method static MenuDto get(string $menuName)
* @method static bool hasMenu(string $menuName)
* @method static array menus()
*
* @see MenuManager
*/
class Menu extends Facade
{
public const KEY_ITEM_LABEL = 'label';
public const KEY_ITEM_LINK = 'link';
public const KEY_ITEM_ROUTE = 'route';
public const KEY_ITEM_MODEL = 'model';

protected static function getFacadeAccessor(): string
{
return 'red-freak::menu';
Expand Down
9 changes: 8 additions & 1 deletion src/Item.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public function __construct(string $label, ?string $link = null, Menu|ResourceIt
$this->setParent($parent);
}
$this->label = $label;
$this->link = $link;
$this->setLink($link);
}

public function setParent(Menu|ResourceItem $parent): self
Expand All @@ -24,6 +24,13 @@ public function setParent(Menu|ResourceItem $parent): self
return $this;
}

public function setLink(?string $link = null): self
{
$this->link = $link;

return $this;
}

public function label(): string
{
if ($this->renderOptions()->useLabelsAsTranslationKeys()) {
Expand Down
76 changes: 71 additions & 5 deletions src/Menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
namespace RedFreak\Menu;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Routing\Route;
use Illuminate\Support\Collection;
use RedFreak\Menu\Facades\Menu as MenuFacade;

class Menu extends Item
{
Expand All @@ -25,21 +27,58 @@ public function hasChildren(): bool
return $this->items->isNotEmpty();
}

public function add(Item $item): self
/**
* Adds an item to the menu.
*
* @param Item|array $item The item or an array of suitable values (@see MenuFacade).
*
* @return $this
*/
public function add(Item|array $item): self
{
// logic
if (is_a($item, Item::class)) {
return $this->addItem($item);
}
if ($link = data_get($item, MenuFacade::KEY_ITEM_LINK)) {
return $this->addItem(new Item(data_get($item, MenuFacade::KEY_ITEM_LABEL), $link, $this));
}

if ($route = data_get($item, MenuFacade::KEY_ITEM_ROUTE)) {
return $this->addItemByRoute($route, $item);
}

if ($model = data_get($item, MenuFacade::KEY_ITEM_MODEL)) {
return $this->addItemByModel($model, $item);
}

throw new \InvalidArgumentException('unknown options-set');
}

/**
* Appends an Item to the Menu. (sets itself as parent)
*
* @param Item $item
*
* @return $this
*/
public function addItem(Item $item): self
{
$item->setParent($this);

$this->items->put($item->label(), $item);

return $this;
}

/**
* Creates navigation items by a model.
* Creates a submenu by a model.
*
* @param class-string|Model $model
*
* @return $this
*/
public function addFromModel(string|Model $model): self
public function addItemByModel(string|Model $model): self
{
if (is_string($model)) {
$model = new $model();
Expand All @@ -50,6 +89,35 @@ public function addFromModel(string|Model $model): self
return $this;
}

/**
* Appends an item to the Menu a Route o route name.
*
* @param string|Route $routeToUse
* @param array $options
*
* @return $this
*/
public function addItemByRoute(string|Route $routeToUse, array $options = []): self
{
$route = is_string($routeToUse) ? app('router')->getRoutes()->getByName($routeToUse) : $routeToUse;

$label = data_get($options, MenuFacade::KEY_ITEM_LABEL) ?? config('menus.default_translation_prefix', 'menu.label.').$route->getName();

$this->items->put($label, new Item($label, route($route->getName()), $this));

return $this;
}

public function hasItem(string $label): bool
{
return $this->items->has($label);
}

public function items(): Collection
{
return $this->items;
}

public function type(): string
{
return $this->renderOptions->style();
Expand All @@ -59,8 +127,6 @@ public function render(int $currentLevel = 0): string
{
$html = str_pad('', $currentLevel*2, ' ') . '<ul class="' . implode(' ', RenderOptions::classes($this, $currentLevel++)) . '">' . PHP_EOL;

// dd($this->items->toArray());

foreach ($this->items as $item) {
$html .= str_pad('', $currentLevel*2, ' ') . '<li class="'.implode(' ', RenderOptions::classes($item, $currentLevel)).'">'.PHP_EOL;
$html .= $item->render($currentLevel+1);
Expand Down
7 changes: 6 additions & 1 deletion src/MenuManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ public function add(string $menuName, array $menuData = []): Menu
return $menu;
}

public function get(string $menuName): Menu
{
return $this->menus->get($menuName);
}

protected function registerMenu(string $menuName, $menuData = []): Menu
{
if ($this->menus->has($menuName)) {
return $this->menus->get($menuName);
return $this->get($menuName);
}

$menu = new Menu($menuName, $menuData);
Expand Down
4 changes: 3 additions & 1 deletion tests/Models/TestModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@

class TestModel extends Model
{

protected $attributes = [
'id' => 1,
];
}
4 changes: 3 additions & 1 deletion tests/Models/TestModel2.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@

class TestModel2 extends Model
{

protected $attributes = [
'id' => 1,
];
}
78 changes: 78 additions & 0 deletions tests/Unit/ItemAddTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

use Illuminate\Support\Facades\Route;
use RedFreak\Menu\Facades\Menu;
use RedFreak\Menu\Item;
use RedFreak\Menu\MenuServiceProvider;
use RedFreak\Menu\Tests\Controllers\TestController;
use RedFreak\Menu\Tests\Models\TestModel;

beforeEach(function() {
app()->register(MenuServiceProvider::class);
Menu::add('test', []);
});

it('throws an exception if the add method is called with an invalid value', function () {
Menu::get('test')->add(['foo' => 'bar']);
})->throws(InvalidArgumentException::class);

it('can add an item-object to a menu', function () {
Menu::get('test')->add(new Item('test-item', 'test-link'));

expect(Menu::get('test')->items())->toHaveCount(1);
expect(Menu::get('test')->items())->toHaveKey('test-item');
expect(Menu::get('test')->items()['test-item']->label())->toBe('test-item');
expect(Menu::get('test')->hasItem('test-item'))->toBeTrue();
});

it('can add an item by an array to a menu', function () {
Menu::get('test')->add([
Menu::KEY_ITEM_LABEL => 'test-item',
Menu::KEY_ITEM_LINK => 'test-link',
]);

expect(Menu::get('test')->items())->toHaveCount(1);
expect(Menu::get('test')->items())->toHaveKey('test-item');
expect(Menu::get('test')->items()['test-item']->label())->toBe('test-item');
expect(Menu::get('test')->hasItem('test-item'))->toBeTrue();
});

it('can add an item by a route to an menu', function () {
$route = Route::get('test-route', fn() => 'test')->name('test-route');

Menu::get('test')->add([Menu::KEY_ITEM_ROUTE => $route]);

$expectedLabel = config('menus.default_translation_prefix', 'menu.label.') . 'test-route';
expect(Menu::get('test')->items())->toHaveCount(1);
expect(Menu::get('test')->items())->toHaveKey($expectedLabel);
expect(Menu::get('test')->items()[$expectedLabel]->label())->toBe($expectedLabel);
expect(Menu::get('test')->hasItem($expectedLabel))->toBeTrue();
});

it('can add an item by a route name to an menu', function () {
// we have to do it this way to set the name in the test context correctly
Route::group(['as' => 'test-route'], function() {
Route::get('test-route', fn() => 'test');
});

Menu::get('test')->add([Menu::KEY_ITEM_ROUTE => 'test-route']);

$expectedLabel = config('menus.default_translation_prefix', 'menu.label.') . 'test-route';
expect(Menu::get('test')->items())->toHaveCount(1);
expect(Menu::get('test')->items())->toHaveKey($expectedLabel);
expect(Menu::get('test')->items()[$expectedLabel]->label())->toBe($expectedLabel);
expect(Menu::get('test')->hasItem($expectedLabel))->toBeTrue();
});

it('can add an item by a model to an menu', function () {
$model = new TestModel();
Route::resource($model->getTable(), TestController::class);

Menu::get('test')->add([Menu::KEY_ITEM_MODEL => $model]);

$expectedLabel = $model::class . '::' . $model->getKey();
expect(Menu::get('test')->items())->toHaveCount(1);
expect(Menu::get('test')->items())->toHaveKey($expectedLabel);
expect(Menu::get('test')->items()[$expectedLabel]->label())->toBe(config('menus.default_translation_prefix', 'menu.label.') . $model->getTable() . '.index');
expect(Menu::get('test')->hasItem($expectedLabel))->toBeTrue();
});
11 changes: 4 additions & 7 deletions tests/Unit/ItemResourceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@
});

it('can attach a model based items', function () {
app()->register(MenuServiceProvider::class);
$model = new TestModel();
Route::resource($model->getTable(), TestController::class);

expect(Menu::add('test', [
RenderOptions::KEY_LINK_SUBMENU_ANCHOR => true, // whether to set the link at the submenu anchor or not
])->addFromModel(TestModel::class)->render())->toBe(
])->addItemByModel(TestModel::class)->render())->toBe(
'<ul class="menu menu-level-0">'.PHP_EOL
. ' <li class="sub-menu menu-item menu-level-1">'.PHP_EOL
. ' <a href="http://localhost/test_models">menu.label.test_models.index</a>'.PHP_EOL
Expand All @@ -37,7 +36,7 @@

expect(Menu::add('test2', [
RenderOptions::KEY_LINK_SUBMENU_ANCHOR => false, // whether to set the link at the submenu anchor or not
])->addFromModel($model)->render())->toBe(
])->addItemByModel($model)->render())->toBe(
'<ul class="menu menu-level-0">'.PHP_EOL
. ' <li class="sub-menu menu-item menu-level-1">'.PHP_EOL
. ' menu.label.test_models.index'.PHP_EOL
Expand All @@ -55,13 +54,12 @@
});

it('overrides a before added model', function() {
app()->register(MenuServiceProvider::class);
$model = new TestModel();
Route::resource($model->getTable(), TestController::class);

Menu::add('test', [
RenderOptions::KEY_LINK_SUBMENU_ANCHOR => false, // whether to set the link at the submenu anchor or not
])->addFromModel($model)->addFromModel($model);
])->addItemByModel($model)->addItemByModel($model);

expect(Menu::add('test')->render())->toBe(
'<ul class="menu menu-level-0">'.PHP_EOL
Expand All @@ -81,15 +79,14 @@
});

it('can add more than one model', function() {
app()->register(MenuServiceProvider::class);
$model = new TestModel();
Route::resource($model->getTable(), TestController::class);
$model2 = new TestModel2();
Route::resource($model2->getTable(), TestController::class);

Menu::add('test', [
RenderOptions::KEY_LINK_SUBMENU_ANCHOR => false, // whether to set the link at the submenu anchor or not
])->addFromModel($model)->addFromModel($model2);
])->addItemByModel($model)->addItemByModel($model2);

expect(Menu::add('test')->render())->toBe(
'<ul class="menu menu-level-0">'.PHP_EOL
Expand Down

0 comments on commit e32b1f1

Please sign in to comment.