├── src
├── Templates
│ ├── Default
│ │ ├── viewShow
│ │ ├── model
│ │ ├── repository
│ │ ├── request
│ │ ├── factory
│ │ ├── viewForm
│ │ ├── viewList
│ │ ├── service
│ │ ├── controller
│ │ └── test
│ └── Api
│ │ ├── model
│ │ ├── repository
│ │ ├── resource
│ │ ├── factory
│ │ ├── resourceCollection
│ │ ├── request
│ │ ├── service
│ │ ├── test
│ │ └── controller
├── Maker
│ ├── Crud
│ │ ├── Files
│ │ │ ├── ServiceFile.php
│ │ │ ├── ControllerFile.php
│ │ │ ├── RepositoryFile.php
│ │ │ ├── ModelFile.php
│ │ │ ├── ResourceCollectionFile.php
│ │ │ ├── FactoryFile.php
│ │ │ ├── ResourceFile.php
│ │ │ ├── ViewListFile.php
│ │ │ ├── ViewShowFile.php
│ │ │ ├── ViewFormFile.php
│ │ │ ├── TestFile.php
│ │ │ ├── RequestFile.php
│ │ │ └── File.php
│ │ ├── Fields.php
│ │ └── Shortcodes.php
│ ├── Interfaces
│ │ ├── PropertyContainerInterface.php
│ │ └── MakerFactoryInterface.php
│ ├── Maker.php
│ ├── PropertyContainer.php
│ └── MakerFactory.php
├── Publishes
│ ├── Filters
│ │ ├── BaseFilters.php
│ │ └── Filterable.php
│ └── Repositories
│ │ └── BaseRepository.php
├── Commands
│ └── MakeCrud.php
├── CrudMakerServiceProvider.php
└── Config
│ └── crudMaker.php
├── .gitignore
├── composer.json
├── README.md
└── LICENSE
/src/Templates/Default/viewShow:
--------------------------------------------------------------------------------
1 | $FIELDS$
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/src/Maker/Crud/Files/ServiceFile.php:
--------------------------------------------------------------------------------
1 | model = $PASCAL_ENTITY$::class;
12 |
13 | $this->filters = [];
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Templates/Default/repository:
--------------------------------------------------------------------------------
1 | model = $PASCAL_ENTITY$::class;
12 |
13 | $this->filters = [];
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Templates/Api/resource:
--------------------------------------------------------------------------------
1 | $this->collection,
14 | 'links' => [
15 | 'self' => 'link-value',
16 | ],
17 | ];
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Templates/Default/viewForm:
--------------------------------------------------------------------------------
1 |
2 | @if(isset($$SNAKE_ENTITY$))
3 |
14 |
15 |
--------------------------------------------------------------------------------
/src/Maker/Interfaces/MakerFactoryInterface.php:
--------------------------------------------------------------------------------
1 | shortcodes->setShortcode('$FILLABLE$', $this->getFillable());
14 |
15 | return parent::buildClass();
16 | }
17 |
18 | protected function getFillable(): string
19 | {
20 | return '\'' . implode('\',' . PHP_EOL . ' \'', $this->fields->getFields()) . '\'';
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Maker/Crud/Files/ResourceCollectionFile.php:
--------------------------------------------------------------------------------
1 | patch = $settings['path'] . '/' . ucfirst($this->propertyContainer->getProperty('entity'));
14 | $this->namespace = $settings['namespace'] . '\\' . ucfirst($this->propertyContainer->getProperty('entity'));
15 |
16 | return parent::setSettings($settings);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Publishes/Filters/BaseFilters.php:
--------------------------------------------------------------------------------
1 | where('name', 'like', '%' . $data . '%');
12 | }
13 | }
14 |
15 | private function filterExcept(object $builder, string $data): void
16 | {
17 | $builder->where('id', '!=', $data);
18 | }
19 |
20 | private function filterStart(object $builder, string $data): void
21 | {
22 | $builder->where('date', '>=', $data);
23 | }
24 |
25 | private function filterEnd(object $builder, string $data): void
26 | {
27 | $builder->where('date', '<=', $data);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Maker/Crud/Files/FactoryFile.php:
--------------------------------------------------------------------------------
1 | shortcodes->setShortcode('$DEFINITION$', $this->getDefinition());
14 |
15 | return parent::buildClass();
16 | }
17 |
18 | protected function getDefinition(): string
19 | {
20 | $fields = '';
21 |
22 | foreach ($this->fields->getFields() as $key => $field) {
23 | $fields .= ($key ? PHP_EOL . ' ' : '')
24 | . '\'' . $field . '\' => \'' . ucfirst($field) . '\',';
25 | }
26 |
27 | return $fields;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Templates/Default/viewList:
--------------------------------------------------------------------------------
1 | @if(session()->has('message'))
2 |
3 | {{ session()->get('message') }}
4 |
5 | @endif
6 |
7 |
8 | Create
9 | @foreach($$SNAKE_ENTITY$s as $$SNAKE_ENTITY$)
10 |
11 | $FIELDS$
12 | |
13 |
14 |
19 | |
20 |
21 | @endforeach
22 |
--------------------------------------------------------------------------------
/src/Publishes/Repositories/BaseRepository.php:
--------------------------------------------------------------------------------
1 | applyFiltersToQuery($this->model::query())->paginate();
16 | }
17 |
18 | public function getById(int $id): mixed
19 | {
20 | return $this->model::find($id);
21 | }
22 |
23 | public function create(array $data): mixed
24 | {
25 | return $this->model::create($data);
26 | }
27 |
28 | public function update(array $data, int $id): mixed
29 | {
30 | return tap($this->model::whereId($id)->first())->update($data);
31 | }
32 |
33 | public function delete(int $id): mixed
34 | {
35 | return $this->model::whereId($id)->delete();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Templates/Api/service:
--------------------------------------------------------------------------------
1 | $SNAKE_ENTITY$Repository->getAll();
16 | }
17 |
18 | public function show(int $id): mixed
19 | {
20 | return $this->$SNAKE_ENTITY$Repository->getById($id);
21 | }
22 |
23 | public function store(array $data): mixed
24 | {
25 | return $this->$SNAKE_ENTITY$Repository->create($data);
26 | }
27 |
28 | public function update(array $data, int $id): mixed
29 | {
30 | return $this->$SNAKE_ENTITY$Repository->update($data, $id);
31 | }
32 |
33 | public function destroy(int $id): mixed
34 | {
35 | return $this->$SNAKE_ENTITY$Repository->delete($id);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Templates/Default/service:
--------------------------------------------------------------------------------
1 | $SNAKE_ENTITY$Repository->getAll();
16 | }
17 |
18 | public function show(int $id): mixed
19 | {
20 | return $this->$SNAKE_ENTITY$Repository->getById($id);
21 | }
22 |
23 | public function store(array $data): mixed
24 | {
25 | return $this->$SNAKE_ENTITY$Repository->create($data);
26 | }
27 |
28 | public function update(array $data, int $id): mixed
29 | {
30 | return $this->$SNAKE_ENTITY$Repository->update($data, $id);
31 | }
32 |
33 | public function destroy(int $id): mixed
34 | {
35 | return $this->$SNAKE_ENTITY$Repository->delete($id);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Maker/Maker.php:
--------------------------------------------------------------------------------
1 | getTemplateConfig() as $file => $settings) {
20 |
21 | $methodName = 'make' . ucfirst($file);
22 |
23 | if (method_exists($builderFactory, $methodName)) {
24 | $builderFactory->$methodName()->setSettings($settings)->make();
25 | }
26 | }
27 | }
28 |
29 | private function getTemplateConfig(): array
30 | {
31 | return config('crudMaker.templates')[strtolower($this->propertyContainer->getProperty('templateName'))] ?? [];
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Maker/Crud/Files/ResourceFile.php:
--------------------------------------------------------------------------------
1 | patch = $settings['path'] . '/' . ucfirst($this->propertyContainer->getProperty('entity'));
14 | $this->namespace = $settings['namespace'] . '\\' . ucfirst($this->propertyContainer->getProperty('entity'));
15 |
16 | return parent::setSettings($settings);
17 | }
18 |
19 | protected function buildClass(): File
20 | {
21 | $this->shortcodes->setShortcode('$FIELDS$', $this->getFields());
22 |
23 | return parent::buildClass();
24 | }
25 |
26 | protected function getFields(): string
27 | {
28 | $rules = '';
29 |
30 | foreach ($this->fields->getFields(false) as $key => $field) {
31 | $rules .= ($key ? PHP_EOL . ' ' : '') . '\''
32 | . $field . '\' => $this->' . $field . ',';
33 | }
34 |
35 | return $rules;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Maker/Crud/Fields.php:
--------------------------------------------------------------------------------
1 | propertyContainer->getProperty('entityPlural'))
25 | );
26 |
27 | if ($ignoreDefaultFields) {
28 | return array_filter($fields, function ($value) {
29 | return !in_array($value, $this->ignoredFields);
30 | });
31 | }
32 |
33 | return $fields;
34 | }
35 |
36 | public function getFieldType(string $field): string
37 | {
38 | return Schema::getColumnType(Str::snake($this->propertyContainer->getProperty('entityPlural')), $field);
39 | }
40 | }
--------------------------------------------------------------------------------
/src/Maker/PropertyContainer.php:
--------------------------------------------------------------------------------
1 | propertyContainer[$propertyName] = $value;
15 | }
16 |
17 | function updateProperty($propertyName, $value): void
18 | {
19 | if (!isset($this->propertyContainer[$propertyName])) {
20 | throw new Exception("Property {$propertyName} not found");
21 | }
22 |
23 | $this->propertyContainer[$propertyName] = $value;
24 | }
25 |
26 | function getProperty($propertyName): mixed
27 | {
28 | if (!isset($this->propertyContainer[$propertyName])) {
29 | throw new Exception("Property {$propertyName} not found");
30 | }
31 |
32 | return $this->propertyContainer[$propertyName];
33 | }
34 |
35 | function deleteProperty($propertyName): void
36 | {
37 | unset($this->propertyContainer[$propertyName]);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Maker/Crud/Files/ViewListFile.php:
--------------------------------------------------------------------------------
1 | patch = $settings['path'] . '/' . lcfirst($this->propertyContainer->getProperty('entity'));
14 |
15 | return $this;
16 | }
17 |
18 | protected function getFileName(): string
19 | {
20 | return 'list' . static::PREFIX_FILE;
21 | }
22 |
23 | protected function buildClass(): File
24 | {
25 | $this->shortcodes->setShortcode('$FIELDS$', $this->getFieldsTemplate());
26 |
27 | return parent::buildClass();
28 | }
29 |
30 | protected function getFieldsTemplate(): string
31 | {
32 | $fields = '';
33 |
34 | foreach ($this->fields->getFields() as $field) {
35 | $fields .= '{{ $'
36 | . lcfirst($this->propertyContainer->getProperty('entity'))
37 | . '->' . $field . ' }} | '
38 | . PHP_EOL;
39 | }
40 |
41 | return $fields;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Maker/Crud/Files/ViewShowFile.php:
--------------------------------------------------------------------------------
1 | patch = $settings['path'] . '/' . lcfirst($this->propertyContainer->getProperty('entity'));
14 |
15 | return $this;
16 | }
17 |
18 | protected function getFileName(): string
19 | {
20 | return 'show' . static::PREFIX_FILE;
21 | }
22 |
23 | protected function buildClass(): File
24 | {
25 | $this->shortcodes->setShortcode('$FIELDS$', $this->getFieldsTemplate());
26 |
27 | return parent::buildClass();
28 | }
29 |
30 | protected function getFieldsTemplate(): string
31 | {
32 | $fields = '';
33 |
34 | foreach ($this->fields->getFields() as $field) {
35 | $fields .= '{{ $'
36 | . lcfirst($this->propertyContainer->getProperty('entity'))
37 | . '->' . $field . ' }}'
38 | . PHP_EOL;
39 | }
40 |
41 | return $fields;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Maker/Crud/Files/ViewFormFile.php:
--------------------------------------------------------------------------------
1 | patch = $settings['path'] . '/' . lcfirst($this->propertyContainer->getProperty('entity'));
14 |
15 | return $this;
16 | }
17 |
18 | protected function getFileName(): string
19 | {
20 | return 'form' . static::PREFIX_FILE;
21 | }
22 |
23 | protected function buildClass(): File
24 | {
25 | $this->shortcodes->setShortcode('$FIELDS$', $this->getFieldsTemplate());
26 |
27 | return parent::buildClass();
28 | }
29 |
30 | protected function getFieldsTemplate(): string
31 | {
32 | $fields = '';
33 |
34 | foreach ($this->fields->getFields() as $field) {
35 | $fields .= ''
38 | . PHP_EOL;
39 | }
40 |
41 | return $fields;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Publishes/Filters/Filterable.php:
--------------------------------------------------------------------------------
1 | checkFilters($requestFilters);
17 | }
18 |
19 | return $this;
20 | }
21 |
22 | private function checkFilters($requestFilters): void
23 | {
24 | foreach ($requestFilters as $key => $value) {
25 | if (in_array($key, $this->filters)) {
26 | $this->filtersForQuery[$key] = $value;
27 | }
28 | }
29 | }
30 |
31 | private function applyFiltersToQuery($query): object
32 | {
33 | foreach ($this->filtersForQuery as $field => $value) {
34 |
35 | $filterMethod = 'filter' . str_replace('_', '', ucwords($field, '_'));
36 |
37 | if (method_exists($this, $filterMethod)) {
38 | $this->$filterMethod($query, $value);
39 | } else {
40 | $query->where($field, $value);
41 | }
42 | }
43 |
44 | return $query;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Maker/Crud/Shortcodes.php:
--------------------------------------------------------------------------------
1 | setDefaultShortcodes();
14 | }
15 |
16 | private function setDefaultShortcodes(): void
17 | {
18 | $this->shortcodes = [
19 | '$ENTITY$' => $this->propertyContainer->getProperty('entity'),
20 | '$PASCAL_ENTITY$' => ucfirst($this->propertyContainer->getProperty('entity')),
21 | '$SNAKE_ENTITY$' => lcfirst($this->propertyContainer->getProperty('entity')),
22 | '$PASCAL_ENTITY_PLURAL$' => ucfirst($this->propertyContainer->getProperty('entityPlural')),
23 | '$SNAKE_ENTITY_PLURAL$' => lcfirst($this->propertyContainer->getProperty('entityPlural')),
24 | ];
25 | }
26 |
27 | public function setShortcode($key, $value): void
28 | {
29 | $this->shortcodes[$key] = $value;
30 | }
31 |
32 | public function getShortcodesKeys(): array
33 | {
34 | return array_keys($this->shortcodes);
35 | }
36 |
37 | public function getShortcodesValues(): array
38 | {
39 | return array_values($this->shortcodes);
40 | }
41 | }
--------------------------------------------------------------------------------
/src/Commands/MakeCrud.php:
--------------------------------------------------------------------------------
1 | ask('Entity:', 'Product');
42 | $propertyContainer->setProperty('entity', $entity);
43 |
44 | $entityPlural = $this->ask('Entity plural:', 'Products');
45 | $propertyContainer->setProperty('entityPlural', $entityPlural);
46 |
47 | $templateName = $this->ask('Template (Api or Default):', 'Api');
48 | $propertyContainer->setProperty('templateName', $templateName);
49 |
50 | App::make(Maker::class)->make();
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/src/CrudMakerServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->bind('make:crud', function ($app) {
22 | return new MakeCrud();
23 | });
24 | $this->commands([
25 | 'make:crud'
26 | ]);
27 |
28 | $this->publishes([
29 | __DIR__ . '/Publishes/Filters/BaseFilters.php' => base_path('app/Filters/BaseFilters.php'),
30 | __DIR__ . '/Publishes/Filters/Filterable.php' => base_path('app/Filters/Filterable.php'),
31 | __DIR__ . '/Publishes/Repositories/BaseRepository.php' => base_path('app/Repositories/BaseRepository.php'),
32 | ], 'crudMaker');
33 |
34 | $this->app->bind(MakerFactoryInterface::class, MakerFactory::class);
35 |
36 | $this->app->singleton(PropertyContainerInterface::class, PropertyContainer::class);
37 | }
38 |
39 | /**
40 | * Register the application services.
41 | *
42 | * @return void
43 | */
44 | public function register(): void
45 | {
46 | $this->mergeConfigFrom(
47 | __DIR__ . DIRECTORY_SEPARATOR . 'Config' . DIRECTORY_SEPARATOR . 'crudMaker.php', 'crudMaker'
48 | );
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/Maker/Crud/Files/TestFile.php:
--------------------------------------------------------------------------------
1 | shortcodes->setShortcode('$JSON_STRUCTURE$', $this->getJsonStructure());
14 | $this->shortcodes->setShortcode('$FIELDS$', $this->getFields());
15 |
16 | return parent::buildClass();
17 | }
18 |
19 | protected function getFields(): string
20 | {
21 | $fields = '';
22 |
23 | foreach ($this->fields->getFields() as $key => $field) {
24 | if ($this->fields->getFieldType($field) === 'string'
25 | || $this->fields->getFieldType($field) === 'varchar') {
26 | $fields .= ($key ? PHP_EOL . ' ' : '')
27 | . '\'' . $field . '\' => \'' . ucfirst($field) . '\',';
28 | }
29 | if ($this->fields->getFieldType($field) === 'boolean') {
30 | $fields .= ($key ? PHP_EOL . ' ' : '')
31 | . '\'' . $field . '\' => \'' . 1 . '\',';
32 | }
33 | if ($this->fields->getFieldType($field) === 'integer') {
34 | $fields .= ($key ? PHP_EOL . ' ' : '')
35 | . '\'' . $field . '\' => \'' . 100 . '\',';
36 | }
37 | if ($this->fields->getFieldType($field) === 'float') {
38 | $fields .= ($key ? PHP_EOL . ' ' : '')
39 | . '\'' . $field . '\' => \'' . 100.1 . '\',';
40 | }
41 | }
42 |
43 | return $fields;
44 | }
45 |
46 | protected function getJsonStructure(): string
47 | {
48 | return '\'' . implode('\',' . PHP_EOL . ' \'', $this->fields->getFields(false)) . '\'';
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Templates/Api/test:
--------------------------------------------------------------------------------
1 | create();
18 |
19 | $this->getJson(self::URL)
20 | ->assertJsonStructure([
21 | 'data',
22 | 'links',
23 | 'meta',
24 | ])->assertStatus(200);
25 | }
26 |
27 | public function test_show_$SNAKE_ENTITY$()
28 | {
29 | $$SNAKE_ENTITY$ = $PASCAL_ENTITY$::factory()->create();
30 |
31 | $this->getJson(self::URL . '/' . $$SNAKE_ENTITY$->id)
32 | ->assertJsonStructure([
33 | 'data' => [
34 | $JSON_STRUCTURE$
35 | ]
36 | ])->assertStatus(200);
37 | }
38 |
39 | public function test_create_$SNAKE_ENTITY$()
40 | {
41 | $this->postJson(self::URL,[$FIELDS$
42 | ])->assertJsonStructure([
43 | 'data' => [
44 | $JSON_STRUCTURE$
45 | ]
46 | ])->assertStatus(201);
47 | }
48 |
49 | public function test_edit_$SNAKE_ENTITY$()
50 | {
51 | $$SNAKE_ENTITY$ = $PASCAL_ENTITY$::factory()->create();
52 |
53 | $this->putJson(self::URL . '/' . $$SNAKE_ENTITY$->id,[$FIELDS$
54 | ])->assertJsonStructure([
55 | 'data' => [
56 | $JSON_STRUCTURE$
57 | ]
58 | ])->assertStatus(200);
59 | }
60 |
61 | public function test_delete_$SNAKE_ENTITY$()
62 | {
63 | $$SNAKE_ENTITY$ = $PASCAL_ENTITY$::factory()->create();
64 |
65 | $this->deleteJson(self::URL . '/' . $$SNAKE_ENTITY$->id)
66 | ->assertStatus(204);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Templates/Default/controller:
--------------------------------------------------------------------------------
1 | $this->$SNAKE_ENTITY$Service->getAll()
20 | ]);
21 | }
22 |
23 | public function create(): View
24 | {
25 | return view('$SNAKE_ENTITY$.form');
26 | }
27 |
28 | public function store($PASCAL_ENTITY$Request $request): RedirectResponse
29 | {
30 | $this->$SNAKE_ENTITY$Service->store($request->validated());
31 |
32 | return redirect('$SNAKE_ENTITY$')->with('message', 'Successfully created!');
33 | }
34 |
35 | public function edit(int $id): View
36 | {
37 | return view('$SNAKE_ENTITY$.form', [
38 | '$SNAKE_ENTITY$' => $this->$SNAKE_ENTITY$Service->show($id)
39 | ]);
40 | }
41 |
42 | public function show(int $id): View
43 | {
44 | return view('$SNAKE_ENTITY$.show', [
45 | '$SNAKE_ENTITY$' => $this->$SNAKE_ENTITY$Service->show($id)
46 | ]);
47 | }
48 |
49 | public function update($PASCAL_ENTITY$Request $request, int $id): RedirectResponse
50 | {
51 | $this->$SNAKE_ENTITY$Service->update($request->validated(), $id);
52 |
53 | return redirect('$SNAKE_ENTITY$')->with('message', 'Successfully updated!');
54 | }
55 |
56 | public function destroy(int $id): RedirectResponse
57 | {
58 | $this->$SNAKE_ENTITY$Service->destroy($id);
59 |
60 | return redirect('$SNAKE_ENTITY$')->with('message', 'Successfully deleted!');
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Maker/Crud/Files/RequestFile.php:
--------------------------------------------------------------------------------
1 | patch = $settings['path'] . '/' . ucfirst($this->propertyContainer->getProperty('entity'));
14 | $this->namespace = $settings['namespace'] . '\\' . ucfirst($this->propertyContainer->getProperty('entity'));
15 |
16 | return parent::setSettings($settings);
17 | }
18 |
19 | /**
20 | * @return ModelFile
21 | */
22 | protected function buildClass(): File
23 | {
24 | $this->shortcodes->setShortcode('$RULES$', $this->getRules());
25 | $this->shortcodes->setShortcode('$OA_FIELDS$', $this->getOAFields());
26 | $this->shortcodes->setShortcode('$REQUIRED$', $this->getRequiredFields());
27 |
28 | return parent::buildClass();
29 | }
30 |
31 | /**
32 | * @return string
33 | */
34 | protected function getRules(): string
35 | {
36 | $rules = '';
37 |
38 | foreach ($this->fields->getFields() as $key => $field) {
39 | $rules .= ($key ? PHP_EOL . ' ' : '')
40 | . '\'' . $field . '\' => [\'required\'],';
41 | }
42 |
43 | return $rules;
44 | }
45 |
46 | /**
47 | * @return string
48 | */
49 | protected function getOAFields(): string
50 | {
51 | $fields = '';
52 |
53 | foreach ($this->fields->getFields() as $field) {
54 | $fields .= '* @OA\Property( property="' . $field . '", type="' . $this->fields->getFieldType($field) . '", title="' . ucfirst($field) . '", example="' . ucfirst($field) . '"),' . PHP_EOL;
55 | }
56 |
57 | return $fields;
58 | }
59 |
60 | /**
61 | * @return string
62 | */
63 | protected function getRequiredFields(): string
64 | {
65 | $fields = '';
66 |
67 | foreach ($this->fields->getFields() as $key => $field) {
68 | $fields .= (!empty($fields) ? ', ': '') . '"' . $field . '"';
69 | }
70 |
71 | return $fields;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Maker/MakerFactory.php:
--------------------------------------------------------------------------------
1 | patch = $this->patch ?? $settings['path'];
26 | $this->namespace = $this->namespace ?? $settings['namespace'] ?? '';
27 |
28 | return $this;
29 | }
30 |
31 | public function make(): void
32 | {
33 | $this->loadTemplate()
34 | ->buildClass()
35 | ->publish();
36 | }
37 |
38 | protected function publish(): void
39 | {
40 | if (!file_exists(base_path($this->patch))) {
41 | mkdir(base_path($this->patch), 0775, true);
42 | }
43 |
44 | file_put_contents(base_path($this->patch) . '/' . $this->getFileName(), $this->template);
45 | }
46 |
47 | protected function getFileName(): string
48 | {
49 | return $this->propertyContainer->getProperty('entity')
50 | . static::PREFIX_FILE;
51 | }
52 |
53 | protected function buildClass(): File
54 | {
55 | $this->setNamespaces();
56 |
57 | $this->template = str_replace(
58 | $this->shortcodes->getShortcodesKeys(),
59 | $this->shortcodes->getShortcodesValues(),
60 | $this->template
61 | );
62 |
63 | return $this;
64 | }
65 |
66 | private function setNamespaces(): void
67 | {
68 | $this->shortcodes->setShortcode('$NAMESPACE$', $this->namespace ?? '');
69 |
70 | $files = config('crudMaker.templates')
71 | [strtolower($this->propertyContainer->getProperty('templateName'))];
72 |
73 | foreach ($files as $file => $data) {
74 | $this->shortcodes->setShortcode('$' . strtoupper($file) .'_NAMESPACE$', $data['namespace'] ?? '');
75 | }
76 | }
77 |
78 | protected function loadTemplate(): static
79 | {
80 | $this->template = file_get_contents($this->getTemplatePath());
81 |
82 | return $this;
83 | }
84 |
85 | protected function getTemplatePath(): string
86 | {
87 | return config('crudMaker.dir_templates')
88 | . $this->propertyContainer->getProperty('templateName')
89 | . '/'
90 | . static::FILE_NAME;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/Templates/Default/test:
--------------------------------------------------------------------------------
1 | '$PASCAL_ENTITY$ test',
19 | 'price' => 10,
20 | 'status' => 1,
21 | ]);
22 |
23 | $response = $this->get(self::URL);
24 | $response->assertStatus(200);
25 | }
26 |
27 | public function test_show_$SNAKE_ENTITY$()
28 | {
29 | $$SNAKE_ENTITY$ = $PASCAL_ENTITY$::create([
30 | 'name' => '$PASCAL_ENTITY$ test',
31 | 'price' => 10,
32 | 'status' => 1,
33 | ]);
34 |
35 | $response = $this->get(self::URL . '/' . $$SNAKE_ENTITY$->id);
36 | $response->assertStatus(200);
37 | }
38 |
39 | public function test_create_$SNAKE_ENTITY$()
40 | {
41 | $response = $this->get(self::URL . '/create');
42 | $response->assertStatus(200);
43 |
44 | $response = $this->post(self::URL, [
45 | 'name' => '$PASCAL_ENTITY$ test',
46 | 'price' => 10,
47 | 'status' => 1,
48 | ]);
49 |
50 | $response->assertRedirect(self::URL);
51 |
52 | $$SNAKE_ENTITY$ = $PASCAL_ENTITY$::where('name', '$PASCAL_ENTITY$ test')->first();
53 | $this->assertNotNull($$SNAKE_ENTITY$);
54 | }
55 |
56 | public function test_edit_$SNAKE_ENTITY$()
57 | {
58 | $$SNAKE_ENTITY$ = $PASCAL_ENTITY$::create([
59 | 'name' => '$PASCAL_ENTITY$ test',
60 | 'price' => 10,
61 | 'status' => 1,
62 | ]);
63 |
64 | $response = $this->get(self::URL . '/' . $$SNAKE_ENTITY$->id . '/edit');
65 | $response->assertStatus(200);
66 |
67 | $response = $this->put(self::URL . '/' . $$SNAKE_ENTITY$->id, [
68 | 'name' => '$PASCAL_ENTITY$ test updated',
69 | 'price' => 10,
70 | 'status' => 1,
71 | ]);
72 |
73 | $response->assertRedirect(self::URL);
74 |
75 | $$SNAKE_ENTITY$ = $PASCAL_ENTITY$::where('name', '$PASCAL_ENTITY$ test updated')->first();
76 | $this->assertNotNull($$SNAKE_ENTITY$);
77 | }
78 |
79 | public function test_delete_$SNAKE_ENTITY$()
80 | {
81 | $$SNAKE_ENTITY$ = $PASCAL_ENTITY$::create([
82 | 'name' => '$PASCAL_ENTITY$ test',
83 | 'price' => 10,
84 | 'status' => 1,
85 | ]);
86 |
87 | $response = $this->delete(self::URL . '/' . $$SNAKE_ENTITY$->id);
88 | $response->assertRedirect(self::URL);
89 |
90 | $$SNAKE_ENTITY$ = $PASCAL_ENTITY$::where('name', '$PASCAL_ENTITY$ test')->first();
91 | $this->assertNull($$SNAKE_ENTITY$);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/Config/crudMaker.php:
--------------------------------------------------------------------------------
1 | dirname(__DIR__ . '../') . '/Templates/',
7 | 'templates' => [
8 | 'default' => [
9 | 'controller' => [
10 | 'path' => 'app/Http/Controllers',
11 | 'namespace' => 'App\Http\Controllers'
12 | ],
13 | 'model' => [
14 | 'path' => 'app/Models',
15 | 'namespace' => 'App\Models'
16 | ],
17 | 'repository' => [
18 | 'path' => 'app/Repositories',
19 | 'namespace' => 'App\Repositories'
20 | ],
21 | 'request' => [
22 | 'path' => 'app/Http/Requests',
23 | 'namespace' => 'App\Http\Requests'
24 | ],
25 | 'viewList' => [
26 | 'path' => 'resources/views',
27 | ],
28 | 'viewForm' => [
29 | 'path' => 'resources/views',
30 | ],
31 | 'viewShow' => [
32 | 'path' => 'resources/views',
33 | ],
34 | 'test' => [
35 | 'path' => 'tests/Feature',
36 | 'namespace' => 'Tests\Feature'
37 | ],
38 | 'factory' => [
39 | 'path' => 'database/factories',
40 | 'namespace' => 'Database\Factories'
41 | ],
42 | 'service' => [
43 | 'path' => 'app/Services',
44 | 'namespace' => 'App\Services'
45 | ],
46 | ],
47 |
48 | 'api' => [
49 | 'controller' => [
50 | 'path' => 'app/Http/Controllers',
51 | 'namespace' => 'App\Http\Controllers'
52 | ],
53 | 'model' => [
54 | 'path' => 'app/Models',
55 | 'namespace' => 'App\Models'
56 | ],
57 | 'repository' => [
58 | 'path' => 'app/Repositories',
59 | 'namespace' => 'App\Repositories'
60 | ],
61 | 'request' => [
62 | 'path' => 'app/Http/Requests',
63 | 'namespace' => 'App\Http\Requests'
64 | ],
65 | 'resource' => [
66 | 'path' => 'app/Http/Resources',
67 | 'namespace' => 'App\Http\Resources'
68 | ],
69 | 'resourceCollection' => [
70 | 'path' => 'app/Http/Resources',
71 | 'namespace' => 'App\Http\Resources'
72 | ],
73 | 'test' => [
74 | 'path' => 'tests/Feature',
75 | 'namespace' => 'Tests\Feature'
76 | ],
77 | 'factory' => [
78 | 'path' => 'database/factories',
79 | 'namespace' => 'Database\Factories'
80 | ],
81 | 'service' => [
82 | 'path' => 'app/Services',
83 | 'namespace' => 'App\Services'
84 | ],
85 | ],
86 | ],
87 | ];
88 |
--------------------------------------------------------------------------------
/src/Templates/Api/controller:
--------------------------------------------------------------------------------
1 | $SNAKE_ENTITY$Service->getAll());
31 | }
32 |
33 | /**
34 | * @OA\Get(
35 | * path="/$SNAKE_ENTITY_PLURAL$/{id}",
36 | * operationId="show$PASCAL_ENTITY$",
37 | * tags={"$PASCAL_ENTITY_PLURAL$"},
38 | * summary="Show $SNAKE_ENTITY$",
39 | * @OA\Parameter(name="id", in="path", required=true),
40 | * @OA\Response(response=200, description="Successful operation", @OA\JsonContent()),
41 | * )
42 | */
43 | public function show(int $id): $PASCAL_ENTITY$Resource
44 | {
45 | return new $PASCAL_ENTITY$Resource($this->$SNAKE_ENTITY$Service->show($id));
46 | }
47 |
48 | /**
49 | * @OA\Post(
50 | * path="/$SNAKE_ENTITY_PLURAL$",
51 | * operationId="store$PASCAL_ENTITY$",
52 | * tags={"$PASCAL_ENTITY_PLURAL$"},
53 | * summary="Store new $SNAKE_ENTITY$",
54 | * @OA\RequestBody(required=true,
55 | * @OA\JsonContent(ref="#/components/schemas/$PASCAL_ENTITY$Request")
56 | * ),
57 | * @OA\Response(response=201, description="Successful operation", @OA\JsonContent()),
58 | * )
59 | */
60 | public function store($PASCAL_ENTITY$Request $request): $PASCAL_ENTITY$Resource
61 | {
62 | return new $PASCAL_ENTITY$Resource($this->$SNAKE_ENTITY$Service->store($request->validated()));
63 | }
64 |
65 | /**
66 | * @OA\Put(
67 | * path="/$SNAKE_ENTITY_PLURAL$/{id}",
68 | * operationId="update$PASCAL_ENTITY$",
69 | * tags={"$PASCAL_ENTITY_PLURAL$"},
70 | * summary="Update $SNAKE_ENTITY$",
71 | * @OA\Parameter(name="id", in="path", required=true),
72 | * @OA\RequestBody(required=true,
73 | * @OA\JsonContent(ref="#/components/schemas/$PASCAL_ENTITY$Request")
74 | * ),
75 | * @OA\Response(response=200, description="Successful operation", @OA\JsonContent()),
76 | * )
77 | */
78 | public function update($PASCAL_ENTITY$Request $request, int $id): $PASCAL_ENTITY$Resource
79 | {
80 | return new $PASCAL_ENTITY$Resource($this->$SNAKE_ENTITY$Service->update($request->validated(), $id));
81 | }
82 |
83 | /**
84 | * @OA\Delete(
85 | * path="/$SNAKE_ENTITY_PLURAL$/{id}",
86 | * operationId="delete$PASCAL_ENTITY$",
87 | * tags={"$PASCAL_ENTITY_PLURAL$"},
88 | * summary="Delete existing $SNAKE_ENTITY$",
89 | * @OA\Parameter(name="id", in="path", required=true),
90 | * @OA\Response(response=204, description="No Content", @OA\JsonContent()),
91 | * )
92 | */
93 | public function destroy(int $id): Response
94 | {
95 | $this->$SNAKE_ENTITY$Service->destroy($id);
96 |
97 | return response()->noContent();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------