Skip to content

Commit

Permalink
Added AJAX supported HTTP methods. Also, some refactoring...
Browse files Browse the repository at this point in the history
  • Loading branch information
izniburak committed Feb 8, 2022
1 parent 6de1599 commit b91d82e
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 53 deletions.
71 changes: 63 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,21 @@

Automatically Route Generator Package for Laravel.

## Features
- All HTTP Methods which supported by Laravel
- AJAX supported HTTP Methods (XMLHttpRequest)
- Custom patterns for parameters with Regex
- kebab-case and snake_case supported URLs

## Install

*Supported Laravel Versions:* **>= 6.x**

composer.json file:
Run the following command directly in your Project path:
```
$ composer require izniburak/laravel-auto-routes
```
**OR** open your `composer.json` file and add the package like this:
```json
{
"require": {
Expand All @@ -31,12 +41,6 @@ after run the install command.
$ composer install
```

**OR** Run the following command directly in your Project path:

```
$ composer require izniburak/laravel-auto-routes
```

The service provider of the Package will be **automatically discovered** by Laravel.

After that, you should publish the config file via following command:
Expand Down Expand Up @@ -68,7 +72,7 @@ use Buki\AutoRoute\AutoRouteFacade as Route;
```
- All methods which will be auto generated must have `public` accessor to discovered by the **AutoRoute** Package.


### Methods
- If you use `camelCase` style for your method names in the Controllers, these methods endpoints will automatically convert to `kebab-case` to make pretty URLs. For example:
```php
Route::auto('/test', 'TestController');
Expand Down Expand Up @@ -149,6 +153,56 @@ class TestController extends Controller
}
```

### Ajax Supported Methods

Also, you can add **AJAX supported** routes. For example; If you want to have a route which only access with GET method and XMLHttpRequest, you can define it simply.
This package has some AJAX supported methods. These are;
```
XGET, XPOST, XPUT, XDELETE, XPATCH, XOPTIONS, XANY.
```
```php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TestController extends Controller
{
/**
* URL: "/test/foo"
* This method will only work with 'GET' method and XMLHttpRequest.
*/
public function xgetFoo(Request $request)
{
// your codes
}

/**
* URL: "/test/bar"
* This method will only work with 'POST' method and XMLHttpRequest.
*/
public function xpostBar(Request $request)
{
// your codes
}

/**
* URL: "/test/baz"
* This method will work with any method and XMLHttpRequest.
*/
public function xanyBaz(Request $request)
{
// your codes
}
}
```
As you see, you need to add only `x` char as prefix to define the AJAX supported routes.
If you want to support XMLHttpRequest and all HTTP methods which supported by Laravel, you can use `xany` prefix.

For AJAX supported methods, the package will automatically add a middleware in order to check XMLHttpRequest for the routes.
This middleware throws a `MethodNotAllowedException` exception. But, you can change this middleware from `auto-routes.php` file in `config` directory, if you want.

### Options

- You can add route options via third parameter of the `auto` method.
```php
Route::auto('/test', 'TestController', [
Expand Down Expand Up @@ -215,6 +269,7 @@ class TestController extends Controller
}
}
```
### Parameters
- You can use parameters as `required` and `optional` for the methods in your Controllers. For example;
```php
namespace App\Http\Controllers;
Expand Down
14 changes: 14 additions & 0 deletions config/auto-route.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,18 @@
'date' => '([0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]))',
],

/*
|--------------------------------------------------------------------------
| AJAX Middleware Class
|--------------------------------------------------------------------------
| The middleware class that check AJAX request for your methods
| which starts with 'x' char in your Controller file.
| For example: xgetFoo, xpostBar, xanyBaz.
| If you have any method in your controller like above, this middleware
| will be triggered while trying to access your route.
|
| Default: \Buki\AutoRoute\Middleware\AjaxRequestMiddleware::class
*/
// 'ajax_middleware' => App\\Http\\Middleware\\YourMiddleware::class,

];
111 changes: 66 additions & 45 deletions src/AutoRoute.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Buki\AutoRoute;

use Buki\AutoRoute\Middleware\AjaxRequestMiddleware;
use Illuminate\Container\Container;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Routing\Controller as BaseController;
Expand Down Expand Up @@ -37,6 +38,11 @@ class AutoRoute
*/
protected $availableMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];

/**
* @var string[] Custom methods for the package
*/
protected $customMethods = ['XGET', 'XPOST', 'XPUT', 'XPATCH', 'XDELETE', 'XOPTIONS', 'XANY'];

/**
* @var string Main Method
*/
Expand All @@ -47,6 +53,11 @@ class AutoRoute
*/
protected $defaultHttpMethods;

/**
* @var string Ajax Middleware class for the Requests
*/
protected $ajaxMiddleware;

