├── 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 |
{{ __('DummyPlural') }}
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 |
2 |
3 |
4 |
10 | @yield('content')
11 |
12 |
13 |
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 |
7 | @include('dummy-route.form')
8 |
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 |
7 | @bind($dummy)
8 | @include('dummy-route.form')
9 | @endbind
10 |
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 |
6 | @foreach($dummy->getFillable() as $fillable)
7 |
{{ Str::title(str_replace('_', ' ', $fillable)) }}
8 |
9 | @if(is_array($dummy->$fillable))
10 |
{{ json_encode($dummy->$fillable, JSON_PRETTY_PRINT) }}
11 | @elseif(in_array($fillable, $dummy->getDates()))
12 | @displayDate($dummy->$fillable)
13 | @else
14 | {{ $dummy->$fillable ?? __('N/A') }}
15 | @endif
16 |
17 | @endforeach
18 |
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 |
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 |
--------------------------------------------------------------------------------