Mercurial > packages > magicforger
changeset 26:555bfaa500ac codex
Big update for view creation based on the column type.
| author | Luka Sitas <sitas.luka.97@gmail.com> |
|---|---|
| date | Thu, 15 May 2025 21:50:38 -0400 |
| parents | 1a717c7b211f |
| children | b17f81b804ff |
| files | src/Generator/BaseGenerator.php src/Generator/Controller/stubs/controller.stub src/Generator/Model/ModelGenerator.php src/Generator/Model/stubs/model.stub src/Generator/View/CreateEditViewGenerator.php src/Generator/View/IndexViewGenerator.php src/Generator/View/ViewGenerator.php src/Generator/View/snippets/index/header.stub src/Generator/View/snippets/index/value.stub src/Generator/View/snippets/input/checkbox.stub src/Generator/View/snippets/input/date.stub src/Generator/View/snippets/input/select.stub src/Generator/View/snippets/input/text.stub src/Generator/View/snippets/input/textarea.stub src/Generator/View/stubs/create_edit.stub src/Generator/View/stubs/index.stub |
| diffstat | 16 files changed, 351 insertions(+), 47 deletions(-) [+] |
line wrap: on
line diff
--- a/src/Generator/BaseGenerator.php Sun May 11 21:03:51 2025 -0400 +++ b/src/Generator/BaseGenerator.php Thu May 15 21:50:38 2025 -0400 @@ -21,6 +21,8 @@ protected $currentTable = null; + protected static $cached_snippets = []; + public function handle() { @@ -187,4 +189,15 @@ { exec('./vendor/bin/pint '.escapeshellarg($path)); } + + protected function getSnippet($snippet_name) + { + // Cache snippet contents to avoid re-reading files + if (! isset(self::$cached_snippets[$snippet_name])) { + self::$cached_snippets[$snippet_name] = $this->files->get( + $this->resolveStubPath("/snippets/$snippet_name.stub")); + } + + return self::$cached_snippets[$snippet_name]; + } }
--- a/src/Generator/Controller/stubs/controller.stub Sun May 11 21:03:51 2025 -0400 +++ b/src/Generator/Controller/stubs/controller.stub Thu May 15 21:50:38 2025 -0400 @@ -16,8 +16,8 @@ public function index() { $data = []; - $data['items'] = {{ model }}::all(); - $data['fields'] = (new {{ model }}()->getFillable()); + $data['items'] = {{ model }}::get_data(); + $data = array_merge($data, {{ model }}::load_index()); return view('{{ tableName }}.index', $data); } @@ -30,7 +30,7 @@ public function create() { $data = []; - $data['fields'] = (new {{ model }}()->getFillable()); + $data = array_merge($data, {{ model }}::load_create()); return view('{{ tableName }}.create_edit', $data); } @@ -60,7 +60,7 @@ { $data = []; $data['item'] = ${{ modelVariable }}; - $data['fields'] = (new {{ model }}()->getFillable()); + $data['fields'] = (new {{ model }}())->getFillable(); return view('{{ tableName }}.show', $data); } @@ -75,9 +75,9 @@ { $data = []; $data['item'] = ${{ modelVariable }}; - $data['fields'] = (new {{ model }}()->getFillable()); // Load data for relationships + $data = array_merge($data, {{ model }}::load_edit()); return view('{{ tableName }}.create_edit', $data); }
--- a/src/Generator/Model/ModelGenerator.php Sun May 11 21:03:51 2025 -0400 +++ b/src/Generator/Model/ModelGenerator.php Thu May 15 21:50:38 2025 -0400 @@ -86,17 +86,6 @@ return str_replace(['App\\', '\\'], ['app/', '/'], $this->getModelNamespace().'/'.$this->model_name($this->getTableInput()).'.php'); } - protected function getSnippet($snippet_name) - { - // Cache snippet contents to avoid re-reading files - if (! isset(self::$cached_snippets[$snippet_name])) { - self::$cached_snippets[$snippet_name] = $this->files->get( - $this->resolveStubPath("/snippets/$snippet_name.stub")); - } - - return self::$cached_snippets[$snippet_name]; - } - protected function gatherRelations() { $relations = RelationshipNavigator::getRelations($this->getCurrentTable()); @@ -205,10 +194,14 @@ $hasMany = !empty($rendered['hasMany']) ? implode("\n ", $rendered['hasMany']) : ''; $belongsMany = !empty($rendered['belongsToMany']) ? implode("\n ", $rendered['belongsToMany']) : ''; + // Default relations are based on the belongsTo relationship + $default_relations = implode(", \n", array_map(function ($rel) {return '\'' . Str::singular($rel['table']) . '\''; }, $relations['belongsTo'])); + // Assign to stub placeholders $inserts['# {{ belongs_to_relationships }}'] = $belongs; $inserts['# {{ has_many_relationships }}'] = $hasMany; $inserts['# {{ has_many_through_relationships }}'] = $belongsMany; + $inserts['# {{ defaultRelationsInsertPoint }}'] = $default_relations; return $inserts; }
--- a/src/Generator/Model/stubs/model.stub Sun May 11 21:03:51 2025 -0400 +++ b/src/Generator/Model/stubs/model.stub Thu May 15 21:50:38 2025 -0400 @@ -27,21 +27,25 @@ # {{ atributeInsertPoint }} ]; - protected $fillable =[ + protected $fillable = [ # {{ fillableInsertPoint }} ]; + protected $default_relations = [ + # {{ defaultRelationsInsertPoint }} + ]; + //MARK FOR MODEL public static function boot() : void { parent::boot(); self::creating(function ($item) { - $item->created_by = \Auth::user()->id; - $item->updated_by = \Auth::user()->id; + $item->created_by = \Auth::user()?->id ?? ''; + $item->updated_by = \Auth::user()?->id ?? ''; }); self::saving(function ($item) { - $item->updated_by = \Auth::user()->id; + $item->updated_by = \Auth::user()?->id ?? ''; }); } @@ -57,4 +61,76 @@ // HasManyThrough # {{ has_many_through_relationships }} + + /** + * Load the default relations for the model. + * + * @return $this + */ + public function load_relations() { + foreach($this->default_relations as $relation) { + $this->load($relation); + } + return $this; + } + + //MARK FOR MODEL + protected static function load_auxilary_data() { + $data = []; + + $instance = new static(); + + foreach($instance->default_relations as $relation) { + $related_model = $instance->$relation()->getRelated(); + $related_table = $related_model->getTable(); + $data[$related_table] = $related_model->all()->pluck('name','id')->toArray(); + } + + return $data; + } + + //MARK FOR MODEL + public static function load_index() { + return static::load_auxilary_data(); + } + + //MARK FOR MODEL + public static function load_create() { + return static::load_auxilary_data(); + } + + //MARK FOR MODEL + public static function load_edit() { + return static::load_auxilary_data(); + } + + + /** + * Retrieve a query builder instance with default relations loaded. + * + * @return \Illuminate\Database\Eloquent\Builder + */ + //MARK FOR MODEL + public static function data_query() { + $query = static::query(); + + $instance = new static(); + + foreach($instance->default_relations as $relation) { + $query->with($relation); + } + + return $query; + } + + /** + * Retrieve a query builder instance with default relations loaded. + * + * @return \Illuminate\Database\Eloquent\Builder + */ + public static function get_data() + { + return static::data_query()->get(); + } + }
--- a/src/Generator/View/CreateEditViewGenerator.php Sun May 11 21:03:51 2025 -0400 +++ b/src/Generator/View/CreateEditViewGenerator.php Thu May 15 21:50:38 2025 -0400 @@ -4,6 +4,8 @@ use Symfony\Component\Console\Attribute\AsCommand; use Wizard\MagicForger\Generator\BaseGenerator; +use Wizard\MagicForger\Helpers\RelationshipNavigator; +use Illuminate\Support\Str; #[AsCommand(name: 'mf:create_edit_view')] class CreateEditViewGenerator extends BaseGenerator @@ -74,4 +76,89 @@ { return str_replace(['Resources\\', '\\'], ['resources/', '/'], $this->getViewNamespace($this->getTableInput()).'create_edit.blade.php'); } + + protected function renderColumns() { + $renders = []; + + $columns = $this->getTableColumns($this->getCurrentTable()); + $relations = RelationshipNavigator::getRelations($this->getCurrentTable()); + //gether the select columns based on the relations + $selects = []; + + foreach($relations['belongsTo'] as $relation) { + $selects[$relation['column']] = $relation['table']; + } + + foreach($columns as $column) { + $name = $column['name']; + if(in_array($name, $this->columns_to_ignore)) continue; + + $type = $column['type_name']; + + $replacements = [ + '{{fieldName}}' => $name, + '{{fieldLabel}}' => Str::headline($name), + '{{required}}' => $column['nullable'] ? 'false' : 'true', + ]; + $snippet = ''; + + //date + if(in_array($type, ['date', 'timestamp'])) { + $snippet = $this->getSnippet('input/date'); + } + //checkbox + elseif(in_array($type, ['tinyint']) && array_key_exists($name, $selects)) { + $snippet = $this->getSnippet('input/checkbox'); + } + + //select + elseif(in_array($type, ['bigint']) && array_key_exists($name, $selects)) { + $snippet = $this->getSnippet('input/select'); + $replacements['{{fieldLabel}}'] = Str::headline(Str::singular($selects[$name])); + $replacements['{{options}}'] = '$'.$selects[$name]; + } + + //text area + elseif(in_array($type, ['text'])) { + $snippet = $this->getSnippet('input/textarea'); + } + else { + //varchar, bigint, float, etc + $snippet = $this->getSnippet('input/text'); + } + + + // Replace placeholders with actual values + $renders[] = str_replace( + array_keys($replacements), + $replacements, + $snippet + ); + } + return $renders; + } + + + + /** + * Get available insertions including model relationships. + * + * @return array + */ + public function get_available_inserts(): array + { + // Merge parent insertions (attributes, fillable, etc.) + $inserts = parent::get_available_inserts(); + + // Gather and render relationships for this model + $rendered = $this->renderColumns(); + + // Build code blocks for each relation type + $columns = !empty($rendered) ? implode("\n ", $rendered) : ''; + + // Assign to stub placeholders + $inserts['{{ fieldsInsertPoint }}'] = $columns; + + return $inserts; + } }
--- a/src/Generator/View/IndexViewGenerator.php Sun May 11 21:03:51 2025 -0400 +++ b/src/Generator/View/IndexViewGenerator.php Thu May 15 21:50:38 2025 -0400 @@ -3,7 +3,9 @@ namespace Wizard\MagicForger\Generator\View; use Symfony\Component\Console\Attribute\AsCommand; +use Wizard\MagicForger\Helpers\RelationshipNavigator; use Wizard\MagicForger\Generator\BaseGenerator; +use Illuminate\Support\Str; #[AsCommand(name: 'mf:index_view')] class IndexViewGenerator extends BaseGenerator @@ -74,4 +76,116 @@ { return str_replace(['Resources\\', '\\'], ['resources/', '/'], $this->getViewNamespace($this->getTableInput()).'index.blade.php'); } + + + protected function renderColumns() { + $renders = []; + $headers = []; + $values = []; + $colCount = 0; + + $columns = $this->getTableColumns($this->getCurrentTable()); + $relations = RelationshipNavigator::getRelations($this->getCurrentTable()); + //gether the select columns based on the relations + $selects = []; + + foreach($relations['belongsTo'] as $relation) { + $selects[$relation['column']] = $relation['table']; + } + + foreach($columns as $column) { + $name = $column['name']; + if(in_array($name, $this->columns_to_ignore)) continue; + + + //Get the expected header name + $replacements = [ + '{{header}}' => Str::headline($name), + '{{headerClass}}' => 'p-2', + '{{value}}' => '{{ $item->' . $name . ' ?? "" }}', + '{{valueClass}}' => 'p-2', + ]; + + $type = $column['type_name']; + + + //date + if(in_array($type, ['date'])) { + $replacements['{{value}}'] = '{{ $item->'.$name.'?->format(\'Y-m-d\') ?? "" }}'; + } + //time + if(in_array($type, ['timestamp'])) { + $replacements['{{value}}'] = '{{ $item->'.$name.'?->format(\'Y-m-d H:i\') ?? "" }}'; + } + //checkbox + elseif(in_array($type, ['tinyint'])) { + $replacements['{{valueClass}}'] .= ' text-center'; + $replacements['{{value}}'] = '{{ $item->' . $name . ' ?? "0" }}'; + } + //select + elseif(in_array($type, ['bigint']) && array_key_exists($name, $selects)) { + $replacements['{{header}}'] = Str::headline(Str::singular($selects[$name])); + $replacements['{{value}}'] = '{{ $item->'.Str::singular($selects[$name]) . '?->name ?? "" }}'; + } + // bigint, float + elseif(in_array($type, ['bigint', 'float', 'int'])) { + $replacements['{{valueClass}}'] .= ' text-start'; + } + else { + //text area + //varchar, , etc + } + + $snippet = $this->getSnippet('index/value'); + // Replace placeholders with actual values + $values[] = str_replace( + array_keys($replacements), + $replacements, + $snippet + ); + + $snippet = $this->getSnippet('index/header'); + // Replace placeholders with actual values + $headers[] = str_replace( + array_keys($replacements), + $replacements, + $snippet + ); + $colCount++; + } + + return ['headers' => $headers, 'values' => $values, 'colCount' => $colCount]; + } + + /** + * Get available insertions including model relationships. + * + * @return array + */ + public function get_available_inserts(): array + { + // Merge parent insertions (attributes, fillable, etc.) + $inserts = parent::get_available_inserts(); + + // Gather and render relationships for this model + $rendered = $this->renderColumns(); + + // Build code blocks for each relation type + $headers = ''; + $values = ''; + $colCount = ''; + + if(!empty($rendered)) { + $headers = !empty($rendered['headers']) ? implode("\n ", $rendered['headers']) : ''; + $values = !empty($rendered['values']) ? implode("\n ", $rendered['values']) : ''; + $colCount = isset($rendered['colCount']) ? $rendered['colCount'] : ''; + } + + // Assign to stub placeholders + $inserts['{{ headerInsertPoint }}'] = $headers; + $inserts['{{ valueInsertPoint }}'] = $values; + $inserts['{{ colCount }}'] = $colCount; + + return $inserts; + } }
--- a/src/Generator/View/ViewGenerator.php Sun May 11 21:03:51 2025 -0400 +++ b/src/Generator/View/ViewGenerator.php Thu May 15 21:50:38 2025 -0400 @@ -90,16 +90,16 @@ protected function createIndexView() { - $this->call('mf:index_view', ['table' => $this->getTableInput()]); + $this->call('mf:index_view', ['table' => $this->getTableInput(), '--fresh' => $this->option('fresh')]); } protected function createCreateEditView() { - $this->call('mf:create_edit_view', ['table' => $this->getTableInput()]); + $this->call('mf:create_edit_view', ['table' => $this->getTableInput(), '--fresh' => $this->option('fresh')]); } protected function createShowView() { - $this->call('mf:show_view', ['table' => $this->getTableInput()]); + $this->call('mf:show_view', ['table' => $this->getTableInput(), '--fresh' => $this->option('fresh')]); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/index/header.stub Thu May 15 21:50:38 2025 -0400 @@ -0,0 +1,1 @@ +<th class="{{headerClass}}">{{header}}</th>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/index/value.stub Thu May 15 21:50:38 2025 -0400 @@ -0,0 +1,1 @@ +<td class="{{valueClass}}">{{value}}</td>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/input/checkbox.stub Thu May 15 21:50:38 2025 -0400 @@ -0,0 +1,5 @@ +<x-form.checkbox + name="{{fieldName}}" + label="{{fieldLabel}}" + :checked="old('{{fieldName}}', $item?->{{fieldName}} ?? 'false')" +></x-form.checkbox>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/input/date.stub Thu May 15 21:50:38 2025 -0400 @@ -0,0 +1,6 @@ +<x-form.date + name="{{fieldName}}" + label="{{fieldLabel}}" + :value="old('{{fieldName}}', $item?->{{fieldName}} ?? '')" + :required="{{required}}" +></x-form.date>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/input/select.stub Thu May 15 21:50:38 2025 -0400 @@ -0,0 +1,7 @@ +<x-form.select + name="{{fieldName}}" + label="{{fieldLabel}}" + :options="{{options}}" + :value="old('{{fieldName}}', $item?->{{fieldName}} ?? '')" + :required="{{required}}" +></x-form.select>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/input/text.stub Thu May 15 21:50:38 2025 -0400 @@ -0,0 +1,6 @@ +<x-form.text + name="{{fieldName}}" + label="{{fieldLabel}}" + :value="old('{{fieldName}}', $item?->{{fieldName}} ?? '')" + :required="{{required}}" +></x-form.text>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/input/textarea.stub Thu May 15 21:50:38 2025 -0400 @@ -0,0 +1,6 @@ +<x-form.textarea + name="{{fieldName}}" + label="{{fieldLabel}}" + :value="old('{{fieldName}}', $item?->{{fieldName}} ?? '')" + :required="{{required}}" +></x-form.textarea>
--- a/src/Generator/View/stubs/create_edit.stub Sun May 11 21:03:51 2025 -0400 +++ b/src/Generator/View/stubs/create_edit.stub Thu May 15 21:50:38 2025 -0400 @@ -14,17 +14,8 @@ @method('PUT') @endif - @foreach($fields as $field) - <div class="mb-4"> - <label for="{{ $field }}" class="block text-gray-700">{{ ucfirst($field) }}</label> - <input type="text" name="{{ $field }}" id="{{ $field }}" - class="mt-1 block w-full rounded border-gray-300 shadow-sm focus:ring focus:ring-blue-200" - value="{{ old('$field', isset($item) ? $item->$field : '') }}"> - @error($field) - <span class="text-red-600 text-sm">{{ $message }}</span> - @enderror - </div> - @endforeach + {{ fieldsInsertPoint }} + <div class="flex justify-end"> <a href="{{ route('{{ tableName }}.index') }}" class="mr-2 px-4 py-2 bg-gray-200 rounded hover:bg-gray-300">Back</a>
--- a/src/Generator/View/stubs/index.stub Sun May 11 21:03:51 2025 -0400 +++ b/src/Generator/View/stubs/index.stub Thu May 15 21:50:38 2025 -0400 @@ -14,34 +14,32 @@ + New {{ ucfirst('{{ modelVariable }}') }} </a> </div> - <table class="w-full text-left border-collapse"> + <table class="w-full text-left border-collapse table-auto"> <thead> - <tr> - @foreach($fields as $field) - <th class="border-b p-2">{{ ucfirst($field) }}</th> - @endforeach - <th class="border-b p-2">Actions</th> + <tr class="border-b"> + {{ headerInsertPoint }} + <th class=" p-2">Actions</th> </tr> </thead> <tbody> @forelse ($items as $item) - <tr> - @foreach($fields as $field) - <td class="border-b p-2">{{ $item->$field }}</td> - @endforeach - <td class="border-b p-2 flex space-x-2"> - <a href="{{ route('{{ tableName }}.show', $item) }}" class="text-blue-600 hover:underline">View</a> + <tr class="border-b"> + {{ valueInsertPoint }} + <td class="p-2"> + <div class="flex space-x-2"> + <a href="{{ route('{{ tableName }}.show', $item) }}" class="text-blue-600 hover:underline">View</a> <a href="{{ route('{{ tableName }}.edit', $item) }}" class="text-yellow-600 hover:underline">Edit</a> <form action="{{ route('{{ tableName }}.destroy', $item) }}" method="POST" class="inline"> @csrf @method('DELETE') <button type="submit" class="text-red-600 hover:underline" onclick="return confirm('Delete?')">Delete</button> </form> + </div> </td> </tr> @empty <tr> - <td class="p-2" colspan="{{ count($fields)+1 }}">No {{ tableName }} found.</td> + <td class="p-2" colspan="{{ colCount }}">No {{ tableName }} found.</td> </tr> @endforelse </tbody>