/**
* @var string[]
*/
Expand All @@ -61,6 +72,8 @@ class AutoRoute
* AutoRoute constructor.
*
* @param Container $app
*
* @throws
*/
public function __construct(Container $app)
{
Expand All @@ -77,6 +90,7 @@ public function setConfigurations(array $config): void
{
$this->mainMethod = $config['main_method'] ?? 'index';
$this->namespace = $config['namespace'] ?? 'App\\Http\\Controllers';
$this->ajaxMiddleware = $config['ajax_middleware'] ?? AjaxRequestMiddleware::class;
$this->defaultPatterns = array_merge($this->defaultPatterns, $config['patterns'] ?? []);
$this->defaultHttpMethods = $config['http_methods'] ?? $this->availableMethods;

Expand All @@ -95,55 +109,57 @@ public function setConfigurations(array $config): void
*/
public function auto(string $prefix, string $controller, array $options = []): void
{
[$class, $className] = $this->resolveControllerName($controller);
$classRef = new ReflectionClass($class);
foreach ($classRef->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
// Check the method should be added into Routes or not.
if (in_array($method->class, [BaseController::class, "{$this->namespace}\\Controller"])
|| $method->getDeclaringClass()->getParentClass()->getName() === BaseController::class
|| !$method->isPublic()
|| strpos($method->name, '__') === 0) {
continue;
}

// Needed definitions
$methodName = $method->name;
$only = $options['only'] ?? [];
$except = $options['except'] ?? [];
$patterns = $options['patterns'] ?? [];

if ((!empty($only) && !in_array($methodName, $only))
|| (!empty($except) && in_array($methodName, $except))) {
continue;
}
$only = $options['only'] ?? [];
$except = $options['except'] ?? [];
$patterns = $options['patterns'] ?? [];

$this->router->group(
array_merge($options, [
'prefix' => $prefix,
'as' => isset($options['as'])
? "{$options['as']}."
: (isset($options['name'])
? "{$options['name']}."
: trim($prefix, '/') . '.'
),
]),
function () use ($controller, $only, $except, $patterns) {
[$class, $className] = $this->resolveControllerName($controller);
$classRef = new ReflectionClass($class);
foreach ($classRef->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
// Check the method should be added into Routes or not.
if (in_array($method->class, [BaseController::class, "{$this->namespace}\\Controller"])
|| $method->getDeclaringClass()->getParentClass()->getName() === BaseController::class
|| !$method->isPublic()
|| strpos($method->name, '__') === 0) {
continue;
}

// Needed definitions
$methodName = $method->name;

if ((!empty($only) && !in_array($methodName, $only))
|| (!empty($except) && in_array($methodName, $except))) {
continue;
}

// Find the HTTP method which will be used and method name.
[$httpMethods, $methodName, $middleware] = $this->getHttpMethodAndName($methodName);

// Get endpoints and parameter patterns for Route
[$endpoints, $routePatterns] = $this->getRouteValues($method, $patterns);

// Find the HTTP method which will be used and method name.
[$httpMethods, $methodName] = $this->getHttpMethodAndName($methodName);

// Get endpoints and parameter patterns for Route
[$endpoints, $routePatterns] = $this->getRouteValues($method, $patterns);
$this->router->group(
array_merge($options, [
'prefix' => $prefix,
'as' => isset($options['as'])
? "{$options['as']}."
: (isset($options['name'])
? "{$options['name']}."
: trim($prefix, '/') . '.'
),
]),
function () use ($endpoints, $methodName, $method, $httpMethods, $routePatterns, $classRef) {
$endpoints = implode('/', $endpoints);
$this->router->addRoute(
array_map(function ($method) {
return strtoupper($method);
}, $httpMethods),
($methodName !== $this->mainMethod ? $methodName : '') . "/{$endpoints}",
[$classRef->getName(), $method->name]
)->where($routePatterns)->name("{$method->name}");
)->where($routePatterns)->name("{$method->name}")->middleware($middleware);
}
);
}
}
);
}

/**
Expand All @@ -154,11 +170,16 @@ function () use ($endpoints, $methodName, $method, $httpMethods, $routePatterns,
private function getHttpMethodAndName(string $methodName): array
{
$httpMethods = $this->defaultHttpMethods;
foreach ($this->availableMethods as $httpMethod) {
if (stripos($methodName, strtolower($httpMethod), 0) === 0) {
$httpMethods = [$httpMethod];
$middleware = null;
foreach (array_merge($this->availableMethods, $this->customMethods) as $httpMethod) {
$httpMethod = strtolower($httpMethod);
if (stripos($methodName, $httpMethod, 0) === 0) {
if ($httpMethod !== 'xany') {
$httpMethods = [ltrim($httpMethod, 'x')];
}
$middleware = strpos($httpMethod, 'x') === 0 ? $this->ajaxMiddleware : null;
$methodName = lcfirst(
preg_replace('/' . strtolower($httpMethod) . '_?/i', '', $methodName, 1)
preg_replace('/' . $httpMethod . '_?/i', '', $methodName, 1)
);
break;
}
Expand All @@ -167,7 +188,7 @@ private function getHttpMethodAndName(string $methodName): array
// Convert URL from camelCase to snake-case.
$methodName = strtolower(preg_replace('%([a-z]|[0-9])([A-Z])%', '\1-\2', $methodName));

return [$httpMethods, $methodName];
return [$httpMethods, $methodName, $middleware];
}

/**
Expand Down
29 changes: 29 additions & 0 deletions src/Middleware/AjaxRequestMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Buki\AutoRoute\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;

class AjaxRequestMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
if (!($request->ajax() || $request->pjax())) {
throw new MethodNotAllowedException(
['XMLHttpRequest'],
"You cannot use this route without XMLHttpRequest."
);
}

return $next($request);
}
}

0 comments on commit b91d82e

Please sign in to comment.