Skip to content

Commit b91d82e

Browse files
committed
Added AJAX supported HTTP methods. Also, some refactoring...
1 parent 6de1599 commit b91d82e

File tree

4 files changed

+172
-53
lines changed

4 files changed

+172
-53
lines changed

README.md

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,21 @@
1414

1515
Automatically Route Generator Package for Laravel.
1616

17+
## Features
18+
- All HTTP Methods which supported by Laravel
19+
- AJAX supported HTTP Methods (XMLHttpRequest)
20+
- Custom patterns for parameters with Regex
21+
- kebab-case and snake_case supported URLs
22+
1723
## Install
1824

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

21-
composer.json file:
27+
Run the following command directly in your Project path:
28+
```
29+
$ composer require izniburak/laravel-auto-routes
30+
```
31+
**OR** open your `composer.json` file and add the package like this:
2232
```json
2333
{
2434
"require": {
@@ -31,12 +41,6 @@ after run the install command.
3141
$ composer install
3242
```
3343

34-
**OR** Run the following command directly in your Project path:
35-
36-
```
37-
$ composer require izniburak/laravel-auto-routes
38-
```
39-
4044
The service provider of the Package will be **automatically discovered** by Laravel.
4145

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

71-
75+
### Methods
7276
- 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:
7377
```php
7478
Route::auto('/test', 'TestController');
@@ -149,6 +153,56 @@ class TestController extends Controller
149153
}
150154
```
151155

