summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Concerns/CreatesGroups.php28
-rw-r--r--src/Concerns/CreatesRoutes.php41
-rw-r--r--src/Concerns/HasAncestors.php37
-rw-r--r--src/Group.php65
-rw-r--r--src/GroupDefinition.php22
-rw-r--r--src/PathSegment.php29
-rw-r--r--src/Router.php43
-rw-r--r--tests/Unit/GroupDefinitionTest.php15
-rw-r--r--tests/Unit/GroupTest.php81
9 files changed, 301 insertions, 60 deletions
diff --git a/src/Concerns/CreatesGroups.php b/src/Concerns/CreatesGroups.php
new file mode 100644
index 0000000..e6e4588
--- /dev/null
+++ b/src/Concerns/CreatesGroups.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Lightscale\Router\Concerns;
+
+use Lightscale\Router\Group;
+
+trait CreatesGroups
+{
+ public function prefix(string $value): Group
+ {
+ return new Group(
+ router: $this->getRouter(),
+ parent: $this->getGroup(),
+ prefix: $value,
+ );
+ }
+
+ public function name(string $value): Group
+ {
+ return new Group(
+ router: $this->getRouter(),
+ parent: $this->getGroup(),
+ name: $value,
+ );
+ }
+}
diff --git a/src/Concerns/CreatesRoutes.php b/src/Concerns/CreatesRoutes.php
new file mode 100644
index 0000000..6a8f65b
--- /dev/null
+++ b/src/Concerns/CreatesRoutes.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Lightscale\Router\Concerns;
+
+use Lightscale\Router\Enums\HttpMethod;
+use Lightscale\Router\RouteDefinition;
+
+trait CreatesRoutes
+{
+ public function get(string $path, callable $handler): RouteDefinition
+ {
+ return $this->make(HttpMethod::Get, $path, $handler);
+ }
+
+ public function post(string $path, callable $handler): RouteDefinition
+ {
+ return $this->make(HttpMethod::Post, $path, $handler);
+ }
+
+ public function put(string $path, callable $handler): RouteDefinition
+ {
+ return $this->make(HttpMethod::Put, $path, $handler);
+ }
+
+ public function patch(string $path, callable $handler): RouteDefinition
+ {
+ return $this->make(HttpMethod::Patch, $path, $handler);
+ }
+
+ public function delete(string $path, callable $handler): RouteDefinition
+ {
+ return $this->make(HttpMethod::Delete, $path, $handler);
+ }
+
+ public function any(string $path, callable $handler): RouteDefinition
+ {
+ return $this->make(HttpMethod::Any, $path, $handler);
+ }
+}
diff --git a/src/Concerns/HasAncestors.php b/src/Concerns/HasAncestors.php
new file mode 100644
index 0000000..248e9c8
--- /dev/null
+++ b/src/Concerns/HasAncestors.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Lightscale\Router\Concerns;
+
+trait HasAncestors
+{
+ private const MAX_ANCESTORY_DEPTH = 100;
+
+ /** @return self[] */
+ public function getAncestors(): array
+ {
+ $results = [];
+
+ $count = 0;
+ $instance = $this->parent;
+ while (
+ null !== $instance
+ && $count++ < self::MAX_ANCESTORY_DEPTH
+ ) {
+ $results[] = $instance;
+ $instance = $instance->parent;
+ }
+
+ return array_reverse($results);
+ }
+
+ /** @return self[] */
+ public function getAncestorsAndSelf(): array
+ {
+ $results = $this->getAncestors();
+ $results[] = $this;
+
+ return $results;
+ }
+}
diff --git a/src/Group.php b/src/Group.php
new file mode 100644
index 0000000..4e6c71e
--- /dev/null
+++ b/src/Group.php
@@ -0,0 +1,65 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Lightscale\Router;
+
+class Group
+{
+ use Concerns\HasAncestors;
+
+ public function __construct(
+ private Router $router,
+ private ?Group $parent,
+ private ?string $prefix = null,
+ private ?string $name = null,
+ ) {
+ }
+
+ public function getRouter(): Router
+ {
+ return $this->router;
+ }
+
+ public function getParent(): ?static
+ {
+ return $this->parent;
+ }
+
+ public function prefix(string $value): static
+ {
+ $this->prefix = $value;
+
+ return $this;
+ }
+
+ public function getPrefix(): ?string
+ {
+ return $this->prefix;
+ }
+
+ public function getPath(): string
+ {
+
+ }
+
+ public function name(string $value): static
+ {
+ $this->name = $value;
+
+ return $this;
+ }
+
+ public function getName(): ?string
+ {
+ return $this->name;
+ }
+
+ /** @param callable(self): void $cb */
+ public function group(callable $cb): void
+ {
+ ($cb)(new GroupDefinition(
+ $this
+ ));
+ }
+}
diff --git a/src/GroupDefinition.php b/src/GroupDefinition.php
index 444ba91..41e359d 100644
--- a/src/GroupDefinition.php
+++ b/src/GroupDefinition.php
@@ -4,10 +4,30 @@ declare(strict_types=1);
namespace Lightscale\Router;
+use Lightscale\Router\Enums\HttpMethod;
+
class GroupDefinition
{
+ use Concerns\CreatesGroups;
+ use Concerns\CreatesRoutes;
+
public function __construct(
- private Router $router,
+ private Group $group,
) {
}
+
+ public function make(HttpMethod $method, string $path, callable $handler): RouteDefinition
+ {
+
+ }
+
+ private function getGroup(): Group
+ {
+ return $this->group;
+ }
+
+ private function getRouter(): Router
+ {
+ return $this->group->getRouter();
+ }
}
diff --git a/src/PathSegment.php b/src/PathSegment.php
index 2fc4cbb..01f3fc7 100644
--- a/src/PathSegment.php
+++ b/src/PathSegment.php
@@ -10,7 +10,7 @@ use Lightscale\Router\Exceptions\MissingParameterException;
class PathSegment
{
- private const MAX_ANCESTORY_DEPTH = 100;
+ use Concerns\HasAncestors;
/** @var array<string, self> */
protected array $children;
@@ -73,33 +73,6 @@ class PathSegment
return $this->parent;
}
- /** @return self[] */
- public function getAncestors(): array
- {
- $results = [];
-
- $count = 0;
- $instance = $this->parent;
- while (
- null !== $instance
- && $count++ < self::MAX_ANCESTORY_DEPTH
- ) {
- $results[] = $instance;
- $instance = $instance->parent;
- }
-
- return array_reverse($results);
- }
-
- /** @return self[] */
- public function getAncestorsAndSelf(): array
- {
- $results = $this->getAncestors();
- $results[] = $this;
-
- return $results;
- }
-
public function addChild(self $segment): void
{
$segment->setParent($this);
diff --git a/src/Router.php b/src/Router.php
index b219651..d6b255a 100644
--- a/src/Router.php
+++ b/src/Router.php
@@ -12,6 +12,9 @@ use Psr\Http\Message\ResponseInterface;
class Router
{
+ use Concerns\CreatesRoutes;
+ use Concerns\CreatesGroups;
+
private PathSegment $root;
private Strategy $strategy;
@@ -24,6 +27,16 @@ class Router
$this->strategy = new BasicStrategy();
}
+ private function getRouter(): self
+ {
+ return $this;
+ }
+
+ private function getGroup(): null
+ {
+ return null;
+ }
+
public function getStrategy(): Strategy
{
return $this->strategy;
@@ -136,36 +149,6 @@ class Router
return new RouteDefinition($this, $route);
}
- public function get(string $path, callable $handler): RouteDefinition
- {
- return $this->make(HttpMethod::Get, $path, $handler);
- }
-
- public function post(string $path, callable $handler): RouteDefinition
- {
- return $this->make(HttpMethod::Post, $path, $handler);
- }
-
- public function put(string $path, callable $handler): RouteDefinition
- {
- return $this->make(HttpMethod::Put, $path, $handler);
- }
-
- public function patch(string $path, callable $handler): RouteDefinition
- {
- return $this->make(HttpMethod::Patch, $path, $handler);
- }
-
- public function delete(string $path, callable $handler): RouteDefinition
- {
- return $this->make(HttpMethod::Delete, $path, $handler);
- }
-
- public function any(string $path, callable $handler): RouteDefinition
- {
- return $this->make(HttpMethod::Any, $path, $handler);
- }
-
public function addNamedRoute(string $name, Route $route): void
{
$this->namedRoutes[$name] = $route;
diff --git a/tests/Unit/GroupDefinitionTest.php b/tests/Unit/GroupDefinitionTest.php
index 3a064a1..59a8d3d 100644
--- a/tests/Unit/GroupDefinitionTest.php
+++ b/tests/Unit/GroupDefinitionTest.php
@@ -2,9 +2,22 @@
declare(strict_types=1);
+use Lightscale\Router\Group;
use Lightscale\Router\GroupDefinition;
use Lightscale\Router\Router;
+$make = fn () => new GroupDefinition(new Group(new Router(), null));
+
it('initializes')
- ->expect(fn () => new GroupDefinition(new Router()))
+ ->expect(fn () => $make())
->toBeInstanceOf(GroupDefinition::class);
+
+it('creates group with prefix')
+ ->expect(fn () => $make()->prefix('/test'))
+ ->toBeInstanceOf(Group::class)
+ ->getPrefix()->toBe('/test');
+
+it('creates group with name')
+ ->expect(fn () => $make()->name('name'))
+ ->toBeInstanceOf(Group::class)
+ ->getName()->toBe('name');
diff --git a/tests/Unit/GroupTest.php b/tests/Unit/GroupTest.php
new file mode 100644
index 0000000..3b17f19
--- /dev/null
+++ b/tests/Unit/GroupTest.php
@@ -0,0 +1,81 @@
+<?php
+
+declare(strict_types=1);
+
+use Lightscale\Router\Group;
+use Lightscale\Router\GroupDefinition;
+use Lightscale\Router\Router;
+use Lightscale\Router\Test\Utils\TestCallable;
+
+$make = fn(?Group $parent = null, ...$args) => new Group(new Router, $parent, ...$args);
+
+it('initializes with null defaults')
+ ->expect(fn() => $make())
+ ->toBeInstanceOf(Group::class)
+ ->getName()->toBeNull()
+ ->getPrefix()->toBeNull();
+
+it('initializes with values')
+ ->expect(new Group(new Router, null, '/test-path', 'test-name'))
+ ->toBeInstanceOf(Group::class)
+ ->getName()->toBe('test-name')
+ ->getPrefix()->toBe('/test-path');
+
+it('can set/get prefix')
+ ->expect(fn() => $make()->prefix('/test'))
+ ->toBeInstanceOf(Group::class)
+ ->getPrefix()->toBe('/test');
+
+it('can set/get name')
+ ->expect(fn() => $make()->name('test'))
+ ->toBeInstanceOf(Group::class)
+ ->getName()->toBe('test');
+
+it('calls group call back with definition', function () use ($make) {
+ $cb = TestCallable::make(fn() => null);
+ $make()->group($cb);
+
+ $cb->assertCalled();
+ $call = $cb->getLastCall();
+
+ expect($call?->args)->{0}->toBeInstanceOf(GroupDefinition::class);
+});
+
+it('can have a parent')
+ ->expect(fn () => $make($make())->getParent())
+ ->toBeInstanceOf(Group::class);
+
+it('can have a null parent')
+ ->expect(fn () => $make(null)->getParent())
+ ->toBeNull();
+
+it('can get all ancestors')
+ ->expect(fn () => $make($make($make()))->getAncestors())
+ ->toBeArray()
+ ->toContainOnlyInstancesOf(Group::class)
+ ->toHaveCount(2);
+
+it('order all ancestors root first', function () use($make) {
+ $g3 = $make($g2 = $make($g1 = $make()));
+
+ expect($g3->getAncestors())
+ ->toHaveCount(2)
+ ->{0}->toBe($g1)
+ ->{1}->toBe($g2);
+});
+
+it('can get all ancestors and self')
+ ->expect(fn () => $make($make($make()))->getAncestorsAndSelf())
+ ->toBeArray()
+ ->toContainOnlyInstancesOf(Group::class)
+ ->toHaveCount(3);
+
+it('order all ancestors and self root first', function () use($make) {
+ $g3 = $make($g2 = $make($g1 = $make()));
+
+ expect($g3->getAncestorsAndSelf())
+ ->toHaveCount(3)
+ ->{0}->toBe($g1)
+ ->{1}->toBe($g2)
+ ->{2}->toBe($g3);
+});