33namespace Backpack \CRUD \app \Console \Commands ;
44
55use Illuminate \Console \Command ;
6- use Illuminate \Support \Facades \Artisan ;
7- use Illuminate \Support \Facades \Storage ;
86
97class 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 /**
0 commit comments