├── docs ├── .vitepress │ ├── cache │ │ └── deps │ │ │ ├── package.json │ │ │ ├── _metadata.json │ │ │ ├── @theme_index.js.map │ │ │ └── @theme_index.js │ └── config.js ├── guide │ ├── roadmap.md │ ├── about.md │ ├── license.md │ ├── search.md │ ├── remove.md │ ├── soft-deletes.md │ ├── authentication.md │ ├── gotchas.md │ ├── nesting.md │ ├── install.md │ ├── options.md │ ├── crud.md │ ├── customization.md │ └── index.md ├── badges.md ├── index.md ├── readme.php ├── LICENSE.md └── CONTRIBUTING.md ├── config └── crud.php ├── src ├── template │ └── stubs │ │ ├── views │ │ ├── bootstrap │ │ │ ├── partials │ │ │ │ ├── display │ │ │ │ │ ├── boolean.blade.php │ │ │ │ │ ├── string.blade.php │ │ │ │ │ ├── text.blade.php │ │ │ │ │ └── json.blade.php │ │ │ │ ├── inputs │ │ │ │ │ ├── json.blade.php │ │ │ │ │ ├── textarea.blade.php │ │ │ │ │ ├── input.blade.php │ │ │ │ │ ├── select.blade.php │ │ │ │ │ ├── boolean.blade.php │ │ │ │ │ └── related.blade.php │ │ │ │ ├── render │ │ │ │ │ ├── breadcrumbs.blade.php │ │ │ │ │ ├── form.blade.php │ │ │ │ │ ├── cards.blade.php │ │ │ │ │ ├── show.blade.php │ │ │ │ │ ├── chat.blade.php │ │ │ │ │ └── table.blade.php │ │ │ │ └── buttons │ │ │ │ │ ├── actions-expanded.blade.php │ │ │ │ │ ├── actions-trash.blade.php │ │ │ │ │ └── actions.blade.php │ │ │ ├── layout.blade.php │ │ │ ├── create.blade.php │ │ │ ├── edit.blade.php │ │ │ ├── show.blade.php │ │ │ ├── create-edit.blade.php │ │ │ └── index.blade.php │ │ ├── larastrap │ │ │ ├── partials │ │ │ │ ├── display │ │ │ │ │ ├── boolean.blade.php │ │ │ │ │ ├── string.blade.php │ │ │ │ │ ├── text.blade.php │ │ │ │ │ └── json.blade.php │ │ │ │ ├── render │ │ │ │ │ ├── form.blade.php │ │ │ │ │ ├── breadcrumbs.blade.php │ │ │ │ │ ├── cards.blade.php │ │ │ │ │ ├── show.blade.php │ │ │ │ │ ├── chat.blade.php │ │ │ │ │ └── table.blade.php │ │ │ │ ├── inputs │ │ │ │ │ ├── input.blade.php │ │ │ │ │ ├── json.blade.php │ │ │ │ │ ├── textarea.blade.php │ │ │ │ │ ├── select.blade.php │ │ │ │ │ ├── boolean.blade.php │ │ │ │ │ └── related.blade.php │ │ │ │ └── buttons │ │ │ │ │ ├── actions-expanded.blade.php │ │ │ │ │ ├── actions-trash.blade.php │ │ │ │ │ └── actions.blade.php │ │ │ ├── layout.blade.php │ │ │ ├── edit.blade.php │ │ │ ├── create.blade.php │ │ │ ├── show.blade.php │ │ │ ├── create-edit.blade.php │ │ │ └── index.blade.php │ │ └── tailwind │ │ │ ├── partials │ │ │ ├── display │ │ │ │ ├── boolean.blade.php │ │ │ │ ├── string.blade.php │ │ │ │ ├── text.blade.php │ │ │ │ └── json.blade.php │ │ │ ├── inputs │ │ │ │ ├── json.blade.php │ │ │ │ ├── textarea.blade.php │ │ │ │ ├── input.blade.php │ │ │ │ ├── select.blade.php │ │ │ │ ├── boolean.blade.php │ │ │ │ └── related.blade.php │ │ │ ├── render │ │ │ │ ├── form.blade.php │ │ │ │ ├── breadcrumbs.blade.php │ │ │ │ ├── cards.blade.php │ │ │ │ ├── show.blade.php │ │ │ │ ├── chat.blade.php │ │ │ │ └── table.blade.php │ │ │ └── buttons │ │ │ │ ├── actions-trash.blade.php │ │ │ │ └── actions.blade.php │ │ │ ├── edit.blade.php │ │ │ ├── create.blade.php │ │ │ ├── create-edit.blade.php │ │ │ ├── index.blade.php │ │ │ ├── show.blade.php │ │ │ └── layout.blade.php │ │ ├── Models │ │ └── _model_.php │ │ ├── routes │ │ └── _route_.php │ │ ├── Policies │ │ └── _policy_.php │ │ └── Controllers │ │ └── _controller_.php ├── Crud.php ├── Formatters │ └── Formatter.php ├── Utils │ ├── TextUtils.php │ ├── RouteUtils.php │ ├── NameUtils.php │ ├── FileUtils.php │ └── SchemaUtils.php ├── ServiceProvider.php ├── Generators │ ├── Templates.php │ ├── PolicyGen.php │ ├── ModelGen.php │ ├── RouteGen.php │ ├── BaseGen.php │ ├── ViewGen.php │ └── ControllerGen.php └── Commands │ ├── CrudTemplate.php │ ├── CrudBase.php │ ├── CrudRemove.php │ └── CrudGenerate.php ├── package.json ├── .github ├── ISSUE_TEMPLATE │ └── bug-reporting.md └── workflows │ └── deploy.yml ├── database └── migrations │ ├── replies_table.php │ ├── leads_table.php │ ├── tickets_table.php │ └── users_table.php ├── composer.json ├── LICENSE.md ├── .phpunit.result.cache └── yarn.lock /docs/.vitepress/cache/deps/package.json: -------------------------------------------------------------------------------- 1 | {"type":"module"} -------------------------------------------------------------------------------- /config/crud.php: -------------------------------------------------------------------------------- 1 | NULL, 5 | ]; -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/display/boolean.blade.php: -------------------------------------------------------------------------------- 1 | {{ $_var_->_col_ ? "Yes" : "No" }} 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/display/string.blade.php: -------------------------------------------------------------------------------- 1 | {{ $_var_->_col_ ?: "(blank)" }} 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/display/boolean.blade.php: -------------------------------------------------------------------------------- 1 | {{ $_var_->_col_ ? "Yes" : "No" }} 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/display/string.blade.php: -------------------------------------------------------------------------------- 1 | {{ $_var_->_col_ ?: "(blank)" }} 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/display/boolean.blade.php: -------------------------------------------------------------------------------- 1 | {{ $_var_->_col_ ? "Yes" : "No" }} 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/display/string.blade.php: -------------------------------------------------------------------------------- 1 | {{ $_var_->_col_ ?: "(blank)" }} 2 | -------------------------------------------------------------------------------- /docs/guide/roadmap.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | - Add support for Livewire 4 | - Add support for Laravel Jetstream 5 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/display/text.blade.php: -------------------------------------------------------------------------------- 1 | {{ Str::limit($_var_->_col_, 50) ?: "(blank)"}} 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/display/text.blade.php: -------------------------------------------------------------------------------- 1 | {{ Str::limit($_var_->_col_, 50) ?: "(blank)"}} 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/display/text.blade.php: -------------------------------------------------------------------------------- 1 | {{ Str::limit($_var_->_col_, 50) ?: "(blank)"}} 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/render/form.blade.php: -------------------------------------------------------------------------------- 1 | @foreach($fields as $field) 2 | {!! $input($field) !!} 3 | @endforeach 4 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/inputs/input.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/inputs/json.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/inputs/textarea.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/inputs/json.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/render/breadcrumbs.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/render/breadcrumbs.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/display/json.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/display/json.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/inputs/select.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/inputs/boolean.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/inputs/textarea.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/inputs/input.blade.php: -------------------------------------------------------------------------------- 1 | $errors->has('_id_')])> 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/display/json.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/guide/about.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | **Found any bugs?** Report them on the [issue tracker](https://github.com/san-kumar/laravel-crud/issues). 4 | 5 | **Want to contribute?** Fork the project on GitHub and send a 6 | pull request. 7 | 8 | **Like the project?** Star it on GitHub. 9 | -------------------------------------------------------------------------------- /src/Crud.php: -------------------------------------------------------------------------------- 1 | {{_val_}} 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/inputs/textarea.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/guide/license.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | The software is licensed using a MIT License. It means you 4 | can do whatever you want with it (including using it for 5 | commercial purposes freely), as long as you include the 6 | original copyright and license notice in any copy of the 7 | software/source. 8 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/render/form.blade.php: -------------------------------------------------------------------------------- 1 | @foreach($fields as $field) 2 |
3 | 4 | {!! $input($field) !!} 5 | {!! $err($field) !!} 6 |
7 | @endforeach 8 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/inputs/input.blade.php: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/inputs/select.blade.php: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/inputs/related.blade.php: -------------------------------------------------------------------------------- 1 | 2 | Create new _label_ 3 | -------------------------------------------------------------------------------- /src/Formatters/Formatter.php: -------------------------------------------------------------------------------- 1 | hasSoftDeletes()) { 11 | $str = preg_replace('#//@softdelete.*?@endsoftdelete#s', '', $str); 12 | } 13 | 14 | return $str; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/render/form.blade.php: -------------------------------------------------------------------------------- 1 | @foreach($fields as $field) 2 |
3 |
4 | 5 | {!! $input($field) !!} 6 | {!! $err($field) !!} 7 |
8 |
9 | @endforeach 10 | -------------------------------------------------------------------------------- /src/template/stubs/Models/_model_.php: -------------------------------------------------------------------------------- 1 | ", 7 | "license": "MIT", 8 | "scripts": { 9 | "docs:dev": "vitepress dev docs --host localhost", 10 | "docs:build": "vitepress build docs", 11 | "docs:preview": "vitepress preview docs" 12 | }, 13 | "devDependencies": { 14 | "vitepress": "^1.0.0-alpha.33", 15 | "vue": "^3.2.45" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docs/guide/search.md: -------------------------------------------------------------------------------- 1 | # Inbuilt search 2 | 3 | A very simple search is added to the list view by default. 4 | It searches the first text column of the table and filters 5 | the results. 6 | 7 | This generated search code is just a placeholder, and you 8 | should customize it as per your own needs. 9 | 10 | > ![Inbuilt search](https://cdn.articlevideorobot.com/hosted/22-12-2022/selection-306-99d5.webp) 11 | > (Inbuilt search) 12 | 13 | To modify the search algorithm just edit the `index` method 14 | of the Resource controller for that route. -------------------------------------------------------------------------------- /src/template/stubs/routes/_route_.php: -------------------------------------------------------------------------------- 1 | group(function () { 7 | Route::resource('_parentrouteurlwithvars_', _controller_::class, [/*_asroute_*/]); 8 | //@softdelete 9 | Route::put('_routeurlwithvars_/restore', [_controller_::class, 'restore'])->name('_route_.restore'); 10 | Route::delete('_routeurlwithvars_/purge', [_controller_::class, 'purge'])->name('_route_.purge'); 11 | //@endsoftdelete 12 | }); 13 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/_metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "576f85bf", 3 | "browserHash": "2852436b", 4 | "optimized": { 5 | "vue": { 6 | "src": "../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js", 7 | "file": "vue.js", 8 | "fileHash": "2b8c4af0", 9 | "needsInterop": false 10 | }, 11 | "@theme/index": { 12 | "src": "../../../../node_modules/vitepress/dist/client/theme-default/index.js", 13 | "file": "@theme_index.js", 14 | "fileHash": "eaeed8d1", 15 | "needsInterop": false 16 | } 17 | }, 18 | "chunks": {} 19 | } -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/inputs/select.blade.php: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/inputs/boolean.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
6 |
7 | 8 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-reporting.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: bug reporting 3 | about: How to report an issue 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Before submitting a bug report please make sure to include the following information 11 | 12 | 1. Have you set the mysql password in the .env file? 13 | 14 | 2. Have you created the db migrations using `artisan migrate`? 15 | 16 | 3. Can laravel can connect to your mysql db? 17 | 18 | 4. Are you able to see the output for `User:all()`? 19 | 20 | 5. Can you see the tables you've created in the mysql database? (using phpmyadmin or mysql client) 21 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/inputs/related.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 8 | 9 | New 10 |
11 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/buttons/actions-expanded.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @@lang('Show') 3 | @@lang('Edit') 4 |
5 | @@csrf 6 | @@method('DELETE') 7 | 8 |
9 |
-------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/buttons/actions-expanded.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @@lang('Show') 3 | @@lang('Edit') 4 |
5 | @@csrf 6 | @@method('DELETE') 7 | 8 |
9 |
-------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/render/breadcrumbs.blade.php: -------------------------------------------------------------------------------- 1 |
  • 2 | 3 | 4 | 5 | 6 | {!! $anchortext !!} 7 | 8 |
  • -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/render/cards.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 | @@foreach ($_vars_ as $_var_) 4 |
    5 |
    6 |
    _title_ #@{{ $_var_->id }}
    7 |

    @{{ $_var_->_readable_ }}

    8 |
    9 | 16 |
    17 | @@endforeach 18 | 19 |
    -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/render/cards.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 | @@foreach ($_vars_ as $_var_) 4 |
    5 |
    6 |
    _title_ #@{{ $_var_->id }}
    7 |

    @{{ $_var_->_readable_ }}

    8 |
    9 | 16 |
    17 | @@endforeach 18 | 19 |
    -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/buttons/actions-trash.blade.php: -------------------------------------------------------------------------------- 1 | @@if($_var_->trashed()) 2 |
    3 | @@csrf 4 | @@method('PUT') 5 | 6 |
    7 |
    8 | @@csrf 9 | @@method('DELETE') 10 | 11 |
    12 | @@else 13 | {!! $render('buttons/actions') !!} 14 | @@endif 15 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/buttons/actions-trash.blade.php: -------------------------------------------------------------------------------- 1 | @@if($_var_->trashed()) 2 |
    3 | @@csrf 4 | @@method('PUT') 5 | 6 |
    7 |
    8 | @@csrf 9 | @@method('DELETE') 10 | 11 |
    12 | @@else 13 | {!! $render('buttons/actions') !!} 14 | @@endif 15 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | with: 14 | fetch-depth: 0 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: 16 18 | cache: yarn 19 | - run: yarn install --frozen-lockfile 20 | 21 | - name: Build 22 | run: yarn docs:build 23 | 24 | - name: Deploy 25 | uses: peaceiris/actions-gh-pages@v3 26 | with: 27 | github_token: ${{ secrets.GITHUB_TOKEN }} 28 | publish_dir: docs/.vitepress/dist 29 | # cname: example.com # if wanna deploy to custom domain -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/inputs/boolean.blade.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 |
    6 |
    7 | 8 | 9 |
    10 |
    11 | -------------------------------------------------------------------------------- /docs/guide/remove.md: -------------------------------------------------------------------------------- 1 | # Removing the generated CRUD files 2 | 3 | ## Remove files 4 | 5 | Laravel CRUD Generator allows you to remove the generated 6 | files using the `artisan crud:remove` command. 7 | 8 | ```bash 9 | $ php artisan crud:remove customers.tickets.replies 10 | ``` 11 | 12 | This will remove all the files generated by 13 | the `crud:generate` for this table. 14 | 15 | ## Backup 16 | 17 | If you want to keep the generated files, you can use the 18 | `--backup` option to move the files into a Zip archive 19 | instead. 20 | 21 | ```bash 22 | $ php artisan crud:remove customers.tickets.replies --backup=backup.zip 23 | ``` 24 | 25 | This too will remove the files from the disk but will also 26 | create a Zip archive of all the removed files too. 27 | 28 | -------------------------------------------------------------------------------- /docs/badges.md: -------------------------------------------------------------------------------- 1 | [![Software License](https://img.shields.io/badge/license-MIT-green.svg?style=flat-square)](LICENSE.md) 2 | [![Build Status](https://img.shields.io/circleci/project/github/san-kumar/laravel-crud.svg?style=flat-square)](https://circleci.com/gh/san-kumar/laravel-crud) 3 | [![Coverage Status](https://img.shields.io/badge/coverage-94%25-brightgreen)](https://raw.githack.com/san-kumar/laravel-crud/main/build/coverage/index.html) 4 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/san-kumar/laravel-crud.svg?style=flat-square)](https://packagist.org/packages/san-kumar/laravel-crud) 5 | [![Total Downloads](https://img.shields.io/packagist/dt/san-kumar/laravel-crud.svg?style=flat-square)](https://img.shields.io/packagist/dt/san-kumar/laravel-crud.svg?style=flat-square) 6 | 7 | -------------------------------------------------------------------------------- /src/Utils/TextUtils.php: -------------------------------------------------------------------------------- 1 | $value) { 10 | $k = "_{$key}_"; 11 | $text = str_replace("/*\$$k*/", "\$$value", $text); 12 | $text = str_replace("/*$k*/", $value, $text); 13 | $text = str_replace($k, $value, $text); 14 | } 15 | 16 | return preg_replace('/(\s*\n){2,}/', "\n\n", $text); 17 | } 18 | 19 | public static function arrayExport($arr) { 20 | return sprintf('[%s]', implode(', ', array_map(fn($v) => is_string($v) ? sprintf('"%s" => "%s"', $v, Str::title($v)) : $v, $arr))); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/render/cards.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 | @@foreach ($_vars_ as $_var_) 4 |
    5 |
    6 |
    _title_ #@{{ $_var_->id }}
    7 |

    @{{ $_var_->_readable_ }}

    8 |
    9 |
    10 | @if(!empty($hasSoftDelete)) 11 | {!! $render('buttons/actions-trash') !!} 12 | @else 13 | {!! $render('buttons/actions') !!} 14 | @endif 15 |
    16 |
    17 | @@endforeach 18 | 19 |
    -------------------------------------------------------------------------------- /docs/guide/soft-deletes.md: -------------------------------------------------------------------------------- 1 | # Soft deletes 2 | 3 | Laravel provides a simple way to implement soft deletes on 4 | your Eloquent models. Soft deletes allow you to keep a 5 | record of a model's deletion without actually deleting the 6 | model from your database. This allows you to restore the 7 | model at a later time if needed. 8 | 9 | Laravel CRUD generator handles tables with soft deletes 10 | automatically and generates the appropriate code for you to 11 | restore or purge deleted records. 12 | 13 | > ![Handling soft deletes](https://cdn.articlevideorobot.com/hosted/22-12-2022/selection-305-e2e0.webp) 14 | > (Handling soft deletes) 15 | 16 | To show deleted records in the list view, just append 17 | `?trashed=1` to the URL (this link isn't added to the 18 | navigation menu by default, so you have to add it manually). -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/render/show.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | @foreach ($fields as $field) 8 | 9 | 10 | 11 | 12 | @endforeach 13 | @if($hasTimestamps) 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | @endif 23 | 24 |
    ID:@{{$_var_->id}}
    {{ $field->name }}:{!! $display($field) !!}
    Created at@{{Carbon\Carbon::parse($_var_->created_at)->format('d/m/Y H:i:s')}}
    Updated at@{{Carbon\Carbon::parse($_var_->updated_at)->format('d/m/Y H:i:s')}}
    25 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/render/show.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | @foreach ($fields as $field) 8 | 9 | 10 | 11 | 12 | @endforeach 13 | @if($hasTimestamps) 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | @endif 23 | 24 |
    ID:@{{$_var_->id}}
    {{ $field->name }}:{!! $display($field) !!}
    Created at@{{Carbon\Carbon::parse($_var_->created_at)->format('d/m/Y H:i:s')}}
    Updated at@{{Carbon\Carbon::parse($_var_->updated_at)->format('d/m/Y H:i:s')}}
    25 | -------------------------------------------------------------------------------- /database/migrations/replies_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->integer('ticket_id'); 17 | $table->text('body'); 18 | $table->boolean('is_admin')->default(FALSE); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() { 29 | Schema::dropIfExists('replies'); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/render/show.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | @foreach ($fields as $field) 8 | 9 | 10 | 11 | 12 | @endforeach 13 | @if($hasTimestamps) 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | @endif 23 | 24 |
    ID:@{{$_var_->id}}
    {{ $field->name }}:{!! $display($field) !!}
    Created at@{{Carbon\Carbon::parse($_var_->created_at)->format('d/m/Y H:i:s')}}
    Updated at@{{Carbon\Carbon::parse($_var_->updated_at)->format('d/m/Y H:i:s')}}
    25 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/buttons/actions.blade.php: -------------------------------------------------------------------------------- 1 | @@lang('Show') 2 |
    3 | 4 | 14 |
    15 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/buttons/actions.blade.php: -------------------------------------------------------------------------------- 1 | @@lang('Show') 2 |
    3 | 4 | 14 |
    15 | -------------------------------------------------------------------------------- /database/migrations/leads_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->integer('user_id'); 19 | $table->string('name'); 20 | $table->string('email'); 21 | 22 | $table->timestamps(); 23 | $table->softDeletes(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('leads'); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /database/migrations/tickets_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->integer('lead_id'); 17 | $table->integer('user_id'); 18 | $table->string('subject'); 19 | $table->json('extra_data')->nullable(); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() { 30 | Schema::dropIfExists('tickets'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/users_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('name'); 19 | $table->string('email')->unique(); 20 | $table->timestamp('email_verified_at')->nullable(); 21 | $table->string('password'); 22 | $table->rememberToken(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('users'); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/render/chat.blade.php: -------------------------------------------------------------------------------- 1 | @@for($i = 0, $_var_ = $_vars_[0]; $i < count($_vars_); $i++, $_var_ = $_vars_[$i]) 2 |
    3 | photo 4 | 5 |
    6 |
    7 |

    8 | _title_ #@{{ $_var_->id }} 9 | 12 mins ago 10 |

    11 |
    12 | @{{ $_var_->_readable_ }} 13 |
    14 |
    15 |
    16 | 17 | @@endfor 18 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/render/chat.blade.php: -------------------------------------------------------------------------------- 1 | @@for($i = 0, $_var_ = $_vars_[0]; $i < count($_vars_); $i++, $_var_ = $_vars_[$i]) 2 |
    3 | photo 4 | 5 |
    6 |
    7 |

    8 | _title_ #@{{ $_var_->id }} 9 | 12 mins ago 10 |

    11 |
    12 | @{{ $_var_->_readable_ }} 13 |
    14 |
    15 |
    16 | 17 | @@endfor 18 | -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | commands([ 13 | Commands\CrudGenerate::class, 14 | Commands\CrudRemove::class, 15 | Commands\CrudTemplate::class, 16 | ]); 17 | } 18 | 19 | /** 20 | * {@inheritdoc} 21 | */ 22 | public function boot() { 23 | $this->publishes([ 24 | __DIR__ . '/../config/crud.php' => config_path('crud.php'), 25 | ]); 26 | } 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | public function provides() { 32 | return [ 33 | Commands\CrudGenerate::class, 34 | Commands\CrudRemove::class, 35 | Commands\CrudTemplate::class, 36 | ]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: Laravel CRUD 6 | text: Generate CRUD for your db tables instantly! 7 | tagline: Zero learning curve. Save time. Avoid errors. 8 | actions: 9 | - theme: brand 10 | text: Get Started 11 | link: /guide/index 12 | - theme: alt 13 | text: View on GitHub 14 | link: https://github.com/san-kumar/laravel-crud 15 | 16 | features: 17 | - icon: ⚡️ 18 | title: Nested Tables Support 19 | details: Generate CRUD for nested tables. For example, generate CRUD for comments inside a blog post. 20 | link: /test 21 | - icon: 🖖 22 | title: Bootstrap 5 and Tailwind CSS 23 | details: Supports both Bootstrap 5 and Tailwind CSS or you can use your own CSS framework. 24 | link: /test 25 | - icon: 🛠️ 26 | title: Models, Gates, Policies, Views, Controllers, Routes and more 27 | details: Generate everything you need with one single "artisan crud:generate" command 28 | link: /test 29 | --- 30 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/partials/render/table.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @foreach($fields as $field) 5 | 6 | @endforeach 7 | 8 | 9 | 10 | 11 | @@foreach ($_vars_ as $_var_) 12 | 13 | @foreach($fields as $field) 14 | 15 | @endforeach 16 | 17 | 24 | 25 | @@endforeach 26 | 27 |
    {{ $field->name }}Actions
    {!! $display($field) !!} 18 | @if(!empty($hasSoftDelete)) 19 | {!! $render('buttons/actions-trash') !!} 20 | @else 21 | {!! $render('buttons/actions') !!} 22 | @endif 23 |
    28 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/partials/render/table.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @foreach($fields as $field) 5 | 6 | @endforeach 7 | 8 | 9 | 10 | 11 | @@foreach ($_vars_ as $_var_) 12 | 13 | @foreach($fields as $field) 14 | 15 | @endforeach 16 | 17 | 24 | 25 | @@endforeach 26 | 27 |
    {{ $field->name }}Actions
    {!! $display($field) !!} 18 | @if(!empty($hasSoftDelete)) 19 | {!! $render('buttons/actions-trash') !!} 20 | @else 21 | {!! $render('buttons/actions') !!} 22 | @endif 23 |
    28 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/inputs/related.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | 8 | 9 | New 10 |
    11 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/buttons/actions-trash.blade.php: -------------------------------------------------------------------------------- 1 | @@if($_var_->trashed()) 2 |
    3 |
    4 | @@csrf 5 | @@method('PUT') 6 | 7 |
    8 |
    9 | @@csrf 10 | @@method('DELETE') 11 | 12 |
    13 |
    14 | @@else 15 | {!! $render('buttons/actions') !!} 16 | @@endif 17 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "san-kumar/laravel-crud", 3 | "description": "Laravel CRUD generator: Generate CRUD for any db table with the crud:generate command.", 4 | "keywords": [ 5 | "artisan", 6 | "laravel", 7 | "crud", 8 | "view", 9 | "templates", 10 | "blade" 11 | ], 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "San Kumar", 16 | "email": "sanchitbh@gmail.com", 17 | "homepage": "https://dayandnight.in" 18 | } 19 | ], 20 | "require": { 21 | "php": "^8.0", 22 | "doctrine/dbal": "^3.5" 23 | }, 24 | "require-dev": { 25 | "laravel/framework": "^8.0 || ^9.0", 26 | "phpunit/phpunit": "^9.0 || ^10.0", 27 | "mockery/mockery": "^1.4", 28 | "orchestra/testbench": "*" 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "San\\Crud\\": "src/" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "San\\Crud\\Tests\\": "tests/" 38 | } 39 | }, 40 | "extra": { 41 | "laravel": { 42 | "providers": [ 43 | "San\\Crud\\ServiceProvider" 44 | ] 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/readme.php: -------------------------------------------------------------------------------- 1 | ## Please see the [**full documentation here**](https://san-kumar.github.io/laravel-crud/).\n\n"; 17 | $content = preg_replace("/## Introduction\n/", "## Introduction\n\n" . file_get_contents(__DIR__ . '/badges.md') . "\n\n$docs", $content); 18 | $content = preg_replace_callback("/:::warning(.*?):::/ms", fn($match) => "> " . join("\n> ", explode("\n", $match[1])), $content); 19 | $content = preg_replace("#\(/guide#", "(https://san-kumar.github.io/laravel-crud/guide", $content); 20 | 21 | file_put_contents(__DIR__ . '/../README.md', $content); 22 | } else { 23 | echo "No files found"; 24 | } 25 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/buttons/actions.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | @@lang('Show') 3 | @@lang('Edit') 4 |
    5 | @@csrf 6 | @@method('DELETE') 7 | 8 |
    9 |
    -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/layout.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_layout_') 2 | 3 | @section('_section_') 4 | @if(isset($showErrors)) 5 | @if ($errors->any()) 6 |
    7 |
    8 |
      9 | @foreach ($errors->all() as $error) 10 |
    1. {{ $error }}
    2. 11 | @endforeach 12 |
    13 |
    14 |
    15 | @endif 16 | 17 | @if (session('success') || session('error')) 18 |
    19 | 23 |
    24 | @endif 25 | @endif 26 | 27 | @yield('_vars_.content') 28 | @endsection 29 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/layout.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_layout_') 2 | 3 | @section('_section_') 4 | @if(isset($showErrors)) 5 | @if ($errors->any()) 6 |
    7 |
    8 |
      9 | @foreach ($errors->all() as $error) 10 |
    1. {{ $error }}
    2. 11 | @endforeach 12 |
    13 |
    14 |
    15 | @endif 16 | 17 | @if (session('success') || session('error')) 18 |
    19 | 23 |
    24 | @endif 25 | @endif 26 | 27 | @yield('_vars_.content') 28 | @endsection 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) San Kumar 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) San Kumar 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/guide/authentication.md: -------------------------------------------------------------------------------- 1 | # Authentication 2 | 3 | Laravel CRUD generator uses the default Laravel 4 | authentication 5 | package [laravel/ui](https://laravel.com/docs/7.x/authentication#included-routing) 6 | for handling user authentication. The CRUD generator will 7 | automatically add authentication to routes (using the 8 | `auth` middleware) and generate Policy classes for Models. 9 | 10 | ## `user_id` field in table 11 | 12 | If you generate CRUD for any table that has a `user_id` 13 | field, then the generated CRUD will automatically generate a 14 | Policy file for the Model and add the 15 | [`authorizeResource`](https://laravel.com/docs/7.x/authorization#authorizing-actions-using-policies) 16 | method to the Resource Controller. 17 | 18 | ## `user_id` in parent tables 19 | 20 | If you generate CRUD for a table that has a `user_id` field 21 | in the parent tables (i.e. `authors.posts.comments` has a 22 | `user_id` field in the `authors` table), then the generated 23 | CRUD will automatically generate a Policy file for 24 | the `Authors` Model and add it to your Resource Controller 25 | in the appropriate places. 26 | 27 | This way access to `posts` and `comments` will be restricted 28 | to the owner of the `authors` table only. 29 | 30 | -------------------------------------------------------------------------------- /docs/guide/gotchas.md: -------------------------------------------------------------------------------- 1 | # Known Gotchas 2 | 3 | ## Laravel Jetstream 4 | 5 | Laravel CRUD generator doesn't seem to work with 6 | [Laravel jetstream](https://jetstream.laravel.com/) 7 | just yet. If you are using jetstream, you can still use the 8 | package, but you will have to manually modify some generated 9 | code to fix the layout and authentication issues. 10 | 11 | ## Inbuilt search 12 | 13 | A very simple search is added to the list view by default. 14 | It searches the first text column of the table and filters 15 | the results. But this generated search code is just a 16 | rudimentary placeholder, and you should customize the search 17 | algorithm as per your own needs. 18 | 19 | ## Opinionated code 20 | 21 | While the package tries to be as flexible as possible, it 22 | still generates some opinionated code. For example, the 23 | generated CRUD uses Laravel's `Policy` classes for handling 24 | authorization. But you may prefer to use something else 25 | like `spatie/laravel-permission`, which can be done but 26 | requires you 27 | to [create your own templates](/guide/customization). 28 | 29 | ## Alpha version 30 | 31 | You should only use it with new projects. If you are using 32 | it in an existing project, make sure to back up (or commit) 33 | any project changes before running the command. 34 | -------------------------------------------------------------------------------- /src/Generators/Templates.php: -------------------------------------------------------------------------------- 1 | path/stubs/$suffix", ['php']); 17 | return array_values(array_filter($files, fn($file) => !\str_contains($file, 'partials'))); 18 | } 19 | 20 | public function getFirstStub(string $suffix) { 21 | $stubs = $this->getStubs($suffix); 22 | return $stubs[0] ?? NULL; 23 | } 24 | 25 | public function getDest(string $type, mixed $stub, array $replacements = [], string $suffix = '') { 26 | $dest = \str_contains($type, 'views/') ? resource_path('views') . "/$suffix" : ($type === 'routes' ? base_path("routes/crud") : ($type === 'Controllers' ? app_path('Http/Controllers') : app_path($type))); 27 | $str = sprintf('%s/%s', $dest, basename($stub)); 28 | return TextUtils::replaceBlanks($str, $replacements); 29 | } 30 | 31 | public function templatePath() { 32 | return $this->path; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 | 11 |
    12 | 13 |
    14 |
    15 |
    16 | @csrf 17 | _create_ 18 |
    19 | 20 | 26 |
    27 |
    28 |
    29 |
    30 | @endsection 31 | -------------------------------------------------------------------------------- /src/Utils/RouteUtils.php: -------------------------------------------------------------------------------- 1 | sprintf("'%s'", $var), (array) $vars)) ?: '[]'); 13 | return sprintf("route('%s', %s)", $name, $args); 14 | } else { //fallback code to handle undefined routes to prevent errors 15 | $parts = explode('.', $name); 16 | $values = array_values((array) $vars); 17 | 18 | $path = [sprintf("'%s'", rtrim("/" . trim($prefix ?: '', '/'), '/'))]; 19 | 20 | for ($i = 0; $i < count($parts); $i++) { 21 | if ($parts[$i] === 'index') continue; 22 | 23 | if ($parts[$i] !== 'show') { 24 | $path[] = sprintf("'%s'", $parts[$i]); 25 | } 26 | 27 | if (!empty($values[$i])) { 28 | $path[] = is_scalar($vars) ? $values[$i] : sprintf("$%s?->id ?: 0", $values[$i]); 29 | } 30 | } 31 | 32 | return sprintf("implode('/', [%s])", join(',', $path)); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Utils/NameUtils.php: -------------------------------------------------------------------------------- 1 | Str::studly(Str::singular($tableName)), (array) $tables)); 14 | } 15 | 16 | public static function getModelName(string|array $tables) { 17 | return self::studlyCase((array) $tables); 18 | } 19 | 20 | public static function getControllerName(string|array $tables) { 21 | return self::studlyCase((array) $tables) . 'Controller'; 22 | } 23 | 24 | public static function getRouteName(string|array $tables) { 25 | return join('.', array_map(fn($tableName) => Str::kebab(Str::plural($tableName)), (array) $tables)); 26 | } 27 | 28 | public static function getVariableName(string $table) { 29 | return Str::singular(self::getVariableNamePlural($table)); 30 | } 31 | 32 | public static function getVariableNamePlural(string $table) { 33 | return Str::camel($table); 34 | } 35 | 36 | public static function getPolicyName(string|array $tables) { 37 | return self::studlyCase((array) $tables) . 'Policy'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 | 11 |
    12 |
    13 | 14 |
    15 | _edit_ 16 |
    17 | 23 |
    24 |
    25 |
    26 |
    27 | @endsection 28 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 | 11 |
    12 |
    13 |
    14 | @method('PUT') 15 | @csrf 16 |
    17 | _edit_ 18 |
    19 | 25 |
    26 |
    27 |
    28 |
    29 | @endsection 30 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 | 11 |
    12 | 13 |
    14 | 15 |
    16 | _create_ 17 |
    18 | 19 | 25 |
    26 |
    27 |
    28 |
    29 | @endsection 30 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/render/chat.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | @@for($i = 0, $_var_ = $_vars_[0]; $i < count($_vars_); $i++, $_var_ = $_vars_[$i]) 3 | @@if($i % 2) 4 |
    5 |
    6 |
    7 |
    @{{ $_var_->_readable_ }}
    8 |
    9 | My profile 10 |
    11 |
    12 | @@else 13 |
    14 |
    15 |
    16 |
    @{{ $_var_->_readable_ }}
    17 |
    18 | My profile 19 |
    20 |
    21 | @@endif 22 | @@endfor 23 |
    -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 | 11 | 12 | Back 13 |
    14 | 15 |
    16 | _show_ 17 |
    18 | 19 | 27 |
    28 |
    29 | @endsection 30 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 | 11 | 12 | Back 13 |
    14 | 15 |
    16 | _show_ 17 |
    18 | 19 | 27 |
    28 |
    29 | @endsection 30 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/create-edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 | 11 |
    12 |
    13 | 14 |
    15 | _edit_ 16 |
    17 | 23 |
    24 |
    25 |
    26 |
    27 | @endsection 28 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/create-edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 | 11 |
    12 |
    13 |
    14 | @method(isset($_var_?->id) ? 'PUT' : 'POST') 15 | @csrf 16 |
    17 | _edit_ 18 |
    19 | 25 |
    26 |
    27 |
    28 |
    29 | @endsection 30 | -------------------------------------------------------------------------------- /src/template/stubs/views/bootstrap/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 | 10 | 11 |
    12 |
    13 | 14 | 15 | 16 | 17 |
    18 |
    19 |
    20 |
    21 | _index_ 22 | 23 | {{ $_vars_->withQueryString()->links() }} 24 |
    25 | 28 |
    29 |
    30 | @endsection 31 | -------------------------------------------------------------------------------- /src/template/stubs/views/larastrap/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 | 10 | 11 |
    12 |
    13 | 14 | 15 | 16 | 17 |
    18 |
    19 |
    20 |
    21 | _index_ 22 | 23 | {{ $_vars_->withQueryString()->links() }} 24 |
    25 | 28 |
    29 |
    30 | @endsection 31 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/partials/render/table.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @foreach($fields as $field) 5 | 6 | @endforeach 7 | 8 | 9 | 10 | 11 | @@foreach ($_vars_ as $_var_) 12 | 13 | @foreach($fields as $field) 14 | 18 | @endforeach 19 | 20 | 27 | 28 | @@endforeach 29 | 30 |
    15 | {{ $field->name }} 16 | {!! $display($field) !!} 17 | 21 | @if(!empty($hasSoftDelete)) 22 | {!! $render('buttons/actions-trash') !!} 23 | @else 24 | {!! $render('buttons/actions') !!} 25 | @endif 26 |
    31 | -------------------------------------------------------------------------------- /src/Utils/FileUtils.php: -------------------------------------------------------------------------------- 1 | isFile()) continue; 15 | if (count($filter) > 0 && !in_array($file->getExtension(), $filter)) continue; 16 | 17 | $files[] = $file->getRealPath(); 18 | } 19 | 20 | return $files ?? []; 21 | } 22 | 23 | public static function recursiveCopy($src, $dest, $force = FALSE) { 24 | $files = self::getFiles($src); 25 | $count = 0; 26 | 27 | foreach ($files as $file) { 28 | $target = $dest . DIRECTORY_SEPARATOR . str_replace($src, '', $file); 29 | 30 | if (file_exists($target) && !$force) { 31 | continue; 32 | } else { 33 | $dir = dirname($target); 34 | if (!is_dir($dir)) { 35 | mkdir($dir, 0755, TRUE); 36 | } 37 | 38 | copy($file, $target); 39 | $count++; 40 | } 41 | } 42 | 43 | return $count; 44 | } 45 | 46 | public static function writeFile(string $dest, string $str) { 47 | $dir = dirname($dest); 48 | if (!is_dir($dir)) { 49 | mkdir($dir, 0777, TRUE); 50 | } 51 | 52 | return file_put_contents($dest, $str); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /docs/guide/nesting.md: -------------------------------------------------------------------------------- 1 | # Relational tables and nesting 2 | 3 | In addition to generating simple CRUD for a single table, 4 | you can also generate CRUD for tables related to each other. 5 | 6 | ## Example #1 7 | 8 | For example, let's say we have the following tables: 9 | 10 | - Authors 11 | - Posts 12 | - Comments 13 | 14 | To generate CRUD for these tables you can use the dot 15 | notation to specify the parent tables like this: 16 | `authors.posts.comments`. 17 | 18 | ```bash 19 | 20 | ```bash 21 | # Create CRUD for authors table 22 | $ php artisan crud:generate authors 23 | 24 | # Create CRUD for posts table 25 | $ php artisan crud:generate authors.posts 26 | 27 | # Create CRUD for comments table 28 | $ php artisan crud:generate authors.posts.comments 29 | ``` 30 | 31 | This will generate CRUD files for the `authors` table, so 32 | you can view, create, edit and delete authors using the 33 | `/authors` route. 34 | 35 | Then for each author, you can view, create, edit and delete 36 | posts using the `/authors/{author}/posts` route. 37 | 38 | And for each post, you can view, create, edit and delete 39 | comments using the `/authors/{author}/posts/{post}/comments` 40 | route. 41 | 42 | ## Example #2 43 | 44 | On the other hand, if you want to generate CRUD for the 45 | `posts` table without nesting it under the `authors` route, 46 | you can do this: 47 | 48 | ```bash 49 | # Create CRUD for posts table 50 | $ php artisan crud:generate posts 51 | 52 | # Create CRUD for comments table 53 | $ php artisan crud:generate posts.comments 54 | ``` 55 | 56 | So now you can view, create, edit and delete all posts 57 | regardless of the author using the `/posts` route and then 58 | for each post, you can view, create, edit and delete 59 | comments using the `/posts/{post}/comments` route. -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 |
      8 | _breadcrumbs_ 9 |
    10 |
    11 |
    12 |
    13 | @method('PUT') 14 | @csrf 15 |
    16 | _edit_ 17 |
    18 |
    19 |
    20 | Cancel 21 | 22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 | @endsection 29 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 |
      8 | _breadcrumbs_ 9 |
    10 |
    11 | 12 |
    13 |
    14 |
    15 | @csrf 16 | _create_ 17 |
    18 | 19 |
    20 |
    21 | @lang('Cancel') 22 | 23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 | @endsection 30 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/create-edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 |
      8 | _breadcrumbs_ 9 |
    10 |
    11 |
    12 |
    13 | @method(isset($_var_?->id) ? 'PUT' : 'POST') 14 | @csrf 15 |
    16 | _edit_ 17 |
    18 |
    19 |
    20 | Cancel 21 | 22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 | @endsection 29 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 |
      8 | _breadcrumbs_ 9 |
    10 |
    11 |
    12 | 13 | 14 |
    15 |
    16 |
    17 |
    18 | _index_ 19 | 20 | {{ $_vars_->withQueryString()->links() }} 21 |
    22 | 25 |
    26 |
    27 | @endsection 28 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/@theme_index.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["../../../../node_modules/vitepress/dist/client/theme-default/index.js"], 4 | "sourcesContent": ["import './styles/fonts.css';\r\nimport './styles/vars.css';\r\nimport './styles/base.css';\r\nimport './styles/utils.css';\r\nimport './styles/components/custom-block.css';\r\nimport './styles/components/vp-code.css';\r\nimport './styles/components/vp-code-group.css';\r\nimport './styles/components/vp-doc.css';\r\nimport './styles/components/vp-sponsor.css';\r\nimport VPBadge from './components/VPBadge.vue';\r\nimport Layout from './Layout.vue';\r\nimport NotFound from './NotFound.vue';\r\nexport { default as VPHomeHero } from './components/VPHomeHero.vue';\r\nexport { default as VPHomeFeatures } from './components/VPHomeFeatures.vue';\r\nexport { default as VPHomeSponsors } from './components/VPHomeSponsors.vue';\r\nexport { default as VPDocAsideSponsors } from './components/VPDocAsideSponsors.vue';\r\nexport { default as VPTeamPage } from './components/VPTeamPage.vue';\r\nexport { default as VPTeamPageTitle } from './components/VPTeamPageTitle.vue';\r\nexport { default as VPTeamPageSection } from './components/VPTeamPageSection.vue';\r\nexport { default as VPTeamMembers } from './components/VPTeamMembers.vue';\r\nconst theme = {\r\n Layout,\r\n NotFound,\r\n enhanceApp: ({ app }) => {\r\n app.component('Badge', VPBadge);\r\n }\r\n};\r\nexport default theme;\r\n"], 5 | "mappings": ";AAAA,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO;AACP,OAAO,aAAa;AACpB,OAAO,YAAY;AACnB,OAAO,cAAc;AACrB,SAAoB,WAAXA,gBAA6B;AACtC,SAAoB,WAAXA,gBAAiC;AAC1C,SAAoB,WAAXA,gBAAiC;AAC1C,SAAoB,WAAXA,gBAAqC;AAC9C,SAAoB,WAAXA,gBAA6B;AACtC,SAAoB,WAAXA,gBAAkC;AAC3C,SAAoB,WAAXA,gBAAoC;AAC7C,SAAoB,WAAXA,gBAAgC;AACzC,IAAM,QAAQ;AAAA,EACV;AAAA,EACA;AAAA,EACA,YAAY,CAAC,EAAE,IAAI,MAAM;AACrB,QAAI,UAAU,SAAS,OAAO;AAAA,EAClC;AACJ;AACA,IAAO,wBAAQ;", 6 | "names": ["default"] 7 | } 8 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_route_.layout') 2 | 3 | @section('_vars_.content') 4 |
    5 |
    6 |
    7 |
      8 | _breadcrumbs_ 9 |
    10 | 11 | Back 12 |
    13 | 14 |
    15 | _show_ 16 |
    17 | 18 |
    19 | @lang('Edit') 20 |
    21 | @csrf 22 | @method('DELETE') 23 | 24 |
    25 |
    26 |
    27 |
    28 | @endsection 29 | -------------------------------------------------------------------------------- /src/Commands/CrudTemplate.php: -------------------------------------------------------------------------------- 1 | option('output') ?: 'templates'); 32 | $dest = sprintf('%s/%s', $base, $this->argument('name')); 33 | $force = $this->option('force'); 34 | 35 | if (is_dir($dest) && !$force) { 36 | $this->error("Template '$dest' already exists"); 37 | return Command::FAILURE; 38 | } 39 | 40 | $src = realpath(__DIR__ . '/../template'); 41 | if (!is_dir($src)) { 42 | $this->error("Template source '$src' not found"); 43 | return Command::FAILURE; 44 | } 45 | 46 | $count = FileUtils::recursiveCopy($src, $dest, $force); 47 | $this->info("Created $count files in '$dest'"); 48 | 49 | return Command::SUCCESS; 50 | } 51 | 52 | protected function getOptions() { 53 | return [ 54 | ['output', 'o', InputOption::VALUE_REQUIRED, 'Output directory name (default: "templates")'], 55 | ['force', 'f', InputOption::VALUE_NONE, 'Overwrite existing files'], 56 | ]; 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | protected function getArguments() { 63 | return [ 64 | ['name', InputArgument::REQUIRED, 'The name of the new template'], 65 | ]; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /docs/guide/install.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Install with Composer 4 | 5 | Via [composer](http://getcomposer.org): 6 | 7 | ```bash 8 | $ composer require san-kumar/laravel-crud --dev 9 | ``` 10 | 11 | ## Generate files 12 | 13 | :::warning Important! 14 | 15 | Make sure to create the db tables and run migrations before 16 | starting the crud generator. 17 | ::: 18 | 19 | After installing the package you should see a 20 | new `crud:generate` command in your Artisan commands list. 21 | 22 | To generate the Controller, Model, Policy, routes, etc use 23 | the following command: 24 | 25 | ```bash 26 | # Create CRUD for authors table 27 | $ php artisan crud:generate authors 28 | 29 | # Create CRUD for the authors > posts table 30 | $ php artisan crud:generate authors.posts 31 | 32 | # Create CRUD for the authors > posts > comments table 33 | $ php artisan crud:generate authors.posts.comments 34 | ``` 35 | 36 | ## Including the generated routes 37 | 38 | By default, the generated routes are placed in 39 | the `./routes/crud/` directory. To include the generated 40 | routes just open your `routes/web.php` file and add the 41 | following line at the end of the file: 42 | 43 | ```php 44 | /* inside routes/web.php */ 45 | 46 | \San\Crud\Crud::routes(); 47 | ``` 48 | 49 | This will `glob` all the generated route files and include 50 | them in the `routes/web.php` file for you. 51 | 52 | Alternatively, you can copy-paste the generated routes from 53 | the `./routes/crud/` directory to your `routes/web.php` 54 | file. This way you make any changes to the generated route 55 | code as per your needs. 56 | 57 | ## Location of the generated files 58 | 59 | The `crud:generate` command will generate the following 60 | files and place them in the appropriate directories: 61 | 62 | - Resource Controller 63 | - `app/Http/Controllers/[Name].php` 64 | - Model 65 | - `app/Model/[Name].php` 66 | - Policy 67 | - `app/Policies/[Name]Policy.php` 68 | - Routes 69 | - `routes/crud/[name].php` 70 | - Views 71 | - `resources/views/[name]/(index|edit|create|show).blade.php` 72 | 73 | By default, the generated code will not overwrite any 74 | existing files. If you want to overwrite the existing files, 75 | you can use the `--force` option. 76 | -------------------------------------------------------------------------------- /docs/guide/options.md: -------------------------------------------------------------------------------- 1 | # Options 2 | 3 | ### CSS Framework: Bootstrap 5, and TailwindCSS. 4 | 5 | You can choose between Bootstrap 5 and Tailwind CSS or you 6 | can use your own CSS framework. By default, Bootstrap 5 is 7 | used when nothing is specified. 8 | 9 | ```bash 10 | # Create CRUD for authors table using Tailwind CSS 11 | $ php artisan crud:generate authors --css=tailwind 12 | 13 | # Shortcut for Tailwind CSS 14 | $ php artisan crud:generate authors -t 15 | ``` 16 | 17 | ```bash 18 | # Create CRUD for authors table using layout 'layouts/admin.blade.php' 19 | $ php artisan crud:generate authors --layout=admin 20 | $ php artisan crud:generate authors --section=mysection 21 | ``` 22 | 23 | ### Prefix 24 | 25 | You can add a route prefix for the generated CRUD by passing 26 | a `--prefix` option. 27 | 28 | ```bash 29 | # Create CRUD for authors table using Tailwind CSS 30 | $ php artisan crud:generate authors --prefix=admin 31 | ``` 32 | 33 | This will change the route from `/authors` to 34 | `/admin/authors` in all the generated files. 35 | 36 | ### Index templates 37 | 38 | You can choose between 3 ready-made index templates: table, 39 | cards and chat. By default, the table template is used. 40 | 41 | ```bash 42 | # Create CRUD for authors table using cards template 43 | $ php artisan crud:generate authors -i cards 44 | 45 | # Create CRUD for authors table using chat template 46 | $ php artisan crud:generate authors -i chat 47 | ``` 48 | 49 | Please see the [index page here](/guide/index.md#screenshots) 50 | for screenshots. 51 | 52 | ### Single create and edit form 53 | 54 | In some cases, you may want to use a single form for both 55 | creating and editing a resource. You can do that by passing 56 | `--merge-forms` option. 57 | 58 | ```bash 59 | # Create CRUD for authors table using a single form 60 | $ php artisan crud:generate authors --merge-forms 61 | ``` 62 | 63 | This will generate a single `view` file called 64 | `create-edit.blade.php` and update the controller code 65 | accordingly so that it can be used for both creating and 66 | editing a resource. 67 | 68 | ### Layout and section names 69 | 70 | By default, the generated views use `layouts/app.blade.php` 71 | for layout and `content` for section name. You can change 72 | these default names by passing your own layout and section 73 | names. 74 | 75 | -------------------------------------------------------------------------------- /docs/guide/crud.md: -------------------------------------------------------------------------------- 1 | # Create and Edit records 2 | 3 | Laravel CRUD generator can automatically generate the FORM 4 | for creating and editing records. It can also generate the 5 | validation rules for the form. 6 | 7 | ## Form fields 8 | 9 | The form fields are generated based on the database table 10 | columns. The following column types are supported: 11 | 12 | > ![Example of generated fields](https://cdn.articlevideorobot.com/hosted/22-12-2022/selection-307-f402.webp) 13 | > (Example of generated fields) 14 | 15 | - `string` (input type text) 16 | - `text` (textarea) 17 | - `integer` (input type number) 18 | - `enum` (select) 19 | - `boolean` (radio buttons) 20 | - `relation` (select with hydrated options) 21 | - and so on... 22 | 23 | It can also infer the field type from the column names. For 24 | example, if the column name is `email`, the field type will 25 | be `email`. 26 | 27 | ## Validation rules 28 | 29 | By default, all non-nullable fields are required. Columns 30 | with a `unique` index are also validated to be unique in the 31 | table. 32 | 33 | Some additional validation rules are also added based on the 34 | column name. For example, if the column name is `email`, the 35 | validation rule will be `email`. 36 | 37 | ## Relation fields 38 | 39 | If the table has any foreign key columns, the generator will 40 | automatically add a select field for that column. The 41 | options for the select field will be hydrated from the 42 | related table. 43 | 44 | For example, if your table has a `country_id` column and if 45 | the `country` table exists: the generator will add a select 46 | field for country (as shown in the image above) and the 47 | options for that select field will be hydrated from 48 | the `countries` table (if the country table has a `user_id` 49 | column, the options will be filtered by the current user). 50 | 51 | ## Quick note about relation fields 52 | 53 | You do need to add relation fields manually or create any 54 | foreign key columns in the database. The relation is 55 | inferred from the column name only. Any column name that 56 | matches the pattern `*_id` are considered relation. 57 | 58 | For example, if the column name is `country_id`, the 59 | generator will look for a `countries` table and add if that 60 | exists (and if the current user has permission to access 61 | that table) it will add a select field for that column and 62 | hydrate the options from the `countries` table. 63 | -------------------------------------------------------------------------------- /docs/guide/customization.md: -------------------------------------------------------------------------------- 1 | # Customization 2 | 3 | ## Using your own templates 4 | 5 | At the heart of Laravel CRUD generator are the templates 6 | files that are used to generate the CRUD files. 7 | 8 | The templates files included with this package are stored in 9 | the `./vendor/san-kumar/laravel-crud/src/template` 10 | directory. 11 | 12 | It contains all the stub files that are used to generate the 13 | Controller, Model, Policy, routes, etc. 14 | 15 | ## Customizing the templates 16 | 17 | You can customize the templates by copying the template into 18 | a new directory and then modifying the template files. 19 | 20 | To make this simple Laravel CRUD generator provides an 21 | artisan command that will copy the template files into a new 22 | directory for you. 23 | 24 | ```bash 25 | # Copy the template files into a new directory 26 | $ php artisan crud:template newname 27 | ``` 28 | 29 | This will copy the template files into a new directory 30 | `./templates/newname/` directory. 31 | 32 | It you want to specify a different directory to copy the 33 | template files into you can use the `--directory` or 34 | `-d` option. 35 | 36 | ```bash 37 | # Copy the template files into a new directory 38 | $ php artisan crud:template newname -d etc/templates 39 | ``` 40 | 41 | This will copy the template files into a new directory 42 | `./etc/templates/newname/` directory. 43 | 44 | ## Using your own templates 45 | 46 | To use your own templates you need to specify the template 47 | directory in the `config/crud.php` file. 48 | 49 | ```php 50 | // config/crud.php 51 | 52 | return [ 53 | 'template_dir' => 'etc/templates/newname', 54 | ]; 55 | 56 | ``` 57 | 58 | Alternatively you can specify the template directory using 59 | the `--template` or `-t` option while generating the CRUD. 60 | 61 | ```bash 62 | # Generate the CRUD using the new template 63 | $ php artisan crud:generate Post --template etc/templates/newname 64 | ``` 65 | 66 | ## Customizing the templates 67 | 68 | You will need to modify the template files in the new 69 | directory to customize the generated files. 70 | 71 | You will find multiple variables that are used in the 72 | template files that are replaced with the actual values when 73 | the files are generated. 74 | 75 | To see the full list of variables that are used in the 76 | template files check out the source code for 77 | [GenerateCrud.php](https://github.com/san-kumar/laravel-crud/blob/main/src/Commands/CrudGenerate.php#L49) 78 | 79 | -------------------------------------------------------------------------------- /src/template/stubs/views/tailwind/layout.blade.php: -------------------------------------------------------------------------------- 1 | @extends('_layout_') 2 | 3 | @section('_section_') 4 | @if(isset($showErrors)) 5 | @if ($errors->any()) 6 |
    7 | 17 |
    18 | @endif 19 | 20 | @if (session('success')) 21 |
    22 | 28 |
    29 | @endif 30 | 31 | @if (session('error')) 32 |
    33 | 39 |
    40 | @endif 41 | @endif 42 | 43 | @yield('_vars_.content') 44 | @endsection 45 | -------------------------------------------------------------------------------- /src/template/stubs/Policies/_policy_.php: -------------------------------------------------------------------------------- 1 | tables)); 12 | } 13 | 14 | public function getPolicyName() { 15 | return NameUtils::getPolicyName($this->mainTable()); 16 | } 17 | 18 | public function getControllerPolicy() { 19 | return sprintf("public function __construct() {\n\t\t\$this->authorizeResource(%s::class, '%s');\n\t}", $this->getMainModelName(), $this->getMainVarName()); 20 | } 21 | 22 | public function getPolicyReadArgs() { 23 | return $this->getPolicyArgs([]); 24 | } 25 | 26 | public function getPolicyArgs($tables) { 27 | foreach (array_merge(['User'], (array) $tables) as $table) { 28 | $args[] = sprintf('%s $%s', NameUtils::getModelName((array) $table), NameUtils::getVariableName($table)); 29 | } 30 | 31 | return join(', ', $args ?? []); 32 | } 33 | 34 | public function getPolicyReadRules() { 35 | return $this->getPolicyRules([]); 36 | } 37 | 38 | public function getPolicyRules($tables) { 39 | foreach ((array) $tables as $table) { 40 | if (SchemaUtils::getUserIdField($this->getTableNameFromAlias($table))) { 41 | $checks[] = sprintf('($%s->user_id == $user->id)', NameUtils::getVariableName($table)); 42 | } 43 | } 44 | 45 | return empty($checks) ? "return true;" : sprintf('return ($user->id > 0) && %s;', join(' && ', $checks)); 46 | } 47 | 48 | public function getPolicyWriteArgs() { 49 | return $this->getPolicyArgs($this->mainTable()); 50 | } 51 | 52 | public function getPolicyWriteRules() { 53 | return $this->getPolicyRules($this->mainTable()); 54 | } 55 | 56 | public function getParentAuthorization() { 57 | return $this->getPolicyAuthorization($this->parentTables(), 'view'); 58 | } 59 | 60 | public function getPolicyAuthorization($tables, $fn) { 61 | foreach ((array) $tables as $table) { 62 | $checks[] = sprintf("\$this->authorize('%s', [%s::class, \$%s]);", $fn, NameUtils::getModelName($table), NameUtils::getVariableName($table)); 63 | } 64 | 65 | return empty($checks) ? "" : join("\n\t\t", $checks); 66 | } 67 | 68 | public function getModelAuthorization() { 69 | return $this->getPolicyAuthorization($this->mainTable(), 'delete'); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Commands/CrudBase.php: -------------------------------------------------------------------------------- 1 | getConfig('template_dir'); 15 | 16 | if (!empty($customDir)) { 17 | if (!is_dir($customDir)) { 18 | $this->error("Template directory '$customDir' does not exist"); 19 | exit(1); 20 | } 21 | 22 | return $customDir; 23 | } 24 | 25 | return $this->getConfig('template_dir', __DIR__ . '/../template'); 26 | } 27 | 28 | protected function getConfig($path = NULL, $default = NULL) { 29 | $config = config('crud') ?: []; 30 | 31 | return !empty($path) ? (data_get($config, $path) ?: $default) : $config; 32 | } 33 | 34 | protected function getCssFramework() { 35 | if (preg_match('/^(tailwind|larastrap)$/', $this->option('css'))) { 36 | return $this->option('css'); 37 | } 38 | 39 | if ($this->option('tailwind')) { 40 | return 'tailwind'; 41 | } 42 | 43 | return $this->getConfig('css', 'bootstrap'); 44 | } 45 | 46 | protected function getTables($checkExists = TRUE) { 47 | $tables = array_values(array_filter(array_map('trim', preg_split('/\.\s*/', $this->argument('table'))))); 48 | $aliases = $this->getTableAliases(); 49 | 50 | if ($checkExists) { 51 | foreach ($tables as $table) { 52 | if (!SchemaUtils::tableExists($aliases[$table] ?? $table)) { 53 | $this->warn("Table $table does not exist in the database. Did you forget to run migrations?"); 54 | } 55 | } 56 | } 57 | 58 | return $tables; 59 | } 60 | 61 | protected function getTableAliases() { 62 | if (!$this->hasOption('alias')) return []; 63 | 64 | $aliases = $this->option('alias'); 65 | 66 | foreach ($aliases as $alias) { 67 | [$a, $t] = explode('=', $alias); 68 | if (!empty($a) && !empty($t)) { 69 | if (SchemaUtils::tableExists($t)) { 70 | $results[$a] = $t; 71 | } else { 72 | $this->warn("Table $t (alias $a) does not exist in the database. Did you forget to run migrations?"); 73 | } 74 | } 75 | } 76 | 77 | return $results ?? []; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Generators/ModelGen.php: -------------------------------------------------------------------------------- 1 | sprintf('use App\Models\%s;', NameUtils::getModelName((array) $table)), $this->tables)); 12 | } 13 | 14 | public function getModelName() { 15 | return NameUtils::getModelName($this->mainTable()); 16 | } 17 | 18 | public function getSoftDelete() { 19 | return $this->hasSoftDeletes() ? 'use \Illuminate\Database\Eloquent\SoftDeletes;' : ''; 20 | } 21 | 22 | public function getFillable() { 23 | foreach ($this->getFillableFields() as $field) { 24 | $fillable[] = $field['id']; 25 | } 26 | 27 | return join(', ', array_map(fn($f) => sprintf('"%s"', $f), $fillable ?? [])); 28 | } 29 | 30 | public function getBelongsTo() { 31 | foreach (SchemaUtils::getTableFieldsWithIds($this->mainTableReal(), ['user_id']) as $field) { 32 | $relations[] = sprintf("public function %s() {\n\t\treturn \$this->belongsTo(%s::class);\n\t}", $field['relation'], NameUtils::getModelName((array) $field['related_table'])); 33 | } 34 | 35 | return join("\n\n", $relations ?? []); 36 | } 37 | 38 | public function getHasMany() { 39 | $tables = SchemaUtils::getTables($this->mainTableReal()); 40 | 41 | foreach ($tables as $table) { 42 | $fields = SchemaUtils::getTableFieldsWithIds($this->getTableNameFromAlias($table), ['user_id']); 43 | foreach ($fields as $field) { 44 | if ($field['related_table'] == $this->mainTableReal()) { 45 | $fKey = $field['id']; 46 | $relations[] = sprintf("public function %s() {\n\t\treturn \$this->hasMany(%s::class, '%s');\n\t}", NameUtils::getVariableNamePlural($table), NameUtils::getModelName((array) $table), $fKey); 47 | } 48 | } 49 | } 50 | 51 | return join("\n\n", $relations ?? []); 52 | } 53 | 54 | public function getCasts() { 55 | foreach (SchemaUtils::getTableFields($this->mainTableReal(), ['user_id']) as $field) { 56 | if ($field['type'] == 'json') { 57 | $casts[] = sprintf("'%s' => AsArrayObject::class", $field['id']); 58 | } elseif (preg_match('/^date/', $field['type'])) { 59 | $casts[] = sprintf("'%s' => 'datetime'", $field['id']); 60 | } 61 | } 62 | 63 | return !empty($casts) ? sprintf("protected \$casts = [\n\t\t%s\n\t];", join(",\n\t\t", $casts)) : ''; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /docs/.vitepress/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Laravel Crud Generator', 3 | host: 'localhost', 4 | description: 'Laravel CRUD generator: Generate CRUD for any db table with the crud:generate command.', 5 | //base: 'https://san-kumar.github.io/laravel-crud/', 6 | base: '/laravel-crud/', 7 | appearance: 'dark', 8 | themeConfig: { 9 | nav: [ 10 | {text: 'Getting started', link: '/guide'}, 11 | { 12 | text: 'Artisan commands', 13 | items: [ 14 | {text: 'artisan crud:generate', link: '/guide/'}, 15 | {text: 'artisan crud:template', link: '/guide/customization'}, 16 | {text: 'artisan crud:remove', link: '/guide/remove'}, 17 | ] 18 | } 19 | ], 20 | sidebar: [ 21 | { 22 | text: 'Introduction', 23 | items: [ 24 | {text: 'Index', link: '/guide/'}, 25 | {text: 'Installation', link: '/guide/install'}, 26 | {text: 'Options', link: '/guide/options'}, 27 | ] 28 | }, 29 | { 30 | text: 'Details', 31 | items: [ 32 | {text: 'Nested parents', link: '/guide/nesting'}, 33 | {text: 'Authentication', link: '/guide/authentication'}, 34 | {text: 'Create / edit form', link: '/guide/crud'}, 35 | {text: 'Soft deletes', link: '/guide/soft-deletes'}, 36 | {text: 'Inbuilt search', link: '/guide/search'}, 37 | {text: 'Customization', link: '/guide/customization'}, 38 | ] 39 | }, 40 | { 41 | text: 'Appendix', 42 | items: [ 43 | {text: 'Gotchas', link: '/guide/gotchas'}, 44 | {text: 'CRUD removal', link: '/guide/remove'}, 45 | {text: 'License', link: '/guide/license'}, 46 | {text: 'Roadmap', link: '/guide/roadmap'}, 47 | {text: 'About', link: '/guide/about'}, 48 | ] 49 | }, 50 | ], 51 | editLink: { 52 | pattern: 'https://github.com/san-kumar/laravel-crud/docs/:path' 53 | }, 54 | search: { 55 | maxSuggestions: 10 56 | }, 57 | footer: { 58 | message: 'Released under the MIT License.', 59 | copyright: 'Copyright © 2022-present San Kumar' 60 | }, 61 | socialLinks: [ 62 | { icon: 'github', link: 'https://github.com/san-kumar/laravel-crud' } 63 | ], 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /docs/guide/index.md: -------------------------------------------------------------------------------- 1 | # Laravel CRUD Generator 2 | 3 | ![laravel-crud-generator](https://cdn.articlevideorobot.com/hosted/16-12-2022/laravel-crud-152f.webp) 4 | 5 | ## Introduction 6 | 7 | This package adds a **crud:generate** command to Artisan in 8 | your Laravel project. With this command you can generate 9 | CRUD (Create, Read, Update, Delete) for your db tables 10 | instantly! 11 | 12 | The generated CRUD includes a Controller, Model, Policy, 13 | routes, validations, and 4 view files (index, create, edit, 14 | show). It also supports relations between tables, so you can 15 | generate nested CRUDs, for example: generating CRUD for 16 | comments inside a blog post. 17 | 18 | The output is fully customizable and supports 19 | both [Bootstrap 5](https://getbootstrap.com/docs/5.0/getting-started/introduction/) 20 | and [Tailwind CSS](https://tailwindcss.com/). 21 | 22 | ## Demo Video 23 | 24 | [![Laravel crud generator package](https://cdn.articlevideorobot.com/hosted/02-01-2023/laravel-crud-generator-5d81.webp)](https://www.youtube.com/watch?v=N_N2FqPDLvQ) 25 | 26 | ## Screenshots 27 | 28 | ### Index page 29 | 30 | Laravel-crud can generate nested CRUDs. For example, you can 31 | generate CRUD for customers > tickets > replies by simply 32 | running: 33 | 34 | ```bash 35 | $ php artisan crud:generate customers.tickets.replies 36 | ``` 37 | 38 | #### Table layout 39 | 40 | > ![Table index](https://cdn.articlevideorobot.com/hosted/22-12-2022/image-2-dbed.webp) 41 | > (Automatically generated index page with responsive table 42 | > layout) 43 | 44 | The generated views are fully customizable and template 45 | based. For example, you can change the look of the index 46 | page by using different templates like table, cards or 47 | chat (included in the package). 48 | 49 | #### Card layout 50 | 51 | ```bash 52 | $ php artisan crud:generate customers.tickets.replies -i cards 53 | ``` 54 | 55 | > ![Card layout](https://cdn.articlevideorobot.com/hosted/22-12-2022/selection-303-4a64.webp) 56 | > (Card layout) 57 | 58 | #### Chat layout 59 | 60 | ```bash 61 | $ php artisan crud:generate customers.tickets.replies -i chat 62 | ``` 63 | 64 | > ![Chat layout](https://cdn.articlevideorobot.com/hosted/22-12-2022/selection-304-c429.webp) 65 | > (Chat layout) 66 | 67 | #### Create / Edit Forms 68 | 69 | Laravel CRUD generator can automatically generate the FORM 70 | for creating and editing records. It can also generate the 71 | validation rules for the form. 72 | 73 | > ![Example of generated fields](https://cdn.articlevideorobot.com/hosted/22-12-2022/selection-307-f402.webp) 74 | > (Example of generated fields) 75 | 76 | ## Heads up! 77 | 78 | :::warning The package is still in _alpha_ 79 | 80 | You should only use it with new projects. If you are using 81 | it in an existing project, make sure to back up (or commit) 82 | any project changes before running the command. 83 | ::: 84 | 85 | The aim of the CRUD generator is to save you time by 86 | generating common boilerplate CRUD code for any database 87 | table instantly. 88 | 89 | But this code is **not** intended to be used as-is. You 90 | should always customize the generated code to fit your needs 91 | and manually review the generated files before deploying it 92 | to production. 93 | -------------------------------------------------------------------------------- /src/Generators/RouteGen.php: -------------------------------------------------------------------------------- 1 | tables); 14 | } 15 | 16 | public function getParentRouteName() { 17 | return NameUtils::getRouteName($this->parentTables()); 18 | } 19 | 20 | public function getRouteUrl() { 21 | return $this->withRoutePrefix(str_replace('.', '/', $this->getRouteName())); 22 | } 23 | 24 | public function getRouteUrlWithoutPrefix() { 25 | return str_replace('.', '/', $this->getRouteName()); 26 | } 27 | 28 | public function getRouteUrlWithPlaceholders() { 29 | return $this->getUrlWithPlaceholders($this->tables); 30 | } 31 | 32 | public function getParentRouteUrlWithPlaceholders() { 33 | return $this->getUrlWithPlaceholders($this->parentTables(), $this->mainTable()); 34 | } 35 | 36 | public function getUrlWithPlaceholders($tables, $suffix = '') { 37 | foreach ((array) $tables as $table) { 38 | $parts[] = sprintf('%s/{%s}', $table, NameUtils::getVariableName($table)); 39 | } 40 | 41 | if (!empty($suffix)) { 42 | $parts[] = $suffix; 43 | } 44 | 45 | return $this->withRoutePrefix(implode('/', $parts ?? [])); 46 | } 47 | 48 | public function getRouteUrlWithId() { 49 | foreach ((array) $this->tables as $table) { 50 | if ($table == $this->mainTable()) { 51 | $parts[] = sprintf('%s/{%s_id}', $table, NameUtils::getVariableName($table)); 52 | } else { 53 | $parts[] = sprintf('%s/{%s}', $table, NameUtils::getVariableName($table)); 54 | } 55 | } 56 | 57 | return $this->withRoutePrefix(implode('/', $parts)); 58 | } 59 | 60 | public function getParentRouteUrl() { 61 | return $this->withRoutePrefix(str_replace('.', '/', $this->getParentRouteName())); 62 | } 63 | 64 | public function getRouteVars() { 65 | return sprintf('[%s]', join(', ', array_map(fn($table) => sprintf("'%s' => $%s", NameUtils::getVariableName($table), NameUtils::getVariableName($table)), $this->tables))); 66 | } 67 | 68 | public function getRouteVarsWithId() { 69 | foreach ($this->tables as $table) { 70 | if ($table == $this->mainTable()) { 71 | $vars[] = sprintf("'%s_id' => $%s->id", NameUtils::getVariableName($table), NameUtils::getVariableName($table)); 72 | } else { 73 | $vars[] = sprintf("'%s' => $%s", NameUtils::getVariableName($table), NameUtils::getVariableName($table)); 74 | } 75 | } 76 | 77 | return sprintf('[%s]', join(', ', $vars ?? [])); 78 | } 79 | 80 | public function getAsRoute() { 81 | return $this->hasParentTable() ? sprintf('"as" => "%s",', $this->getParentRouteName()) : ''; 82 | } 83 | 84 | public function withRoutePrefix(string $route) { 85 | return empty($this->routePrefix) ? $route : sprintf('%s/%s', trim($this->routePrefix, '/'), $route); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/@theme_index.js: -------------------------------------------------------------------------------- 1 | // node_modules/vitepress/dist/client/theme-default/index.js 2 | import "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/styles/fonts.css"; 3 | import "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/styles/vars.css"; 4 | import "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/styles/base.css"; 5 | import "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/styles/utils.css"; 6 | import "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/styles/components/custom-block.css"; 7 | import "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/styles/components/vp-code.css"; 8 | import "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/styles/components/vp-code-group.css"; 9 | import "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/styles/components/vp-doc.css"; 10 | import "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/styles/components/vp-sponsor.css"; 11 | import VPBadge from "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/components/VPBadge.vue"; 12 | import Layout from "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/Layout.vue"; 13 | import NotFound from "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/NotFound.vue"; 14 | import { default as default2 } from "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/components/VPHomeHero.vue"; 15 | import { default as default3 } from "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/components/VPHomeFeatures.vue"; 16 | import { default as default4 } from "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/components/VPHomeSponsors.vue"; 17 | import { default as default5 } from "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/components/VPDocAsideSponsors.vue"; 18 | import { default as default6 } from "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/components/VPTeamPage.vue"; 19 | import { default as default7 } from "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/components/VPTeamPageTitle.vue"; 20 | import { default as default8 } from "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/components/VPTeamPageSection.vue"; 21 | import { default as default9 } from "/home/san/laravel/plugins/php/laravel-crud/node_modules/vitepress/dist/client/theme-default/components/VPTeamMembers.vue"; 22 | var theme = { 23 | Layout, 24 | NotFound, 25 | enhanceApp: ({ app }) => { 26 | app.component("Badge", VPBadge); 27 | } 28 | }; 29 | var theme_default_default = theme; 30 | export { 31 | default5 as VPDocAsideSponsors, 32 | default3 as VPHomeFeatures, 33 | default2 as VPHomeHero, 34 | default4 as VPHomeSponsors, 35 | default9 as VPTeamMembers, 36 | default6 as VPTeamPage, 37 | default8 as VPTeamPageSection, 38 | default7 as VPTeamPageTitle, 39 | theme_default_default as default 40 | }; 41 | //# sourceMappingURL=@theme_index.js.map 42 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | Thank you for considering to contribute to this repository! This file will walk you through all the steps to ensure both 3 | you and I have a good time submitting and reviewing your contribution. First off, some basic rules and reading material: 4 | 5 | - Submit your work in a new branch and make the PR to the master branch. 6 | - [Write a short & descriptive commit message](http://chris.beams.io/posts/git-commit/). 7 | - Rebase before committing the final change. 8 | - Stick to [PSR-2](http://www.php-fig.org/psr/psr-2/). 9 | - Add tests if necessary and ensure all the tests are green on the final commit. 10 | - Make sure the CI tools used by the repository are all in order. If one fails, you should make it pass. 11 | 12 | ## Contributing 13 | Here are the steps to follow to contribute to this repository: 14 | 15 | - [Fork this repository on GitHub](#fork-this-repository). 16 | - [Clone your fork to your local machine](#clone-your-fork). 17 | - [Create a feature branch](#create-a-branch). 18 | - [Add an 'upstream' remote](#add-a-remote). 19 | - [Regularly pull & rebase from the upstream remote](#pull-and-rebase). 20 | - [Work on feature branch](#working-on-branch). 21 | - [Submit a pull request to the master branch](#submitting-pull-request). 22 | 23 | ### Fork this repository 24 | Fork the repository right here on GitHub. To learn more about forking a repository, visit 25 | [GitHub's help article](https://help.github.com/articles/fork-a-repo/). 26 | 27 | ### Clone your fork 28 | Clone your repository on your local machine to start work on your pull request. 29 | 30 | ```bash 31 | $ git clone git@github.com:/.git 32 | # Or if you prefer HTTPS: 33 | $ git clone https://github.com//.git 34 | 35 | $ cd 36 | ``` 37 | 38 | ### Create a branch 39 | Make your own feature branch in order not to clutter up master. 40 | 41 | ```bash 42 | # Think of a good name for your branch: 43 | # fix/typo-in-readme 44 | # feature/some-feature 45 | # bug/some-bug-you-are-fixing 46 | $ git checkout -b 47 | ``` 48 | 49 | ### Add a remote 50 | Add an 'upstream' remote to pull from and to stay up to date with the work being done in there. 51 | 52 | ```bash 53 | # List all current remotes 54 | $ git remote -v 55 | origin git@github.com//.git (fetch) 56 | origin git@github.com//.git (push) 57 | 58 | # Add a new remote called 'upstream' 59 | $ git remote add upstream git@github.com:san-kumar/.git 60 | # Or if you prefer HTTPS: 61 | $ git remote add upstream https://github.com/san-kumar/.git 62 | 63 | # The new remote should now be in the list 64 | $ git remote -v 65 | origin git@github.com//.git (fetch) 66 | origin git@github.com//.git (push) 67 | upstream git@github.com/san-kumar/.git (fetch) 68 | upstream git@github.com/san-kumar/.git (push) 69 | ``` 70 | 71 | ### Pull and rebase 72 | Pull from upstream to stay up to date with what others might be doing in this repository. Any merge conflicts that arise 73 | are your responsibility to resolve. 74 | 75 | ```bash 76 | $ git pull --rebase upstream master 77 | ``` 78 | 79 | ### Working on branch 80 | Do your magic and make your fix. I can't help you with this :wink:. Once you're happy with the result and all tests pass, 81 | go ahead and push to your feature branch. 82 | 83 | ```bash 84 | $ git push origin 85 | ``` 86 | 87 | ### Submitting pull request 88 | Now, let's head back over to this repository on GitHub and submit the pull request! 89 | -------------------------------------------------------------------------------- /src/Commands/CrudRemove.php: -------------------------------------------------------------------------------- 1 | getTables(); 37 | 38 | $cgen = new ControllerGen($tables, $tables); 39 | $mgen = new ModelGen($tables, $tables); 40 | $rgen = new RouteGen($tables, $tables); 41 | $vgen = new ViewGen($tables, $tables); 42 | $pgen = new PolicyGen($tables, $tables); 43 | 44 | $blanks = [ 45 | 'controller' => $cgen->getControllerName(), 46 | 'model' => $mgen->getModelName(), 47 | 'route' => $rgen->getRouteName(), 48 | 'policy' => $pgen->getPolicyName(), 49 | ]; 50 | 51 | $files = new Templates($this->getTemplateDir()); 52 | $cssFramework = 'bootstrap'; 53 | $interactive = $this->option('interactive'); 54 | 55 | if ($zipFile = $this->option('backup')) { 56 | if (!class_exists('ZipArchive')) { 57 | $this->error('ZipArchive class not found. Please install php-zip extension.'); 58 | return COMMAND::FAILURE; 59 | } 60 | 61 | $zip = new \ZipArchive(); 62 | $result = $zip->open($zipFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE); 63 | 64 | if (($result !== TRUE) || !is_writable(dirname($zipFile))) { 65 | $this->error('Cannot create zip file: ' . $zipFile); 66 | return COMMAND::FAILURE; 67 | } 68 | } 69 | 70 | foreach ($this->getStubTypes($cssFramework) as $type) { 71 | $stubs = $files->getStubs($type); 72 | 73 | foreach ($stubs as $stub) { 74 | $dest = $files->getDest($type, $stub, $blanks, $rgen->getRouteUrl()); 75 | 76 | if (file_exists($dest)) { 77 | if ($interactive && !$this->confirm("Delete $dest?")) { 78 | continue; 79 | } 80 | 81 | if (!empty($zip)) { 82 | $zip->addFromString(file_get_contents($dest), sprintf('%s/%s', $type, basename($dest))); 83 | } 84 | 85 | $this->info("Removing file $dest"); 86 | 87 | unlink($dest); 88 | } 89 | } 90 | } 91 | 92 | if (!empty($zip) && ($zip->numFiles > 0)) { 93 | $zip->close(); 94 | $this->info(sprintf("Backup created at: %s", realpath($zipFile))); 95 | } 96 | 97 | return Command::SUCCESS; 98 | } 99 | 100 | protected function getOptions() { 101 | return [ 102 | ['backup', 'b', InputOption::VALUE_REQUIRED, 'Name of zip file to backup files before removing'], 103 | ['interactive', 'i', InputOption::VALUE_NONE, 'Ask for confirmation before removing each file'], 104 | ]; 105 | } 106 | 107 | /** 108 | * {@inheritdoc} 109 | */ 110 | protected function getArguments() { 111 | return [ 112 | ['table', InputArgument::REQUIRED, 'Remove all generated files for the given tables'], 113 | ]; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Generators/BaseGen.php: -------------------------------------------------------------------------------- 1 | getVarNamePlural()); 14 | } 15 | 16 | public function getVarNamePlural() { 17 | return NameUtils::getVariableNamePlural($this->mainTable()); 18 | } 19 | 20 | public function mainTable() { 21 | return $this->tables[count($this->tables) - 1]; 22 | } 23 | 24 | public function getTitle() { 25 | return Str::singular($this->getTitlePlural()); 26 | } 27 | 28 | public function getTitlePlural() { 29 | return NameUtils::titleCase($this->mainTable()); 30 | } 31 | 32 | public function getParentVarName() { 33 | return Str::singular($this->getParentVarNamePlural()); 34 | } 35 | 36 | public function getParentVarNamePlural() { 37 | return NameUtils::getVariableNamePlural($this->parentTable()); 38 | } 39 | 40 | public function parentTable() { 41 | return $this->tables[count($this->tables) - 2]; 42 | } 43 | 44 | public function getMainModelName() { 45 | return NameUtils::getModelName((array) $this->mainTable()); 46 | } 47 | 48 | public function getMainVarName() { 49 | return NameUtils::getVariableName($this->mainTable()); 50 | } 51 | 52 | public function hasParentTable() { 53 | return count($this->tables) > 1; 54 | } 55 | 56 | public function parentTables() { 57 | return array_slice($this->tables, 0, -1); 58 | } 59 | 60 | public function getVars(array $tables, array $extraVars = []) { 61 | $vars = $this->getRawVars($tables, $extraVars); 62 | 63 | return !empty($vars) ? sprintf("compact(%s)", join(', ', array_map(fn($var) => sprintf("'%s'", $var), $vars))) : '[]'; 64 | } 65 | 66 | public function getRawVars(array $tables, ?array $extraVars = []) { 67 | foreach ($tables as $table) { 68 | $vars[] = NameUtils::getVariableName($table); 69 | } 70 | 71 | return array_merge($vars ?? [], (array) $extraVars); 72 | } 73 | 74 | protected function hasUserId() { 75 | return SchemaUtils::getUserIdField($this->mainTableReal()); 76 | } 77 | 78 | public function mainTableReal() { 79 | return $this->getTableNameFromAlias($this->mainTable()); 80 | } 81 | 82 | public function getTableNameFromAlias($tableName) { 83 | return $this->aliases[$tableName] ?? $tableName; 84 | } 85 | 86 | public function getAliasFromTableName($tableName) { 87 | return array_search($tableName, $this->aliases) ?: $tableName; 88 | } 89 | 90 | protected function hasTimestamps() { 91 | return SchemaUtils::hasTimestamps($this->mainTableReal()); 92 | } 93 | 94 | protected function hasSoftDeletes() { 95 | return SchemaUtils::hasSoftDelete($this->mainTableReal()); 96 | } 97 | 98 | protected function getFirstReadableField($key = NULL) { 99 | return SchemaUtils::firstHumanReadableField($this->mainTableReal(), $key) ?: 'id'; 100 | } 101 | 102 | protected function getFillableFields($exceptColumns = ['user_id']) { 103 | foreach (SchemaUtils::getTableFields($this->mainTableReal(), $exceptColumns) as $field) { 104 | if (in_array($field['related_table'] ?? '', $this->realTables())) continue; 105 | $fillable[] = $field; 106 | } 107 | 108 | return $fillable ?? []; 109 | } 110 | 111 | public function realTables() { 112 | return array_map(fn($table) => $this->getTableNameFromAlias($table), $this->tables); 113 | } 114 | 115 | protected function getExternallyRelatedFields() { 116 | $fields = SchemaUtils::getTableFieldsWithIds($this->mainTableReal(), ['user_id']); 117 | 118 | foreach ($fields as $field) { 119 | if (in_array($field['related_table'], $this->realTables())) continue; 120 | $externallyRelatedFields[] = $field; 121 | } 122 | 123 | return $externallyRelatedFields ?? []; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Utils/SchemaUtils.php: -------------------------------------------------------------------------------- 1 | getDoctrineSchemaManager()->listTableNames(); 10 | if ($withViews) { 11 | $views = DB::connection()->getDoctrineSchemaManager()->listViews(); 12 | $tables = array_merge($tables, array_keys($views)); 13 | } 14 | 15 | return array_values(array_filter($tables, fn($table) => !in_array($table, (array) $exclude))); 16 | } 17 | 18 | public static function getTableFields(string $tableName, array $excludedColumns = [], array $alwaysIgnoredColumns = ['id', 'created_at', 'updated_at', 'deleted_at']) { 19 | // ugly enum hack as doctrine does not support enum types 20 | // https://www.doctrine-project.org/projects/doctrine-orm/en/latest/cookbook/mysql-enums.html#solution-1-mapping-to-varchars 21 | 22 | DB::connection()->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'guid'); 23 | 24 | $columns = DB::getDoctrineSchemaManager()->listTableColumns($tableName); 25 | $ignoredColumns = array_merge((array) $excludedColumns, (array) $alwaysIgnoredColumns); 26 | $indexes = DB::getDoctrineSchemaManager()->listTableIndexes($tableName); 27 | $uniqueColumns = []; 28 | 29 | foreach ($indexes as $index) { 30 | if ($index->isUnique() && count($index->getColumns()) === 1) { 31 | $uniqueColumns = array_merge($uniqueColumns, $index->getColumns()); 32 | } 33 | } 34 | 35 | foreach ($columns as $column) { 36 | if (in_array($column->getName(), $ignoredColumns)) continue; 37 | 38 | $field = [ 39 | 'id' => $column->getName(), 40 | 'type' => $column->getType()->getName(), 41 | 'name' => \Str::title(str_replace('_', ' ', $column->getName())), 42 | 'nullable' => !$column->getNotnull(), 43 | 'default' => $column->getDefault(), 44 | ]; 45 | 46 | if ($field['type'] == 'guid') { 47 | try { 48 | $enums = DB::select("SHOW COLUMNS FROM $tableName WHERE Field = '$field[id]'"); 49 | $field['values'] = explode(',', str_replace("'", '', substr($enums[0]->Type, 5, -1))); 50 | } catch (\Throwable $e) { 51 | echo ''; 52 | } 53 | } 54 | 55 | if (preg_match('/^(.*?)_id$/', $field['id'], $matches)) { 56 | $relatedTable = \Str::plural($matches[1]); 57 | if (self::tableExists($relatedTable)) { 58 | $field['relation'] = $matches[1]; 59 | $field['related_table'] = $relatedTable; 60 | } 61 | } 62 | 63 | //check if column is unique index 64 | if (in_array($field['id'], $uniqueColumns)) { 65 | $field['unique'] = TRUE; 66 | } 67 | 68 | $fields[] = $field; 69 | } 70 | 71 | return $fields ?? []; 72 | } 73 | 74 | public static function firstHumanReadableField(string $table, string $key = NULL) { 75 | $all = self::getTableFields($table); 76 | if (empty($all)) return NULL; 77 | 78 | foreach ($all as $f) { 79 | if (preg_match('/_id$/', $f['id'])) continue; 80 | if (preg_match('/(string|text)/', $f['type'])) return $key ? $f[$key] : $f; 81 | $last = $f['id']; 82 | } 83 | 84 | $result = $last ?? $all[0]; 85 | return $key ? $result[$key] : $result; 86 | } 87 | 88 | public static function getTableFieldsWithIds(string $table, array $excludedColumns = []) { 89 | return array_values(array_filter(self::getTableFields($table, $excludedColumns), fn($f) => !empty($f['relation']))); 90 | } 91 | 92 | public static function getUserIdField(string $tableName, $userIdField = 'user_id') { 93 | if (!self::tableExists($tableName)) return NULL; 94 | return \Schema::hasColumn($tableName, $userIdField) ? $userIdField : NULL; 95 | } 96 | 97 | public static function hasTable(string $tableName) { 98 | return self::tableExists($tableName); 99 | } 100 | 101 | public static function hasTimestamps(string $tableName) { 102 | if (!self::tableExists($tableName)) return FALSE; 103 | return \Schema::hasColumn($tableName, 'created_at') && \Schema::hasColumn($tableName, 'updated_at'); 104 | } 105 | 106 | public static function hasSoftDelete(string $tableName) { 107 | if (!self::tableExists($tableName)) return FALSE; 108 | return \Schema::hasColumn($tableName, 'deleted_at'); 109 | } 110 | 111 | public static function tableExists(string $tableName) { 112 | return \Schema::hasTable($tableName); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/template/stubs/Controllers/_controller_.php: -------------------------------------------------------------------------------- 1 | validate(/*_cvalidatecreate_*/); 58 | 59 | try { 60 | 61 | $_var_ = new _model_(); 62 | /*_cstore_*/ 63 | $_var_->save(); 64 | 65 | return redirect()->route('_route_.index', /*_cparentvars_*/)->with('success', __('_title_ created successfully.')); 66 | } catch (\Throwable $e) { 67 | return redirect()->route('_route_.create', /*_cparentvars_*/)->withInput($request->input())->withErrors(['error' => $e->getMessage()]); 68 | } 69 | } 70 | 71 | /** 72 | * Display the specified resource. 73 | * 74 | * @param \App\Models\_model_ $_var_ 75 | * 76 | * @return \Illuminate\Contracts\Support\Renderable 77 | */ 78 | public function show(/*_callargs_*/) { 79 | /*_pauthorize_*/ 80 | 81 | return view('_route_.show', /*_callvars_*/); 82 | } 83 | 84 | /** 85 | * Show the form for editing the specified resource. 86 | * 87 | * @param \App\Models\_model_ $_var_ 88 | * 89 | * @return \Illuminate\Contracts\Support\Renderable 90 | */ 91 | public function edit(/*_callargs_*/) { 92 | /*_pauthorize_*/ 93 | 94 | /*_cselects_*/ 95 | 96 | return view('_route_._editview_', /*_ceditvars_*/); 97 | } 98 | 99 | /** 100 | * Update the specified resource in storage. 101 | * 102 | * @param \Illuminate\Http\Request $request 103 | * 104 | * @return \Illuminate\Http\RedirectResponse 105 | */ 106 | public function update(Request $request, /*_callargs_*/) { 107 | /*_pauthorize_*/ 108 | 109 | $request->validate(/*_cvalidateedit_*/); 110 | 111 | try { 112 | /*_cedit_*/ 113 | $_var_->save(); 114 | 115 | return redirect()->route('_route_.index', /*_cparentvars_*/)->with('success', __('_title_ edited successfully.')); 116 | } catch (\Throwable $e) { 117 | return redirect()->route('_route_.edit', /*_callvars_*/)->withInput($request->input())->withErrors(['error' => $e->getMessage()]); 118 | } 119 | } 120 | 121 | /** 122 | * Remove the specified resource from storage. 123 | * 124 | * @param \App\Models\_model_ $_var_ 125 | * 126 | * @return \Illuminate\Http\RedirectResponse 127 | */ 128 | public function destroy(/*_callargs_*/) { 129 | /*_pauthorize_*/ 130 | 131 | try { 132 | $_var_->delete(); 133 | 134 | return redirect()->route('_route_.index', /*_cparentvars_*/)->with('success', __('_title_ deleted successfully')); 135 | } catch (\Throwable $e) { 136 | return redirect()->route('_route_.index', /*_cparentvars_*/)->with('error', 'Cannot delete _title_: ' . $e->getMessage()); 137 | } 138 | } 139 | 140 | //@softdelete 141 | 142 | /** 143 | * Restore the specified deleted resource from storage. 144 | * 145 | * @param \App\Models\_model_ $_var_ 146 | * 147 | * @return \Illuminate\Http\RedirectResponse 148 | */ 149 | public function restore(/*_cparentargs_*/ int $_var__id,) { 150 | /*_pauthorize_*/ 151 | 152 | /*_cfindbyid_*/ 153 | /*_mauthorize_*/ 154 | 155 | if (!empty($_var_)) { 156 | $_var_->restore(); 157 | return redirect()->route('_route_.index', /*_cparentvars_*/)->with('success', __('_title_ restored successfully')); 158 | } else { 159 | return redirect()->route('_route_.index', /*_cparentvars_*/)->with('error', '_title_ not found'); 160 | } 161 | } 162 | 163 | /** 164 | * Permanently delete the specified resource from storage. 165 | * 166 | * @param \App\Models\_model_ $_var_ 167 | * 168 | * @return \Illuminate\Http\RedirectResponse 169 | */ 170 | public function purge(/*_cparentargs_*/ int $_var__id,) { 171 | /*_pauthorize_*/ 172 | 173 | /*_cfindbyid_*/ 174 | /*_mauthorize_*/ 175 | 176 | if (!empty($_var_)) { 177 | $_var_->forceDelete(); 178 | return redirect()->route('_route_.index', /*_cparentvars_*/)->with('success', __('_title_ purged successfully')); 179 | } else { 180 | return redirect()->route('_route_.index', /*_cparentvars_*/)->with('error', __('_title_ not found')); 181 | } 182 | } 183 | //@endsoftdelete 184 | } 185 | -------------------------------------------------------------------------------- /src/Generators/ViewGen.php: -------------------------------------------------------------------------------- 1 | getVarName(); 18 | } 19 | 20 | public function getBreadcrumbs($template, $path) { 21 | $index = 1; 22 | $total = (count($this->tables) * 2) - 1; 23 | 24 | foreach ($this->tables as $table) { 25 | $tables[] = $table; 26 | 27 | if (!empty($lastTables)) { 28 | $lastTable = $lastTables[count($lastTables) - 1]; 29 | $href = RouteUtils::makeRoute(NameUtils::getRouteName($lastTables) . '.show', $this->getRawVars($lastTables), $this->routePrefix); 30 | $anchorText = sprintf('%s #{{ $%s->id }}', NameUtils::titleCase($lastTable), NameUtils::getVariableName($lastTable)); 31 | $links[] = $this->render($template, $path, [], ['href' => "{{ $href }}", 'anchortext' => $anchorText, 'index' => $index++, 'total' => $total]); 32 | } 33 | 34 | $href = RouteUtils::makeRoute(NameUtils::getRouteName($tables) . '.index', $this->getRawVars(array_slice($tables, 0, -1)), $this->routePrefix); 35 | $anchorText = NameUtils::titleCase($table); 36 | $links[] = $this->render($template, $path, [], ['href' => "{{ $href }}", 'anchortext' => $anchorText, 'index' => $index++, 'total' => $total]); 37 | 38 | $lastTables[] = $table; 39 | } 40 | 41 | return join("\n\t\t\t\t\t\t", $links ?? []); 42 | } 43 | 44 | public function genForm(string $template, string $path, array $replacements, bool $edit) { 45 | $input = function ($f) use ($edit, $path) { 46 | $type = (!empty($f->values) ? 'select' : (preg_match('/text/i', $f->type) ? 'textarea' : (preg_match('/bool/i', $f->type) ? 'boolean' : (preg_match('/json/i', $f->type) ? 'json' : 'input')))); 47 | $default = !empty($f->default) ? " ?? '$f->default'" : NULL; 48 | $val = $edit ? sprintf('@old(\'%s\', %s)%s', $f->id, $f->type === 'json' ? "json_encode(\$_var_->{$f->id})" : "\$_var_->{$f->id}", $default) : sprintf('@old(\'%s\')%s', $f->id, $default); 49 | $inputType = preg_match('/int|double|float|decimal/i', $f->type) ? 'number' : (preg_match('/url/i', $f->id) ? 'url' :(preg_match('/color/i', $f->id) ? 'color' : (preg_match('/mail/i', $f->id) ? 'email' : (preg_match('/password/i', $f->id) ? 'password' : (preg_match('/datetime/i', $f->type) ? 'datetime-local' : (preg_match('/date/i', $f->type) ? 'date' : (preg_match('/time/i', $f->type) ? 'time' : 'text'))))))); 50 | $laraType = $inputType === 'datetime-local' ? 'datetime' : $inputType; 51 | $vars = ['_id_' => $f->id, '_name_' => $f->name, '_label_' => $f->name, '_enums_' => TextUtils::arrayExport($f->values ?? [], TRUE), '_val_' => $val, '_type_' => $inputType, '_laratype_' => $laraType, '_required_' => $f->nullable ? '' : 'required']; 52 | 53 | if (!empty($f->relation)) { 54 | $type = 'related'; 55 | $vars['_related_'] = NameUtils::getVariableName($f->related_table); 56 | $vars['_relateds_'] = NameUtils::getVariableNamePlural($f->related_table); 57 | $vars['_readable_'] = SchemaUtils::firstHumanReadableField($this->getTableNameFromAlias($f->related_table), 'id') ?: 'id'; 58 | $vars['_relatedroute_'] = RouteUtils::makeRoute($vars['_relateds_'] . '.create', [], $this->routePrefix); 59 | } 60 | 61 | return trim(strtr(file_get_contents("$path/inputs/$type.blade.php"), $vars)); 62 | }; 63 | 64 | $err = function ($f) { 65 | return sprintf("@if(\$errors->has('%s'))\n\t\t\t
    {{\$errors->first('%s')}}
    \n\t\t@endif", $f->id, $f->id); 66 | }; 67 | 68 | return $this->render($template, $path, $replacements, ['input' => $input, 'err' => $err]); 69 | } 70 | 71 | public function genIndex(string $template, string $path = '', array $replacements = []) { 72 | $display = function ($f) use ($path) { 73 | if (!empty($f->relation)) { 74 | if ($index = array_search($f->related_table, $this->parentTables())) { 75 | $tables = array_slice($this->parentTables(), 0, $index + 1); 76 | 77 | return sprintf('{{$_var_?->%s?->%s ?: "(blank)"}}', 78 | RouteUtils::makeRoute(NameUtils::getRouteName($tables) . '.show', $this->getRawVars($tables), $this->routePrefix), $f->relation, SchemaUtils::firstHumanReadableField($this->getTableNameFromAlias($f->related_table), 'id') ?? 'id'); 79 | } else { 80 | return sprintf('{{$_var_?->%s?->%s ?: "(blank)"}}', 81 | RouteUtils::makeRoute(NameUtils::getRouteName($f->related_table) . '.show', "\$_var_->$f->id ?: 0", $this->routePrefix), $f->relation, SchemaUtils::firstHumanReadableField($this->getTableNameFromAlias($f->related_table), 'id') ?? 'id'); 82 | } 83 | } else { 84 | $type = preg_match('/(json|boolean|text)/', $f->type) ? $f->type : 'string'; 85 | return trim(strtr(file_get_contents("$path/display/$type.blade.php"), ['_col_' => $f->id])); 86 | } 87 | }; 88 | 89 | 90 | return $this->render($template, $path, $replacements, ['display' => $display]); 91 | } 92 | 93 | protected function render($f, $path, $replacements, $vars) { 94 | $fields = array_map(fn($f) => (object) array_merge($f, ['name' => preg_replace('/ Id$/', '', $f['name'])]), $this->getFillableFields(['user_id'])); 95 | $readable = $this->getFirstReadableField('id'); 96 | $vars = array_merge(['fields' => $fields, 'hasSoftDelete' => $this->hasSoftDeletes(), 'hasTimestamps' => $this->hasTimestamps(), 'render' => fn($f) => call_user_func_array([$this, 'render'], [$f, $path, $replacements, $vars])], (array) $vars); 97 | $template = file_get_contents("$path/$f.blade.php"); 98 | $str = BladeCompiler::render($template, $vars); 99 | $str = TextUtils::replaceBlanks($str, array_merge((array) $replacements, ['readable' => $readable])); 100 | 101 | return $str; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Generators/ControllerGen.php: -------------------------------------------------------------------------------- 1 | tables); 15 | } 16 | 17 | public function getControllerAllArgs() { 18 | return $this->getControllerArgs($this->tables); 19 | } 20 | 21 | public function getControllerArgs(array $tables) { 22 | $value = join(', ', array_map(fn($table) => sprintf('%s $%s', NameUtils::getModelName((array) $table), NameUtils::getVariableName($table)), $tables)); 23 | return !empty($value) ? "$value," : ""; 24 | } 25 | 26 | public function getControllerParentArgs() { 27 | return $this->getControllerArgs($this->parentTables()); 28 | } 29 | 30 | public function getFindById() { 31 | return sprintf('$%s = %s::withTrashed()%s->find($%s_id);', $this->getVarName(), $this->getMainModelName(), 32 | $this->hasUserId() ? "->where('user_id', auth()->id())" : "", $this->getMainVarName()); 33 | } 34 | 35 | public function getQuery() { 36 | if ($this->hasParentTable()) { 37 | $code[] = sprintf('$%s = $%s->%s();', $this->getVarNamePlural(), $this->getParentVarName(), $this->mainTable()); 38 | } else { 39 | $code[] = sprintf('$%s = %s::query();', $this->getVarNamePlural(), $this->getMainModelName()); 40 | } 41 | 42 | foreach (array_slice($this->tables, -2) as $table) { 43 | $realTable = $this->getTableNameFromAlias($table); 44 | if ($userIdField = SchemaUtils::getUserIdField($realTable)) { 45 | $code[] = sprintf("\t\t\$%s->where('%s', auth()->id());", $realTable === $this->mainTableReal() ? NameUtils::getVariableNamePlural($table) : NameUtils::getVariableName($table), $userIdField); 46 | } 47 | } 48 | 49 | if ($this->hasSoftDeletes()) { 50 | $code[] = sprintf("\n\t\tif (!!\$request->trashed) {\n\t\t\t\$%s->withTrashed();\n\t\t}", $this->getVarNamePlural()); 51 | } 52 | 53 | return join("\n", $code); 54 | } 55 | 56 | public function getSearch() { 57 | return sprintf("if(!empty(\$request->search)) {\n\t\t\t\$%s->where('%s', 'like', '%%' . \$request->search . '%%');\n\t\t}", $this->getVarNamePlural(), SchemaUtils::firstHumanReadableField($this->mainTableReal(), 'id') ?: 'id'); 58 | } 59 | 60 | public function getPager() { 61 | return sprintf('$%s = $%s->paginate(10);', $this->getVarNamePlural(), $this->getVarNamePlural()); 62 | } 63 | 64 | public function getIndexVars() { 65 | return $this->getVars($this->parentTables(), (array) $this->getVarNamePlural()); 66 | } 67 | 68 | public function getAllVars() { 69 | return $this->getVars($this->tables); 70 | } 71 | 72 | public function getParentVars() { 73 | return $this->getVars($this->parentTables()); 74 | } 75 | 76 | public function getCreateVar() { 77 | return $this->singleForm ? sprintf('$%s = new %s();', $this->getVarName(), $this->getMainModelName()) : ''; 78 | } 79 | 80 | public function getCreateVars() { 81 | $extraVars = (array) array_map(fn($field) => $field['related_table'], (array) $this->getExternallyRelatedFields()); 82 | $createVar = $this->singleForm ? [$this->getVarName()] : []; 83 | $result = $this->getVars($this->parentTables(), array_merge($extraVars, $createVar)); 84 | 85 | return $result; 86 | } 87 | 88 | public function getEditVars() { 89 | return $this->getVars($this->tables, (array) array_map(fn($field) => $field['related_table'], (array) $this->getExternallyRelatedFields())); 90 | } 91 | 92 | public function getCreateView() { 93 | return $this->singleForm ? 'create-edit' : 'create'; 94 | } 95 | 96 | public function getEditView() { 97 | return $this->singleForm ? 'create-edit' : 'edit'; 98 | } 99 | 100 | public function getWith() { 101 | foreach ($this->getExternallyRelatedFields() as $field) { 102 | $code[] = sprintf('$%s->with(\'%s\');', $this->getVarNamePlural(), $field['relation']); 103 | } 104 | 105 | return join("\n\t\t", $code ?? []); 106 | } 107 | 108 | public function getSelects() { 109 | foreach ($this->getExternallyRelatedFields() as $field) { 110 | $realTable = $this->getTableNameFromAlias($field['related_table']); 111 | if (SchemaUtils::getUserIdField($realTable)) { 112 | $code[] = sprintf("\$%s = \App\Models\%s::where('%s', auth()->id())->get();", NameUtils::getVariableNamePlural($realTable), NameUtils::getModelName($realTable), SchemaUtils::getUserIdField($realTable)); 113 | } else { 114 | $code[] = sprintf("\$%s = \App\Models\%s::all();", NameUtils::getVariableNamePlural($realTable), NameUtils::getModelName($realTable)); 115 | } 116 | } 117 | 118 | return join("\n\t\t", $code ?? []); 119 | } 120 | 121 | public function getValidations(bool $edit) { 122 | foreach ($this->getFillableFields() as $field) { 123 | if (preg_match('/boolean|timestamp/', $field['type'])) continue; 124 | 125 | $requirements = []; 126 | 127 | if (!$field['nullable']) { 128 | $requirements[] = 'required'; 129 | } 130 | 131 | if (preg_match('/email$/i', $field['id'])) { 132 | $requirements[] = 'email'; 133 | } elseif (preg_match('/(url)/i', $field['id'])) { 134 | $requirements[] = 'url'; 135 | } elseif (preg_match('/(integer|float|double|decimal)/i', $field['type'])) { 136 | $requirements[] = 'numeric'; 137 | } elseif (preg_match('/(date|datetime)/i', $field['type'])) { 138 | $requirements[] = 'date'; 139 | } 140 | 141 | if (!empty($field['unique'])) { 142 | $unique = "unique:{$this->mainTableReal()},{$field['id']}"; 143 | if ($edit) { 144 | $unique .= ",\${$this->getVarName()}->id"; 145 | } 146 | 147 | $requirements[] = $unique; 148 | } 149 | 150 | $validations[$field['id']] = join('|', $requirements); 151 | } 152 | 153 | if (!empty($validations)) { 154 | $keyValues = array_map(fn($key, $value) => sprintf('"%s" => "%s"', $key, $value), array_keys($validations), $validations); 155 | return sprintf("[%s]", join(", ", $keyValues)); 156 | } else { 157 | return '[]'; 158 | } 159 | } 160 | 161 | public function getStore($edit) { 162 | foreach (SchemaUtils::getTableFields($this->mainTableReal()) as $field) { 163 | if ($field['id'] === 'user_id') { 164 | if (!$edit) $fills[] = sprintf("\$%s->user_id = auth()->id();", $this->getVarName()); 165 | } else if (in_array($field['related_table'] ?? '', $this->realTables())) { 166 | if (!$edit) { 167 | $fills[] = sprintf("\$%s->%s = \$%s->id;", $this->getVarName(), $field['id'], NameUtils::getVariableName($this->getAliasFromTableName($field['related_table']))); 168 | } 169 | } else { 170 | $bool = preg_match('/bool/', $field['type']) ? '!!' : ''; 171 | $fills[] = sprintf("\$%s->%s = %s\$request->%s;", $this->getVarName(), $field['id'], $bool, $field['id']); 172 | } 173 | } 174 | 175 | return join("\n\t\t", $fills ?? []); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/Commands/CrudGenerate.php: -------------------------------------------------------------------------------- 1 | getTables(); 41 | $aliases = $this->getTableAliases(); 42 | 43 | $routePrefix = $this->option('prefix'); 44 | $singleForm = $this->option('merge-forms'); 45 | 46 | $cgen = new ControllerGen($tables, $aliases, $singleForm); 47 | $mgen = new ModelGen($tables, $aliases); 48 | $rgen = new RouteGen($tables, $aliases, $routePrefix); 49 | $vgen = new ViewGen($tables, $aliases, $routePrefix); 50 | $pgen = new PolicyGen($tables, $aliases); 51 | 52 | $blanks = [ 53 | 'var' => $cgen->getVarName(), 54 | 'vars' => $cgen->getVarNamePlural(), 55 | 'title' => $cgen->getTitle(), 56 | 'titles' => $cgen->getTitlePlural(), 57 | 58 | 'controller' => $cgen->getControllerName(), 59 | 'cquery' => $cgen->getQuery(), 60 | 'cfindbyid' => $cgen->getFindById(), 61 | 'cwith' => $cgen->getWith(), 62 | 'csearch' => $cgen->getSearch(), 63 | 'cpager' => $cgen->getPager(), 64 | 65 | 'cparentargs' => $cgen->getControllerParentArgs(), 66 | 'callargs' => $cgen->getControllerAllArgs(), 67 | 68 | 'cselects' => $cgen->getSelects(), 69 | 'cindexvars' => $cgen->getIndexVars(), 70 | 'callvars' => $cgen->getAllVars(), 71 | 'cparentvars' => $cgen->getParentVars(), 72 | 'ccreatevar' => $cgen->getCreateVar(), 73 | 'ccreatevars' => $cgen->getCreateVars(), 74 | 'ceditvars' => $cgen->getEditVars(), 75 | 'createview' => $cgen->getCreateView(), 76 | 'editview' => $cgen->getEditView(), 77 | 78 | 'cvalidatecreate' => $cgen->getValidations(FALSE), 79 | 'cvalidateedit' => $cgen->getValidations(TRUE), 80 | 'cstore' => $cgen->getStore(FALSE), 81 | 'cedit' => $cgen->getStore(TRUE), 82 | 83 | 'model' => $mgen->getModelName(), 84 | 'usesmodels' => $mgen->getUsesModels(), 85 | 'softdelete' => $mgen->getSoftDelete(), 86 | 'fillable' => $mgen->getFillable(), 87 | 'belongsto' => $mgen->getBelongsTo(), 88 | 'hasmany' => $mgen->getHasMany(), 89 | 'casts' => $mgen->getCasts(), 90 | 'tablename' => $mgen->mainTableReal(), 91 | 92 | 'route' => $rgen->getRouteName(), 93 | 'routevars' => $rgen->getRouteVars(), 94 | 'routevarswithid' => $rgen->getRouteVarsWithId(), 95 | 'routeurl' => $rgen->getRouteUrl(), 96 | 'parentrouteurl' => $rgen->getParentRouteUrl(), 97 | 'routeurlwithvars' => $rgen->getRouteUrlWithPlaceholders(), 98 | 'routeurlwithid' => $rgen->getRouteUrlWithId(), 99 | 'parentrouteurlwithvars' => $rgen->getParentRouteUrlWithPlaceholders(), 100 | 101 | 'asroute' => $rgen->getAsRoute(), 102 | 103 | 'policy' => $pgen->getPolicyName(), 104 | 'cpolicy' => $pgen->getControllerPolicy(), 105 | 'policyreadargs' => $pgen->getPolicyReadArgs(), 106 | 'policyreadrules' => $pgen->getPolicyReadRules(), 107 | 'policywriteargs' => $pgen->getPolicyWriteArgs(), 108 | 'policywriterules' => $pgen->getPolicyWriteRules(), 109 | 110 | 'view' => $vgen->getViewName(), 111 | 112 | 'authorize' => $pgen->getControllerPolicy(), 113 | 'pauthorize' => $pgen->getParentAuthorization(), 114 | 'mauthorize' => $pgen->getModelAuthorization(), 115 | 116 | 'layout' => sprintf('layouts.%s', $this->option('layout') ?: 'app'), 117 | 'section' => $this->option('section') ?: 'content', 118 | ]; 119 | 120 | $files = new Templates($this->getTemplateDir()); 121 | $indexView = preg_match('/^(cards|chat)$/', $this->option('index')) ? $this->option('index') : 'table'; 122 | 123 | $cssFramework = $this->getCssFramework(); 124 | $partialsDir = "{$files->templatePath()}/stubs/views/$cssFramework/partials"; 125 | 126 | $blanks['breadcrumbs'] = $vgen->getBreadcrumbs('render/breadcrumbs', $partialsDir); 127 | $blanks['index'] = $vgen->genIndex("render/$indexView", $partialsDir, $blanks); 128 | $blanks['create'] = $vgen->genForm('render/form', $partialsDir, $blanks, FALSE); 129 | $blanks['edit'] = $vgen->genForm('render/form', $partialsDir, $blanks, TRUE); 130 | $blanks['show'] = $vgen->genIndex('render/show', $partialsDir, $blanks); 131 | 132 | $formatter = new Formatter($tables, $aliases); 133 | $forceOverwrite = $this->option('force'); 134 | 135 | foreach ($this->getStubTypes($cssFramework) as $type) { 136 | $stubs = $files->getStubs($type); 137 | 138 | foreach ($stubs as $stub) { 139 | $str = TextUtils::replaceBlanks(file_get_contents($stub), $blanks); 140 | $str = $formatter->fixSoftDelete($str); 141 | 142 | $dest = $files->getDest($type, $stub, $blanks, $rgen->getRouteUrlWithoutPrefix()); 143 | 144 | if ($forceOverwrite || !file_exists($dest)) { 145 | $this->info("Writing $dest"); 146 | FileUtils::writeFile($dest, $str); 147 | } else { 148 | $this->warn("File already exists (skipping):\n$dest"); 149 | } 150 | } 151 | } 152 | 153 | return Command::SUCCESS; 154 | } 155 | 156 | protected function getOptions() { 157 | return [ 158 | ['css', 'c', InputOption::VALUE_REQUIRED, 'Which css framework to use: bootstrap, larastrap or tailwind (default: bootstrap)'], 159 | ['prefix', 'p', InputOption::VALUE_REQUIRED, 'The prefix for the route name (default: none)'], 160 | ['index', 'i', InputOption::VALUE_REQUIRED, 'The index view to use: "table", "cards" or "chat" (default table)'], 161 | ['layout', 'l', InputOption::VALUE_REQUIRED, 'The @layout name used by the views (default "app")'], 162 | ['section', 's', InputOption::VALUE_REQUIRED, 'The @section name used by the views (default "content")'], 163 | ['tailwind', 't', InputOption::VALUE_NONE, 'Use Tailwind CSS instead of Bootstrap 5 (shorthand for --css=tailwind)'], 164 | ['template', 'd', InputOption::VALUE_REQUIRED, 'The template directory (if you want to use custom templates)'], 165 | ['alias', 'a', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'The alias for the table name (e.g. --alias some_alias=table_name --alias some_other_alias=another_table)'], 166 | ['merge-forms', 'm', InputOption::VALUE_NONE, 'Merge create and edit views into a single form'], 167 | ['force', 'f', InputOption::VALUE_NONE, 'Force overwriting of existing files'], 168 | ]; 169 | } 170 | 171 | /** 172 | * {@inheritdoc} 173 | */ 174 | protected function getArguments() { 175 | return [ 176 | ['table', InputArgument::REQUIRED, 'The name of the table (with parent tables separated by a dot)'], 177 | ]; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /.phpunit.result.cache: -------------------------------------------------------------------------------- 1 | {"version":1,"defects":{"San\\Crud\\Tests\\Utils\\TextUtilsTest::testReplaceBlanks":4,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetVarName":4,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetParentVarNamePlural":4,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetTitle":4,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetTitlePlural":4,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetVars":4,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetVarNamePlural":4,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetParentVarName":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetIndexVars":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetWith":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetControllerAllArgs":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetSearch":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetValidations":3,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetQuery":3,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetControllerArgs":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetParentVars":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetControllerName":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetCreateVars":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetSelects":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetEditVars":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetStore":3,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetAllVars":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetPager":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetControllerParentArgs":4,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetFindById":4,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetHasMany":4,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetCasts":4,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetFillable":4,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetModelName":4,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetBelongsTo":4,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetSoftDelete":4,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetUsesModels":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetUsesPolicies":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetControllerPolicy":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyWriteArgs":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyReadArgs":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetParentAuthorization":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyRules":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyWriteRules":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetModelAuthorization":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyAuthorization":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyName":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyArgs":4,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteUrlWithPlaceholders":4,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetParentRouteName":4,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteName":4,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteUrl":4,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetParentRouteUrlWithPlaceholders":4,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteVars":4,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteUrlWithId":4,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetParentRouteUrl":4,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteVarsWithId":4,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetAsRoute":4,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetUrlWithPlaceholders":4,"San\\Crud\\Tests\\Generators\\TemplatesTest::testGetStubs":4,"San\\Crud\\Tests\\Generators\\TemplatesTest::testGetFirstStub":4,"San\\Crud\\Tests\\Generators\\TemplatesTest::testGetDest":4,"San\\Crud\\Tests\\Utils\\FileUtilsTest::testGetFiles":4,"San\\Crud\\Tests\\Utils\\FileUtilsTest::testWriteFile":4,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetVariableNamePlural":4,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetVariableName":4,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetPolicyName":4,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testStudlyCase":4,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetModelName":4,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetControllerName":4,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testTitleCase":4,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetRouteName":4,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testHasTimestamps":4,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testFirstHumanReadableField":4,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testHasTable":4,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testGetTables":4,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testGetTableFields":4,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testGetTableFieldsWithIds":4,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testGetUserIdField":4,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testHasSoftDelete":4,"San\\Crud\\Tests\\Generators\\BaseGenTest::testMainTable":4,"San\\Crud\\Tests\\Generators\\BaseGenTest::testHasParentTable":4,"San\\Crud\\Tests\\Generators\\BaseGenTest::testParentTable":4,"San\\Crud\\Tests\\Generators\\BaseGenTest::testParentTables":4,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyReadRules":4,"San\\Crud\\Tests\\Generators\\ViewGenTest::testGetInfo":4,"San\\Crud\\Tests\\Generators\\ViewGenTest::testGenIndex":4,"San\\Crud\\Tests\\Generators\\ViewGenTest::testGenForm":4,"San\\Crud\\Tests\\Generators\\ViewGenTest::testGetBreadcrumbs":3,"San\\Crud\\Tests\\Utils\\TextUtilsTest::testArrayExport":4,"San\\Crud\\Tests\\Formatters\\FormatterTest::testFixSoftDelete":4,"San\\Crud\\Tests\\Commands\\MakeCrudTest::testHandle":4,"San\\Crud\\Tests\\Commands\\MakeCrudTest::testGetTables":2,"San\\Crud\\Tests\\Generators\\TemplatesTest::testInvalidPath":4,"San\\Crud\\Tests\\Utils\\RouteUtilsTest::testMakeRoute":3,"San\\Crud\\Tests\\Commands\\CrudRemoveTest::testHandle":4,"San\\Crud\\Tests\\Commands\\CrudTemplateTest::testHandle":3},"times":{"San\\Crud\\Tests\\Utils\\TextUtilsTest::testReplaceBlanks":0.016,"San\\Crud\\Tests\\Utils\\TextUtilsTest::testArrayExport":0.015,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetVarName":0.016,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetParentVarNamePlural":0.015,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetTitle":0.017,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetTitlePlural":0.016,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetVars":0.014,"San\\Crud\\Tests\\Generators\\BaseGenTest::testMainTable":0.014,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetVarNamePlural":0.014,"San\\Crud\\Tests\\Generators\\BaseGenTest::testHasParentTable":0.015,"San\\Crud\\Tests\\Generators\\BaseGenTest::testParentTable":0.014,"San\\Crud\\Tests\\Generators\\BaseGenTest::testParentTables":0.015,"San\\Crud\\Tests\\Generators\\BaseGenTest::testGetParentVarName":0.016,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetIndexVars":0.016,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetWith":0.016,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetControllerAllArgs":0.015,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetSearch":0.017,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetValidations":0.016,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetQuery":0.015,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetControllerArgs":0.016,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetParentVars":0.017,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetControllerName":0.017,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetCreateVars":0.019,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetSelects":0.02,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetEditVars":0.016,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetStore":0.017,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetAllVars":0.017,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetPager":0.017,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetControllerParentArgs":0.017,"San\\Crud\\Tests\\Generators\\ControllerGenTest::testGetFindById":0.017,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetHasMany":0.02,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetCasts":0.016,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetFillable":0.015,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetModelName":0.016,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetBelongsTo":0.017,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetSoftDelete":0.015,"San\\Crud\\Tests\\Generators\\ModelGenTest::testGetUsesModels":0.016,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetUsesPolicies":0.015,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetControllerPolicy":0.015,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyWriteArgs":0.015,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyReadArgs":0.015,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyReadRules":0.015,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetParentAuthorization":0.014,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyRules":0.016,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyWriteRules":0.016,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetModelAuthorization":0.014,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyAuthorization":0.016,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyName":0.015,"San\\Crud\\Tests\\Generators\\PolicyGenTest::testGetPolicyArgs":0.015,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteUrlWithPlaceholders":0.016,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetParentRouteName":0.016,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteName":0.02,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteUrl":0.016,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetParentRouteUrlWithPlaceholders":0.015,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteVars":0.014,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteUrlWithId":0.015,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetParentRouteUrl":0.013,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetRouteVarsWithId":0.015,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetAsRoute":0.014,"San\\Crud\\Tests\\Generators\\RouteGenTest::testGetUrlWithPlaceholders":0.016,"San\\Crud\\Tests\\Generators\\TemplatesTest::testGetStubs":0.016,"San\\Crud\\Tests\\Generators\\TemplatesTest::testGetFirstStub":0.015,"San\\Crud\\Tests\\Generators\\TemplatesTest::testGetDest":0.016,"San\\Crud\\Tests\\Generators\\ViewGenTest::testGetInfo":0.124,"San\\Crud\\Tests\\Generators\\ViewGenTest::testGenIndex":0.024,"San\\Crud\\Tests\\Generators\\ViewGenTest::testGenForm":0.018,"San\\Crud\\Tests\\Generators\\ViewGenTest::testGetBreadcrumbs":0.035,"San\\Crud\\Tests\\Utils\\FileUtilsTest::testGetFiles":0.016,"San\\Crud\\Tests\\Utils\\FileUtilsTest::testWriteFile":0.016,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetVariableNamePlural":0.016,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetVariableName":0.015,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetPolicyName":0.014,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testStudlyCase":0.015,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetModelName":0.014,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetControllerName":0.015,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testTitleCase":0.014,"San\\Crud\\Tests\\Utils\\NameUtilsTest::testGetRouteName":0.015,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testHasTimestamps":0.021,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testFirstHumanReadableField":0.016,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testHasTable":0.016,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testGetTables":0.016,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testGetTableFields":0.017,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testGetTableFieldsWithIds":0.017,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testGetUserIdField":0.015,"San\\Crud\\Tests\\Utils\\SchemaUtilsTest::testHasSoftDelete":0.014,"San\\Crud\\Tests\\Formatters\\FormatterTest::testFixSoftDelete":0.016,"San\\Crud\\Tests\\Commands\\MakeCrudTest::testHandle":0.272,"San\\Crud\\Tests\\Commands\\MakeCrudTest::testGetTables":0.026,"San\\Crud\\Tests\\Generators\\TemplatesTest::testInvalidPath":0.016,"San\\Crud\\Tests\\Utils\\RouteUtilsTest::testMakeRoute":0.016,"San\\Crud\\Tests\\Utils\\FileUtilsTest::testRecursiveCopy":0.016,"San\\Crud\\Tests\\Commands\\CrudRemoveTest::testHandle":0.046,"San\\Crud\\Tests\\Commands\\CrudTemplateTest::testHandle":0.019,"San\\Crud\\Tests\\Commands\\CrudGenerateTest::testHandle":0.176}} -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@algolia/autocomplete-core@1.7.2": 6 | version "1.7.2" 7 | resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.7.2.tgz#8abbed88082f611997538760dffcb43b33b1fd1d" 8 | integrity sha512-eclwUDC6qfApNnEfu1uWcL/rudQsn59tjEoUYZYE2JSXZrHLRjBUGMxiCoknobU2Pva8ejb0eRxpIYDtVVqdsw== 9 | dependencies: 10 | "@algolia/autocomplete-shared" "1.7.2" 11 | 12 | "@algolia/autocomplete-preset-algolia@1.7.2": 13 | version "1.7.2" 14 | resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.2.tgz#9cd4f64b3d64399657ee2dc2b7e0a939e0713a26" 15 | integrity sha512-+RYEG6B0QiGGfRb2G3MtPfyrl0dALF3cQNTWBzBX6p5o01vCCGTTinAm2UKG3tfc2CnOMAtnPLkzNZyJUpnVJw== 16 | dependencies: 17 | "@algolia/autocomplete-shared" "1.7.2" 18 | 19 | "@algolia/autocomplete-shared@1.7.2": 20 | version "1.7.2" 21 | resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.2.tgz#daa23280e78d3b42ae9564d12470ae034db51a89" 22 | integrity sha512-QCckjiC7xXHIUaIL3ektBtjJ0w7tTA3iqKcAE/Hjn1lZ5omp7i3Y4e09rAr9ZybqirL7AbxCLLq0Ra5DDPKeug== 23 | 24 | "@algolia/cache-browser-local-storage@4.14.3": 25 | version "4.14.3" 26 | resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.14.3.tgz#b9e0da012b2f124f785134a4d468ee0841b2399d" 27 | integrity sha512-hWH1yCxgG3+R/xZIscmUrWAIBnmBFHH5j30fY/+aPkEZWt90wYILfAHIOZ1/Wxhho5SkPfwFmT7ooX2d9JeQBw== 28 | dependencies: 29 | "@algolia/cache-common" "4.14.3" 30 | 31 | "@algolia/cache-common@4.14.3": 32 | version "4.14.3" 33 | resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.14.3.tgz#a78e9faee3dfec018eab7b0996e918e06b476ac7" 34 | integrity sha512-oZJofOoD9FQOwiGTzyRnmzvh3ZP8WVTNPBLH5xU5JNF7drDbRT0ocVT0h/xB2rPHYzOeXRrLaQQBwRT/CKom0Q== 35 | 36 | "@algolia/cache-in-memory@4.14.3": 37 | version "4.14.3" 38 | resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.14.3.tgz#96cefb942aeb80e51e6a7e29f25f4f7f3439b736" 39 | integrity sha512-ES0hHQnzWjeioLQf5Nq+x1AWdZJ50znNPSH3puB/Y4Xsg4Av1bvLmTJe7SY2uqONaeMTvL0OaVcoVtQgJVw0vg== 40 | dependencies: 41 | "@algolia/cache-common" "4.14.3" 42 | 43 | "@algolia/client-account@4.14.3": 44 | version "4.14.3" 45 | resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.14.3.tgz#6d7d032a65c600339ce066505c77013d9a9e4966" 46 | integrity sha512-PBcPb0+f5Xbh5UfLZNx2Ow589OdP8WYjB4CnvupfYBrl9JyC1sdH4jcq/ri8osO/mCZYjZrQsKAPIqW/gQmizQ== 47 | dependencies: 48 | "@algolia/client-common" "4.14.3" 49 | "@algolia/client-search" "4.14.3" 50 | "@algolia/transporter" "4.14.3" 51 | 52 | "@algolia/client-analytics@4.14.3": 53 | version "4.14.3" 54 | resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.14.3.tgz#ca409d00a8fff98fdcc215dc96731039900055dc" 55 | integrity sha512-eAwQq0Hb/aauv9NhCH5Dp3Nm29oFx28sayFN2fdOWemwSeJHIl7TmcsxVlRsO50fsD8CtPcDhtGeD3AIFLNvqw== 56 | dependencies: 57 | "@algolia/client-common" "4.14.3" 58 | "@algolia/client-search" "4.14.3" 59 | "@algolia/requester-common" "4.14.3" 60 | "@algolia/transporter" "4.14.3" 61 | 62 | "@algolia/client-common@4.14.3": 63 | version "4.14.3" 64 | resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.14.3.tgz#c44e48652b2121a20d7a40cfd68d095ebb4191a8" 65 | integrity sha512-jkPPDZdi63IK64Yg4WccdCsAP4pHxSkr4usplkUZM5C1l1oEpZXsy2c579LQ0rvwCs5JFmwfNG4ahOszidfWPw== 66 | dependencies: 67 | "@algolia/requester-common" "4.14.3" 68 | "@algolia/transporter" "4.14.3" 69 | 70 | "@algolia/client-personalization@4.14.3": 71 | version "4.14.3" 72 | resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.14.3.tgz#8f71325035aa2a5fa7d1d567575235cf1d6c654f" 73 | integrity sha512-UCX1MtkVNgaOL9f0e22x6tC9e2H3unZQlSUdnVaSKpZ+hdSChXGaRjp2UIT7pxmPqNCyv51F597KEX5WT60jNg== 74 | dependencies: 75 | "@algolia/client-common" "4.14.3" 76 | "@algolia/requester-common" "4.14.3" 77 | "@algolia/transporter" "4.14.3" 78 | 79 | "@algolia/client-search@4.14.3": 80 | version "4.14.3" 81 | resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.14.3.tgz#cf1e77549f5c3e73408ffe6441ede985fde69da0" 82 | integrity sha512-I2U7xBx5OPFdPLA8AXKUPPxGY3HDxZ4r7+mlZ8ZpLbI8/ri6fnu6B4z3wcL7sgHhDYMwnAE8Xr0AB0h3Hnkp4A== 83 | dependencies: 84 | "@algolia/client-common" "4.14.3" 85 | "@algolia/requester-common" "4.14.3" 86 | "@algolia/transporter" "4.14.3" 87 | 88 | "@algolia/logger-common@4.14.3": 89 | version "4.14.3" 90 | resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.14.3.tgz#87d4725e7f56ea5a39b605771b7149fff62032a7" 91 | integrity sha512-kUEAZaBt/J3RjYi8MEBT2QEexJR2kAE2mtLmezsmqMQZTV502TkHCxYzTwY2dE7OKcUTxi4OFlMuS4GId9CWPw== 92 | 93 | "@algolia/logger-console@4.14.3": 94 | version "4.14.3" 95 | resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.14.3.tgz#1f19f8f0a5ef11f01d1f9545290eb6a89b71fb8a" 96 | integrity sha512-ZWqAlUITktiMN2EiFpQIFCJS10N96A++yrexqC2Z+3hgF/JcKrOxOdT4nSCQoEPvU4Ki9QKbpzbebRDemZt/hw== 97 | dependencies: 98 | "@algolia/logger-common" "4.14.3" 99 | 100 | "@algolia/requester-browser-xhr@4.14.3": 101 | version "4.14.3" 102 | resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.14.3.tgz#bcf55cba20f58fd9bc95ee55793b5219f3ce8888" 103 | integrity sha512-AZeg2T08WLUPvDncl2XLX2O67W5wIO8MNaT7z5ii5LgBTuk/rU4CikTjCe2xsUleIZeFl++QrPAi4Bdxws6r/Q== 104 | dependencies: 105 | "@algolia/requester-common" "4.14.3" 106 | 107 | "@algolia/requester-common@4.14.3": 108 | version "4.14.3" 109 | resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.14.3.tgz#2d02fbe01afb7ae5651ae8dfe62d6c089f103714" 110 | integrity sha512-RrRzqNyKFDP7IkTuV3XvYGF9cDPn9h6qEDl595lXva3YUk9YSS8+MGZnnkOMHvjkrSCKfoLeLbm/T4tmoIeclw== 111 | 112 | "@algolia/requester-node-http@4.14.3": 113 | version "4.14.3" 114 | resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.14.3.tgz#72389e1c2e5d964702451e75e368eefe85a09d8f" 115 | integrity sha512-O5wnPxtDRPuW2U0EaOz9rMMWdlhwP0J0eSL1Z7TtXF8xnUeeUyNJrdhV5uy2CAp6RbhM1VuC3sOJcIR6Av+vbA== 116 | dependencies: 117 | "@algolia/requester-common" "4.14.3" 118 | 119 | "@algolia/transporter@4.14.3": 120 | version "4.14.3" 121 | resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.14.3.tgz#5593036bd9cf2adfd077fdc3e81d2e6118660a7a" 122 | integrity sha512-2qlKlKsnGJ008exFRb5RTeTOqhLZj0bkMCMVskxoqWejs2Q2QtWmsiH98hDfpw0fmnyhzHEt0Z7lqxBYp8bW2w== 123 | dependencies: 124 | "@algolia/cache-common" "4.14.3" 125 | "@algolia/logger-common" "4.14.3" 126 | "@algolia/requester-common" "4.14.3" 127 | 128 | "@babel/parser@^7.16.4": 129 | version "7.20.5" 130 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" 131 | integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== 132 | 133 | "@docsearch/css@3.3.0", "@docsearch/css@^3.3.0": 134 | version "3.3.0" 135 | resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.3.0.tgz#d698e48302d12240d7c2f7452ccb2d2239a8cd80" 136 | integrity sha512-rODCdDtGyudLj+Va8b6w6Y85KE85bXRsps/R4Yjwt5vueXKXZQKYw0aA9knxLBT6a/bI/GMrAcmCR75KYOM6hg== 137 | 138 | "@docsearch/js@^3.3.0": 139 | version "3.3.0" 140 | resolved "https://registry.yarnpkg.com/@docsearch/js/-/js-3.3.0.tgz#c8f614b722cc8a6375e83f9c27557e9398d6a4d4" 141 | integrity sha512-oFXWRPNvPxAzBhnFJ9UCFIYZiQNc3Yrv6912nZHw/UIGxsyzKpNRZgHq8HDk1niYmOSoLKtVFcxkccpQmYGFyg== 142 | dependencies: 143 | "@docsearch/react" "3.3.0" 144 | preact "^10.0.0" 145 | 146 | "@docsearch/react@3.3.0": 147 | version "3.3.0" 148 | resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.3.0.tgz#b8ac8e7f49b9bf2f96d34c24bc1cfd097ec0eead" 149 | integrity sha512-fhS5adZkae2SSdMYEMVg6pxI5a/cE+tW16ki1V0/ur4Fdok3hBRkmN/H8VvlXnxzggkQIIRIVvYPn00JPjen3A== 150 | dependencies: 151 | "@algolia/autocomplete-core" "1.7.2" 152 | "@algolia/autocomplete-preset-algolia" "1.7.2" 153 | "@docsearch/css" "3.3.0" 154 | algoliasearch "^4.0.0" 155 | 156 | "@esbuild/android-arm64@0.16.10": 157 | version "0.16.10" 158 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.10.tgz#d784d8f13dbef50492ea55456fb50651e4036fbf" 159 | integrity sha512-47Y+NwVKTldTlDhSgJHZ/RpvBQMUDG7eKihqaF/u6g7s0ZPz4J1vy8A3rwnnUOF2CuDn7w7Gj/QcMoWz3U3SJw== 160 | 161 | "@esbuild/android-arm@0.16.10": 162 | version "0.16.10" 163 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.10.tgz#becf6b5647c091b039121db8c17300a7dfd1ab4a" 164 | integrity sha512-RmJjQTRrO6VwUWDrzTBLmV4OJZTarYsiepLGlF2rYTVB701hSorPywPGvP6d8HCuuRibyXa5JX4s3jN2kHEtjQ== 165 | 166 | "@esbuild/android-x64@0.16.10": 167 | version "0.16.10" 168 | resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.10.tgz#648cacbb13a5047380a038e5d6d895015e31b525" 169 | integrity sha512-C4PfnrBMcuAcOurQzpF1tTtZz94IXO5JmICJJ3NFJRHbXXsQUg9RFG45KvydKqtFfBaFLCHpduUkUfXwIvGnRg== 170 | 171 | "@esbuild/darwin-arm64@0.16.10": 172 | version "0.16.10" 173 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.10.tgz#3ca7fd9a456d11752df77df6c030f2d08f27bda9" 174 | integrity sha512-bH/bpFwldyOKdi9HSLCLhhKeVgRYr9KblchwXgY2NeUHBB/BzTUHtUSBgGBmpydB1/4E37m+ggXXfSrnD7/E7g== 175 | 176 | "@esbuild/darwin-x64@0.16.10": 177 | version "0.16.10" 178 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.10.tgz#7eb71b8da4106627f01553def517d3c5e5942592" 179 | integrity sha512-OXt7ijoLuy+AjDSKQWu+KdDFMBbdeaL6wtgMKtDUXKWHiAMKHan5+R1QAG6HD4+K0nnOvEJXKHeA9QhXNAjOTQ== 180 | 181 | "@esbuild/freebsd-arm64@0.16.10": 182 | version "0.16.10" 183 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.10.tgz#c69c78ee1d17d35ad2cf76a1bb67788000a84b43" 184 | integrity sha512-shSQX/3GHuspE3Uxtq5kcFG/zqC+VuMnJkqV7LczO41cIe6CQaXHD3QdMLA4ziRq/m0vZo7JdterlgbmgNIAlQ== 185 | 186 | "@esbuild/freebsd-x64@0.16.10": 187 | version "0.16.10" 188 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.10.tgz#a9804ab1b9366f915812af24ad5cfc1c0db01441" 189 | integrity sha512-5YVc1zdeaJGASijZmTzSO4h6uKzsQGG3pkjI6fuXvolhm3hVRhZwnHJkforaZLmzvNv5Tb7a3QL2FAVmrgySIA== 190 | 191 | "@esbuild/linux-arm64@0.16.10": 192 | version "0.16.10" 193 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.10.tgz#d9a9ddfcb28ed8cced688bc112ef66283d6fa77f" 194 | integrity sha512-2aqeNVxIaRfPcIaMZIFoblLh588sWyCbmj1HHCCs9WmeNWm+EIN0SmvsmPvTa/TsNZFKnxTcvkX2eszTcCqIrA== 195 | 196 | "@esbuild/linux-arm@0.16.10": 197 | version "0.16.10" 198 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.10.tgz#f32cdac1d3319c83ae7f9f31238dd1284ee6bba2" 199 | integrity sha512-c360287ZWI2miBnvIj23bPyVctgzeMT2kQKR+x94pVqIN44h3GF8VMEs1SFPH1UgyDr3yBbx3vowDS1SVhyVhA== 200 | 201 | "@esbuild/linux-ia32@0.16.10": 202 | version "0.16.10" 203 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.10.tgz#1e023478e42f3a01cad48f4af50120d4b639af03" 204 | integrity sha512-sqMIEWeyrLGU7J5RB5fTkLRIFwsgsQ7ieWXlDLEmC2HblPYGb3AucD7inw2OrKFpRPKsec1l+lssiM3+NV5aOw== 205 | 206 | "@esbuild/linux-loong64@0.16.10": 207 | version "0.16.10" 208 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.10.tgz#f9098865a69d1d6e2f8bda51c7f9d4240f20b771" 209 | integrity sha512-O7Pd5hLEtTg37NC73pfhUOGTjx/+aXu5YoSq3ahCxcN7Bcr2F47mv+kG5t840thnsEzrv0oB70+LJu3gUgchvg== 210 | 211 | "@esbuild/linux-mips64el@0.16.10": 212 | version "0.16.10" 213 | resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.10.tgz#574725ad2ea81b7783b7ba7d1ab3475f8fdd8d32" 214 | integrity sha512-FN8mZOH7531iPHM0kaFhAOqqNHoAb6r/YHW2ZIxNi0a85UBi2DO4Vuyn7t1p4UN8a4LoAnLOT1PqNgHkgBJgbA== 215 | 216 | "@esbuild/linux-ppc64@0.16.10": 217 | version "0.16.10" 218 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.10.tgz#11da658c54514a693813af56bb28951d563a90c3" 219 | integrity sha512-Dg9RiqdvHOAWnOKIOTsIx8dFX9EDlY2IbPEY7YFzchrCiTZmMkD7jWA9UdZbNUygPjdmQBVPRCrLydReFlX9yg== 220 | 221 | "@esbuild/linux-riscv64@0.16.10": 222 | version "0.16.10" 223 | resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.10.tgz#3af4600adbd6c5a4a6f1da05771f4aa6774baab2" 224 | integrity sha512-XMqtpjwzbmlar0BJIxmzu/RZ7EWlfVfH68Vadrva0Wj5UKOdKvqskuev2jY2oPV3aoQUyXwnMbMrFmloO2GfAw== 225 | 226 | "@esbuild/linux-s390x@0.16.10": 227 | version "0.16.10" 228 | resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.10.tgz#9e3377aaf0191a9d6628e806a279085ec4391f3e" 229 | integrity sha512-fu7XtnoeRNFMx8DjK3gPWpFBDM2u5ba+FYwg27SjMJwKvJr4bDyKz5c+FLXLUSSAkMAt/UL+cUbEbra+rYtUgw== 230 | 231 | "@esbuild/linux-x64@0.16.10": 232 | version "0.16.10" 233 | resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.10.tgz#7c41d4d697ce674e0083e7baa6231468f4650d85" 234 | integrity sha512-61lcjVC/RldNNMUzQQdyCWjCxp9YLEQgIxErxU9XluX7juBdGKb0pvddS0vPNuCvotRbzijZ1pzII+26haWzbA== 235 | 236 | "@esbuild/netbsd-x64@0.16.10": 237 | version "0.16.10" 238 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.10.tgz#ebac59e3986834af04bbafcee7b0c1f31cd477c6" 239 | integrity sha512-JeZXCX3viSA9j4HqSoygjssdqYdfHd6yCFWyfSekLbz4Ef+D2EjvsN02ZQPwYl5a5gg/ehdHgegHhlfOFP0HCA== 240 | 241 | "@esbuild/openbsd-x64@0.16.10": 242 | version "0.16.10" 243 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.10.tgz#9eaa6cac3b80db45090c0946e62de5b5689c61d1" 244 | integrity sha512-3qpxQKuEVIIg8SebpXsp82OBrqjPV/OwNWmG+TnZDr3VGyChNnGMHccC1xkbxCHDQNnnXjxhMQNyHmdFJbmbRA== 245 | 246 | "@esbuild/sunos-x64@0.16.10": 247 | version "0.16.10" 248 | resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.10.tgz#31e5e4b814ef43d300e26511e486a4716a390d5f" 249 | integrity sha512-z+q0xZ+et/7etz7WoMyXTHZ1rB8PMSNp/FOqURLJLOPb3GWJ2aj4oCqFCjPwEbW1rsT7JPpxeH/DwGAWk/I1Bg== 250 | 251 | "@esbuild/win32-arm64@0.16.10": 252 | version "0.16.10" 253 | resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.10.tgz#ca58472dc03ca79e6d03f8a31113979ff253d94f" 254 | integrity sha512-+YYu5sbQ9npkNT9Dec+tn1F/kjg6SMgr6bfi/6FpXYZvCRfu2YFPZGb+3x8K30s8eRxFpoG4sGhiSUkr1xbHEw== 255 | 256 | "@esbuild/win32-ia32@0.16.10": 257 | version "0.16.10" 258 | resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.10.tgz#c572df2c65ab118feed0a5da5a4a193846d74e43" 259 | integrity sha512-Aw7Fupk7XNehR1ftHGYwUteyJ2q+em/aE+fVU3YMTBN2V5A7Z4aVCSV+SvCp9HIIHZavPFBpbdP3VfjQpdf6Xg== 260 | 261 | "@esbuild/win32-x64@0.16.10": 262 | version "0.16.10" 263 | resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.10.tgz#0e9c6a5e69c10d96aff2386b7ee9646138c2a831" 264 | integrity sha512-qddWullt3sC1EIpfHvCRBq3H4g3L86DZpD6n8k2XFjFVyp01D++uNbN1hT/JRsHxTbyyemZcpwL5aRlJwc/zFw== 265 | 266 | "@types/web-bluetooth@^0.0.16": 267 | version "0.0.16" 268 | resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz#1d12873a8e49567371f2a75fe3e7f7edca6662d8" 269 | integrity sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ== 270 | 271 | "@vitejs/plugin-vue@^4.0.0": 272 | version "4.0.0" 273 | resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-4.0.0.tgz#93815beffd23db46288c787352a8ea31a0c03e5e" 274 | integrity sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA== 275 | 276 | "@vue/compiler-core@3.2.45": 277 | version "3.2.45" 278 | resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz#d9311207d96f6ebd5f4660be129fb99f01ddb41b" 279 | integrity sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A== 280 | dependencies: 281 | "@babel/parser" "^7.16.4" 282 | "@vue/shared" "3.2.45" 283 | estree-walker "^2.0.2" 284 | source-map "^0.6.1" 285 | 286 | "@vue/compiler-dom@3.2.45": 287 | version "3.2.45" 288 | resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz#c43cc15e50da62ecc16a42f2622d25dc5fd97dce" 289 | integrity sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw== 290 | dependencies: 291 | "@vue/compiler-core" "3.2.45" 292 | "@vue/shared" "3.2.45" 293 | 294 | "@vue/compiler-sfc@3.2.45": 295 | version "3.2.45" 296 | resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz#7f7989cc04ec9e7c55acd406827a2c4e96872c70" 297 | integrity sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q== 298 | dependencies: 299 | "@babel/parser" "^7.16.4" 300 | "@vue/compiler-core" "3.2.45" 301 | "@vue/compiler-dom" "3.2.45" 302 | "@vue/compiler-ssr" "3.2.45" 303 | "@vue/reactivity-transform" "3.2.45" 304 | "@vue/shared" "3.2.45" 305 | estree-walker "^2.0.2" 306 | magic-string "^0.25.7" 307 | postcss "^8.1.10" 308 | source-map "^0.6.1" 309 | 310 | "@vue/compiler-ssr@3.2.45": 311 | version "3.2.45" 312 | resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz#bd20604b6e64ea15344d5b6278c4141191c983b2" 313 | integrity sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ== 314 | dependencies: 315 | "@vue/compiler-dom" "3.2.45" 316 | "@vue/shared" "3.2.45" 317 | 318 | "@vue/devtools-api@^6.4.5": 319 | version "6.4.5" 320 | resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz#d54e844c1adbb1e677c81c665ecef1a2b4bb8380" 321 | integrity sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ== 322 | 323 | "@vue/reactivity-transform@3.2.45": 324 | version "3.2.45" 325 | resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz#07ac83b8138550c83dfb50db43cde1e0e5e8124d" 326 | integrity sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ== 327 | dependencies: 328 | "@babel/parser" "^7.16.4" 329 | "@vue/compiler-core" "3.2.45" 330 | "@vue/shared" "3.2.45" 331 | estree-walker "^2.0.2" 332 | magic-string "^0.25.7" 333 | 334 | "@vue/reactivity@3.2.45": 335 | version "3.2.45" 336 | resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.45.tgz#412a45b574de601be5a4a5d9a8cbd4dee4662ff0" 337 | integrity sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A== 338 | dependencies: 339 | "@vue/shared" "3.2.45" 340 | 341 | "@vue/runtime-core@3.2.45": 342 | version "3.2.45" 343 | resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.45.tgz#7ad7ef9b2519d41062a30c6fa001ec43ac549c7f" 344 | integrity sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A== 345 | dependencies: 346 | "@vue/reactivity" "3.2.45" 347 | "@vue/shared" "3.2.45" 348 | 349 | "@vue/runtime-dom@3.2.45": 350 | version "3.2.45" 351 | resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz#1a2ef6ee2ad876206fbbe2a884554bba2d0faf59" 352 | integrity sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA== 353 | dependencies: 354 | "@vue/runtime-core" "3.2.45" 355 | "@vue/shared" "3.2.45" 356 | csstype "^2.6.8" 357 | 358 | "@vue/server-renderer@3.2.45": 359 | version "3.2.45" 360 | resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.45.tgz#ca9306a0c12b0530a1a250e44f4a0abac6b81f3f" 361 | integrity sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g== 362 | dependencies: 363 | "@vue/compiler-ssr" "3.2.45" 364 | "@vue/shared" "3.2.45" 365 | 366 | "@vue/shared@3.2.45": 367 | version "3.2.45" 368 | resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.45.tgz#a3fffa7489eafff38d984e23d0236e230c818bc2" 369 | integrity sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg== 370 | 371 | "@vueuse/core@^9.8.2": 372 | version "9.8.2" 373 | resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-9.8.2.tgz#a142281bb33368c74668a180a813c7c8d91f89d8" 374 | integrity sha512-aWiCmcYIpPt7xjuqYiceODEMHchDYthrJ4AqI+FXPZrR23PZOqdiktbUVyQl2kGlR3H4i9UJ/uimQrwhz9UouQ== 375 | dependencies: 376 | "@types/web-bluetooth" "^0.0.16" 377 | "@vueuse/metadata" "9.8.2" 378 | "@vueuse/shared" "9.8.2" 379 | vue-demi "*" 380 | 381 | "@vueuse/metadata@9.8.2": 382 | version "9.8.2" 383 | resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-9.8.2.tgz#864905e351a88165717c66d35d4b59a84bed2ae1" 384 | integrity sha512-N4E/BKS+9VsUeD4WLVRU1J2kCOLh+iikBcMtipFcTyL204132vDYHs27zLAVabJYGnhC0dIVGdhg9pbOZiY2TQ== 385 | 386 | "@vueuse/shared@9.8.2": 387 | version "9.8.2" 388 | resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-9.8.2.tgz#fd05866e5626218c9b0758b0422c666676cc4559" 389 | integrity sha512-ACjrPQzowd5dnabNJt9EoGVobco9/ENiA5qP53vjiuxndlJYuc/UegwhXC7KdQbPX4F45a50+45K3g1wNqOzmA== 390 | dependencies: 391 | vue-demi "*" 392 | 393 | algoliasearch@^4.0.0: 394 | version "4.14.3" 395 | resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.14.3.tgz#f02a77a4db17de2f676018938847494b692035e7" 396 | integrity sha512-GZTEuxzfWbP/vr7ZJfGzIl8fOsoxN916Z6FY2Egc9q2TmZ6hvq5KfAxY89pPW01oW/2HDEKA8d30f9iAH9eXYg== 397 | dependencies: 398 | "@algolia/cache-browser-local-storage" "4.14.3" 399 | "@algolia/cache-common" "4.14.3" 400 | "@algolia/cache-in-memory" "4.14.3" 401 | "@algolia/client-account" "4.14.3" 402 | "@algolia/client-analytics" "4.14.3" 403 | "@algolia/client-common" "4.14.3" 404 | "@algolia/client-personalization" "4.14.3" 405 | "@algolia/client-search" "4.14.3" 406 | "@algolia/logger-common" "4.14.3" 407 | "@algolia/logger-console" "4.14.3" 408 | "@algolia/requester-browser-xhr" "4.14.3" 409 | "@algolia/requester-common" "4.14.3" 410 | "@algolia/requester-node-http" "4.14.3" 411 | "@algolia/transporter" "4.14.3" 412 | 413 | body-scroll-lock@4.0.0-beta.0: 414 | version "4.0.0-beta.0" 415 | resolved "https://registry.yarnpkg.com/body-scroll-lock/-/body-scroll-lock-4.0.0-beta.0.tgz#4f78789d10e6388115c0460cd6d7d4dd2bbc4f7e" 416 | integrity sha512-a7tP5+0Mw3YlUJcGAKUqIBkYYGlYxk2fnCasq/FUph1hadxlTRjF+gAcZksxANnaMnALjxEddmSi/H3OR8ugcQ== 417 | 418 | csstype@^2.6.8: 419 | version "2.6.21" 420 | resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" 421 | integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w== 422 | 423 | esbuild@^0.16.3: 424 | version "0.16.10" 425 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.10.tgz#d485c28f1626a3f9c1796c952e4cd0561f0031bb" 426 | integrity sha512-z5dIViHoVnw2l+NCJ3zj5behdXjYvXne9gL18OOivCadXDUhyDkeSvEtLcGVAJW2fNmh33TDUpsi704XYlDodw== 427 | optionalDependencies: 428 | "@esbuild/android-arm" "0.16.10" 429 | "@esbuild/android-arm64" "0.16.10" 430 | "@esbuild/android-x64" "0.16.10" 431 | "@esbuild/darwin-arm64" "0.16.10" 432 | "@esbuild/darwin-x64" "0.16.10" 433 | "@esbuild/freebsd-arm64" "0.16.10" 434 | "@esbuild/freebsd-x64" "0.16.10" 435 | "@esbuild/linux-arm" "0.16.10" 436 | "@esbuild/linux-arm64" "0.16.10" 437 | "@esbuild/linux-ia32" "0.16.10" 438 | "@esbuild/linux-loong64" "0.16.10" 439 | "@esbuild/linux-mips64el" "0.16.10" 440 | "@esbuild/linux-ppc64" "0.16.10" 441 | "@esbuild/linux-riscv64" "0.16.10" 442 | "@esbuild/linux-s390x" "0.16.10" 443 | "@esbuild/linux-x64" "0.16.10" 444 | "@esbuild/netbsd-x64" "0.16.10" 445 | "@esbuild/openbsd-x64" "0.16.10" 446 | "@esbuild/sunos-x64" "0.16.10" 447 | "@esbuild/win32-arm64" "0.16.10" 448 | "@esbuild/win32-ia32" "0.16.10" 449 | "@esbuild/win32-x64" "0.16.10" 450 | 451 | estree-walker@^2.0.2: 452 | version "2.0.2" 453 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" 454 | integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== 455 | 456 | fsevents@~2.3.2: 457 | version "2.3.2" 458 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 459 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 460 | 461 | function-bind@^1.1.1: 462 | version "1.1.1" 463 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 464 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 465 | 466 | has@^1.0.3: 467 | version "1.0.3" 468 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 469 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 470 | dependencies: 471 | function-bind "^1.1.1" 472 | 473 | is-core-module@^2.9.0: 474 | version "2.11.0" 475 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" 476 | integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== 477 | dependencies: 478 | has "^1.0.3" 479 | 480 | jsonc-parser@^3.2.0: 481 | version "3.2.0" 482 | resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" 483 | integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== 484 | 485 | magic-string@^0.25.7: 486 | version "0.25.9" 487 | resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" 488 | integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== 489 | dependencies: 490 | sourcemap-codec "^1.4.8" 491 | 492 | nanoid@^3.3.4: 493 | version "3.3.4" 494 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" 495 | integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== 496 | 497 | path-parse@^1.0.7: 498 | version "1.0.7" 499 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 500 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 501 | 502 | picocolors@^1.0.0: 503 | version "1.0.0" 504 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 505 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 506 | 507 | postcss@^8.1.10, postcss@^8.4.20: 508 | version "8.4.20" 509 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.20.tgz#64c52f509644cecad8567e949f4081d98349dc56" 510 | integrity sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g== 511 | dependencies: 512 | nanoid "^3.3.4" 513 | picocolors "^1.0.0" 514 | source-map-js "^1.0.2" 515 | 516 | preact@^10.0.0: 517 | version "10.11.3" 518 | resolved "https://registry.yarnpkg.com/preact/-/preact-10.11.3.tgz#8a7e4ba19d3992c488b0785afcc0f8aa13c78d19" 519 | integrity sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg== 520 | 521 | resolve@^1.22.1: 522 | version "1.22.1" 523 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 524 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 525 | dependencies: 526 | is-core-module "^2.9.0" 527 | path-parse "^1.0.7" 528 | supports-preserve-symlinks-flag "^1.0.0" 529 | 530 | rollup@^3.7.0: 531 | version "3.8.0" 532 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.8.0.tgz#f61dc3ae02467d842687bd55b17a047c93b456ad" 533 | integrity sha512-+UR6PnUslneJNiJfLSzy4XH6R50ZGF0MS7UCv20ftXrktF/TkvZDwiBtXX65esblLR5p8w6LmXgPwt2f2B8SoQ== 534 | optionalDependencies: 535 | fsevents "~2.3.2" 536 | 537 | shiki@^0.12.1: 538 | version "0.12.1" 539 | resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.12.1.tgz#26fce51da12d055f479a091a5307470786f300cd" 540 | integrity sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ== 541 | dependencies: 542 | jsonc-parser "^3.2.0" 543 | vscode-oniguruma "^1.7.0" 544 | vscode-textmate "^8.0.0" 545 | 546 | source-map-js@^1.0.2: 547 | version "1.0.2" 548 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" 549 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== 550 | 551 | source-map@^0.6.1: 552 | version "0.6.1" 553 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 554 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 555 | 556 | sourcemap-codec@^1.4.8: 557 | version "1.4.8" 558 | resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" 559 | integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== 560 | 561 | supports-preserve-symlinks-flag@^1.0.0: 562 | version "1.0.0" 563 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 564 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 565 | 566 | vite@^4.0.2: 567 | version "4.0.3" 568 | resolved "https://registry.yarnpkg.com/vite/-/vite-4.0.3.tgz#de27ad3f263a03ae9419cdc8bc07721eadcba8b9" 569 | integrity sha512-HvuNv1RdE7deIfQb8mPk51UKjqptO/4RXZ5yXSAvurd5xOckwS/gg8h9Tky3uSbnjYTgUm0hVCet1cyhKd73ZA== 570 | dependencies: 571 | esbuild "^0.16.3" 572 | postcss "^8.4.20" 573 | resolve "^1.22.1" 574 | rollup "^3.7.0" 575 | optionalDependencies: 576 | fsevents "~2.3.2" 577 | 578 | vitepress@^1.0.0-alpha.33: 579 | version "1.0.0-alpha.33" 580 | resolved "https://registry.yarnpkg.com/vitepress/-/vitepress-1.0.0-alpha.33.tgz#5002ce435ad98b8e0c259e9df7770542bb3676c3" 581 | integrity sha512-EhMDqWLllYr5mXqAz4GCQ1o/bu5umQ6C2d8voiSaTHMkYCxsGc31ETykflM6NOhGx6yccwXygrYIIeN1l6BUEA== 582 | dependencies: 583 | "@docsearch/css" "^3.3.0" 584 | "@docsearch/js" "^3.3.0" 585 | "@vitejs/plugin-vue" "^4.0.0" 586 | "@vue/devtools-api" "^6.4.5" 587 | "@vueuse/core" "^9.8.2" 588 | body-scroll-lock "4.0.0-beta.0" 589 | shiki "^0.12.1" 590 | vite "^4.0.2" 591 | vue "^3.2.45" 592 | 593 | vscode-oniguruma@^1.7.0: 594 | version "1.7.0" 595 | resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" 596 | integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== 597 | 598 | vscode-textmate@^8.0.0: 599 | version "8.0.0" 600 | resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" 601 | integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== 602 | 603 | vue-demi@*: 604 | version "0.13.11" 605 | resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99" 606 | integrity sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A== 607 | 608 | vue@^3.2.45: 609 | version "3.2.45" 610 | resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.45.tgz#94a116784447eb7dbd892167784619fef379b3c8" 611 | integrity sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA== 612 | dependencies: 613 | "@vue/compiler-dom" "3.2.45" 614 | "@vue/compiler-sfc" "3.2.45" 615 | "@vue/runtime-dom" "3.2.45" 616 | "@vue/server-renderer" "3.2.45" 617 | "@vue/shared" "3.2.45" 618 | --------------------------------------------------------------------------------