4
4
5
5
use Closure ;
6
6
use Illuminate \Console \Command ;
7
+ use Illuminate \Contracts \Routing \UrlGenerator ;
7
8
use Illuminate \Routing \Route ;
8
9
use Illuminate \Routing \Router ;
10
+ use Illuminate \Routing \ViewController ;
9
11
use Illuminate \Support \Arr ;
10
12
use Illuminate \Support \Str ;
13
+ use ReflectionClass ;
11
14
use Symfony \Component \Console \Input \InputOption ;
15
+ use Symfony \Component \Console \Terminal ;
12
16
13
17
class RouteListCommand extends Command
14
18
{
@@ -50,11 +54,27 @@ class RouteListCommand extends Command
50
54
protected $ headers = ['Domain ' , 'Method ' , 'URI ' , 'Name ' , 'Action ' , 'Middleware ' ];
51
55
52
56
/**
53
- * The columns to display when using the "compact" flag .
57
+ * The terminal width resolver callback .
54
58
*
55
- * @var string[]
59
+ * @var \Closure|null
56
60
*/
57
- protected $ compactColumns = ['method ' , 'uri ' , 'action ' ];
61
+ protected static $ terminalWidthResolver ;
62
+
63
+ /**
64
+ * The verb colors for the command.
65
+ *
66
+ * @var array
67
+ */
68
+ protected $ verbColors = [
69
+ 'ANY ' => 'red ' ,
70
+ 'GET ' => 'blue ' ,
71
+ 'HEAD ' => '#6C7280 ' ,
72
+ 'OPTIONS ' => '#6C7280 ' ,
73
+ 'POST ' => 'yellow ' ,
74
+ 'PUT ' => 'yellow ' ,
75
+ 'PATCH ' => 'yellow ' ,
76
+ 'DELETE ' => 'red ' ,
77
+ ];
58
78
59
79
/**
60
80
* Create a new route command instance.
@@ -164,13 +184,11 @@ protected function pluckColumns(array $routes)
164
184
*/
165
185
protected function displayRoutes (array $ routes )
166
186
{
167
- if ($ this ->option ('json ' )) {
168
- $ this ->line ($ this ->asJson ($ routes ));
169
-
170
- return ;
171
- }
187
+ $ routes = collect ($ routes );
172
188
173
- $ this ->table ($ this ->getHeaders (), $ routes );
189
+ $ this ->output ->writeln (
190
+ $ this ->option ('json ' ) ? $ this ->asJson ($ routes ) : $ this ->forCli ($ routes )
191
+ );
174
192
}
175
193
176
194
/**
@@ -195,8 +213,8 @@ protected function getMiddleware($route)
195
213
protected function filterRoute (array $ route )
196
214
{
197
215
if (($ this ->option ('name ' ) && ! Str::contains ($ route ['name ' ], $ this ->option ('name ' ))) ||
198
- $ this ->option ('path ' ) && ! Str::contains ($ route ['uri ' ], $ this ->option ('path ' )) ||
199
- $ this ->option ('method ' ) && ! Str::contains ($ route ['method ' ], strtoupper ($ this ->option ('method ' )))) {
216
+ $ this ->option ('path ' ) && ! Str::contains ($ route ['uri ' ], $ this ->option ('path ' )) ||
217
+ $ this ->option ('method ' ) && ! Str::contains ($ route ['method ' ], strtoupper ($ this ->option ('method ' )))) {
200
218
return ;
201
219
}
202
220
@@ -228,17 +246,7 @@ protected function getHeaders()
228
246
*/
229
247
protected function getColumns ()
230
248
{
231
- $ availableColumns = array_map ('strtolower ' , $ this ->headers );
232
-
233
- if ($ this ->option ('compact ' )) {
234
- return array_intersect ($ availableColumns , $ this ->compactColumns );
235
- }
236
-
237
- if ($ columns = $ this ->option ('columns ' )) {
238
- return array_intersect ($ availableColumns , $ this ->parseColumns ($ columns ));
239
- }
240
-
241
- return $ availableColumns ;
249
+ return array_map ('strtolower ' , $ this ->headers );
242
250
}
243
251
244
252
/**
@@ -265,12 +273,12 @@ protected function parseColumns(array $columns)
265
273
/**
266
274
* Convert the given routes to JSON.
267
275
*
268
- * @param array $routes
276
+ * @param \Illuminate\Support\Collection $routes
269
277
* @return string
270
278
*/
271
- protected function asJson (array $ routes )
279
+ protected function asJson ($ routes )
272
280
{
273
- return collect ( $ routes)
281
+ return $ routes
274
282
->map (function ($ route ) {
275
283
$ route ['middleware ' ] = empty ($ route ['middleware ' ]) ? [] : explode ("\n" , $ route ['middleware ' ]);
276
284
@@ -280,6 +288,125 @@ protected function asJson(array $routes)
280
288
->toJson ();
281
289
}
282
290
291
+ /**
292
+ * Convert the given routes to regular CLI output.
293
+ *
294
+ * @param \Illuminate\Support\Collection $routes
295
+ * @return array
296
+ */
297
+ protected function forCli ($ routes )
298
+ {
299
+ $ routes = $ routes ->map (
300
+ fn ($ route ) => array_merge ($ route , [
301
+ 'action ' => $ this ->formatActionForCli ($ route ),
302
+ 'method ' => $ route ['method ' ] == 'GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS ' ? 'ANY ' : $ route ['method ' ],
303
+ 'uri ' => $ route ['domain ' ] ? ($ route ['domain ' ].'/ ' .$ route ['uri ' ]) : $ route ['uri ' ],
304
+ ]),
305
+ );
306
+
307
+ $ maxMethod = mb_strlen ($ routes ->max ('method ' ));
308
+
309
+ $ terminalWidth = $ this ->getTerminalWidth ();
310
+
311
+ return $ routes ->map (function ($ route ) use ($ maxMethod , $ terminalWidth ) {
312
+ [
313
+ 'action ' => $ action ,
314
+ 'domain ' => $ domain ,
315
+ 'method ' => $ method ,
316
+ 'middleware ' => $ middleware ,
317
+ 'uri ' => $ uri ,
318
+ ] = $ route ;
319
+
320
+ $ middleware = Str::of ($ middleware )->explode ("\n" )->filter ()->whenNotEmpty (
321
+ fn ($ collection ) => $ collection ->map (
322
+ fn ($ middleware ) => sprintf (' %s⇂ %s ' , str_repeat (' ' , $ maxMethod ), $ middleware )
323
+ )
324
+ )->implode ("\n" );
325
+
326
+ $ spaces = str_repeat (' ' , max ($ maxMethod + 6 - mb_strlen ($ method ), 0 ));
327
+
328
+ $ dots = str_repeat ('. ' , max (
329
+ $ terminalWidth - mb_strlen ($ method .$ spaces .$ uri .$ action ) - 6 - ($ action ? 1 : 0 ), 0
330
+ ));
331
+
332
+ $ dots = empty ($ dots ) ? $ dots : " $ dots " ;
333
+
334
+ if ($ action && ! $ this ->output ->isVerbose () && mb_strlen ($ method .$ spaces .$ uri .$ action .$ dots ) > ($ terminalWidth - 6 )) {
335
+ $ action = substr ($ action , 0 , $ terminalWidth - 7 - mb_strlen ($ method .$ spaces .$ uri .$ dots )).'… ' ;
336
+ }
337
+
338
+ $ method = Str::of ($ method )->explode ('| ' )->map (
339
+ fn ($ method ) => sprintf ('<fg=%s>%s</> ' , $ this ->verbColors [$ method ] ?? 'default ' , $ method ),
340
+ )->implode ('<fg=#6C7280>|</> ' );
341
+
342
+ return [sprintf (
343
+ ' <fg=white;options=bold>%s</> %s<fg=white>%s</><fg=#6C7280>%s %s</> ' ,
344
+ $ method ,
345
+ $ spaces ,
346
+ preg_replace ('#({[^}]+})# ' , '<fg=yellow>$1</> ' , $ uri ),
347
+ $ dots ,
348
+ str_replace (' ' , ' › ' , $ action ),
349
+ ), $ this ->output ->isVerbose () && ! empty ($ middleware ) ? "<fg=#6C7280> $ middleware</> " : null ];
350
+ })->flatten ()->filter ()->prepend ('' )->push ('' )->toArray ();
351
+ }
352
+
353
+ /**
354
+ * Get the formatted action for display on the CLI.
355
+ *
356
+ * @param array $route
357
+ * @return string
358
+ */
359
+ protected function formatActionForCli ($ route )
360
+ {
361
+ ['action ' => $ action , 'name ' => $ name ] = $ route ;
362
+
363
+ if ($ action === 'Closure ' || $ action === ViewController::class) {
364
+ return $ name ;
365
+ }
366
+
367
+ $ name = $ name ? "$ name " : null ;
368
+
369
+ $ rootControllerNamespace = $ this ->laravel [UrlGenerator::class]->getRootControllerNamespace ()
370
+ ?? ($ this ->laravel ->getNamespace ().'Http \\Controllers ' );
371
+
372
+ if (str_starts_with ($ action , $ rootControllerNamespace )) {
373
+ return $ name .substr ($ action , mb_strlen ($ rootControllerNamespace ) + 1 );
374
+ }
375
+
376
+ $ actionClass = explode ('@ ' , $ action )[0 ];
377
+
378
+ if (class_exists ($ actionClass ) && str_starts_with ((new ReflectionClass ($ actionClass ))->getFilename (), base_path ('vendor ' ))) {
379
+ $ actionCollection = collect (explode ('\\' , $ action ));
380
+
381
+ return $ name .$ actionCollection ->take (2 )->implode ('\\' ).' ' .$ actionCollection ->last ();
382
+ }
383
+
384
+ return $ name .$ action ;
385
+ }
386
+
387
+ /**
388
+ * Get the terminal width.
389
+ *
390
+ * @return int
391
+ */
392
+ public static function getTerminalWidth ()
393
+ {
394
+ return is_null (static ::$ terminalWidthResolver )
395
+ ? (new Terminal )->getWidth ()
396
+ : call_user_func (static ::$ terminalWidthResolver );
397
+ }
398
+
399
+ /**
400
+ * Set a callback that should be used when resolving the terminal width.
401
+ *
402
+ * @param \Closure|null $callback
403
+ * @return void
404
+ */
405
+ public static function resolveTerminalWidthUsing ($ resolver )
406
+ {
407
+ static ::$ terminalWidthResolver = $ resolver ;
408
+ }
409
+
283
410
/**
284
411
* Get the console command options.
285
412
*
@@ -288,8 +415,6 @@ protected function asJson(array $routes)
288
415
protected function getOptions ()
289
416
{
290
417
return [
291
- ['columns ' , null , InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY , 'Columns to include in the route table ' ],
292
- ['compact ' , 'c ' , InputOption::VALUE_NONE , 'Only show method, URI and action columns ' ],
293
418
['json ' , null , InputOption::VALUE_NONE , 'Output the route list as JSON ' ],
294
419
['method ' , null , InputOption::VALUE_OPTIONAL , 'Filter the routes by method ' ],
295
420
['name ' , null , InputOption::VALUE_OPTIONAL , 'Filter the routes by name ' ],
0 commit comments