156+
### Ajax Supported Methods
157+
158+
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.
159+
This package has some AJAX supported methods. These are;
160+
```
161+
XGET, XPOST, XPUT, XDELETE, XPATCH, XOPTIONS, XANY.
162+
```
163+
```php
164+
namespace App\Http\Controllers;
165+
166+
use Illuminate\Http\Request;
167+
168+
class TestController extends Controller
169+
{
170+
/**
171+
* URL: "/test/foo"
172+
* This method will only work with 'GET' method and XMLHttpRequest.
173+
*/
174+
public function xgetFoo(Request $request)
175+
{
176+
// your codes
177+
}
178+
179+
/**
180+
* URL: "/test/bar"
181+
* This method will only work with 'POST' method and XMLHttpRequest.
182+
*/
183+
public function xpostBar(Request $request)
184+
{
185+
// your codes
186+
}
187+
188+
/**
189+
* URL: "/test/baz"
190+
* This method will work with any method and XMLHttpRequest.
191+
*/
192+
public function xanyBaz(Request $request)
193+
{
194+
// your codes
195+
}
196+
}
197+
```
198+
As you see, you need to add only `x` char as prefix to define the AJAX supported routes.
199+
If you want to support XMLHttpRequest and all HTTP methods which supported by Laravel, you can use `xany` prefix.
200+
201+
For AJAX supported methods, the package will automatically add a middleware in order to check XMLHttpRequest for the routes.
202+
This middleware throws a `MethodNotAllowedException` exception. But, you can change this middleware from `auto-routes.php` file in `config` directory, if you want.
203+
204+
### Options
205+
152206
- You can add route options via third parameter of the `auto` method.
153207
```php
154208
Route::auto('/test', 'TestController', [
@@ -215,6 +269,7 @@ class TestController extends Controller
215269
}
216270
}
217271
```
272+
### Parameters
218273
- You can use parameters as `required` and `optional` for the methods in your Controllers. For example;
219274
```php
220275
namespace App\Http\Controllers;

config/auto-route.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,18 @@
4949
'date' => '([0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]))',
5050
],
5151

52+
/*
53+
|--------------------------------------------------------------------------
54+
| AJAX Middleware Class
55+
|--------------------------------------------------------------------------
56+
| The middleware class that check AJAX request for your methods
57+
| which starts with 'x' char in your Controller file.
58+
| For example: xgetFoo, xpostBar, xanyBaz.
59+
| If you have any method in your controller like above, this middleware
60+
| will be triggered while trying to access your route.
61+
|
62+
| Default: \Buki\AutoRoute\Middleware\AjaxRequestMiddleware::class
63+
*/
64+
// 'ajax_middleware' => App\\Http\\Middleware\\YourMiddleware::class,
65+
5266
];

src/AutoRoute.php

Lines changed: 66 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Buki\AutoRoute;
44

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

41+
/**
42+
* @var string[] Custom methods for the package
43+
*/
44+
protected $customMethods = ['XGET', 'XPOST', 'XPUT', 'XPATCH', 'XDELETE', 'XOPTIONS', 'XANY'];
45+
4046
/**
4147
* @var string Main Method
4248
*/
@@ -47,6 +53,11 @@ class AutoRoute
4753
*/
4854
protected $defaultHttpMethods;
4955

56+
/**
57+
* @var string Ajax Middleware class for the Requests
58+
*/
59+
protected $ajaxMiddleware;
60+
5061
/**
5162
* @var string[]
5263
*/
@@ -61,6 +72,8 @@ class AutoRoute
6172
* AutoRoute constructor.
6273
*
6374
* @param Container $app
75+
*
76+
* @throws
6477
*/
6578
public function __construct(Container $app)
6679
{
@@ -77,6 +90,7 @@ public function setConfigurations(array $config): void
7790
{
7891
$this->mainMethod = $config['main_method'] ?? 'index';
7992
$this->namespace = $config['namespace'] ?? 'App\\Http\\Controllers';
93+
$this->ajaxMiddleware = $config['ajax_middleware'] ?? AjaxRequestMiddleware::class;
8094
$this->defaultPatterns = array_merge($this->defaultPatterns, $config['patterns'] ?? []);
8195
$this->defaultHttpMethods = $config['http_methods'] ?? $this->availableMethods;
8296

@@ -95,55 +109,57 @@ public function setConfigurations(array $config): void
95109
*/
96110
public function auto(string $prefix, string $controller, array $options = []): void
97111
{
98-
[$class, $className] = $this->resolveControllerName($controller);
99-
$classRef = new ReflectionClass($class);
100-
foreach ($classRef->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
101-
// Check the method should be added into Routes or not.
102-
if (in_array($method->class, [BaseController::class, "{$this->namespace}\\Controller"])
103-
|| $method->getDeclaringClass()->getParentClass()->getName() === BaseController::class
104-
|| !$method->isPublic()
105-
|| strpos($method->name, '__') === 0) {
106-
continue;
107-
}
108-
109-
// Needed definitions
110-
$methodName = $method->name;
111-
$only = $options['only'] ?? [];
112-
$except = $options['except'] ?? [];
113-
$patterns = $options['patterns'] ?? [];
114-
115-
if ((!empty($only) && !in_array($methodName, $only))
116-
|| (!empty($except) && in_array($methodName, $except))) {
117-
continue;
118-
}
112+
$only = $options['only'] ?? [];
113+
$except = $options['except'] ?? [];
114+
$patterns = $options['patterns'] ?? [];
115+
116+
$this->router->group(
117+
array_merge($options, [
118+
'prefix' => $prefix,
119+
'as' => isset($options['as'])
120+
? "{$options['as']}."
121+
: (isset($options['name'])
122+
? "{$options['name']}."
123+
: trim($prefix, '/') . '.'
124+
),
125+
]),
126+
function () use ($controller, $only, $except, $patterns) {
127+
[$class, $className] = $this->resolveControllerName($controller);
128+
$classRef = new ReflectionClass($class);
129+
foreach ($classRef->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
130+
// Check the method should be added into Routes or not.
131+
if (in_array($method->class, [BaseController::class, "{$this->namespace}\\Controller"])
132+
|| $method->getDeclaringClass()->getParentClass()->getName() === BaseController::class
133+
|| !$method->isPublic()
134+
|| strpos($method->name, '__') === 0) {
135+
continue;
136+
}
137+
138+
// Needed definitions
139+
$methodName = $method->name;
140+
141+
if ((!empty($only) && !in_array($methodName, $only))
142+
|| (!empty($except) && in_array($methodName, $except))) {
143+
continue;
144+
}
145+
146+
// Find the HTTP method which will be used and method name.
147+
[$httpMethods, $methodName, $middleware] = $this->getHttpMethodAndName($methodName);
148+
149+
// Get endpoints and parameter patterns for Route
150+
[$endpoints, $routePatterns] = $this->getRouteValues($method, $patterns);
119151

120-
// Find the HTTP method which will be used and method name.
121-
[$httpMethods, $methodName] = $this->getHttpMethodAndName($methodName);
122-
123-
// Get endpoints and parameter patterns for Route
124-
[$endpoints, $routePatterns] = $this->getRouteValues($method, $patterns);
125-
$this->router->group(
126-
array_merge($options, [
127-
'prefix' => $prefix,
128-
'as' => isset($options['as'])
129-
? "{$options['as']}."
130-
: (isset($options['name'])
131-
? "{$options['name']}."
132-
: trim($prefix, '/') . '.'
133-
),
134-
]),
135-
function () use ($endpoints, $methodName, $method, $httpMethods, $routePatterns, $classRef) {
136152
$endpoints = implode('/', $endpoints);
137153
$this->router->addRoute(
138154
array_map(function ($method) {
139155
return strtoupper($method);
140156
}, $httpMethods),
141157
($methodName !== $this->mainMethod ? $methodName : '') . "/{$endpoints}",
142158
[$classRef->getName(), $method->name]
143-
)->where($routePatterns)->name("{$method->name}");
159+
)->where($routePatterns)->name("{$method->name}")->middleware($middleware);
144160
}
145-
);
146-
}
161+
}
162+
);
147163
}
148164

149165
/**
@@ -154,11 +170,16 @@ function () use ($endpoints, $methodName, $method, $httpMethods, $routePatterns,
154170
private function getHttpMethodAndName(string $methodName): array
155171
{
156172
$httpMethods = $this->defaultHttpMethods;
157-
foreach ($this->availableMethods as $httpMethod) {
158-
if (stripos($methodName, strtolower($httpMethod), 0) === 0) {
159-
$httpMethods = [$httpMethod];
173+
$middleware = null;
174+
foreach (array_merge($this->availableMethods, $this->customMethods) as $httpMethod) {
175+
$httpMethod = strtolower($httpMethod);
176+
if (stripos($methodName, $httpMethod, 0) === 0) {
177+
if ($httpMethod !== 'xany') {
178+
$httpMethods = [ltrim($httpMethod, 'x')];
179+
}
180+
$middleware = strpos($httpMethod, 'x') === 0 ? $this->ajaxMiddleware : null;
160181
$methodName = lcfirst(
161-
preg_replace('/' . strtolower($httpMethod) . '_?/i', '', $methodName, 1)
182+
preg_replace('/' . $httpMethod . '_?/i', '', $methodName, 1)
162183
);
163184
break;
164185
}
@@ -167,7 +188,7 @@ private function getHttpMethodAndName(string $methodName): array
167188
// Convert URL from camelCase to snake-case.
168189
$methodName = strtolower(preg_replace('%([a-z]|[0-9])([A-Z])%', '\1-\2', $methodName));
169190

170-
return [$httpMethods, $methodName];
191+
return [$httpMethods, $methodName, $middleware];
171192
}
172193

173194
/**
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Buki\AutoRoute\Middleware;
4+
5+
use Closure;
6+
use Illuminate\Http\Request;
7+
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
8+
9+
class AjaxRequestMiddleware
10+
{
11+
/**
12+
* Handle an incoming request.
13+
*
14+
* @param \Illuminate\Http\Request $request
15+
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
16+
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
17+
*/
18+
public function handle(Request $request, Closure $next)
19+
{
20+
if (!($request->ajax() || $request->pjax())) {
21+
throw new MethodNotAllowedException(
22+
['XMLHttpRequest'],
23+
"You cannot use this route without XMLHttpRequest."
24+
);
25+
}
26+
27+
return $next($request);
28+
}
29+
}

0 commit comments

Comments
 (0)