From 206e67c319d2f314cacf5f9f698366a131086db4 Mon Sep 17 00:00:00 2001 From: chenc <1458513@qq.com> Date: Wed, 21 Sep 2022 14:17:05 +0800 Subject: [PATCH] initcode --- .gitignore | 7 + LICENSE | 20 ++ README.md | 37 ++- composer.json | 34 +++ resources/views/artisan.blade.php | 173 ++++++++++++++ resources/views/database.blade.php | 166 ++++++++++++++ resources/views/scaffold.blade.php | 301 +++++++++++++++++++++++++ src/Controllers/RouteController.php | 171 ++++++++++++++ src/Controllers/ScaffoldController.php | 116 ++++++++++ src/Controllers/TerminalController.php | 265 ++++++++++++++++++++++ src/Helpers.php | 88 ++++++++ src/HelpersServiceProvider.php | 18 ++ src/Scaffold/ControllerCreator.php | 128 +++++++++++ src/Scaffold/MigrationCreator.php | 121 ++++++++++ src/Scaffold/ModelCreator.php | 238 +++++++++++++++++++ src/Scaffold/stubs/controller.stub | 123 ++++++++++ src/Scaffold/stubs/create.stub | 30 +++ src/Scaffold/stubs/model.stub | 17 ++ 18 files changed, 2052 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 composer.json create mode 100644 resources/views/artisan.blade.php create mode 100644 resources/views/database.blade.php create mode 100644 resources/views/scaffold.blade.php create mode 100644 src/Controllers/RouteController.php create mode 100644 src/Controllers/ScaffoldController.php create mode 100644 src/Controllers/TerminalController.php create mode 100644 src/Helpers.php create mode 100644 src/HelpersServiceProvider.php create mode 100644 src/Scaffold/ControllerCreator.php create mode 100644 src/Scaffold/MigrationCreator.php create mode 100644 src/Scaffold/ModelCreator.php create mode 100644 src/Scaffold/stubs/controller.stub create mode 100644 src/Scaffold/stubs/create.stub create mode 100644 src/Scaffold/stubs/model.stub diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d4b362 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +phpunit.phar +/vendor +composer.phar +composer.lock +*.project +.idea/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..229071a --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jens Segers + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index c063c03..faf6ebb 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,37 @@ -# laravel-admin-ext-helpers +Helpers for laravel-admin +========================= +[](https://styleci.io/repos/97900966) +[](https://packagist.org/packages/laravel-admin-ext/helpers) +[](https://packagist.org/packages/laravel-admin-ext/helpers) +[]() + +[Documentation](http://laravel-admin.org/docs/#/en/extension-helpers) | [中文文档](http://laravel-admin.org/docs/#/zh/extension-helpers) + +DEMO [helpers](http://demo.laravel-admin.org/helpers/scaffold) + +Login using `admin/admin` + +## Installation + +``` +$ composer require laravel-admin-ext/helpers + +$ php artisan admin:import helpers +``` + +## Usage + +See [wiki](http://laravel-admin.org/docs/#/en/extension-helpers?id=helpers) + +## Donate + +> Help keeping the project development going, by donating a little. Thanks in advance. + +[](https://www.paypal.me/zousong) + + + +License +------------ +Licensed under [The MIT License (MIT)](LICENSE). diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..4a7e153 --- /dev/null +++ b/composer.json @@ -0,0 +1,34 @@ +{ + "name": "aix/laravel-admin-ext-helpers", + "description": "Helpers extension for laravel-admin", + "version": "1.0.1", + "type": "library", + "keywords": ["laravel-admin", "helpers"], + "homepage": "https://github.com/laravel-admin-extensions/helpers", + "license": "MIT", + "authors": [ + { + "name": "z-song", + "email": "zosong@126.com" + } + ], + "require": { + "php": ">=7.0.0", + "aix/laravel-admin": "1.0.*" + }, + "require-dev": { + "phpunit/phpunit": "~6.0" + }, + "autoload": { + "psr-4": { + "Encore\\Admin\\Helpers\\": "src/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Encore\\Admin\\Helpers\\HelpersServiceProvider" + ] + } + } +} diff --git a/resources/views/artisan.blade.php b/resources/views/artisan.blade.php new file mode 100644 index 0000000..2a7feb8 --- /dev/null +++ b/resources/views/artisan.blade.php @@ -0,0 +1,173 @@ + + +
$0', $uri);
+ })->sortable();
+
+ $grid->name();
+
+ $grid->action()->display(function ($uri) {
+ return preg_replace('/@.+/', '$0', $uri);
+ });
+ $grid->middleware()->badge('yellow');
+
+ $grid->disablePagination();
+ $grid->disableRowSelector();
+ $grid->disableActions();
+ $grid->disableCreation();
+ $grid->disableExport();
+
+ $grid->filter(function ($filter) {
+ $filter->disableIdFilter();
+ $filter->equal('action');
+ $filter->equal('uri');
+ });
+ }));
+ });
+ }
+
+ protected function getModel()
+ {
+ return new class() extends Model {
+ protected $routes;
+
+ protected $where = [];
+
+ public function setRoutes($routes)
+ {
+ $this->routes = $routes;
+
+ return $this;
+ }
+
+ public function where($column, $condition)
+ {
+ $this->where[$column] = trim($condition);
+
+ return $this;
+ }
+
+ public function orderBy()
+ {
+ return $this;
+ }
+
+ public function get()
+ {
+ $this->routes = collect($this->routes)->filter(function ($route) {
+ foreach ($this->where as $column => $condition) {
+ if (!Str::contains($route[$column], $condition)) {
+ return false;
+ }
+ }
+
+ return true;
+ })->all();
+
+ $instance = $this->newModelInstance();
+
+ return $instance->newCollection(array_map(function ($item) use ($instance) {
+ return $instance->newFromBuilder($item);
+ }, $this->routes));
+ }
+ };
+ }
+
+ public function getRoutes()
+ {
+ $routes = app('router')->getRoutes();
+
+ $routes = collect($routes)->map(function ($route) {
+ return $this->getRouteInformation($route);
+ })->all();
+
+ if ($sort = request('_sort')) {
+ $routes = $this->sortRoutes($sort, $routes);
+ }
+
+ return array_filter($routes);
+ }
+
+ /**
+ * Get the route information for a given route.
+ *
+ * @param \Illuminate\Routing\Route $route
+ *
+ * @return array
+ */
+ protected function getRouteInformation(Route $route)
+ {
+ return [
+ 'host' => $route->domain(),
+ 'method' => $route->methods(),
+ 'uri' => $route->uri(),
+ 'name' => $route->getName(),
+ 'action' => $route->getActionName(),
+ 'middleware' => $this->getRouteMiddleware($route),
+ ];
+ }
+
+ /**
+ * Sort the routes by a given element.
+ *
+ * @param string $sort
+ * @param array $routes
+ *
+ * @return array
+ */
+ protected function sortRoutes($sort, $routes)
+ {
+ return Arr::sort($routes, function ($route) use ($sort) {
+ return $route[$sort];
+ });
+ }
+
+ /**
+ * Get before filters.
+ *
+ * @param \Illuminate\Routing\Route $route
+ *
+ * @return string
+ */
+ protected function getRouteMiddleware($route)
+ {
+ return collect($route->gatherMiddleware())->map(function ($middleware) {
+ return $middleware instanceof \Closure ? 'Closure' : $middleware;
+ });
+ }
+}
diff --git a/src/Controllers/ScaffoldController.php b/src/Controllers/ScaffoldController.php
new file mode 100644
index 0000000..88d78d9
--- /dev/null
+++ b/src/Controllers/ScaffoldController.php
@@ -0,0 +1,116 @@
+header('Scaffold');
+
+ $dbTypes = [
+ 'string', 'integer', 'text', 'float', 'double', 'decimal', 'boolean', 'date', 'time',
+ 'dateTime', 'timestamp', 'char', 'mediumText', 'longText', 'tinyInteger', 'smallInteger',
+ 'mediumInteger', 'bigInteger', 'unsignedTinyInteger', 'unsignedSmallInteger', 'unsignedMediumInteger',
+ 'unsignedInteger', 'unsignedBigInteger', 'enum', 'json', 'jsonb', 'dateTimeTz', 'timeTz',
+ 'timestampTz', 'nullableTimestamps', 'binary', 'ipAddress', 'macAddress',
+ ];
+
+ $action = URL::current();
+
+ $content->row(view('laravel-admin-helpers::scaffold', compact('dbTypes', 'action')));
+ });
+ }
+
+ public function store(Request $request)
+ {
+ $paths = [];
+ $message = '';
+
+ try {
+
+ // 1. Create model.
+ if (in_array('model', $request->get('create'))) {
+ $modelCreator = new ModelCreator($request->get('table_name'), $request->get('model_name'));
+
+ $paths['model'] = $modelCreator->create(
+ $request->get('primary_key'),
+ $request->get('timestamps') == 'on',
+ $request->get('soft_deletes') == 'on'
+ );
+ }
+
+ // 2. Create controller.
+ if (in_array('controller', $request->get('create'))) {
+ $paths['controller'] = (new ControllerCreator($request->get('controller_name')))
+ ->create($request->get('model_name'));
+ }
+
+ // 3. Create migration.
+ if (in_array('migration', $request->get('create'))) {
+ $migrationName = 'create_'.$request->get('table_name').'_table';
+
+ $paths['migration'] = (new MigrationCreator(app('files')))->buildBluePrint(
+ $request->get('fields'),
+ $request->get('primary_key', 'id'),
+ $request->get('timestamps') == 'on',
+ $request->get('soft_deletes') == 'on'
+ )->create($migrationName, database_path('migrations'), $request->get('table_name'));
+ }
+
+ // 4. Run migrate.
+ if (in_array('migrate', $request->get('create'))) {
+ Artisan::call('migrate');
+ $message = Artisan::output();
+ }
+ } catch (\Exception $exception) {
+
+ // Delete generated files if exception thrown.
+ app('files')->delete($paths);
+
+ return $this->backWithException($exception);
+ }
+
+ return $this->backWithSuccess($paths, $message);
+ }
+
+ protected function backWithException(\Exception $exception)
+ {
+ $error = new MessageBag([
+ 'title' => 'Error',
+ 'message' => $exception->getMessage(),
+ ]);
+
+ return back()->withInput()->with(compact('error'));
+ }
+
+ protected function backWithSuccess($paths, $message)
+ {
+ $messages = [];
+
+ foreach ($paths as $name => $path) {
+ $messages[] = ucfirst($name).": $path";
+ }
+
+ $messages[] = "
$message";
+
+ $success = new MessageBag([
+ 'title' => 'Success',
+ 'message' => implode('
', $messages),
+ ]);
+
+ return back()->with(compact('success'));
+ }
+}
diff --git a/src/Controllers/TerminalController.php b/src/Controllers/TerminalController.php
new file mode 100644
index 0000000..950a829
--- /dev/null
+++ b/src/Controllers/TerminalController.php
@@ -0,0 +1,265 @@
+header('Artisan terminal');
+
+ $content->row(view('laravel-admin-helpers::artisan', ['commands' => $this->organizedCommands()]));
+ });
+ }
+
+ public function runArtisan()
+ {
+ $command = Request::get('c', 'list');
+
+ // If Exception raised.
+ if (1 === Artisan::handle(
+ new ArgvInput(explode(' ', 'artisan '.trim($command))),
+ $output = new StringOutput()
+ )) {
+ return $this->renderException(new Exception($output->getContent()));
+ }
+
+ return sprintf('%s
', $output->getContent());
+ }
+
+ public function database()
+ {
+ return Admin::content(function (Content $content) {
+ $content->header('Database terminal');
+
+ $content->row(view('laravel-admin-helpers::database', ['connections' => $this->connections()]));
+ });
+ }
+
+ public function runDatabase()
+ {
+ $query = Request::get('q');
+
+ $connection = Request::get('c', config('database.default'));
+
+ return $this->dispatchQuery($connection, $query);
+ }
+
+ protected function getDumpedHtml($var)
+ {
+ ob_start();
+
+ dump($var);
+
+ $content = ob_get_contents();
+
+ ob_get_clean();
+
+ return substr($content, strpos($content, ' $_) {
+ $dbs[] = [
+ 'option' => $name,
+ 'value' => "db:$name",
+ 'selected' => $name == config('database.default'),
+ ];
+ }
+
+ $connections = array_filter(config('database.redis'), function ($config) {
+ return is_array($config);
+ });
+
+ foreach ($connections as $name => $_) {
+ $redis[] = [
+ 'value' => "redis:$name",
+ 'option' => $name,
+ ];
+ }
+
+ return compact('dbs', 'redis');
+ }
+
+ protected function table(array $headers, $rows, $style = 'default')
+ {
+ $output = new StringOutput();
+
+ $table = new Table($output);
+
+ if ($rows instanceof Arrayable) {
+ $rows = $rows->toArray();
+ }
+
+ $table->setHeaders($headers)->setRows($rows)->setStyle($style)->render();
+
+ return $output->getContent();
+ }
+
+ protected function dispatchQuery($connection, $query)
+ {
+ list($type, $connection) = explode(':', $connection);
+
+ if ($type == 'redis') {
+ return $this->execRedisCommand($connection, $query);
+ }
+
+ $config = config('database.connections.'.$connection);
+
+ if ($config['driver'] == 'mongodb') {
+ return $this->execMongodbQuery($config, $query);
+ }
+
+ /* @var \Illuminate\Database\Connection $connection */
+ $connection = DB::connection($connection);
+
+ $connection->enableQueryLog();
+
+ try {
+ $result = $connection->select(str_replace([';', "\G"], '', $query));
+ } catch (Exception $exception) {
+ return $this->renderException($exception);
+ }
+
+ $log = current($connection->getQueryLog());
+
+ if (empty($result)) {
+ return sprintf("Empty set (%s sec)
\r\n", number_format($log['time'] / 1000, 2));
+ }
+
+ $result = json_decode(json_encode($result), true);
+
+ if (Str::contains($query, "\G")) {
+ return $this->getDumpedHtml($result);
+ }
+
+ return sprintf(
+ "%s \n%d %s in set (%s sec)
\r\n",
+ $this->table(array_keys(current($result)), $result),
+ count($result),
+ count($result) == 1 ? 'row' : 'rows',
+ number_format($log['time'] / 1000, 2)
+ );
+ }
+
+ protected function execMongodbQuery($config, $query)
+ {
+ if (Str::contains($query, '.find(') && !Str::contains($query, '.toArray(')) {
+ $query .= '.toArray()';
+ }
+
+ $manager = new Manager("mongodb://{$config['host']}:{$config['port']}");
+ $command = new Command(['eval' => $query]);
+
+ try {
+ $cursor = $manager->executeCommand($config['database'], $command);
+ } catch (Exception $exception) {
+ return $this->renderException($exception);
+ }
+
+ $result = $cursor->toArray()[0];
+
+ $result = json_decode(json_encode($result), true);
+
+ if (isset($result['errmsg'])) {
+ return $this->renderException(new Exception($result['errmsg']));
+ }
+
+ return $this->getDumpedHtml($result['retval']);
+ }
+
+ protected function execRedisCommand($connection, $command)
+ {
+ $command = explode(' ', $command);
+
+ try {
+ $result = Redis::connection($connection)->executeRaw($command);
+ } catch (Exception $exception) {
+ return $this->renderException($exception);
+ }
+
+ if (is_string($result) && Str::startsWith($result, ['ERR ', 'WRONGTYPE '])) {
+ return $this->renderException(new Exception($result));
+ }
+
+ return $this->getDumpedHtml($result);
+ }
+
+ protected function organizedCommands()
+ {
+ $commands = array_keys(Artisan::all());
+
+ $groups = $others = [];
+
+ foreach ($commands as $command) {
+ $parts = explode(':', $command);
+
+ if (isset($parts[1])) {
+ $groups[$parts[0]][] = $command;
+ } else {
+ $others[] = $command;
+ }
+ }
+
+ foreach ($groups as $key => $group) {
+ if (count($group) === 1) {
+ $others[] = $group[0];
+
+ unset($groups[$key]);
+ }
+ }
+
+ ksort($groups);
+ sort($others);
+
+ return compact('groups', 'others');
+ }
+
+ protected function renderException(Exception $exception)
+ {
+ return sprintf(
+ " %s",
+ str_replace("\n", '
', $exception->getMessage())
+ );
+ }
+}
+
+class StringOutput extends Output
+{
+ public $output = '';
+
+ public function clear()
+ {
+ $this->output = '';
+ }
+
+ protected function doWrite($message, $newline)
+ {
+ $this->output .= $message.($newline ? "\n" : '');
+ }
+
+ public function getContent()
+ {
+ return trim($this->output);
+ }
+}
diff --git a/src/Helpers.php b/src/Helpers.php
new file mode 100644
index 0000000..c13d7d7
--- /dev/null
+++ b/src/Helpers.php
@@ -0,0 +1,88 @@
+get('helpers/terminal/database', 'Encore\Admin\Helpers\Controllers\TerminalController@database');
+ $router->post('helpers/terminal/database', 'Encore\Admin\Helpers\Controllers\TerminalController@runDatabase');
+ $router->get('helpers/terminal/artisan', 'Encore\Admin\Helpers\Controllers\TerminalController@artisan');
+ $router->post('helpers/terminal/artisan', 'Encore\Admin\Helpers\Controllers\TerminalController@runArtisan');
+ $router->get('helpers/scaffold', 'Encore\Admin\Helpers\Controllers\ScaffoldController@index');
+ $router->post('helpers/scaffold', 'Encore\Admin\Helpers\Controllers\ScaffoldController@store');
+ $router->get('helpers/routes', 'Encore\Admin\Helpers\Controllers\RouteController@index');
+ });
+ }
+
+ public static function import()
+ {
+ $lastOrder = Menu::max('order');
+
+ $root = [
+ 'parent_id' => 0,
+ 'order' => $lastOrder++,
+ 'title' => 'Helpers',
+ 'icon' => 'fa-gears',
+ 'uri' => '',
+ ];
+
+ $root = Menu::create($root);
+
+ $menus = [
+ [
+ 'title' => 'Scaffold',
+ 'icon' => 'fa-keyboard-o',
+ 'uri' => 'helpers/scaffold',
+ ],
+ [
+ 'title' => 'Database terminal',
+ 'icon' => 'fa-database',
+ 'uri' => 'helpers/terminal/database',
+ ],
+ [
+ 'title' => 'Laravel artisan',
+ 'icon' => 'fa-terminal',
+ 'uri' => 'helpers/terminal/artisan',
+ ],
+ [
+ 'title' => 'Routes',
+ 'icon' => 'fa-list-alt',
+ 'uri' => 'helpers/routes',
+ ],
+ ];
+
+ foreach ($menus as $menu) {
+ $menu['parent_id'] = $root->id;
+ $menu['order'] = $lastOrder++;
+
+ Menu::create($menu);
+ }
+
+ parent::createPermission('Admin helpers', 'ext.helpers', 'helpers/*');
+ }
+}
diff --git a/src/HelpersServiceProvider.php b/src/HelpersServiceProvider.php
new file mode 100644
index 0000000..69bd7fa
--- /dev/null
+++ b/src/HelpersServiceProvider.php
@@ -0,0 +1,18 @@
+loadViewsFrom(__DIR__.'/../resources/views', 'laravel-admin-helpers');
+
+ Helpers::boot();
+ }
+}
diff --git a/src/Scaffold/ControllerCreator.php b/src/Scaffold/ControllerCreator.php
new file mode 100644
index 0000000..038f7f7
--- /dev/null
+++ b/src/Scaffold/ControllerCreator.php
@@ -0,0 +1,128 @@
+name = $name;
+
+ $this->files = $files ?: app('files');
+ }
+
+ /**
+ * Create a controller.
+ *
+ * @param string $model
+ *
+ * @throws \Exception
+ *
+ * @return string
+ */
+ public function create($model)
+ {
+ $path = $this->getpath($this->name);
+
+ if ($this->files->exists($path)) {
+ throw new \Exception("Controller [$this->name] already exists!");
+ }
+
+ $stub = $this->files->get($this->getStub());
+
+ $this->files->put($path, $this->replace($stub, $this->name, $model));
+
+ return $path;
+ }
+
+ /**
+ * @param string $stub
+ * @param string $name
+ * @param string $model
+ *
+ * @return string
+ */
+ protected function replace($stub, $name, $model)
+ {
+ $stub = $this->replaceClass($stub, $name);
+
+ return str_replace(
+ ['DummyModelNamespace', 'DummyModel'],
+ [$model, class_basename($model)],
+ $stub
+ );
+ }
+
+ /**
+ * Get controller namespace from giving name.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ protected function getNamespace($name)
+ {
+ return trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
+ }
+
+ /**
+ * Replace the class name for the given stub.
+ *
+ * @param string $stub
+ * @param string $name
+ *
+ * @return string
+ */
+ protected function replaceClass($stub, $name)
+ {
+ $class = str_replace($this->getNamespace($name).'\\', '', $name);
+
+ return str_replace(['DummyClass', 'DummyNamespace'], [$class, $this->getNamespace($name)], $stub);
+ }
+
+ /**
+ * Get file path from giving controller name.
+ *
+ * @param $name
+ *
+ * @return string
+ */
+ public function getPath($name)
+ {
+ $segments = explode('\\', $name);
+
+ array_shift($segments);
+
+ return app_path(implode('/', $segments)).'.php';
+ }
+
+ /**
+ * Get stub file path.
+ *
+ * @return string
+ */
+ public function getStub()
+ {
+ return __DIR__.'/stubs/controller.stub';
+ }
+}
diff --git a/src/Scaffold/MigrationCreator.php b/src/Scaffold/MigrationCreator.php
new file mode 100644
index 0000000..e59935f
--- /dev/null
+++ b/src/Scaffold/MigrationCreator.php
@@ -0,0 +1,121 @@
+ensureMigrationDoesntAlreadyExist($name);
+
+ $path = $this->getPath($name, $path);
+
+ $stub = $this->files->get(__DIR__.'/stubs/create.stub');
+
+ $this->files->put($path, $this->populateStub($name, $stub, $table));
+
+ $this->firePostCreateHooks($table);
+
+ return $path;
+ }
+
+ /**
+ * Populate stub.
+ *
+ * @param string $name
+ * @param string $stub
+ * @param string $table
+ *
+ * @return mixed
+ */
+ protected function populateStub($name, $stub, $table)
+ {
+ return str_replace(
+ ['DummyClass', 'DummyTable', 'DummyStructure'],
+ [$this->getClassName($name), $table, $this->bluePrint],
+ $stub
+ );
+ }
+
+ /**
+ * Build the table blueprint.
+ *
+ * @param array $fields
+ * @param string $keyName
+ * @param bool|true $useTimestamps
+ * @param bool|false $softDeletes
+ *
+ * @throws \Exception
+ *
+ * @return $this
+ */
+ public function buildBluePrint($fields = [], $keyName = 'id', $useTimestamps = true, $softDeletes = false)
+ {
+ $fields = array_filter($fields, function ($field) {
+ return isset($field['name']) && !empty($field['name']);
+ });
+
+ if (empty($fields)) {
+ throw new \Exception('Table fields can\'t be empty');
+ }
+
+ $rows[] = "\$table->increments('$keyName');\n";
+
+ foreach ($fields as $field) {
+
+ $column = "\$table->{$field['type']}('{$field['name']}')";
+
+ if ($field['key']) {
+ $column .= "->{$field['key']}()";
+ }
+
+ if ($field['size'] > 0) {
+ $column .= "->length({$field['size']})";
+ }
+
+ if (isset($field['default']) && $field['default']) {
+ $column .= "->default('{$field['default']}')";
+ }
+
+ if (isset($field['comment']) && $field['comment']) {
+ $column .= "->comment('{$field['comment']}')";
+ }
+
+ if (array_get($field, 'nullable') == 'on') {
+ $column .= '->nullable()';
+ }
+
+ $rows[] = $column.";\n";
+ }
+
+ if ($useTimestamps) {
+ $rows[] = "\$table->timestamps();\n";
+ }
+
+ if ($softDeletes) {
+ $rows[] = "\$table->softDeletes();\n";
+ }
+
+ $this->bluePrint = trim(implode(str_repeat(' ', 12), $rows), "\n");
+
+ return $this;
+ }
+}
diff --git a/src/Scaffold/ModelCreator.php b/src/Scaffold/ModelCreator.php
new file mode 100644
index 0000000..7f4dfc2
--- /dev/null
+++ b/src/Scaffold/ModelCreator.php
@@ -0,0 +1,238 @@
+tableName = $tableName;
+
+ $this->name = $name;
+
+ $this->files = $files ?: app('files');
+ }
+
+ /**
+ * Create a new migration file.
+ *
+ * @param string $keyName
+ * @param bool|true $timestamps
+ * @param bool|false $softDeletes
+ *
+ * @throws \Exception
+ *
+ * @return string
+ */
+ public function create($keyName = 'id', $timestamps = true, $softDeletes = false)
+ {
+ $path = $this->getpath($this->name);
+
+ if ($this->files->exists($path)) {
+ throw new \Exception("Model [$this->name] already exists!");
+ }
+
+ $stub = $this->files->get($this->getStub());
+
+ $stub = $this->replaceClass($stub, $this->name)
+ ->replaceNamespace($stub, $this->name)
+ ->replaceSoftDeletes($stub, $softDeletes)
+ ->replaceTable($stub, $this->name)
+ ->replaceTimestamp($stub, $timestamps)
+ ->replacePrimaryKey($stub, $keyName)
+ ->replaceSpace($stub);
+
+ $this->files->put($path, $stub);
+
+ return $path;
+ }
+
+ /**
+ * Get path for migration file.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ public function getPath($name)
+ {
+ $segments = explode('\\', $name);
+
+ array_shift($segments);
+
+ return app_path(implode('/', $segments)).'.php';
+ }
+
+ /**
+ * Get namespace of giving class full name.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ protected function getNamespace($name)
+ {
+ return trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
+ }
+
+ /**
+ * Replace class dummy.
+ *
+ * @param string $stub
+ * @param string $name
+ *
+ * @return $this
+ */
+ protected function replaceClass(&$stub, $name)
+ {
+ $class = str_replace($this->getNamespace($name).'\\', '', $name);
+
+ $stub = str_replace('DummyClass', $class, $stub);
+
+ return $this;
+ }
+
+ /**
+ * Replace namespace dummy.
+ *
+ * @param string $stub
+ * @param string $name
+ *
+ * @return $this
+ */
+ protected function replaceNamespace(&$stub, $name)
+ {
+ $stub = str_replace(
+ 'DummyNamespace', $this->getNamespace($name), $stub
+ );
+
+ return $this;
+ }
+
+ /**
+ * Replace soft-deletes dummy.
+ *
+ * @param string $stub
+ * @param bool $softDeletes
+ *
+ * @return $this
+ */
+ protected function replaceSoftDeletes(&$stub, $softDeletes)
+ {
+ $import = $use = '';
+
+ if ($softDeletes) {
+ $import = "use Illuminate\\Database\\Eloquent\\SoftDeletes;\n";
+ $use = "use SoftDeletes;\n";
+ }
+
+ $stub = str_replace(['DummyImportSoftDeletesTrait', 'DummyUseSoftDeletesTrait'], [$import, $use], $stub);
+
+ return $this;
+ }
+
+ /**
+ * Replace primarykey dummy.
+ *
+ * @param string $stub
+ * @param string $keyName
+ *
+ * @return $this
+ */
+ protected function replacePrimaryKey(&$stub, $keyName)
+ {
+ $modelKey = $keyName == 'id' ? '' : "protected \$primaryKey = '$keyName';\n";
+
+ $stub = str_replace('DummyModelKey', $modelKey, $stub);
+
+ return $this;
+ }
+
+ /**
+ * Replace Table name dummy.
+ *
+ * @param string $stub
+ * @param string $name
+ *
+ * @return $this
+ */
+ protected function replaceTable(&$stub, $name)
+ {
+ $class = str_replace($this->getNamespace($name).'\\', '', $name);
+
+ $table = Str::plural(strtolower($class)) !== $this->tableName ? "protected \$table = '$this->tableName';\n" : '';
+
+ $stub = str_replace('DummyModelTable', $table, $stub);
+
+ return $this;
+ }
+
+ /**
+ * Replace timestamps dummy.
+ *
+ * @param string $stub
+ * @param bool $timestamps
+ *
+ * @return $this
+ */
+ protected function replaceTimestamp(&$stub, $timestamps)
+ {
+ $useTimestamps = $timestamps ? '' : "public \$timestamps = false;\n";
+
+ $stub = str_replace('DummyTimestamp', $useTimestamps, $stub);
+
+ return $this;
+ }
+
+ /**
+ * Replace spaces.
+ *
+ * @param string $stub
+ *
+ * @return mixed
+ */
+ public function replaceSpace($stub)
+ {
+ return str_replace(["\n\n\n", "\n \n"], ["\n\n", ''], $stub);
+ }
+
+ /**
+ * Get stub path of model.
+ *
+ * @return string
+ */
+ public function getStub()
+ {
+ return __DIR__.'/stubs/model.stub';
+ }
+}
diff --git a/src/Scaffold/stubs/controller.stub b/src/Scaffold/stubs/controller.stub
new file mode 100644
index 0000000..9415eab
--- /dev/null
+++ b/src/Scaffold/stubs/controller.stub
@@ -0,0 +1,123 @@
+header('Index')
+ ->description('description')
+ ->body($this->grid());
+ }
+
+ /**
+ * Show interface.
+ *
+ * @param mixed $id
+ * @param Content $content
+ * @return Content
+ */
+ public function show($id, Content $content)
+ {
+ return $content
+ ->header('Detail')
+ ->description('description')
+ ->body($this->detail($id));
+ }
+
+ /**
+ * Edit interface.
+ *
+ * @param mixed $id
+ * @param Content $content
+ * @return Content
+ */
+ public function edit($id, Content $content)
+ {
+ return $content
+ ->header('Edit')
+ ->description('description')
+ ->body($this->form()->edit($id));
+ }
+
+ /**
+ * Create interface.
+ *
+ * @param Content $content
+ * @return Content
+ */
+ public function create(Content $content)
+ {
+ return $content
+ ->header('Create')
+ ->description('description')
+ ->body($this->form());
+ }
+
+ /**
+ * Make a grid builder.
+ *
+ * @return Grid
+ */
+ protected function grid()
+ {
+ $grid = new Grid(new DummyModel);
+
+ $grid->id('ID');
+ $grid->created_at('Created at');
+ $grid->updated_at('Updated at');
+
+ return $grid;
+ }
+
+ /**
+ * Make a show builder.
+ *
+ * @param mixed $id
+ * @return Show
+ */
+ protected function detail($id)
+ {
+ $show = new Show(DummyModel::findOrFail($id));
+
+ $show->id('ID');
+ $show->created_at('Created at');
+ $show->updated_at('Updated at');
+
+ return $show;
+ }
+
+ /**
+ * Make a form builder.
+ *
+ * @return Form
+ */
+ protected function form()
+ {
+ $form = new Form(new DummyModel);
+
+ $form->display('ID');
+ $form->display('Created at');
+ $form->display('Updated at');
+
+ return $form;
+ }
+}
diff --git a/src/Scaffold/stubs/create.stub b/src/Scaffold/stubs/create.stub
new file mode 100644
index 0000000..ace8eb1
--- /dev/null
+++ b/src/Scaffold/stubs/create.stub
@@ -0,0 +1,30 @@
+