├── .gitignore ├── .styleci.yml ├── LICENSE ├── README.md ├── composer.json ├── resources └── views │ ├── _shared.blade.php │ ├── artisan.blade.php │ ├── database.blade.php │ └── scaffold.blade.php └── src ├── Controllers ├── RouteController.php ├── ScaffoldController.php └── TerminalController.php ├── Helpers.php ├── HelpersServiceProvider.php └── Scaffold ├── ControllerCreator.php ├── MigrationCreator.php ├── ModelCreator.php └── stubs ├── controller.stub ├── create.stub └── model.stub /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | phpunit.phar 3 | /vendor 4 | composer.phar 5 | composer.lock 6 | *.project 7 | .idea/ -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: recommended 2 | enabled: 3 | disabled: 4 | - unalign_equals 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jens Segers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Helpers for Open-Admin 2 | ========================= 3 | 4 |

5 | 6 | [![StyleCI](https://styleci.io/repos/384432915/shield?branch=main)](https://styleci.io/repos/384432915) 7 | [![Packagist](https://img.shields.io/github/license/open-admin-org/helpers.svg?style=flat-square&color=brightgreen)](https://packagist.org/packages/open-admin-ext/helpers) 8 | [![Total Downloads](https://img.shields.io/packagist/dt/open-admin-ext/helpers.svg?style=flat-square)](https://packagist.org/packages/open-admin-ext/helpers) 9 | [![Pull request welcome](https://img.shields.io/badge/pr-welcome-green.svg?style=flat-square&color=brightgreen)]() 10 | 11 |

12 | 13 | [Documentation](http://open-admin.org/docs/en/extension-helpers) 14 | 15 | ## Screenshot 16 | 17 | ![helpers screenshot](http://open-admin.org/docs/images/screenshots/ext-helpers.png) 18 | 19 | 20 | ## Installation 21 | 22 | ``` 23 | $ composer require open-admin-ext/helpers 24 | ``` 25 | Import menu items. 26 | ``` 27 | $ php artisan admin:import helpers 28 | ``` 29 | 30 | ## Usage 31 | 32 | See [wiki](http://open-admin.org/docs/en/extension-helpers) 33 | 34 | License 35 | ------------ 36 | Licensed under [The MIT License (MIT)](LICENSE). 37 | 38 | Thanks 39 | ------------ 40 | Ported from [laravel-admin-ext/helpers](https://github.com/laravel-admin-extensions/helpers) 41 | 42 | Special thanks to z-song for original development 43 | 44 | 45 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-admin-ext/helpers", 3 | "description": "Helpers extension for open-admin", 4 | "type": "library", 5 | "keywords": ["open-admin", "helpers"], 6 | "homepage": "https://github.com/open-admin-org/helpers", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "open-admin", 11 | "email": "info@open-admin.org" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=7.0.0", 16 | "open-admin-org/open-admin": "~1.0" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "OpenAdmin\\Admin\\Helpers\\": "src/" 21 | } 22 | }, 23 | "extra": { 24 | "laravel": { 25 | "providers": [ 26 | "OpenAdmin\\Admin\\Helpers\\HelpersServiceProvider" 27 | ] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/views/_shared.blade.php: -------------------------------------------------------------------------------- 1 | 18 | 19 | 155 | -------------------------------------------------------------------------------- /resources/views/artisan.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | 6 |
7 |
8 | 9 | 10 | 11 |
12 | 13 | 52 |
53 | @include("open-admin-helpers::_shared") 54 | -------------------------------------------------------------------------------- /resources/views/database.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 |
6 |
7 | 26 |
27 | 28 |
29 |
30 | 31 |
32 | 33 | 44 |
45 | @include("open-admin-helpers::_shared") 46 | -------------------------------------------------------------------------------- /resources/views/scaffold.blade.php: -------------------------------------------------------------------------------- 1 | 6 |
7 |
8 |

Scaffold

9 |
10 | 11 |
12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 | 26 |   Table name can't be empty! 27 | 28 | 29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |
37 | 38 |
39 | 40 | 41 |
42 | 43 |
44 |
45 | 46 |
47 |
48 |
49 | 50 | 51 |
52 |
53 | 54 | 55 |
56 |
57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 |
65 | 66 | 67 |
68 |
69 |
70 | 71 |
72 | 73 |

Fields (Note, id is already included in field list)

74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | @if(old('fields')) 91 | @foreach(old('fields') as $index => $field) 92 | 93 | 94 | 97 | 104 | 105 | 113 | 114 | 115 | 116 | 117 | @endforeach 118 | @else 119 | 120 | 121 | 124 | 131 | 132 | 140 | 141 | 142 | 143 | 144 | @endif 145 | 146 |
OrderField nameTypeNullableKeyDefault valueCommentAction
95 | 96 | 98 | 103 | 106 | 112 | remove
122 | 123 | 125 | 130 | 133 | 139 | remove
147 | 148 |
149 | 150 |
151 | 152 |
153 |   Add field 154 | 155 |
156 | 157 | 158 |
159 | 160 | 161 |    162 | 163 | 164 |
165 | 166 |
167 | 168 | 169 |
170 | 171 | 172 |
173 | 174 | 175 |
176 | 179 | 180 | {{ csrf_field() }} 181 | 182 |
183 |
184 |
185 | 186 | 212 | 213 | 231 | 232 | 291 | -------------------------------------------------------------------------------- /src/Controllers/RouteController.php: -------------------------------------------------------------------------------- 1 | getModel()->setRoutes($this->getRoutes()); 20 | 21 | $content->title(__('Routes'))->body(Admin::grid($model, function (Grid $grid) { 22 | $colors = [ 23 | 'GET' => 'success', 24 | 'HEAD' => 'secondary', 25 | 'POST' => 'primary', 26 | 'PUT' => 'warning', 27 | 'DELETE' => 'danger', 28 | 'PATCH' => 'info', 29 | 'OPTIONS'=> 'secondary', 30 | ]; 31 | 32 | $grid->method()->map(function ($method) use ($colors) { 33 | return "$method"; 34 | })->implode(' '); 35 | 36 | $grid->uri()->display(function ($uri) { 37 | return preg_replace('/\{.+?\}/', '$0', $uri); 38 | })->sortable(); 39 | 40 | $grid->name(); 41 | 42 | $grid->action()->display(function ($uri) { 43 | return preg_replace('/@.+/', '$0', $uri); 44 | }); 45 | $grid->middleware()->badge('yellow'); 46 | 47 | $grid->disablePagination(); 48 | $grid->disableRowSelector(); 49 | $grid->disableActions(); 50 | $grid->disableCreation(); 51 | $grid->disableExport(); 52 | 53 | $grid->filter(function ($filter) { 54 | $filter->disableIdFilter(); 55 | $filter->equal('action'); 56 | $filter->equal('uri'); 57 | }); 58 | })); 59 | }); 60 | } 61 | 62 | protected function getModel() 63 | { 64 | return new class() extends Model { 65 | protected $routes; 66 | 67 | protected $where = []; 68 | 69 | public function setRoutes($routes) 70 | { 71 | $this->routes = $routes; 72 | 73 | return $this; 74 | } 75 | 76 | public function where($column, $condition) 77 | { 78 | $this->where[$column] = trim($condition); 79 | 80 | return $this; 81 | } 82 | 83 | public function orderBy() 84 | { 85 | return $this; 86 | } 87 | 88 | public function get() 89 | { 90 | $this->routes = collect($this->routes)->filter(function ($route) { 91 | foreach ($this->where as $column => $condition) { 92 | if (!Str::contains($route[$column], $condition)) { 93 | return false; 94 | } 95 | } 96 | 97 | return true; 98 | })->all(); 99 | 100 | $instance = $this->newModelInstance(); 101 | 102 | return $instance->newCollection(array_map(function ($item) use ($instance) { 103 | return $instance->newFromBuilder($item); 104 | }, $this->routes)); 105 | } 106 | }; 107 | } 108 | 109 | public function getRoutes() 110 | { 111 | $routes = app('router')->getRoutes(); 112 | 113 | $routes = collect($routes)->map(function ($route) { 114 | return $this->getRouteInformation($route); 115 | })->all(); 116 | 117 | if ($sort = request('_sort')) { 118 | $routes = $this->sortRoutes($sort, $routes); 119 | } 120 | 121 | return array_filter($routes); 122 | } 123 | 124 | /** 125 | * Get the route information for a given route. 126 | * 127 | * @param \Illuminate\Routing\Route $route 128 | * 129 | * @return array 130 | */ 131 | protected function getRouteInformation(Route $route) 132 | { 133 | return [ 134 | 'host' => $route->domain(), 135 | 'method' => $route->methods(), 136 | 'uri' => $route->uri(), 137 | 'name' => $route->getName(), 138 | 'action' => $route->getActionName(), 139 | 'middleware' => $this->getRouteMiddleware($route), 140 | ]; 141 | } 142 | 143 | /** 144 | * Sort the routes by a given element. 145 | * 146 | * @param string $sort 147 | * @param array $routes 148 | * 149 | * @return array 150 | */ 151 | protected function sortRoutes($sort, $routes) 152 | { 153 | return Arr::sort($routes, function ($route) use ($sort) { 154 | return $route[$sort]; 155 | }); 156 | } 157 | 158 | /** 159 | * Get before filters. 160 | * 161 | * @param \Illuminate\Routing\Route $route 162 | * 163 | * @return string 164 | */ 165 | protected function getRouteMiddleware($route) 166 | { 167 | return collect($route->gatherMiddleware())->map(function ($middleware) { 168 | return $middleware instanceof \Closure ? 'Closure' : $middleware; 169 | }); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/Controllers/ScaffoldController.php: -------------------------------------------------------------------------------- 1 | header('Scaffold'); 21 | 22 | $dbTypes = [ 23 | 'string', 'integer', 'text', 'float', 'double', 'decimal', 'boolean', 'date', 'time', 24 | 'dateTime', 'timestamp', 'char', 'mediumText', 'longText', 'tinyInteger', 'smallInteger', 25 | 'mediumInteger', 'bigInteger', 'unsignedTinyInteger', 'unsignedSmallInteger', 'unsignedMediumInteger', 26 | 'unsignedInteger', 'unsignedBigInteger', 'enum', 'json', 'jsonb', 'dateTimeTz', 'timeTz', 27 | 'timestampTz', 'nullableTimestamps', 'binary', 'ipAddress', 'macAddress', 28 | ]; 29 | 30 | $action = URL::current(); 31 | 32 | $content->row(view('open-admin-helpers::scaffold', compact('dbTypes', 'action'))); 33 | 34 | return $content; 35 | } 36 | 37 | public function store(Request $request) 38 | { 39 | $paths = []; 40 | $message = ''; 41 | 42 | try { 43 | 44 | // 1. Create model. 45 | if (in_array('model', $request->get('create'))) { 46 | $modelCreator = new ModelCreator($request->get('table_name'), $request->get('model_name')); 47 | 48 | $paths['model'] = $modelCreator->create( 49 | $request->get('primary_key'), 50 | $request->get('timestamps') == 'on', 51 | $request->get('soft_deletes') == 'on' 52 | ); 53 | } 54 | 55 | // 2. Create migration. 56 | if (in_array('migration', $request->get('create'))) { 57 | $migrationName = 'create_'.$request->get('table_name').'_table'; 58 | 59 | $paths['migration'] = (new MigrationCreator(app('files'), '/'))->buildBluePrint( 60 | $request->get('fields'), 61 | $request->get('primary_key', 'id'), 62 | $request->get('timestamps') == 'on', 63 | $request->get('soft_deletes') == 'on' 64 | )->create($migrationName, database_path('migrations'), $request->get('table_name')); 65 | } 66 | 67 | // 3. Run migrate. 68 | if (in_array('migrate', $request->get('create'))) { 69 | Artisan::call('migrate'); 70 | $message .= str_replace('Migrated:', '
Migrated:', Artisan::output()); 71 | } 72 | 73 | // 4. Create menu item. 74 | if (in_array('menu_item', $request->get('create'))) { 75 | $route = $this->createMenuItem($request); 76 | $message .= '
Menu item: created, route: '.$route; 77 | } 78 | 79 | // 5. Create controller. 80 | if (in_array('controller', $request->get('create'))) { 81 | Artisan::call('admin:controller \\\\'.addslashes($request->get('model_name')).' --name='.$this->getControllerName($request->get('controller_name'))); 82 | $message .= '
Controller:'.nl2br(trim(Artisan::output())); 83 | } 84 | } catch (\Exception $exception) { 85 | 86 | // Delete generated files if exception thrown. 87 | app('files')->delete($paths); 88 | 89 | return $this->backWithException($exception); 90 | } 91 | 92 | return $this->backWithSuccess($paths, $message); 93 | } 94 | 95 | public function getRoute($request) 96 | { 97 | return Str::plural(Str::kebab(class_basename($request->get('model_name')))); 98 | } 99 | 100 | public function createMenuItem($request) 101 | { 102 | $route = $this->getRoute($request); 103 | $lastOrder = Menu::max('order'); 104 | $root = [ 105 | 'parent_id' => 0, 106 | 'order' => $lastOrder++, 107 | 'title' => ucfirst($route), 108 | 'icon' => 'icon-file', 109 | 'uri' => $route, 110 | ]; 111 | $root = Menu::create($root); 112 | 113 | return $route; 114 | } 115 | 116 | public function getControllerName($str) 117 | { 118 | return last(explode('\\', $str)); 119 | } 120 | 121 | protected function backWithException(\Exception $exception) 122 | { 123 | $error = new MessageBag([ 124 | 'title' => 'Error', 125 | 'message' => $exception->getMessage(), 126 | ]); 127 | 128 | return back()->withInput()->with(compact('error')); 129 | } 130 | 131 | protected function backWithSuccess($paths, $message) 132 | { 133 | $messages = []; 134 | 135 | foreach ($paths as $name => $path) { 136 | $messages[] = ucfirst($name).": $path"; 137 | } 138 | 139 | $messages[] = $message; 140 | 141 | $success = new MessageBag([ 142 | 'title' => 'Success', 143 | 'message' => implode('
', $messages), 144 | ]); 145 | 146 | return back()->with(compact('success')); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Controllers/TerminalController.php: -------------------------------------------------------------------------------- 1 | header('Artisan terminal'); 25 | $content->row(view('open-admin-helpers::artisan', ['commands' => $this->organizedCommands()])); 26 | 27 | return $content; 28 | } 29 | 30 | public function runArtisan() 31 | { 32 | $command = Request::get('c', 'list'); 33 | 34 | // If Exception raised. 35 | if (1 === Artisan::handle( 36 | new ArgvInput(explode(' ', 'artisan '.trim($command))), 37 | $output = new StringOutput() 38 | )) { 39 | return $this->renderException(new Exception($output->getContent())); 40 | } 41 | 42 | return sprintf('
%s
', $output->getContent()); 43 | } 44 | 45 | public function database(Content $content) 46 | { 47 | $content->header('Database terminal'); 48 | $content->row(view('open-admin-helpers::database', ['connections' => $this->connections()])); 49 | 50 | return $content; 51 | } 52 | 53 | public function runDatabase() 54 | { 55 | $query = Request::get('q'); 56 | 57 | $connection = Request::get('c', config('database.default')); 58 | 59 | return $this->dispatchQuery($connection, $query); 60 | } 61 | 62 | protected function getDumpedHtml($var) 63 | { 64 | ob_start(); 65 | 66 | dump($var); 67 | 68 | $content = ob_get_contents(); 69 | 70 | ob_get_clean(); 71 | 72 | return substr($content, strpos($content, '
 $_) {
 80 |             $dbs[] = [
 81 |                 'option'   => $name,
 82 |                 'value'    => "db:$name",
 83 |                 'selected' => $name == config('database.default'),
 84 |             ];
 85 |         }
 86 | 
 87 |         $connections = array_filter(config('database.redis'), function ($config) {
 88 |             return is_array($config);
 89 |         });
 90 | 
 91 |         foreach ($connections as $name => $_) {
 92 |             $redis[] = [
 93 |                 'value'  => "redis:$name",
 94 |                 'option' => $name,
 95 |             ];
 96 |         }
 97 | 
 98 |         return compact('dbs', 'redis');
 99 |     }
