Skip to content

Commit 10635f4

Browse files
authored
Merge pull request #5565 from Laravel-Backpack/match-route-file-end
Improve custom route insert command
2 parents ab55e9e + 548cccd commit 10635f4

File tree

2 files changed

+107
-43
lines changed

2 files changed

+107
-43
lines changed

src/app/Console/Commands/AddCustomRouteContent.php

Lines changed: 102 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
namespace Backpack\CRUD\app\Console\Commands;
44

55
use Illuminate\Console\Command;
6-
use Illuminate\Support\Facades\Artisan;
7-
use Illuminate\Support\Facades\Storage;
86

97
class AddCustomRouteContent extends Command
108
{
@@ -16,7 +14,8 @@ class AddCustomRouteContent extends Command
1614
* @var string
1715
*/
1816
protected $signature = 'backpack:add-custom-route
19-
{code : HTML/PHP code that registers a route. Use either single quotes or double quotes. Never both. }';
17+
{code : HTML/PHP code that registers a route. Use either single quotes or double quotes. Never both. }
18+
{--route-file=routes/backpack/custom.php : The file where the code should be added relative to the root of the project. }';
2019

2120
/**
2221
* The console command description.
@@ -42,68 +41,129 @@ public function __construct()
4241
*/
4342
public function handle()
4443
{
45-
$path = 'routes/backpack/custom.php';
46-
$disk_name = config('backpack.base.root_disk_name');
47-
$disk = Storage::disk($disk_name);
48-
$code = $this->argument('code');
44+
$routeFilePath = base_path($this->option('route-file'));
4945

50-
$this->progressBlock("Adding route to <fg=blue>$path</>");
46+
if (! file_exists($routeFilePath)) {
47+
if ($routeFilePath !== base_path($this->backpackCustomRouteFile)) {
48+
$this->info('The route file <fg=blue>'.$routeFilePath.'</> does not exist. Please create it first.');
5149

52-
// Validate file exists
53-
if (! $disk->exists($path)) {
54-
Artisan::call('vendor:publish', ['--provider' => \Backpack\CRUD\BackpackServiceProvider::class, '--tag' => 'custom_routes']);
55-
$this->handle();
50+
return 1;
51+
}
5652

57-
return;
53+
$createRouteFile = $this->confirm('The route file <fg=blue>'.$routeFilePath.'</> does not exist. Should we create it?', 'yes');
54+
if ($createRouteFile === 'yes') {
55+
$this->call('vendor:publish', ['--provider' => \Backpack\CRUD\BackpackServiceProvider::class, '--tag' => 'custom_routes']);
56+
} else {
57+
$this->info('The route file <fg=blue>'.$routeFilePath.'</> does not exist. Please create it first.');
58+
59+
return 1;
60+
}
5861
}
5962

60-
// insert the given code before the file's last line
61-
$old_file_path = $disk->path($path);
62-
$file_lines = file($old_file_path, FILE_IGNORE_NEW_LINES);
63+
$code = $this->argument('code');
64+
65+
$this->progressBlock("Adding route to <fg=blue>$routeFilePath</>");
66+
67+
$originalContent = file($routeFilePath);
6368

64-
// if the code already exists in the file, abort
65-
if ($this->getLastLineNumberThatContains($code, $file_lines)) {
69+
// clean the content from comments etc
70+
$cleanContent = $this->cleanContentArray($originalContent);
71+
72+
// if the content contains code, don't add it again.
73+
if (array_search($code, $cleanContent, true) !== false) {
6674
$this->closeProgressBlock('Already existed', 'yellow');
6775

6876
return;
6977
}
7078

71-
$end_line_number = $this->customRoutesFileEndLine($file_lines);
72-
$file_lines[$end_line_number + 1] = $file_lines[$end_line_number];
73-
$file_lines[$end_line_number] = ' '.$code;
74-
$new_file_content = implode(PHP_EOL, $file_lines);
79+
// get the last element of the array contains '}'
80+
$lastLine = $this->getLastLineNumberThatContains('}', $cleanContent);
7581

76-
if (! $disk->put($path, $new_file_content)) {
77-
$this->errorProgressBlock();
78-
$this->note('Could not write to file.', 'red');
82+
if ($lastLine === false) {
83+
$this->closeProgressBlock('Could not find the last line, file '.$routeFilePath.' may be corrupted.', 'red');
7984

8085
return;
8186
}
8287

83-
$this->closeProgressBlock();
84-
}
88+
// in case the last line contains the last } but also the last {, we need to split them
89+
// so that we can create a space between them and add the new code
90+
if (strpos($cleanContent[$lastLine], '{') !== false) {
91+
$lastLineContent = explode('{', $originalContent[$lastLine]);
92+
$originalContent[$lastLine] = $lastLineContent[0].'{'.PHP_EOL;
93+
// push all other elements one line down creating space for the new code
94+
for ($i = count($originalContent) - 1; $i > $lastLine; $i--) {
95+
$originalContent[$i + 1] = $originalContent[$i];
96+
}
97+
$originalContent[$lastLine + 1] = $lastLineContent[1];
98+
$lastLine++;
99+
}
85100

86-
private function customRoutesFileEndLine($file_lines)
87-
{
88-
// in case the last line has not been modified at all
89-
$end_line_number = array_search('}); // this should be the absolute last line of this file', $file_lines);
101+
// in case the last line contains more than one ";" it means that line closes more than one group
102+
// we need to split the line and create space for the new code
103+
if (substr_count($cleanContent[$lastLine], ';') > 1) {
104+
$lastLineContent = explode(';', $originalContent[$lastLine]);
105+
106+
// find in lastLineContent array the last element that contains the }
107+
$lastElement = $this->getLastLineNumberThatContains('}', $lastLineContent);
90108

91-
if ($end_line_number) {
92-
return $end_line_number;
109+
// merge the first part of the lastLineContent up to the lastElement
110+
$originalContent[$lastLine] = implode(';', array_slice($lastLineContent, 0, $lastElement)).';'.PHP_EOL;
111+
112+
// push all other elements one line down creating space for the new code
113+
for ($i = count($originalContent) - 1; $i > $lastLine; $i--) {
114+
$originalContent[$i + 1] = $originalContent[$i];
115+
}
116+
117+
// merge the second part of the lastLineContent starting from the lastElement
118+
$originalContent[$lastLine + 1] = implode(';', array_slice($lastLineContent, $lastElement));
119+
$lastLine++;
93120
}
94121

95-
// otherwise, in case the last line HAS been modified
96-
// return the last line that has an ending in it
97-
$possible_end_lines = array_filter($file_lines, function ($k) {
98-
return strpos($k, '});') === 0;
99-
});
122+
$sliceLength = 0;
123+
124+
// in case there is already an empty line at the end of the route file, we don't need to add another one
125+
if (trim($originalContent[$lastLine - 1]) === '') {
126+
$lastLine--;
127+
$sliceLength = 1;
128+
}
129+
130+
// add the code to the line before the last line
131+
array_splice($originalContent, $lastLine, $sliceLength, ' '.$code.PHP_EOL);
100132

101-
if ($possible_end_lines) {
102-
end($possible_end_lines);
103-
$end_line_number = key($possible_end_lines);
133+
// write the new content to the file
134+
if (file_put_contents($routeFilePath, implode('', $originalContent)) === false) {
135+
$this->closeProgressBlock('Failed to add route. Failed writing the modified route file. Maybe check file permissions?', 'red');
104136

105-
return $end_line_number;
137+
return;
106138
}
139+
140+
$this->closeProgressBlock('done', 'green');
141+
}
142+
143+
private function cleanContentArray(array $content)
144+
{
145+
return array_filter(array_map(function ($line) {
146+
$lineText = trim($line);
147+
if ($lineText === '' ||
148+
$lineText === '\n' ||
149+
$lineText === '\r' ||
150+
$lineText === '\r\n' ||
151+
$lineText === PHP_EOL ||
152+
str_starts_with($lineText, '<?php') ||
153+
str_starts_with($lineText, '?>') ||
154+
str_starts_with($lineText, '//') ||
155+
str_starts_with($lineText, '/*') ||
156+
str_starts_with($lineText, '*/') ||
157+
str_ends_with($lineText, '*/') ||
158+
str_starts_with($lineText, '*') ||
159+
str_starts_with($lineText, 'use ') ||
160+
str_starts_with($lineText, 'return ') ||
161+
str_starts_with($lineText, 'namespace ')) {
162+
return null;
163+
}
164+
165+
return $lineText;
166+
}, $content));
107167
}
108168

109169
/**

src/routes/backpack/custom.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// --------------------------
66
// Custom Backpack Routes
77
// --------------------------
8-
// This route file is loaded automatically by Backpack\Base.
8+
// This route file is loaded automatically by Backpack\CRUD.
99
// Routes you generate using Backpack\Generators will be placed here.
1010

1111
Route::group([
@@ -17,3 +17,7 @@
1717
'namespace' => 'App\Http\Controllers\Admin',
1818
], function () { // custom admin routes
1919
}); // this should be the absolute last line of this file
20+
21+
/**
22+
* DO NOT ADD ANYTHING HERE.
23+
*/

0 commit comments

Comments
 (0)