Created
July 31, 2020 20:08
-
-
Save kmuenkel/d654ed37e52e8fd4189c9ef60302b8d1 to your computer and use it in GitHub Desktop.
Define Laravel routes by query parameters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Http\Middleware; | |
use Closure; | |
use Illuminate\Support\Arr; | |
use Illuminate\Http\Request; | |
use InvalidArgumentException; | |
use Illuminate\Routing\Route; | |
use Illuminate\Routing\Router; | |
use Illuminate\Support\Facades\Auth; | |
use App\Exceptions\MissingPathParamException; | |
/** | |
* Class RedirectByQuery | |
* @package App\Http\Middleware | |
*/ | |
class RedirectByQuery | |
{ | |
/** | |
* Handle an incoming request. | |
* | |
* @param Request $request | |
* @param Closure $next | |
* @param string|null $guard | |
* @return mixed | |
*/ | |
public function handle($request, Closure $next, $guard = null) | |
{ | |
[$routeName, $routeMapping] = $this->getRouteMapping($request); | |
if ($routeMapping) { | |
if (!($route = app(Router::class)->getRoutes()->getByName($routeName))) { | |
throw new InvalidArgumentException("Unknown route name '$routeName'."); | |
} | |
$this->applyPathParams($request, $routeMapping); | |
$this->applyMethod($request, $routeMapping); | |
} | |
return $next($request); | |
} | |
/** | |
* @param Request $request | |
* @param array $routeMapping | |
* @return string | |
*/ | |
protected function applyMethod(Request $request, array $routeMapping) | |
{ | |
$requestMethod = $request->getMethod(); | |
$routeMethods = $routeMapping['to']['methods'] ?? []; | |
$requestMethod = in_array($requestMethod, $routeMethods) ? $requestMethod : Arr::first($routeMethods); | |
$request->setMethod($requestMethod); | |
return $request; | |
} | |
/** | |
* @param Request $request | |
* @param array $routeMapping | |
* @return Request | |
* @throws MissingPathParamException | |
*/ | |
protected function applyPathParams(Request $request, array $routeMapping) | |
{ | |
$routePath = $routeMapping['to']['path']; | |
$routeParams = $routeMapping['to']['params'] ?? []; | |
foreach ($routeParams as $from => $to) { | |
$fromValue = $request->get($from); | |
if (!preg_match('/{'.preg_quote($to).'}/', $routePath)) { | |
$request->request->add([$to => $fromValue]); | |
continue; | |
} | |
$routePath = str_ireplace('{'.$to.'}', $fromValue, $routePath); | |
} | |
$request->setPathInfo("/$routePath"); | |
if (preg_match_all('/{(.+?)}/', $routePath, $missing)) { | |
throw (new MissingPathParamException('Missing required path params'))->setMissingParams($missing[1]); | |
} | |
return $request; | |
} | |
/** | |
* @param Request $request | |
* @return array[string|null, array|null] | |
*/ | |
protected function getRouteMapping(Request $request) | |
{ | |
$routeMap = collect(Route::getMap()); | |
$routeMapping = $routeMap->filter(function (array $routeMapping) use ($request) { | |
$routeMatches = $this->pathMatches($request, $routeMapping); | |
$routeMatches &= $this->methodMatches($request, $routeMapping); | |
$routeMatches &= $this->paramsMatch($request, $routeMapping); | |
return $routeMatches; | |
}); | |
$routeName = $routeMapping->keys()->first(); | |
$routeMapping = $routeMapping->first(); | |
return [$routeName, $routeMapping]; | |
} | |
/** | |
* @param Request $request | |
* @param array $routeMapping | |
* @return bool | |
*/ | |
protected function pathMatches(Request $request, array $routeMapping) | |
{ | |
$requestPath = ltrim($request->getPathInfo(), '/'); | |
$mappedPath = ltrim($routeMapping['from']['path'] ?? '', '/'); | |
return !$mappedPath || $requestPath === $mappedPath; | |
} | |
/** | |
* @param Request $request | |
* @param array $routeMapping | |
* @return bool | |
*/ | |
protected function methodMatches(Request $request, array $routeMapping) | |
{ | |
$requestMethod = $request->getMethod(); | |
$mappedMethods = $routeMapping['from']['methods'] ?? []; | |
return !$mappedMethods || in_array($requestMethod, $mappedMethods); | |
} | |
/** | |
* @param Request $request | |
* @param array $routeMapping | |
* @return bool | |
*/ | |
protected function paramsMatch(Request $request, array $routeMapping) | |
{ | |
$requestParams = $request->toArray(); | |
$mappedParams = $routeMapping['from']['params'] ?? []; | |
return !$mappedParams || array_intersect_assoc($requestParams, $mappedParams) === $mappedParams; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AppServiceProvider | |
{ | |
/** | |
* @void | |
*/ | |
protected function routeMacros() | |
{ | |
$routeMap = []; | |
Route::macro('mapFrom', function ($path, $methods = ['GET'], $params = [], $map = []) use (&$routeMap) { | |
$methods = array_map('strtoupper', (array)$methods); | |
/** @var Route $this */ | |
if (!($routeName = $this->getName())) { | |
throw new UnexpectedValueException('Mapped routes must have a name.'); | |
} | |
$routeMap[$routeName] = [ | |
'from' => ['path' => $path, 'methods' => $methods, 'params' => $params], | |
'to' => ['path' => $this->uri(), 'methods' => $this->methods(), 'params' => $map] | |
]; | |
}); | |
Route::macro('getMap', function () use (&$routeMap) { | |
return $routeMap; | |
}); | |
Request::macro('setPathInfo', function ($path) { | |
$this->pathInfo = $path; | |
return $this; | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment