├── resources ├── stubs │ ├── install │ │ ├── resources │ │ │ ├── sass │ │ │ │ ├── _variables.scss.stub │ │ │ │ └── app.scss.stub │ │ │ ├── js │ │ │ │ └── app.js.stub │ │ │ └── views │ │ │ │ ├── vendor │ │ │ │ ├── form-components │ │ │ │ │ └── bootstrap-4 │ │ │ │ │ │ ├── form-errors.blade.php.stub │ │ │ │ │ │ ├── form-group.blade.php.stub │ │ │ │ │ │ ├── form-textarea.blade.php.stub │ │ │ │ │ │ ├── form-checkbox.blade.php.stub │ │ │ │ │ │ ├── form-radio.blade.php.stub │ │ │ │ │ │ ├── form-select.blade.php.stub │ │ │ │ │ │ └── form-input.blade.php.stub │ │ │ │ └── datatables │ │ │ │ │ └── script.blade.php.stub │ │ │ │ └── layouts │ │ │ │ └── app.blade.php.stub │ │ ├── config │ │ │ ├── geoip.php.stub │ │ │ ├── timezone.php.stub │ │ │ ├── form-components.php.stub │ │ │ └── datatables-html.php.stub │ │ └── webpack.mix.js.stub │ └── make │ │ ├── AutoRoute.stub │ │ ├── resources │ │ └── views │ │ │ └── dummy-route │ │ │ ├── form.blade.php.stub │ │ │ ├── create.blade.php.stub │ │ │ ├── edit.blade.php.stub │ │ │ ├── action.blade.php.stub │ │ │ ├── index.blade.php.stub │ │ │ └── show.blade.php.stub │ │ ├── nav-item.blade.php.stub │ │ ├── database │ │ └── factories │ │ │ └── DummyFactory.php.stub │ │ └── app │ │ ├── Models │ │ └── Dummy.php.stub │ │ └── Http │ │ ├── DataTables │ │ └── DummyDataTable.php.stub │ │ └── Controllers │ │ └── DummyController.php.stub ├── views │ └── layouts │ │ └── modal.blade.php ├── sass │ └── _crudify.scss └── js │ └── crudify.js ├── src ├── Traits │ ├── FillsColumns.php │ └── BuildsTables.php ├── Providers │ └── CrudifyServiceProvider.php ├── Helpers │ └── AutoRoute.php └── Commands │ ├── CrudifyInstallCommand.php │ ├── MigrateAutoCommand.php │ └── MakeCrudCommand.php ├── database └── migrations │ └── 2020_09_23_074042_add_timezone_column_to_users_table.php ├── composer.json └── readme.md /resources/stubs/install/resources/sass/_variables.scss.stub: -------------------------------------------------------------------------------- 1 | // override variables go here 2 | -------------------------------------------------------------------------------- /resources/stubs/install/config/geoip.php.stub: -------------------------------------------------------------------------------- 1 | null, 6 | 7 | ]; 8 | -------------------------------------------------------------------------------- /resources/stubs/make/AutoRoute.stub: -------------------------------------------------------------------------------- 1 | AutoRoute::controller('dummy-route', \App\Http\Controllers\DummyController::class); 2 | -------------------------------------------------------------------------------- /resources/stubs/make/resources/views/dummy-route/form.blade.php.stub: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/stubs/install/config/timezone.php.stub: -------------------------------------------------------------------------------- 1 | 'M j, Y g:i A', 6 | 7 | ]; 8 | -------------------------------------------------------------------------------- /resources/stubs/install/config/form-components.php.stub: -------------------------------------------------------------------------------- 1 | 'bootstrap-4', 6 | 7 | ]; 8 | -------------------------------------------------------------------------------- /resources/stubs/install/resources/js/app.js.stub: -------------------------------------------------------------------------------- 1 | require('./bootstrap'); 2 | require('../../vendor/redbastie/crudify/resources/js/crudify'); 3 | -------------------------------------------------------------------------------- /resources/stubs/make/nav-item.blade.php.stub: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/stubs/install/resources/views/vendor/form-components/bootstrap-4/form-errors.blade.php.stub: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /resources/stubs/install/resources/sass/app.scss.stub: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | @import '../../vendor/redbastie/crudify/resources/sass/crudify'; 3 | @import '~bootstrap/scss/bootstrap'; 4 | -------------------------------------------------------------------------------- /resources/stubs/install/webpack.mix.js.stub: -------------------------------------------------------------------------------- 1 | const mix = require('laravel-mix'); 2 | 3 | mix.js('resources/js/app.js', 'public/js') 4 | .sass('resources/sass/app.scss', 'public/css') 5 | .sourceMaps(); 6 | -------------------------------------------------------------------------------- /resources/stubs/install/config/datatables-html.php.stub: -------------------------------------------------------------------------------- 1 | [ 6 | 'class' => 'table table-hover', 7 | 'id' => 'dataTableBuilder', 8 | ], 9 | 10 | ]; 11 | -------------------------------------------------------------------------------- /src/Traits/FillsColumns.php: -------------------------------------------------------------------------------- 1 | getTable()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /resources/stubs/install/resources/views/vendor/datatables/script.blade.php.stub: -------------------------------------------------------------------------------- 1 | window.addEventListener('DOMContentLoaded', function() { 2 | (function(window,$){window.LaravelDataTables=window.LaravelDataTables||{};window.LaravelDataTables["%1$s"]=$("#%1$s").DataTable(%2$s);})(window,jQuery); 3 | }); 4 | -------------------------------------------------------------------------------- /resources/stubs/make/database/factories/DummyFactory.php.stub: -------------------------------------------------------------------------------- 1 | faker); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /resources/stubs/install/resources/views/vendor/form-components/bootstrap-4/form-group.blade.php.stub: -------------------------------------------------------------------------------- 1 |
merge(['class' => 'form-group ' . ($hasError($name) ? 'is-invalid' : '')]) !!}> 2 | 3 | 4 |
5 | {!! $slot !!} 6 |
7 | 8 | {!! $help ?? null !!} 9 | 10 | 11 |
12 | -------------------------------------------------------------------------------- /resources/views/layouts/modal.blade.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /resources/stubs/make/resources/views/dummy-route/create.blade.php.stub: -------------------------------------------------------------------------------- 1 | @extends('crudify::layouts.modal') 2 | 3 | @section('title', __('Create DummySingular')) 4 | @section('content') 5 | 6 | 9 | 10 | 14 | 15 | @endsection 16 | -------------------------------------------------------------------------------- /resources/stubs/make/resources/views/dummy-route/edit.blade.php.stub: -------------------------------------------------------------------------------- 1 | @extends('crudify::layouts.modal') 2 | 3 | @section('title', __('Edit DummySingular')) 4 | @section('content') 5 | 6 | 11 | 12 | 16 | 17 | @endsection 18 | -------------------------------------------------------------------------------- /resources/stubs/install/resources/views/vendor/form-components/bootstrap-4/form-textarea.blade.php.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 19 | 20 | {!! $help ?? null !!} 21 | 22 | 23 |
24 | -------------------------------------------------------------------------------- /src/Providers/CrudifyServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 15 | $this->commands([ 16 | CrudifyInstallCommand::class, 17 | MakeCrudCommand::class, 18 | MigrateAutoCommand::class, 19 | ]); 20 | } 21 | 22 | $this->loadMigrationsFrom(__DIR__ . '/../../database/migrations'); 23 | $this->loadViewsFrom(__DIR__ . '/../../resources/views', 'crudify'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /resources/stubs/make/resources/views/dummy-route/action.blade.php.stub: -------------------------------------------------------------------------------- 1 |
2 | 5 | 6 | 9 | 10 | 11 | 14 | 15 |
16 | -------------------------------------------------------------------------------- /resources/stubs/make/resources/views/dummy-route/index.blade.php.stub: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
5 |
6 |
7 |

{{ __('DummyPlural') }}

8 |
9 |
10 | 13 |
14 |
15 | 16 |
17 |
18 | {{ $dataTable->table() }} 19 |
20 |
21 |
22 | @endsection 23 | 24 | @push('scripts') 25 | {{ $dataTable->scripts() }} 26 | @endpush 27 | -------------------------------------------------------------------------------- /resources/stubs/install/resources/views/vendor/form-components/bootstrap-4/form-checkbox.blade.php.stub: -------------------------------------------------------------------------------- 1 |
2 | merge(['class' => 'form-check-input ' . ($hasError($name) ? 'is-invalid' : '')]) !!} 3 | type="checkbox" 4 | value="{{ $value }}" 5 | 6 | @if($isWired()) 7 | wire:model="{{ $name }}" 8 | @else 9 | name="{{ $name }}" 10 | @endif 11 | 12 | @if($label && !$attributes->get('id')) 13 | id="{{ $id() }}" 14 | @endif 15 | 16 | @if($checked) 17 | checked="checked" 18 | @endif 19 | 20 | crudify-form-element="{{ $name }}" 21 | /> 22 | 23 | 24 | 25 | {!! $help ?? null !!} 26 | 27 | 28 |
29 | -------------------------------------------------------------------------------- /resources/stubs/install/resources/views/vendor/form-components/bootstrap-4/form-radio.blade.php.stub: -------------------------------------------------------------------------------- 1 |
2 | merge(['class' => 'form-check-input ' . ($hasError($name) ? 'is-invalid' : '')]) !!} 3 | type="radio" 4 | 5 | @if($isWired()) 6 | wire:model="{{ $name }}" 7 | @else 8 | name="{{ $name }}" 9 | @endif 10 | 11 | value="{{ $value }}" 12 | 13 | @if($checked) 14 | checked="checked" 15 | @endif 16 | 17 | @if($label && !$attributes->get('id')) 18 | id="{{ $id() }}" 19 | @endif 20 | 21 | crudify-form-element="{{ $name }}" 22 | /> 23 | 24 | 25 | 26 | {!! $help ?? null !!} 27 | 28 | 29 |
30 | -------------------------------------------------------------------------------- /database/migrations/2020_09_23_074042_add_timezone_column_to_users_table.php: -------------------------------------------------------------------------------- 1 | string('timezone')->after('remember_token')->nullable(); 18 | }); 19 | } 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::table('users', function (Blueprint $table) { 30 | $table->dropColumn('timezone'); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /resources/stubs/make/app/Models/Dummy.php.stub: -------------------------------------------------------------------------------- 1 | id(); 19 | $table->timestamps(); 20 | $table->string('name'); 21 | } 22 | 23 | public static function definition(Generator $faker) 24 | { 25 | return [ 26 | 'name' => $faker->name, 27 | ]; 28 | } 29 | 30 | public static function rules(Dummy $dummy = null) 31 | { 32 | return [ 33 | 'name' => ['required', Rule::unique('dummy_table')->ignore($dummy->id ?? null)], 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redbastie/crudify", 3 | "description": "A Laravel 8 CRUD package for rapid scaffolding and development.", 4 | "homepage": "https://github.com/redbastie/crudify", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Kevin Dion", 9 | "email": "redbastie@icloud.com", 10 | "role": "Developer" 11 | } 12 | ], 13 | "require": { 14 | "doctrine/dbal": "^2.10", 15 | "jamesmills/laravel-timezone": "^1.9", 16 | "laravel/framework": "^8.0", 17 | "laravel/ui": "^3.0", 18 | "protonemedia/laravel-form-components": "^2.1", 19 | "yajra/laravel-datatables": "^1.5" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "Redbastie\\Crudify\\": "src" 24 | } 25 | }, 26 | "extra": { 27 | "laravel": { 28 | "providers": [ 29 | "Redbastie\\Crudify\\Providers\\CrudifyServiceProvider" 30 | ] 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /resources/stubs/make/resources/views/dummy-route/show.blade.php.stub: -------------------------------------------------------------------------------- 1 | @extends('crudify::layouts.modal') 2 | 3 | @section('title', __('DummySingular')) 4 | @section('content') 5 | 19 | 20 | 23 | @endsection 24 | -------------------------------------------------------------------------------- /resources/stubs/make/app/Http/DataTables/DummyDataTable.php.stub: -------------------------------------------------------------------------------- 1 | dataTables($query)->editColumn('action', 'dummy-route.action'); 19 | } 20 | 21 | public function query(Dummy $dummy) 22 | { 23 | return $dummy->newQuery(); 24 | } 25 | 26 | public function html() 27 | { 28 | return $this->tableBuilder()->orderBy(0, 'desc'); 29 | } 30 | 31 | protected function getColumns() 32 | { 33 | return [ 34 | Column::make('id'), 35 | Column::make('name'), 36 | Column::make('created_at'), 37 | Column::make('updated_at'), 38 | Column::computed('action')->title(''), 39 | ]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Traits/BuildsTables.php: -------------------------------------------------------------------------------- 1 | eloquent($query); 15 | 16 | foreach (app($this->model)->getDates() as $date) { 17 | $dataTables->editColumn($date, function ($model) use ($date) { 18 | return Timezone::convertToLocal($model->$date); 19 | }); 20 | } 21 | 22 | return $dataTables; 23 | } 24 | 25 | protected function tableBuilder() 26 | { 27 | return $this->builder() 28 | ->setTableId(Str::snake(class_basename($this->model), '-') . '-table') 29 | ->autoWidth(false) 30 | ->responsive() 31 | ->stateSave() 32 | ->stateDuration(0) 33 | ->stateSaveParams("function (settings, data) { data.search.search = ''; data.start = 0; }") 34 | ->minifiedAjax() 35 | ->columns($this->getColumns()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /resources/stubs/install/resources/views/vendor/form-components/bootstrap-4/form-select.blade.php.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 33 | 34 | {!! $help ?? null !!} 35 | 36 | 37 |
38 | -------------------------------------------------------------------------------- /resources/stubs/install/resources/views/vendor/form-components/bootstrap-4/form-input.blade.php.stub: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | @isset($prepend) 6 |
7 |
8 | {!! $prepend !!} 9 |
10 |
11 | @endisset 12 | 13 | merge(['class' => 'form-control ' . ($hasError($name) ? 'is-invalid' : '')]) !!} 14 | type="{{ $type }}" 15 | 16 | @if($isWired()) 17 | wire:model="{{ $name }}" 18 | @else 19 | name="{{ $name }}" 20 | value="{{ $value }}" 21 | @endif 22 | 23 | @if($label && !$attributes->get('id')) 24 | id="{{ $id() }}" 25 | @endif 26 | 27 | crudify-form-element="{{ $name }}" 28 | /> 29 | 30 | @isset($append) 31 |
32 |
33 | {!! $append !!} 34 |
35 |
36 | @endisset 37 |
38 | 39 | {!! $help ?? null !!} 40 | 41 | 42 |
43 | -------------------------------------------------------------------------------- /resources/sass/_crudify.scss: -------------------------------------------------------------------------------- 1 | @import '~@fortawesome/fontawesome-free/css/all.css'; 2 | @import '~datatables.net-bs4/css/dataTables.bootstrap4.css'; 3 | @import '~datatables.net-responsive-bs4/css/responsive.bootstrap4.css'; 4 | 5 | $gray-100: #f8f9fa; 6 | $body-bg: $gray-100; 7 | $font-size-base: 0.9rem; 8 | $card-cap-bg: #ffffff; 9 | $border-radius: 0.5rem; 10 | $border-radius-sm: $border-radius; 11 | $border-radius-lg: $border-radius; 12 | $table-hover-bg: $gray-100; 13 | $enable-validation-icons: false; 14 | 15 | .card { 16 | border: none !important; 17 | box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); 18 | } 19 | 20 | .card-body table.dataTable { 21 | margin-left: -1.25rem; 22 | margin-right: -1.25rem; 23 | width: calc(100% + 2.5rem); 24 | } 25 | table.dataTable tr th { 26 | white-space: nowrap; 27 | } 28 | table.dataTable tr th, table.dataTable tr td { 29 | vertical-align: middle; 30 | } 31 | div.dataTables_paginate { 32 | overflow-x: auto 33 | } 34 | div.dataTables_paginate .pagination { 35 | display: inline-table 36 | } 37 | div.dataTables_paginate .pagination li { 38 | display: table-cell 39 | } 40 | 41 | @media (min-width: 768px) { 42 | .card-body table.dataTable tr th:first-child, .card-body table.dataTable tr td:first-child { 43 | padding-left: 1.25rem; 44 | } 45 | .card-body table.dataTable tr th:last-child, .card-body table.dataTable tr td:last-child { 46 | padding-right: 1.25rem; 47 | } 48 | } 49 | 50 | @media (max-width: 767.98px) { 51 | table.dataTable > tbody > tr.child span.dtr-title:empty { 52 | display: none; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Helpers/AutoRoute.php: -------------------------------------------------------------------------------- 1 | getMethods() as $method) { 17 | $httpMethod = explode('_', Str::snake($method->name))[0]; 18 | 19 | if (in_array($httpMethod, $httpMethods) && $method->class == $reflection->name) { 20 | $methodName = Str::snake(Str::replaceFirst($httpMethod, '', $method->name), '-'); 21 | $methodPath = $path . ($methodName != 'index' ? '/' . $methodName : null); 22 | $name = trim(str_replace('/', '.', $path) . '.' . $methodName, '.'); 23 | $parameters = []; 24 | 25 | foreach ($method->getParameters() as $parameter) { 26 | $parameterClass = $parameter->getClass(); 27 | 28 | if (!$parameterClass || $parameterClass->isSubclassOf('Illuminate\Database\Eloquent\Model')) { 29 | $parameters[] = '{' . $parameter->name . ($parameter->isOptional() ? '?' : null) . '}'; 30 | } 31 | } 32 | 33 | if ($parameters) { 34 | $methodPath .= '/' . implode('/', $parameters); 35 | } 36 | 37 | Route::$httpMethod($methodPath, [$reflection->name, $method->name])->name($name); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /resources/stubs/make/app/Http/Controllers/DummyController.php.stub: -------------------------------------------------------------------------------- 1 | middleware('auth'); 13 | } 14 | 15 | public function getIndex(DummyDataTable $dummyDataTable) 16 | { 17 | return $dummyDataTable->render('dummy-route.index'); 18 | } 19 | 20 | public function getCreate() 21 | { 22 | return view('dummy-route.create'); 23 | } 24 | 25 | public function postCreate() 26 | { 27 | request()->validate(Dummy::rules()); 28 | 29 | Dummy::query()->create(request()->all()); 30 | 31 | return response()->json([ 32 | 'dismiss_modal' => true, 33 | 'reload_table' => true, 34 | ]); 35 | } 36 | 37 | public function getShow(Dummy $dummy) 38 | { 39 | return view('dummy-route.show', compact('dummy')); 40 | } 41 | 42 | public function getEdit(Dummy $dummy) 43 | { 44 | return view('dummy-route.edit', compact('dummy')); 45 | } 46 | 47 | public function patchEdit(Dummy $dummy) 48 | { 49 | request()->validate(Dummy::rules($dummy)); 50 | 51 | $dummy->update(request()->all()); 52 | 53 | return response()->json([ 54 | 'dismiss_modal' => true, 55 | 'reload_table' => true, 56 | ]); 57 | } 58 | 59 | public function deleteDestroy(Dummy $dummy) 60 | { 61 | $dummy->forceDelete(); 62 | 63 | return response()->json([ 64 | 'reload_table' => true, 65 | ]); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Commands/CrudifyInstallCommand.php: -------------------------------------------------------------------------------- 1 | getOutput()); 17 | 18 | $filesystem = new Filesystem; 19 | 20 | foreach ($filesystem->allFiles(__DIR__ . '/../../resources/stubs/install') as $stub) { 21 | $filesystem->ensureDirectoryExists(base_path($stub->getRelativePath())); 22 | $path = base_path(Str::replaceLast('.stub', '', $stub->getRelativePathname())); 23 | $filesystem->put($path, $stub->getContents()); 24 | } 25 | 26 | $appConfig = $filesystem->get(config_path('app.php')); 27 | $appVersion = "'version' => '1.0.0',"; 28 | if (!Str::contains($appConfig, $appVersion)) { 29 | $appName = str_repeat(' ', 4) . "'name' => env('APP_NAME', 'Laravel'),"; 30 | $appConfig = str_replace($appName, $appName . PHP_EOL . $appVersion, $appConfig); 31 | $filesystem->put(config_path('app.php'), $appConfig); 32 | } 33 | 34 | $routes = $filesystem->get(base_path('routes/web.php')); 35 | $routes = str_replace("Auth::routes();", "Auth::routes(['register' => false]);", $routes); 36 | $autoRouteImport = 'use Redbastie\Crudify\Helpers\AutoRoute;'; 37 | if (!Str::contains($routes, $autoRouteImport)) { 38 | $routeFacade = 'use Illuminate\Support\Facades\Route;'; 39 | $routes = str_replace($routeFacade, $routeFacade . PHP_EOL . $autoRouteImport, $routes); 40 | } 41 | $filesystem->put(base_path('routes/web.php'), $routes); 42 | 43 | $packages = json_decode($filesystem->get(base_path('package.json')), true); 44 | $packages['devDependencies']['@fortawesome/fontawesome-free'] = '^5.14.0'; 45 | $packages['devDependencies']['datatables.net-bs4'] = '^1.10.22'; 46 | $packages['devDependencies']['datatables.net-responsive-bs4'] = '^2.2.6'; 47 | $packages = json_encode($packages, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); 48 | $filesystem->put(base_path('package.json'), $packages); 49 | 50 | exec('npm install && npm run dev'); 51 | 52 | $this->info('Crudify installed.'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Commands/MigrateAutoCommand.php: -------------------------------------------------------------------------------- 1 | option('fresh')) { 20 | Artisan::call('migrate:fresh --force'); 21 | } 22 | else { 23 | Artisan::call('migrate --force'); 24 | } 25 | 26 | foreach ((new Filesystem)->allFiles(app_path('Models')) as $modelFile) { 27 | $modelClass = str_replace([app_path(), '/', '.php'], ['App', '\\', ''], $modelFile->getPathname()); 28 | $reflection = new ReflectionClass($modelClass); 29 | 30 | if ($reflection->hasMethod('migration')) { 31 | $model = app($reflection->name); 32 | 33 | if (Schema::hasTable($model->getTable())) { 34 | $tempTable = 'temp_' . $model->getTable(); 35 | 36 | Schema::dropIfExists($tempTable); 37 | Schema::create($tempTable, function (Blueprint $table) use ($model) { 38 | $model->migration($table); 39 | }); 40 | 41 | $schemaManager = $model->getConnection()->getDoctrineSchemaManager(); 42 | $modelTableDetails = $schemaManager->listTableDetails($model->getTable()); 43 | $tempTableDetails = $schemaManager->listTableDetails($tempTable); 44 | $tableDiff = (new Comparator)->diffTable($modelTableDetails, $tempTableDetails); 45 | 46 | if ($tableDiff) { 47 | $schemaManager->alterTable($tableDiff); 48 | } 49 | 50 | Schema::drop($tempTable); 51 | } 52 | else { 53 | Schema::create($model->getTable(), function (Blueprint $table) use ($model) { 54 | $model->migration($table); 55 | }); 56 | } 57 | } 58 | } 59 | 60 | $this->info('Migration complete.'); 61 | 62 | if ($this->option('seed')) { 63 | Artisan::call('db:seed'); 64 | 65 | $this->info('Seeding complete.'); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Commands/MakeCrudCommand.php: -------------------------------------------------------------------------------- 1 | argument('model'), ' ')); 17 | $plural = Str::plural($singular); 18 | $this->replace = [ 19 | 'dummy_table' => Str::snake($plural), 20 | 'dummy-route' => Str::snake($plural, '-'), 21 | 'DummySingular' => $singular, 22 | 'DummyPlural' => $plural, 23 | 'Dummy' => $this->argument('model'), 24 | 'dummy' => Str::camel($this->argument('model')), 25 | '.stub' => '', 26 | ]; 27 | $filesystem = new Filesystem; 28 | 29 | foreach ($filesystem->allFiles(__DIR__ . '/../../resources/stubs/make') as $stub) { 30 | if ($stub->getRelativePath()) { 31 | $filesystem->ensureDirectoryExists(base_path($this->replace($stub->getRelativePath()))); 32 | $filesystem->put(base_path($this->replace($stub->getRelativePathname())), $this->replace($stub->getContents())); 33 | } 34 | } 35 | 36 | $navItem = $this->replace(rtrim($filesystem->get(__DIR__ . '/../../resources/stubs/make/nav-item.blade.php.stub'))); 37 | $layout = $filesystem->get(resource_path('views/layouts/app.blade.php')); 38 | if (!Str::contains($layout, $navItem)) { 39 | $hook = '{{--nav-item hook--}}'; 40 | $layout = str_replace($hook, $navItem . PHP_EOL . str_repeat(' ', 24) . $hook, $layout); 41 | $filesystem->put(resource_path('views/layouts/app.blade.php'), $layout); 42 | } 43 | 44 | $autoRoute = $this->replace(rtrim($filesystem->get(__DIR__ . '/../../resources/stubs/make/AutoRoute.stub'))); 45 | $routes = rtrim($filesystem->get(base_path('routes/web.php'))); 46 | if (!Str::contains($routes, $autoRoute)) { 47 | $routes = str_replace($routes, $routes . PHP_EOL . $autoRoute . PHP_EOL, $routes); 48 | $filesystem->put(base_path('routes/web.php'), $routes); 49 | } 50 | 51 | $this->info($this->argument('model') . ' CRUD made.'); 52 | $this->warn("Don't forget to migrate:auto after configuring migration."); 53 | } 54 | 55 | private function replace($contents) 56 | { 57 | return str_replace(array_keys($this->replace), array_values($this->replace), $contents); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /resources/js/crudify.js: -------------------------------------------------------------------------------- 1 | require('datatables.net-bs4'); 2 | require('datatables.net-responsive-bs4'); 3 | 4 | $(document).on('click', '[crudify-show-modal]', function () { 5 | $.get($(this).attr('crudify-show-modal'), function (response) { 6 | $(response).modal('show'); 7 | }); 8 | }); 9 | $(document).on('shown.bs.modal', '[crudify-modal]', function () { 10 | $(this).find('script').each(function () { 11 | eval($(this).text()); 12 | }); 13 | }); 14 | $(document).on('hidden.bs.modal', '[crudify-modal]', function () { 15 | $(this).remove(); 16 | }); 17 | 18 | $(document).on('submit', '[crudify-form]', function (event) { 19 | event.preventDefault(); 20 | 21 | let form = $(this); 22 | let submit = $(this).find(':submit'); 23 | 24 | if (form.attr('crudify-form') !== 'submitted') { 25 | form.attr('crudify-form', 'submitted'); 26 | submit.attr('crudify-form-submit', submit.html()); 27 | submit.css('width', submit.css('width')); 28 | submit.html(''); 29 | 30 | $.ajax({ 31 | url: form.attr('action'), 32 | type: form.attr('method'), 33 | data: new FormData(form[0]), 34 | contentType: false, 35 | processData: false, 36 | error: function (response) { 37 | if (response.hasOwnProperty('responseJSON')) { 38 | $('.is-invalid').removeClass('is-invalid'); 39 | $('.invalid-feedback').html('').addClass('d-none').removeClass('d-block'); 40 | 41 | $.each(response.responseJSON.errors, function (key, value) { 42 | $('[crudify-form-element="' + key + '"]').addClass('is-invalid'); 43 | $('[crudify-form-error="' + key + '"]').html(value[0]).addClass('d-block').removeClass('d-none'); 44 | }); 45 | } 46 | }, 47 | complete: function () { 48 | form.attr('crudify-form', ''); 49 | submit.html(submit.attr('crudify-form-submit')); 50 | submit.removeAttr('crudify-form-submit'); 51 | } 52 | }); 53 | } 54 | }); 55 | 56 | $(document).ajaxComplete(function (event, response) { 57 | if (response.hasOwnProperty('responseJSON')) { 58 | let json = response.responseJSON; 59 | 60 | if (json.hasOwnProperty('redirect')) $(location).attr('href', json.redirect); 61 | if (json.hasOwnProperty('reload_page')) location.reload(); 62 | if (json.hasOwnProperty('reload_table')) $($.fn.dataTable.tables()).DataTable().ajax.reload(null, false); 63 | if (json.hasOwnProperty('show_modal')) $(json.show_modal).modal('show'); 64 | if (json.hasOwnProperty('dismiss_modal')) $('[crudify-modal]').modal('toggle'); 65 | if (json.hasOwnProperty('jquery')) $(json.jquery.selector)[json.jquery.method](json.jquery.content); 66 | } 67 | }); 68 | 69 | $(document).on('click', '[crudify-confirm]', function () { 70 | return confirm($(this).attr('crudify-confirm')); 71 | }); 72 | -------------------------------------------------------------------------------- /resources/stubs/install/resources/views/layouts/app.blade.php.stub: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('app.name', 'Laravel') }} 9 | 10 | 11 | @stack('scripts') 12 | 13 | 14 | 15 | 16 | 60 | 61 |
62 | @yield('content') 63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # NO LONGER MAINTAINED 2 | 3 | This package is no longer maintained. Please consider my latest package here: https://github.com/redbastie/tailwire 4 | 5 | ----- 6 | 7 | # Crudify 8 | 9 | Crudify is a Laravel 8 CRUD package which promotes rapid scaffolding and development. It uses a tried and true stack and intuitive techniques that will save you time and hassles. 10 | 11 | 12 | 13 | ## Requirements 14 | 15 | - A server compatible with Laravel 8 16 | - Composer 17 | - NPM 18 | 19 | ## Features 20 | 21 | - Automatic user timezones 22 | - AJAX forms, modals, and response handlers 23 | - Responsive data tables 24 | - Font Awesome icons 25 | - Sensible Bootstrap styling out of the box 26 | - CRUD generator command (`make:crud`) 27 | - Automatic migrations command (`migrate:auto`) 28 | - Migration, factory, and rule definitions inside models 29 | - Automatic routing based on controller methods 30 | - Dynamic model fillables 31 | - & more 32 | 33 | ## Third Party Packages Used 34 | 35 | - [doctrine/dbal](https://github.com/doctrine/dbal) 36 | - [jamesmills/laravel-timezone](https://github.com/jamesmills/laravel-timezone) 37 | - [laravel/ui](https://github.com/laravel/ui) 38 | - [protonemedia/laravel-form-components](https://github.com/protonemedia/laravel-form-components) 39 | - [yajra/laravel-datatables](https://github.com/yajra/laravel-datatables) 40 | 41 | ## Links 42 | 43 | - Support: [GitHub Issues](https://github.com/redbastie/crudify/issues) 44 | - Contribute: [GitHub Pulls](https://github.com/redbastie/crudify/pulls) 45 | - Donate: [PayPal](https://www.paypal.com/paypalme2/kjjdion) 46 | 47 | ## Installation 48 | 49 | Crudify was designed to work with a clean Laravel 8 install. 50 | 51 | Install Laravel: 52 | 53 | laravel new vehicle-app 54 | 55 | Configure the database in your `.env` file: 56 | 57 | DB_DATABASE=vehicle_app 58 | DB_USERNAME=root 59 | DB_PASSWORD= 60 | 61 | Now, install Crudify via composer: 62 | 63 | composer require redbastie/crudify 64 | 65 | Then, run the Crudify install command: 66 | 67 | php artisan crudify:install 68 | 69 | All done. The only thing left to do is create a user, either via `tinker` or the `DatabaseSeeder`. 70 | 71 | ## Usage Example 72 | 73 | Generate CRUD for a new model e.g. a `Vehicle` 74 | 75 | php artisan make:crud Vehicle 76 | 77 | This will generate your controller, data table, model, factory, views, nav item, and auto route. 78 | 79 | Modify the `migration` method inside the new `Vehicle` model class: 80 | 81 | public function migration(Blueprint $table) 82 | { 83 | $table->id(); 84 | $table->timestamps(); 85 | $table->string('name'); 86 | $table->string('brand'); 87 | } 88 | 89 | You can also specify the factory definition and rules in the model: 90 | 91 | public static function definition(Generator $faker) 92 | { 93 | return [ 94 | 'name' => $faker->name, 95 | 'brand' => $faker->company, 96 | ]; 97 | } 98 | 99 | public static function rules(Vehicle $vehicle = null) 100 | { 101 | return [ 102 | 'name' => ['required', Rule::unique('vehicles')->ignore($vehicle->id ?? null)], 103 | 'brand' => ['required'], 104 | ]; 105 | } 106 | 107 | Specify a `Vehicle` seeder in the `DatabaseSeeder` class: 108 | 109 | \App\Models\User::factory()->create([ 110 | 'email' => 'admin@example.com', 111 | ]); 112 | 113 | \App\Models\Vehicle::factory(100)->create(); 114 | 115 | Note that I've added a `User` seeder here as well, which we will use to log in with using the password `password` after. 116 | 117 | Add some data table columns in the `VehicleDataTable` class: 118 | 119 | protected function getColumns() 120 | { 121 | return [ 122 | Column::make('id'), 123 | Column::make('name'), 124 | Column::make('brand'), 125 | Column::make('created_at'), 126 | Column::computed('action')->title(''), 127 | ]; 128 | } 129 | 130 | Add form fields in the `vehicles/form.blade.php` view file: 131 | 132 | 133 | 134 | 135 | Run a fresh automatic migration command with seeding: 136 | 137 | php artisan migrate:auto --fresh --seed 138 | 139 | You can specify `--fresh` and/or `--seed` in the `migrate:auto` command in order to run fresh migrations and/or seed afterwards. 140 | 141 | Now you should be able to login to your app and click on the `Vehicles` link in the navbar to perform CRUD operations on the seeded data. 142 | 143 | To get an idea how the automatic routing works, check out the `VehicleController`. After updating controller methods, use `php artisan route:list` to see your route info. 144 | --------------------------------------------------------------------------------