100 | 
101 |     protected function table(array $headers, $rows, $style = 'default')
102 |     {
103 |         $output = new StringOutput();
104 | 
105 |         $table = new Table($output);
106 | 
107 |         if ($rows instanceof Arrayable) {
108 |             $rows = $rows->toArray();
109 |         }
110 | 
111 |         $table->setHeaders($headers)->setRows($rows)->setStyle($style)->render();
112 | 
113 |         return $output->getContent();
114 |     }
115 | 
116 |     protected function dispatchQuery($connection, $query)
117 |     {
118 |         list($type, $connection) = explode(':', $connection);
119 | 
120 |         if ($type == 'redis') {
121 |             return $this->execRedisCommand($connection, $query);
122 |         }
123 | 
124 |         $config = config('database.connections.'.$connection);
125 | 
126 |         if ($config['driver'] == 'mongodb') {
127 |             return $this->execMongodbQuery($config, $query);
128 |         }
129 | 
130 |         /* @var \Illuminate\Database\Connection $connection */
131 |         $connection = DB::connection($connection);
132 | 
133 |         $connection->enableQueryLog();
134 | 
135 |         try {
136 |             $result = $connection->select(str_replace([';', "\G"], '', $query));
137 |         } catch (Exception $exception) {
138 |             return $this->renderException($exception);
139 |         }
140 | 
141 |         $log = current($connection->getQueryLog());
142 | 
143 |         if (empty($result)) {
144 |             return sprintf("
Empty set (%s sec)
\r\n", number_format($log['time'] / 1000, 2)); 145 | } 146 | 147 | $result = json_decode(json_encode($result), true); 148 | 149 | if (Str::contains($query, "\G")) { 150 | return $this->getDumpedHtml($result); 151 | } 152 | 153 | return sprintf( 154 | "
%s \n%d %s in set (%s sec)
\r\n", 155 | $this->table(array_keys(current($result)), $result), 156 | count($result), 157 | count($result) == 1 ? 'row' : 'rows', 158 | number_format($log['time'] / 1000, 2) 159 | ); 160 | } 161 | 162 | protected function execMongodbQuery($config, $query) 163 | { 164 | if (Str::contains($query, '.find(') && !Str::contains($query, '.toArray(')) { 165 | $query .= '.toArray()'; 166 | } 167 | 168 | $manager = new Manager("mongodb://{$config['host']}:{$config['port']}"); 169 | $command = new Command(['eval' => $query]); 170 | 171 | try { 172 | $cursor = $manager->executeCommand($config['database'], $command); 173 | } catch (Exception $exception) { 174 | return $this->renderException($exception); 175 | } 176 | 177 | $result = $cursor->toArray()[0]; 178 | 179 | $result = json_decode(json_encode($result), true); 180 | 181 | if (isset($result['errmsg'])) { 182 | return $this->renderException(new Exception($result['errmsg'])); 183 | } 184 | 185 | return $this->getDumpedHtml($result['retval']); 186 | } 187 | 188 | protected function execRedisCommand($connection, $command) 189 | { 190 | $command = explode(' ', $command); 191 | 192 | try { 193 | $result = Redis::connection($connection)->executeRaw($command); 194 | } catch (Exception $exception) { 195 | return $this->renderException($exception); 196 | } 197 | 198 | if (is_string($result) && Str::startsWith($result, ['ERR ', 'WRONGTYPE '])) { 199 | return $this->renderException(new Exception($result)); 200 | } 201 | 202 | return $this->getDumpedHtml($result); 203 | } 204 | 205 | protected function organizedCommands() 206 | { 207 | $commands = array_keys(Artisan::all()); 208 | 209 | $groups = $others = []; 210 | 211 | foreach ($commands as $command) { 212 | $parts = explode(':', $command); 213 | 214 | if (isset($parts[1])) { 215 | $groups[$parts[0]][] = $command; 216 | } else { 217 | $others[] = $command; 218 | } 219 | } 220 | 221 | foreach ($groups as $key => $group) { 222 | if (count($group) === 1) { 223 | $others[] = $group[0]; 224 | 225 | unset($groups[$key]); 226 | } 227 | } 228 | 229 | ksort($groups); 230 | sort($others); 231 | 232 | return compact('groups', 'others'); 233 | } 234 | 235 | protected function renderException(Exception $exception) 236 | { 237 | return sprintf( 238 | "
   %s
", 239 | str_replace("\n", '
', $exception->getMessage()) 240 | ); 241 | } 242 | } 243 | 244 | class StringOutput extends Output 245 | { 246 | public $output = ''; 247 | 248 | public function clear() 249 | { 250 | $this->output = ''; 251 | } 252 | 253 | protected function doWrite(string $message, bool $newline) :void 254 | { 255 | $this->output .= $message.($newline ? "\n" : ''); 256 | } 257 | 258 | public function getContent() 259 | { 260 | return trim($this->output); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/Helpers.php: -------------------------------------------------------------------------------- 1 | get('helpers/terminal/database', 'OpenAdmin\Admin\Helpers\Controllers\TerminalController@database'); 33 | $router->post('helpers/terminal/database', 'OpenAdmin\Admin\Helpers\Controllers\TerminalController@runDatabase'); 34 | $router->get('helpers/terminal/artisan', 'OpenAdmin\Admin\Helpers\Controllers\TerminalController@artisan'); 35 | $router->post('helpers/terminal/artisan', 'OpenAdmin\Admin\Helpers\Controllers\TerminalController@runArtisan'); 36 | $router->get('helpers/scaffold', 'OpenAdmin\Admin\Helpers\Controllers\ScaffoldController@index'); 37 | $router->post('helpers/scaffold', 'OpenAdmin\Admin\Helpers\Controllers\ScaffoldController@store'); 38 | $router->get('helpers/routes', 'OpenAdmin\Admin\Helpers\Controllers\RouteController@index'); 39 | }); 40 | } 41 | 42 | public static function import() 43 | { 44 | $lastOrder = Menu::max('order'); 45 | 46 | $root = [ 47 | 'parent_id' => 0, 48 | 'order' => $lastOrder++, 49 | 'title' => 'Helpers', 50 | 'icon' => 'icon-cogs', 51 | 'uri' => '', 52 | ]; 53 | 54 | $root = Menu::create($root); 55 | 56 | $menus = [ 57 | [ 58 | 'title' => 'Scaffold', 59 | 'icon' => 'icon-keyboard', 60 | 'uri' => 'helpers/scaffold', 61 | ], 62 | [ 63 | 'title' => 'Database terminal', 64 | 'icon' => 'icon-database', 65 | 'uri' => 'helpers/terminal/database', 66 | ], 67 | [ 68 | 'title' => 'Laravel artisan', 69 | 'icon' => 'icon-terminal', 70 | 'uri' => 'helpers/terminal/artisan', 71 | ], 72 | [ 73 | 'title' => 'Routes', 74 | 'icon' => 'icon-list-alt', 75 | 'uri' => 'helpers/routes', 76 | ], 77 | ]; 78 | 79 | foreach ($menus as $menu) { 80 | $menu['parent_id'] = $root->id; 81 | $menu['order'] = $lastOrder++; 82 | 83 | Menu::create($menu); 84 | } 85 | 86 | parent::createPermission('Admin helpers', 'ext.helpers', 'helpers/*'); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/HelpersServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadViewsFrom(__DIR__.'/../resources/views', 'open-admin-helpers'); 15 | 16 | Helpers::boot(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Scaffold/ControllerCreator.php: -------------------------------------------------------------------------------- 1 | name = $name; 36 | 37 | $this->files = $files ?: app('files'); 38 | } 39 | 40 | /** 41 | * Create a controller. 42 | * 43 | * @param string $model 44 | * 45 | * @throws \Exception 46 | * 47 | * @return string 48 | */ 49 | public function create($model, $fields) 50 | { 51 | $path = $this->getpath($this->name); 52 | 53 | if ($this->files->exists($path)) { 54 | throw new \Exception("Controller [$this->name] already exists!"); 55 | } 56 | 57 | $this->generateGridField($fields); 58 | 59 | $this->generateShowField($fields); 60 | 61 | $this->generateFormField($fields); 62 | 63 | $stub = $this->files->get($this->getStub()); 64 | 65 | $this->files->put($path, $this->replace($stub, $this->name, $model)); 66 | 67 | return $path; 68 | } 69 | 70 | /** 71 | * @param string $stub 72 | * @param string $name 73 | * @param string $model 74 | * 75 | * @return string 76 | */ 77 | protected function replace($stub, $name, $model) 78 | { 79 | $stub = $this->replaceClass($stub, $name); 80 | 81 | return str_replace( 82 | ['DummyModelNamespace', 'DummyModel', 'DummyGridField', 'DummyShowField', 'DummyFormField'], 83 | [$model, class_basename($model), $this->DummyGridField, $this->DummyShowField, $this->DummyFormField], 84 | $stub 85 | ); 86 | } 87 | 88 | /** 89 | * Get controller namespace from giving name. 90 | * 91 | * @param string $name 92 | * 93 | * @return string 94 | */ 95 | protected function getNamespace($name) 96 | { 97 | return trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\'); 98 | } 99 | 100 | /** 101 | * Replace the class name for the given stub. 102 | * 103 | * @param string $stub 104 | * @param string $name 105 | * 106 | * @return string 107 | */ 108 | protected function replaceClass($stub, $name) 109 | { 110 | $class = str_replace($this->getNamespace($name).'\\', '', $name); 111 | 112 | return str_replace(['DummyClass', 'DummyNamespace'], [$class, $this->getNamespace($name)], $stub); 113 | } 114 | 115 | /** 116 | * Get file path from giving controller name. 117 | * 118 | * @param $name 119 | * 120 | * @return string 121 | */ 122 | public function getPath($name) 123 | { 124 | $segments = explode('\\', $name); 125 | 126 | array_shift($segments); 127 | 128 | return app_path(implode('/', $segments)).'.php'; 129 | } 130 | 131 | /** 132 | * Get stub file path. 133 | * 134 | * @return string 135 | */ 136 | public function getStub() 137 | { 138 | return __DIR__.'/stubs/controller.stub'; 139 | } 140 | 141 | public function generateFormField($fields = []) 142 | { 143 | $fields = array_filter($fields, function ($field) { 144 | return isset($field['name']) && !empty($field['name']); 145 | }); 146 | 147 | if (empty($fields)) { 148 | throw new \Exception('Table fields can\'t be empty'); 149 | } 150 | 151 | foreach ($fields as $field) { 152 | $rows[] = "\$form->text('{$field['name']}', '{$field['name']}');\n"; 153 | } 154 | 155 | $this->DummyFormField = trim(implode(str_repeat(' ', 8), $rows), "\n"); 156 | 157 | return $this; 158 | } 159 | 160 | public function generateShowField($fields = []) 161 | { 162 | $fields = array_filter($fields, function ($field) { 163 | return isset($field['name']) && !empty($field['name']); 164 | }); 165 | 166 | if (empty($fields)) { 167 | throw new \Exception('Table fields can\'t be empty'); 168 | } 169 | foreach ($fields as $field) { 170 | $rows[] = "\$show->{$field['name']}('{$field['name']}');\n"; 171 | } 172 | 173 | $this->DummyShowField = trim(implode(str_repeat(' ', 8), $rows), "\n"); 174 | 175 | return $this; 176 | } 177 | 178 | public function generateGridField($fields = []) 179 | { 180 | $fields = array_filter($fields, function ($field) { 181 | return isset($field['name']) && !empty($field['name']); 182 | }); 183 | 184 | if (empty($fields)) { 185 | throw new \Exception('Table fields can\'t be empty'); 186 | } 187 | foreach ($fields as $field) { 188 | $rows[] = "\$grid->{$field['name']}('{$field['name']}');\n"; 189 | } 190 | 191 | $this->DummyGridField = trim(implode(str_repeat(' ', 8), $rows), "\n"); 192 | 193 | return $this; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/Scaffold/MigrationCreator.php: -------------------------------------------------------------------------------- 1 | ensureMigrationDoesntAlreadyExist($name); 28 | 29 | $path = $this->getPath($name, $path); 30 | 31 | $stub = $this->files->get(__DIR__.'/stubs/create.stub'); 32 | 33 | $this->files->put($path, $this->customPopulateStub($name, $stub, $table)); 34 | 35 | $this->customFirePostCreateHooks($table, $path); 36 | 37 | return $path; 38 | } 39 | 40 | /** 41 | * Fire the registered post create hooks. 42 | * 43 | * @param string|null $table 44 | * @param string $path 45 | * 46 | * @return void 47 | */ 48 | protected function customFirePostCreateHooks($table, $path) 49 | { 50 | foreach ($this->postCreate as $callback) { 51 | $callback($table, $path); 52 | } 53 | } 54 | 55 | /** 56 | * Populate stub. 57 | * 58 | * @param string $name 59 | * @param string $stub 60 | * @param string $table 61 | * 62 | * @return mixed 63 | */ 64 | protected function customPopulateStub($name, $stub, $table) 65 | { 66 | return str_replace( 67 | ['DummyClass', 'DummyTable', 'DummyStructure'], 68 | [$this->getClassName($name), $table, $this->bluePrint], 69 | $stub 70 | ); 71 | } 72 | 73 | /** 74 | * Build the table blueprint. 75 | * 76 | * @param array $fields 77 | * @param string $keyName 78 | * @param bool|true $useTimestamps 79 | * @param bool|false $softDeletes 80 | * 81 | * @throws \Exception 82 | * 83 | * @return $this 84 | */ 85 | public function buildBluePrint($fields = [], $keyName = 'id', $useTimestamps = true, $softDeletes = false) 86 | { 87 | $fields = array_filter($fields, function ($field) { 88 | return isset($field['name']) && !empty($field['name']); 89 | }); 90 | 91 | if (empty($fields)) { 92 | throw new \Exception('Table fields can\'t be empty'); 93 | } 94 | 95 | $rows[] = "\$table->increments('$keyName');\n"; 96 | 97 | foreach ($fields as $field) { 98 | $column = "\$table->{$field['type']}('{$field['name']}')"; 99 | 100 | if ($field['key']) { 101 | $column .= "->{$field['key']}()"; 102 | } 103 | 104 | if (isset($field['default']) && $field['default']) { 105 | $column .= "->default('{$field['default']}')"; 106 | } 107 | 108 | if (isset($field['comment']) && $field['comment']) { 109 | $column .= "->comment('{$field['comment']}')"; 110 | } 111 | 112 | if (Arr::get($field, 'nullable') == 'on') { 113 | $column .= '->nullable()'; 114 | } 115 | 116 | $rows[] = $column.";\n"; 117 | } 118 | 119 | if ($useTimestamps) { 120 | $rows[] = "\$table->timestamps();\n"; 121 | } 122 | 123 | if ($softDeletes) { 124 | $rows[] = "\$table->softDeletes();\n"; 125 | } 126 | 127 | $this->bluePrint = trim(implode(str_repeat(' ', 12), $rows), "\n"); 128 | 129 | return $this; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/Scaffold/ModelCreator.php: -------------------------------------------------------------------------------- 1 | tableName = $tableName; 40 | 41 | $this->name = $name; 42 | 43 | $this->files = $files ?: app('files'); 44 | } 45 | 46 | /** 47 | * Create a new migration file. 48 | * 49 | * @param string $keyName 50 | * @param bool|true $timestamps 51 | * @param bool|false $softDeletes 52 | * 53 | * @throws \Exception 54 | * 55 | * @return string 56 | */ 57 | public function create($keyName = 'id', $timestamps = true, $softDeletes = false) 58 | { 59 | $path = $this->getpath($this->name); 60 | 61 | if ($this->files->exists($path)) { 62 | throw new \Exception("Model [$this->name] already exists!"); 63 | } 64 | 65 | $stub = $this->files->get($this->getStub()); 66 | 67 | $stub = $this->replaceClass($stub, $this->name) 68 | ->replaceNamespace($stub, $this->name) 69 | ->replaceSoftDeletes($stub, $softDeletes) 70 | ->replaceTable($stub, $this->name) 71 | ->replaceTimestamp($stub, $timestamps) 72 | ->replacePrimaryKey($stub, $keyName) 73 | ->replaceSpace($stub); 74 | 75 | $this->files->put($path, $stub); 76 | 77 | return $path; 78 | } 79 | 80 | /** 81 | * Get path for migration file. 82 | * 83 | * @param string $name 84 | * 85 | * @return string 86 | */ 87 | public function getPath($name) 88 | { 89 | $segments = explode('\\', $name); 90 | 91 | array_shift($segments); 92 | 93 | return app_path(implode('/', $segments)).'.php'; 94 | } 95 | 96 | /** 97 | * Get namespace of giving class full name. 98 | * 99 | * @param string $name 100 | * 101 | * @return string 102 | */ 103 | protected function getNamespace($name) 104 | { 105 | return trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\'); 106 | } 107 | 108 | /** 109 | * Replace class dummy. 110 | * 111 | * @param string $stub 112 | * @param string $name 113 | * 114 | * @return $this 115 | */ 116 | protected function replaceClass(&$stub, $name) 117 | { 118 | $class = str_replace($this->getNamespace($name).'\\', '', $name); 119 | 120 | $stub = str_replace('DummyClass', $class, $stub); 121 | 122 | return $this; 123 | } 124 | 125 | /** 126 | * Replace namespace dummy. 127 | * 128 | * @param string $stub 129 | * @param string $name 130 | * 131 | * @return $this 132 | */ 133 | protected function replaceNamespace(&$stub, $name) 134 | { 135 | $stub = str_replace( 136 | 'DummyNamespace', 137 | $this->getNamespace($name), 138 | $stub 139 | ); 140 | 141 | return $this; 142 | } 143 | 144 | /** 145 | * Replace soft-deletes dummy. 146 | * 147 | * @param string $stub 148 | * @param bool $softDeletes 149 | * 150 | * @return $this 151 | */ 152 | protected function replaceSoftDeletes(&$stub, $softDeletes) 153 | { 154 | $import = $use = ''; 155 | 156 | if ($softDeletes) { 157 | $import = "use Illuminate\\Database\\Eloquent\\SoftDeletes;\n"; 158 | $use = "use SoftDeletes;\n"; 159 | } 160 | 161 | $stub = str_replace(['DummyImportSoftDeletesTrait', 'DummyUseSoftDeletesTrait'], [$import, $use], $stub); 162 | 163 | return $this; 164 | } 165 | 166 | /** 167 | * Replace primarykey dummy. 168 | * 169 | * @param string $stub 170 | * @param string $keyName 171 | * 172 | * @return $this 173 | */ 174 | protected function replacePrimaryKey(&$stub, $keyName) 175 | { 176 | $modelKey = $keyName == 'id' ? '' : "protected \$primaryKey = '$keyName';\n"; 177 | 178 | $stub = str_replace('DummyModelKey', $modelKey, $stub); 179 | 180 | return $this; 181 | } 182 | 183 | /** 184 | * Replace Table name dummy. 185 | * 186 | * @param string $stub 187 | * @param string $name 188 | * 189 | * @return $this 190 | */ 191 | protected function replaceTable(&$stub, $name) 192 | { 193 | $class = str_replace($this->getNamespace($name).'\\', '', $name); 194 | 195 | $table = Str::plural(strtolower($class)) !== $this->tableName ? "protected \$table = '$this->tableName';\n" : ''; 196 | 197 | $stub = str_replace('DummyModelTable', $table, $stub); 198 | 199 | return $this; 200 | } 201 | 202 | /** 203 | * Replace timestamps dummy. 204 | * 205 | * @param string $stub 206 | * @param bool $timestamps 207 | * 208 | * @return $this 209 | */ 210 | protected function replaceTimestamp(&$stub, $timestamps) 211 | { 212 | $useTimestamps = $timestamps ? '' : "public \$timestamps = false;\n"; 213 | 214 | $stub = str_replace('DummyTimestamp', $useTimestamps, $stub); 215 | 216 | return $this; 217 | } 218 | 219 | /** 220 | * Replace spaces. 221 | * 222 | * @param string $stub 223 | * 224 | * @return mixed 225 | */ 226 | public function replaceSpace($stub) 227 | { 228 | return str_replace(["\n\n\n", "\n \n"], ["\n\n", ''], $stub); 229 | } 230 | 231 | /** 232 | * Get stub path of model. 233 | * 234 | * @return string 235 | */ 236 | public function getStub() 237 | { 238 | return __DIR__.'/stubs/model.stub'; 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/Scaffold/stubs/controller.stub: -------------------------------------------------------------------------------- 1 |