Last active
June 24, 2019 16:20
-
-
Save haipham22/c8c97f49ba9632c289c18ce0d04ecef7 to your computer and use it in GitHub Desktop.
Laravel Route:controller marco
This file contains 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 | |
/** | |
* 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; | |
} | |
} |
This file contains 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\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