Skip to content

Instantly share code, notes, and snippets.

@haipham22
Last active June 24, 2019 16:20
Show Gist options
  • Save haipham22/c8c97f49ba9632c289c18ce0d04ecef7 to your computer and use it in GitHub Desktop.
Save haipham22/c8c97f49ba9632c289c18ce0d04ecef7 to your computer and use it in GitHub Desktop.
Laravel Route:controller marco
<?php
/**
* Created by PhpStorm.
* User: haiph
* Date: 1/13/2019
* Time: 1:49 AM
*/
namespace App\Helpers\Routing;
use Illuminate\Support\Str;
use ReflectionClass;
use ReflectionMethod;
class ControllerRouter
{
/**
* An array of HTTP verbs.
*
* @var array
*/
protected $_verbs = [
'any',
'get',
'post',
'put',
'patch',
'delete',
'head',
'options',
];
/**
* The name of index action
*
* @var string
*/
protected $_indexAction = 'index';
/**
* @var string
*/
protected $_baseController;
/**
* @var string
*/
protected $_illuminateController = \Illuminate\Routing\Controller::class;
/**
* Skipp inherited methods or not
* @var bool
*/
protected $_skippInheritedMethods;
public function __construct(bool $skippInheritedMethods, ?string $baseController = null, ?string $illuminateController = null)
{
if ($baseController)
{
$this->_baseController = $baseController;
}
if ($illuminateController)
{
$this->_illuminateController = $illuminateController;
}
$this->_skippInheritedMethods = $skippInheritedMethods;
}
/**
* Get a full routable list from the controller based on method name
*
* @param string $controllerClass
* @param string|null $prefix
* @return array
* @throws \ReflectionException
*/
public function listRoutableActionFromController(string $controllerClass, string $prefix = null): array
{
$reflection = new ReflectionClass($controllerClass);
$controllerName = Str::slug(Str::replaceLast('Controller', '', $reflection->getShortName()));
$prefix = (empty($prefix) ? '' : ($prefix . '.')) . $controllerName;
// only public method that start with specific keywords will be loaded
$methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
$routable = array();
foreach ($methods as $method)
{
// skipp inherited methods
if ($this->isMethodSkipped($method, $controllerClass))
{
continue;
}
$parts = $this->methodToRouteParts($method);
// check if the method is routable
if (!$this->isRoutable($method, $parts['verb']))
{
continue;
}
$parameters = $this->_buildUrlPartParameters($method);
$routeName = $this->_buildRouteName($prefix, $parts);
$controllerAction = sprintf('%s@%s', '\\' . $controllerClass, $parts['full']);
$routable[ $controllerAction ] = $parts + [
'name' => $routeName,
'uri' => $this->_buildUrlPartAction($parts) . (empty($parameters) ? '' : ('/' . $parameters)),
];
}
return $routable;
}
/**
* Determine if the given controller method is routable.
* @param \ReflectionMethod $method
* @param string $verb
* @return bool
*/
public function isRoutable(ReflectionMethod $method, string $verb): bool
{
return in_array($verb, $this->_verbs);
}
/**
* Split the method names into multi-parts
* @param \ReflectionMethod $method
* @return array
*/
public function methodToRouteParts(ReflectionMethod $method): array
{
$name = $method->getName();
$parts = explode('_', Str::snake($name));
$verb = $parts[0];
$parts = array_slice($parts, 1);
if (!in_array($verb, $this->_verbs))
{
array_unshift($parts, $verb);
$verb = 'any';
}
return [
'full' => $name,
'verb' => $verb,
'action' => Str::slug(implode('-', $parts)),
];
}
/**
* Should we skipp this method
* @param \ReflectionMethod $method
* @param string $controllerClass
* @return bool
*/
public function isMethodSkipped(ReflectionMethod $method, string $controllerClass): bool
{
if ($method->class == $this->_illuminateController || ($this->_baseController && $method->class == $this->_baseController))
{
return true;
}
if ($this->_skippInheritedMethods)
{
return $method->getDeclaringClass()->name != $controllerClass;
}
return false;
}
/**
* build the url part for the action name
* @param array $parts
* @return string
*/
protected function _buildUrlPartAction(array $parts): string
{
$isIndex = $parts[ 'action' ] == $this->_indexAction;
// build uri parts
$uri = ($isIndex ? '' : '/' . $parts[ 'action' ]);
return $uri;
}
/**
* build the url part for the parameter
* @param \ReflectionMethod $method
* @return string
*/
protected function _buildUrlPartParameters(ReflectionMethod $method): string
{
$uri = array();
foreach ($method->getParameters() as $parameter)
{
$parameterName = $parameter->getName();
$isOptional = $parameter->isDefaultValueAvailable() || ($parameter->hasType() && $parameter->getType()->allowsNull());
$uri[] = sprintf('{%s%s}', $parameterName, $isOptional ? '?' : '');
}
return implode('/', $uri);
}
/**
* @param string $prefix
* @param $parts
*
* @return string
*/
protected function _buildRouteName(string $prefix, $parts): string
{
$routeNames = [$prefix, $parts[ 'action' ]];
if ($parts[ 'verb' ] != 'any')
{
$routeNames[] = $parts[ 'verb' ];
}
$routeName = implode('.', $routeNames);
return $routeName;
}
}
<?php
namespace App\Providers;
use App\Helpers\Routing\ControllerRouter;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
public $groupStack = [];
/**
* This namespace is applied to your controller routes.
*
* In addition, it is set as the URL generator's root namespace.
*
* @var string
*/
protected $namespace = 'App\Http\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
{
//
parent::boot();
}
/**
* Define the routes for the application.
*
* @return void
*/
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
$this->mapUtilRoutes();
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
*/
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
*/
protected function mapUtilRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/util.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*
* @return void
*/
protected function mapApiRoutes()
{
Route::prefix('api')->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->registerMarcoController();
}
protected function registerMarcoController()
{
Route::macro('controller', function ($uri, $controller, array $options = array()) {
$namespace = '';
// first we check namespace and prefix for the roads
// get prefix on route by group
if (!empty($this->groupStack))
{
$group = end($this->groupStack);
$namespace = isset($group[ 'namespace' ]) ? ($group[ 'namespace' ] . '\\') : $namespace;
}
$fullControllerName = $controller;
if (!class_exists($controller) && strpos($controller, '\\') !== 0)
{
$fullControllerName = $namespace . $controller;
}
$heritage = false;
if (isset($options['heritage']))
{
$heritage = $options['heritage'];
}
$cr = new ControllerRouter($heritage, Controller::class);
$routables = $cr->listRoutableActionFromController($fullControllerName);
foreach ($routables as $actionController => $potentialRoute)
{
// inherited : middleware, prefix, ...
$action = $options + [
'uses' => $actionController,
'as' => $potentialRoute[ 'name' ],
];
if (method_exists($this, $potentialRoute[ 'verb' ]))
{
$this->{$potentialRoute[ 'verb' ]}($uri . $potentialRoute[ 'uri' ], $action);
}
}
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment