mirror of
https://github.com/flarum/core.git
synced 2025-07-22 17:21:27 +02:00
Allow overriding routes (#2577)
This commit is contained in:
@@ -31,6 +31,7 @@ class Frontend implements ExtenderInterface
|
|||||||
private $css = [];
|
private $css = [];
|
||||||
private $js;
|
private $js;
|
||||||
private $routes = [];
|
private $routes = [];
|
||||||
|
private $removedRoutes = [];
|
||||||
private $content = [];
|
private $content = [];
|
||||||
|
|
||||||
public function __construct(string $frontend)
|
public function __construct(string $frontend)
|
||||||
@@ -59,6 +60,13 @@ class Frontend implements ExtenderInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function removeRoute(string $name)
|
||||||
|
{
|
||||||
|
$this->removedRoutes[] = compact('name');
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callable|string $callback
|
* @param callable|string $callback
|
||||||
* @return $this
|
* @return $this
|
||||||
@@ -141,7 +149,7 @@ class Frontend implements ExtenderInterface
|
|||||||
|
|
||||||
private function registerRoutes(Container $container)
|
private function registerRoutes(Container $container)
|
||||||
{
|
{
|
||||||
if (empty($this->routes)) {
|
if (empty($this->routes) && empty($this->removedRoutes)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,6 +159,10 @@ class Frontend implements ExtenderInterface
|
|||||||
/** @var RouteHandlerFactory $factory */
|
/** @var RouteHandlerFactory $factory */
|
||||||
$factory = $container->make(RouteHandlerFactory::class);
|
$factory = $container->make(RouteHandlerFactory::class);
|
||||||
|
|
||||||
|
foreach ($this->removedRoutes as $route) {
|
||||||
|
$collection->removeRoute('GET', $route['name']);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($this->routes as $route) {
|
foreach ($this->routes as $route) {
|
||||||
$collection->get(
|
$collection->get(
|
||||||
$route['path'],
|
$route['path'],
|
||||||
|
@@ -19,6 +19,7 @@ class Routes implements ExtenderInterface
|
|||||||
private $appName;
|
private $appName;
|
||||||
|
|
||||||
private $routes = [];
|
private $routes = [];
|
||||||
|
private $removedRoutes = [];
|
||||||
|
|
||||||
public function __construct($appName)
|
public function __construct($appName)
|
||||||
{
|
{
|
||||||
@@ -62,9 +63,16 @@ class Routes implements ExtenderInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function remove(string $method, string $name)
|
||||||
|
{
|
||||||
|
$this->removedRoutes[] = compact('method', 'name');
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function extend(Container $container, Extension $extension = null)
|
public function extend(Container $container, Extension $extension = null)
|
||||||
{
|
{
|
||||||
if (empty($this->routes)) {
|
if (empty($this->routes) && empty($this->removedRoutes)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +82,10 @@ class Routes implements ExtenderInterface
|
|||||||
/** @var RouteHandlerFactory $factory */
|
/** @var RouteHandlerFactory $factory */
|
||||||
$factory = $container->make(RouteHandlerFactory::class);
|
$factory = $container->make(RouteHandlerFactory::class);
|
||||||
|
|
||||||
|
foreach ($this->removedRoutes as $route) {
|
||||||
|
$collection->removeRoute($route['method'], $route['name']);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($this->routes as $route) {
|
foreach ($this->routes as $route) {
|
||||||
$collection->addRoute(
|
$collection->addRoute(
|
||||||
$route['method'],
|
$route['method'],
|
||||||
|
@@ -203,8 +203,8 @@ class ForumServiceProvider extends AbstractServiceProvider
|
|||||||
$factory = $this->app->make(RouteHandlerFactory::class);
|
$factory = $this->app->make(RouteHandlerFactory::class);
|
||||||
$defaultRoute = $this->app->make('flarum.settings')->get('default_route');
|
$defaultRoute = $this->app->make('flarum.settings')->get('default_route');
|
||||||
|
|
||||||
if (isset($routes->getRouteData()[0]['GET'][$defaultRoute]['handler'])) {
|
if (isset($routes->getRoutes()['GET'][$defaultRoute]['handler'])) {
|
||||||
$toDefaultController = $routes->getRouteData()[0]['GET'][$defaultRoute]['handler'];
|
$toDefaultController = $routes->getRoutes()['GET'][$defaultRoute]['handler'];
|
||||||
} else {
|
} else {
|
||||||
$toDefaultController = $factory->toForum(Content\Index::class);
|
$toDefaultController = $factory->toForum(Content\Index::class);
|
||||||
}
|
}
|
||||||
|
@@ -30,6 +30,16 @@ class RouteCollection
|
|||||||
*/
|
*/
|
||||||
protected $routeParser;
|
protected $routeParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $routes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $pendingRoutes = [];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->dataGenerator = new DataGenerator\GroupCountBased;
|
$this->dataGenerator = new DataGenerator\GroupCountBased;
|
||||||
@@ -63,19 +73,50 @@ class RouteCollection
|
|||||||
|
|
||||||
public function addRoute($method, $path, $name, $handler)
|
public function addRoute($method, $path, $name, $handler)
|
||||||
{
|
{
|
||||||
$routeDatas = $this->routeParser->parse($path);
|
if (isset($this->routes[$method][$name])) {
|
||||||
|
throw new \RuntimeException("Route $name on method $method already exists");
|
||||||
foreach ($routeDatas as $routeData) {
|
|
||||||
$this->dataGenerator->addRoute($method, $routeData, ['name' => $name, 'handler' => $handler]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->reverse[$name] = $routeDatas;
|
$this->routes[$method][$name] = $this->pendingRoutes[$method][$name] = compact('path', 'handler');
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function removeRoute(string $method, string $name): self
|
||||||
|
{
|
||||||
|
unset($this->routes[$method][$name], $this->pendingRoutes[$method][$name]);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyRoutes(): void
|
||||||
|
{
|
||||||
|
foreach ($this->pendingRoutes as $method => $routes) {
|
||||||
|
foreach ($routes as $name => $route) {
|
||||||
|
$routeDatas = $this->routeParser->parse($route['path']);
|
||||||
|
|
||||||
|
foreach ($routeDatas as $routeData) {
|
||||||
|
$this->dataGenerator->addRoute($method, $routeData, ['name' => $name, 'handler' => $route['handler']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->reverse[$name] = $routeDatas;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pendingRoutes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRoutes(): array
|
||||||
|
{
|
||||||
|
return $this->routes;
|
||||||
|
}
|
||||||
|
|
||||||
public function getRouteData()
|
public function getRouteData()
|
||||||
{
|
{
|
||||||
|
if (! empty($this->pendingRoutes)) {
|
||||||
|
$this->applyRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
return $this->dataGenerator->getData();
|
return $this->dataGenerator->getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +129,10 @@ class RouteCollection
|
|||||||
|
|
||||||
public function getPath($name, array $parameters = [])
|
public function getPath($name, array $parameters = [])
|
||||||
{
|
{
|
||||||
|
if (! empty($this->pendingRoutes)) {
|
||||||
|
$this->applyRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($this->reverse[$name])) {
|
if (isset($this->reverse[$name])) {
|
||||||
$maxMatches = 0;
|
$maxMatches = 0;
|
||||||
$matchingParts = $this->reverse[$name][0];
|
$matchingParts = $this->reverse[$name][0];
|
||||||
|
@@ -47,6 +47,41 @@ class RoutesTest extends TestCase
|
|||||||
$this->assertEquals(200, $response->getStatusCode());
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
$this->assertEquals('Hello Flarumites!', $response->getBody());
|
$this->assertEquals('Hello Flarumites!', $response->getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function existing_route_can_be_removed()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\Routes('api'))
|
||||||
|
->remove('GET', 'forum.show')
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(404, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_route_can_override_existing_route_if_removed()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\Routes('api'))
|
||||||
|
->remove('GET', 'forum.show')
|
||||||
|
->get('/', 'forum.show', CustomRoute::class)
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals('Hello Flarumites!', $response->getBody());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomRoute implements RequestHandlerInterface
|
class CustomRoute implements RequestHandlerInterface
|
||||||
|
47
framework/core/tests/unit/Http/RouteCollectionTest.php
Normal file
47
framework/core/tests/unit/Http/RouteCollectionTest.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Tests\unit\Http;
|
||||||
|
|
||||||
|
use Flarum\Http\RouteCollection;
|
||||||
|
use Flarum\Tests\unit\TestCase;
|
||||||
|
|
||||||
|
class RouteCollectionTest extends TestCase
|
||||||
|
{
|
||||||
|
/** @test */
|
||||||
|
public function can_add_routes()
|
||||||
|
{
|
||||||
|
$routeCollection = (new RouteCollection)
|
||||||
|
->addRoute('GET', '/index', 'index', function () {
|
||||||
|
echo 'index';
|
||||||
|
})
|
||||||
|
->addRoute('DELETE', '/posts', 'forum.posts.delete', function () {
|
||||||
|
echo 'delete posts';
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->assertEquals('/index', $routeCollection->getPath('index'));
|
||||||
|
$this->assertEquals('/posts', $routeCollection->getPath('forum.posts.delete'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function can_add_routes_late()
|
||||||
|
{
|
||||||
|
$routeCollection = (new RouteCollection)->addRoute('GET', '/index', 'index', function () {
|
||||||
|
echo 'index';
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->assertEquals('/index', $routeCollection->getPath('index'));
|
||||||
|
|
||||||
|
$routeCollection->addRoute('DELETE', '/posts', 'forum.posts.delete', function () {
|
||||||
|
echo 'delete posts';
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->assertEquals('/posts', $routeCollection->getPath('forum.posts.delete'));
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user