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>