# HG changeset patch # User luka # Date 1757552447 14400 # Node ID f65ab84ee47f4bcf95431f88b7546665fc3def16 # Parent a9ff874afdbdeae33110bdd82eb0937be96ec7ee# Parent 93adaad3ca65db8c34b504a4845f192a18e8e5dd merge with codex diff -r a9ff874afdbd -r f65ab84ee47f .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,9 @@ +syntax: glob +vendor +*.env +*.env.backup +*.env.production +.php-cs-fixer.cache +*.aichat +tags + diff -r a9ff874afdbd -r f65ab84ee47f .php-cs-fixer.cache --- a/.php-cs-fixer.cache Sat Dec 02 10:20:32 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -{"php":"8.2.12","version":"3.19.1","indent":" ","lineEnding":"\n","rules":{"align_multiline_comment":true,"array_syntax":true,"backtick_to_shell_exec":true,"binary_operator_spaces":true,"blank_line_before_statement":{"statements":["return"]},"cast_spaces":true,"class_attributes_separation":{"elements":{"method":"one"}},"class_definition":{"single_line":true},"class_reference_name_casing":true,"clean_namespace":true,"concat_space":true,"curly_braces_position":{"allow_single_line_anonymous_functions":true,"allow_single_line_empty_anonymous_classes":true},"declare_parentheses":true,"echo_tag_syntax":true,"empty_loop_body":{"style":"braces"},"empty_loop_condition":true,"fully_qualified_strict_types":true,"function_typehint_space":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"global_namespace_import":{"import_classes":false,"import_constants":false,"import_functions":false},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"method_argument_space":{"on_multiline":"ignore"},"native_function_casing":true,"native_function_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_blank_lines":{"tokens":["attribute","case","continue","curly_brace_block","default","extra","parenthesis_brace_block","square_brace_block","switch","throw","use"]},"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_null_property_initialization":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"remove_inheritdoc":true},"no_trailing_comma_in_singleline":true,"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","others","return","switch_case","yield","yield_from"]},"no_unneeded_curly_braces":{"namespaces":true},"no_unneeded_import_alias":true,"no_unset_cast":true,"no_unused_imports":true,"no_useless_concat_operator":true,"no_useless_nullsafe_operator":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"nullable_type_declaration_for_default_null_value":{"use_nullable_type_declaration":false},"object_operator_without_whitespace":true,"operator_linebreak":{"only_booleans":true},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"alpha"},"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_order":{"order":["param","return","throws"]},"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":true,"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_without_name":true,"semicolon_after_instruction":true,"simple_to_complex_string_variable":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_comment_spacing":true,"single_line_comment_style":{"comment_types":["hash"]},"single_line_throw":true,"single_quote":true,"single_space_around_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"switch_continue_to_break":true,"trailing_comma_in_multiline":true,"trim_array_spaces":true,"types_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"yoda_style":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"compact_nullable_typehint":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"return_type_declaration":true,"short_scalar_cast":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_line_after_imports":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true},"hashes":{"src\/Generator\/Route\/RouteGenerator.php":"02bf90516b88a1b0b00a84afb10d8a45","src\/Generator\/Controller\/ControllerGenerator.php":"717cdb9124e7e4afe4a28e17b94089e9","src\/Generator\/Model\/ModelGenerator.php":"31743678bf017f63680db19e9682df45","src\/Generator\/Generator.php":"c7cd32166a24be83f6575d33f6c4ba80","src\/Generator\/Requests\/UpdateRequestGenerator.php":"63475513d258b0727637db19629d4ac6","src\/Generator\/Requests\/RequestGenerator.php":"93fbc04a1ea18b794ee7f24902ae9cc1","src\/Generator\/Requests\/StoreRequestGenerator.php":"42bac2b426288d9a08216dfc5fe45faf","src\/Generator\/BaseGenerator.php":"c0aa37a727252383bc28aebf7a0019ec","src\/MagicForgerServiceProvider.php":"00c1321ad894d1e0807036dbe9114bf2","src\/Replacer\/Replacer.php":"501a0ebcf606a7602973b6033ee54b8e","src\/Replacer\/TableReplacer.php":"dc289802665136521abf6ffea0203732","src\/ConfigHelper.php":"c11480fee67c40ca948c38c14bed7d16"}} \ No newline at end of file diff -r a9ff874afdbd -r f65ab84ee47f .vimrc --- a/.vimrc Sat Dec 02 10:20:32 2023 -0500 +++ b/.vimrc Wed Sep 10 21:00:47 2025 -0400 @@ -8,6 +8,7 @@ "set number nnoremap cc :set colorcolumn=80 nnoremap ncc :set colorcolumn-=80 +nnoremap :ALECodeAction set mouse=a function! FixPhpFiles() @@ -27,23 +28,22 @@ " Committing commands map :wa:!hg addremove && hg commit -" Git commands, for now don't port to hg -" function! GitDiffCached() -" let files = system('git diff --cached --name-only') -" -" if v:shell_error -" echo "Error running git diff" -" return -" endif -" -" let filelist = split(files, "\n") -" let chosen_file = inputlist(filelist) -" -" if chosen_file != -1 -" let cmd = 'tabnew ' . filelist[chosen_file] -" execute cmd -" endif -" endfunction -" -" execute "set =\033d" -" map :call GitDiffCached() + + +function! SendBufferToProgram() + " Create a temporary file + let temp_file = tempname() + + " Write current buffer to the temporary file + exe "write! " . temp_file + + " Send the content of the temporary file to your program + " Replace with the actual command to run your program + let command = "cat " . temp_file . " | " + + " Execute the command + call system(command) + + " Optionally, delete the temporary file if not needed + call delete(temp_file) +endfunction diff -r a9ff874afdbd -r f65ab84ee47f composer.json --- a/composer.json Sat Dec 02 10:20:32 2023 -0500 +++ b/composer.json Wed Sep 10 21:00:47 2025 -0400 @@ -1,20 +1,21 @@ { - "name": "wizzard/magicforger", + "name": "wizard/magicforger", + "version": "1.0.0", "description": "Magically makes all your CRUD in a smart and repeatable way.", - "version": "dev-default", "autoload": { "psr-4": { - "Wizzard\\MagicForger\\":"src/" + "Wizard\\MagicForger\\":"src/" } }, "minimum-stability": "stable", "require": { - "laravel/framework": ">=10.3" + "laravel/framework": ">=12", + "doctrine/dbal": "*" }, "extra": { "laravel": { "providers": [ - "Wizzard\\MagicForger\\MagicForgerServiceProvider" + "Wizard\\MagicForger\\MagicForgerServiceProvider" ] } } diff -r a9ff874afdbd -r f65ab84ee47f src/ConfigHelper.php --- a/src/ConfigHelper.php Sat Dec 02 10:20:32 2023 -0500 +++ b/src/ConfigHelper.php Wed Sep 10 21:00:47 2025 -0400 @@ -1,6 +1,8 @@ getDoctrineSchemaManager(); - // get all the tables available in the database $tables = collect($schema->listTableNames())->all(); + $table_foreign_keys = []; + foreach ($tables as $table) { + $table_foreign_keys[$table] = $schema->listTableForeignKeys($table); + } $insert_tables = []; foreach ($tables as $table) { $columns = []; - $table_columns = $schema->introspectTable($table)->getColumns(); + $table_columns = $schema->listTableColumns($table); + + // Initiate new arrays for foreign keys + $foreign_keys = []; + $foreign_keys_reverse = []; + + // Check foreign key references from this table + $foreign_keys_list = $table_foreign_keys[$table]; + foreach ($foreign_keys_list as $fk) { + $foreign_keys[$fk->getLocalColumns()[0]] = [ + 'foreign_table' => $fk->getForeignTableName(), + 'foreign_column' => $fk->getForeignColumns()[0], + ]; + } foreach ($table_columns as $column) { $full_class = get_class($column->getType()); @@ -122,37 +135,52 @@ $class_name = end($class_parts); $columns[$column->getName()] = [ - 'type' => $class_name, - 'should_insert' => [ - 'controller' => true, - 'model' => true, - 'requests' => true, - 'views' => true, - ], - ]; + 'type' => $class_name, + 'should_insert' => [ + 'controller' => true, + 'model' => true, + 'requests' => true, + 'views' => true, + ], + ]; } - + // Check foreign key references to this table + foreach ($tables as $other_table) { + if ($other_table != $table) { + $foreign_keys_list = $table_foreign_keys[$other_table]; + foreach ($foreign_keys_list as $fk) { + if ($fk->getForeignTableName() == $table) { + $foreign_keys_reverse[] = [ + 'table' => $other_table, + 'column' => $fk->getLocalColumns()[0], + ]; + } + } + } + } $insert_tables[$table] = []; $insert_tables[$table]['columns'] = $columns; + $insert_tables[$table]['foreign_keys'] = $foreign_keys; // Foreign keys FROM this table + $insert_tables[$table]['foreign_keys_reverse'] = $foreign_keys_reverse; // Foreign keys TO this table $insert_tables[$table]['type'] = 'default'; } - // Merge the new tables configuration into the initial config + self::merge_array_priority(self::$config['tables'], $insert_tables); return $tables; } /** - * Merge two arrays and ensure priority values do not get overwritten + * Merge two arrays and ensure priority values do not get overwritten. * - * @param array $priority - * @param array $merged + * @param array $priority + * @param array $merged */ - private static function merge_array_priority(&$priority, $merged) + private static function merge_array_priority(&$priority, $merged): void { foreach ($merged as $key => $value) { // if the priority key is not set, automatically add the merged values - if (!isset($priority[$key])) { + if (! isset($priority[$key])) { $priority[$key] = $value; } else { // if the value is an array recursively merge with priority diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/BaseGenerator.php --- a/src/Generator/BaseGenerator.php Sat Dec 02 10:20:32 2023 -0500 +++ b/src/Generator/BaseGenerator.php Wed Sep 10 21:00:47 2025 -0400 @@ -1,242 +1,203 @@ tableExists($this->getTableInput())) { + + if (! $this->tableExists($this->getTableInput())) { $this->components->error('The table: "'.$this->getTableInput().'" does not exist in the database.'); return false; } $this->setCurrentTable($this->getTableInput()); - $path = $this->getPath(); - $file = $this->getFile($path); - $file = $this->apply_replacements($file); - $file = $this->apply_inserts($file); - $this->makeDirectory($path); - $this->files->put($path, $this->sortImports($file)); - $this->format_file($path); - - $info = $this->type; - - $this->components->info(sprintf('%s [%s] created successfully.', $info, $path)); + $this->components->info(sprintf('%s [%s] created successfully.', $this->type, $path)); } - /** - * Override the original so that we can prompt for a table with autocomplete. - */ - protected function promptForMissingArguments(InputInterface $input, OutputInterface $output) + protected function promptForMissingArguments(InputInterface $input, OutputInterface $output): void { $prompted = false; if (is_null($input->getArgument('table'))) { $prompted = true; $table = null; - while (null === $table) { + while ($table === null) { $table = $this->components->askWithCompletion( 'What Table should we use?', $this->possibleTables() ); } - $input->setArgument('table', $table); } parent::promptForMissingArguments($input, $output); - // This will get missed if we prompt here but not in the parent if ($prompted) { $this->afterPromptingForMissingArguments($input, $output); } } - /** - * Get the console command arguments. - * - * @return array - */ - protected function getArguments() + protected function getArguments(): array { return [ ['table', InputOption::VALUE_REQUIRED, 'The table to generate files for.'], ]; } - /** - * Prompt for missing input arguments using the returned questions. - * - * @return array - */ - protected function promptForMissingArgumentsUsing() + protected function promptForMissingArgumentsUsing(): array { - return [ - ]; + return []; } - /** - * Get the console command options. - * - * @return array - */ - protected function getOptions() + protected function getOptions(): array { return [ ['fresh', 'f', InputOption::VALUE_NONE, 'Start from the stub or use existing if possible.'], ]; } - /** - * Interact further with the user if they were prompted for missing arguments. - * - * @return void - */ - protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) + protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void { + // Additional logic after prompting goes here } - /** - * Determines if the file exists. - */ protected function fileExists(string $path): bool { return $this->files->exists($path); } - /** - * Gets the file that will be worked on. If there is already an existing file - * then we can open that. However if we are forcing the operation, then we - * will start with an empty stub. - */ - protected function getFile($name) + protected function getFile($name): string { - if (!($this->hasOption('fresh') - && $this->option('fresh')) - && $this->fileExists($name)) { - // Working with an existing file + if (! ($this->hasOption('fresh') && $this->option('fresh')) && $this->fileExists($name)) { return $this->files->get($name); } - // Working with a stub return $this->files->get($this->getStub()); } - /** - * Get the desired class table from the input. - * - * @return string - */ - protected function getTableInput() + protected function getTableInput(): string { return trim($this->argument('table')); } - /** - * Determines if the table exists in the current database. - */ protected function tableExists(string $table_name): bool { return in_array($table_name, $this->getTables()); } - /** - * Get a list of possible table names. - */ - protected function possibleTables() + protected function possibleTables(): array { return $this->getTables(); } - /** - * Get the tables in the schema. - */ - protected function getTables() + protected function getTables(): array { if (is_null($this->tables)) { - $this->tables = collect($this->getSchema()->listTableNames())->all(); + $this->tables = Schema::getTableListing(schema: config('database.connections.mariadb.database'), schemaQualified: false); } return $this->tables; } - /** - * Get the database schema for DB interactions. - */ - protected function getSchema() - { - if (is_null($this->schema)) { - $this->schema = \DB::connection()->getDoctrineSchemaManager(); - } - - return $this->schema; - } - protected function getTable(string $table_name) { return $this->getSchema()->introspectTable($table_name); } + /* + * returns array of columns in the form of: + * + [ + "name" => "column_type" + "type_name" => "bigint" + "type" => "bigint(20) unsigned" + "collation" => null + "nullable" => true + "default" => "NULL" + "auto_increment" => false + "comment" => null + "generation" => null + ] + */ + protected static function getTableColumns(string $table_name) + { + return Schema::getColumns($table_name); + } + + /* + * returns array of foreign keys in the form of: + * + [ + "name" => "foreign_key_name" + "columns" => [ + 0 => "local_column_name" + ] + "foreign_schema" => "schema_name" + "foreign_table" => "foreign_table_name" + "foreign_columns" => [ + 0 => "foreign_column_name" + ] + "on_update" => "restrict" + "on_delete" => "restrict" + ] + * + */ + protected static function getTableForeignKeys(string $table_name) + { + return Schema::getForeignKeys($table_name); + } + protected function getCurrentTable() { return $this->currentTable; } - protected function setCurrentTable(string $table_name) + protected function setCurrentTable(string $table_name): void { - $table = null; - if (!is_null($table_name) && '' !== trim($table_name)) { - $table = $this->getTable($table_name); - } - $this->currentTable = $table; + $this->currentTable = $table_name; + } + + protected function format_file(string $path): void + { + exec('./vendor/bin/pint '.escapeshellarg($path)); } - protected function format_file(string $path) + protected function getSnippet($snippet_name) { - exec('php-cs-fixer fix '.$path); + // 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]; } } diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Controller/ControllerGenerator.php --- a/src/Generator/Controller/ControllerGenerator.php Sat Dec 02 10:20:32 2023 -0500 +++ b/src/Generator/Controller/ControllerGenerator.php Wed Sep 10 21:00:47 2025 -0400 @@ -1,9 +1,9 @@ resolveStubPath('/stubs/controller.stub'); } /** * Resolve the fully-qualified path to the stub. - * - * @param string $stub - * - * @return string */ - protected function resolveStubPath($stub) + protected function resolveStubPath(string $stub): string { - return is_file($customPath = $this->laravel->basePath(trim($stub, '/'))) - ? $customPath - : __DIR__.$stub; + $customPath = $this->laravel->basePath(trim($stub, '/')); + + return is_file($customPath) ? $customPath : __DIR__.$stub; } - protected function getClassName($name) + /** + * Get the path for the generated file. + */ + protected function getPath($name = null) + { + return str_replace( + ['App\\', '\\'], + ['app/', '/'], + $this->getControllerNamespace().'/'.$this->controller_name($this->getTableInput()).'.php' + ); + } + + /** + * Get the class name for the controller. + */ + protected function getClassName(string $name): string { return $this->controller_name($name); } - - /** - * Get the stub file for the generator. - * - * @return string - */ - protected function getPath($name = null) - { - return str_replace(['App\\', '\\'], ['app/', '/'], $this->getControllerNamespace().'/'.$this->controller_name($this->getTableInput()).'.php'); - } } diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Controller/stubs/controller.stub --- a/src/Generator/Controller/stubs/controller.stub Sat Dec 02 10:20:32 2023 -0500 +++ b/src/Generator/Controller/stubs/controller.stub Wed Sep 10 21:00:47 2025 -0400 @@ -10,91 +10,126 @@ { /** * Display a listing of the resource. + * + * @param {{ filterRequest }} $request + * @return \Illuminate\View\View */ - public function index() + public function index({{ filterRequest }} $request) { - $data = []; - - $data['items'] = {{ model }}::all(); + $validated = $request->validated(); + $data = []; + $data['items'] = {{ model }}::get_data($validated); + $data = array_merge($data, {{ model }}::load_index()); return view('{{ tableName }}.index', $data); } + public function get_data() + { + $data = []; + $data['records'] = {{ model }}::get_data([]); + $data['total_records'] = count($data['records']); + + return $data; + } + /** * Show the form for creating a new resource. + * + * @return \Illuminate\View\View */ public function create() { - $data = []; + $data = []; + $data = array_merge($data, {{ model }}::load_create()); return view('{{ tableName }}.create_edit', $data); } /** * Store a newly created resource in storage. + * + * @param {{ storeRequest }} $request + * @return \Illuminate\Http\RedirectResponse */ public function store({{ storeRequest }} $request) { - $validated = $request->validated(); - - // - ${{ modelVariable }} = new {{ model }}(); + $validated = $request->validated(); - //insert the values into the model - // {{ valuesForCreation }} - - ${{ modelVariable }}->save(); + {{ model }}::create($validated); return redirect()->route('{{ tableName }}.index'); } /** * Display the specified resource. + * + * @param {{ model }} ${{ modelVariable }} + * @return \Illuminate\View\View */ public function show({{ model }} ${{ modelVariable }}) { - $data = []; - - $data['item'] = ${{ modelVariable }}; + $data = []; + $data['item'] = ${{ modelVariable }}; + $data['fields'] = (new {{ model }}())->getFillable(); + + return view('{{ tableName }}.show', $data); + } - return view('{{ tableName }}.show', $data); + /** + * Returns the resource in JSON format. + * + * @param ModelType $modelVariable + * @return string + */ + public function load({{ model }} ${{ modelVariable }}) + { + return ${{ modelVariable }}->toJson(); } /** * Show the form for editing the specified resource. + * + * @param {{ model }} ${{ modelVariable }} + * @return \Illuminate\View\View */ public function edit({{ model }} ${{ modelVariable }}) { - $data = []; + $data = []; + $data['item'] = ${{ modelVariable }}; - $data['item'] = ${{ modelVariable }}; + // Load data for relationships + $data = array_merge($data, {{ model }}::load_edit()); - return view('{{ tableName }}.create_edit', $data); + return view('{{ tableName }}.create_edit', $data); } /** * Update the specified resource in storage. + * + * @param {{ updateRequest }} $request + * @param {{ model }} ${{ modelVariable }} + * @return \Illuminate\Http\RedirectResponse */ public function update({{ updateRequest }} $request, {{ model }} ${{ modelVariable }}) { - $validated = $request->validated(); + $validated = $request->validated(); - // Set the variables - //{{ valuesForCreation }} + ${{ modelVariable }}->update($validated); - ${{ modelVariable }}->save(); - - return redirect()->route('{{ tableName }}.index'); + return redirect()->route('{{ tableName }}.index'); } /** * Remove the specified resource from storage. + * + * @param {{ model }} ${{ modelVariable }} + * @return \Illuminate\Http\RedirectResponse */ public function destroy({{ model }} ${{ modelVariable }}) { - // - ${{ modelVariable }}->delete(); + ${{ modelVariable }}->delete(); - return redirect()->route('{{ tableName }}.index'); + return redirect()->route('{{ tableName }}.index'); } } diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Generator.php --- a/src/Generator/Generator.php Sat Dec 02 10:20:32 2023 -0500 +++ b/src/Generator/Generator.php Wed Sep 10 21:00:47 2025 -0400 @@ -1,6 +1,6 @@ tableExists($this->getTableInput())) { + if (! $this->tableExists($this->getTableInput())) { $this->components->error('The table: "'.$this->getTableInput().'" does not exist in the database.'); return false; @@ -46,6 +47,7 @@ $this->input->setOption('controller', true); $this->input->setOption('model', true); $this->input->setOption('request', true); + $this->input->setOption('view', true); $this->input->setOption('route', true); } @@ -73,6 +75,10 @@ $this->createRequest(); } + if ($this->option('view')) { + $this->createView(); + } + if ($this->option('route')) { $this->createRoute(); } @@ -80,34 +86,27 @@ /** * Get the console command options. - * - * @return array */ - protected function getOptions() + protected function getOptions(): array { return array_merge(parent::getOptions(), [ ['all', 'a', InputOption::VALUE_NONE, 'Generate a migration, seeder, factory, policy, resource controller, and form request classes for the table.'], ['controller', 'c', InputOption::VALUE_NONE, 'Generate a controller class for the table.'], ['model', 'm', InputOption::VALUE_NONE, 'Generate a model class for the table.'], ['request', 'r', InputOption::VALUE_NONE, 'Generate base request classes for the table.'], + ['view', '', InputOption::VALUE_NONE, 'Generate base views for the table.'], ['route', 'w', InputOption::VALUE_NONE, 'Generate base routes classes for the table.'], ]); } /** * Interact further with the user if they were prompted for missing arguments. - * - * @return void */ - protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) - { - } + protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void {} - protected function getStub() - { - } + protected function getStub(): void {} - protected function createController() + protected function createController(): void { $this->call('mf:controller', ['table' => $this->getTableInput(), '--fresh' => $this->option('fresh')]); } @@ -122,6 +121,11 @@ $this->call('mf:request', ['table' => $this->getTableInput(), '--fresh' => $this->option('fresh'), '--all' => true]); } + protected function createView() + { + $this->call('mf:view', ['table' => $this->getTableInput(), '--fresh' => $this->option('fresh'), '--all' => true]); + } + protected function createRoute() { $this->call('mf:route', ['table' => $this->getTableInput(), '--fresh' => $this->option('fresh')]); diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Model/ModelGenerator.php --- a/src/Generator/Model/ModelGenerator.php Sat Dec 02 10:20:32 2023 -0500 +++ b/src/Generator/Model/ModelGenerator.php Wed Sep 10 21:00:47 2025 -0400 @@ -1,9 +1,11 @@ getCurrentTable()))) { + return $this->resolveStubPath('/stubs/model.pivot.stub'); + } + return $this->resolveStubPath('/stubs/model.stub'); } /** * Resolve the fully-qualified path to the stub. * - * @param string $stub - * + * @param string $stub * @return string */ protected function resolveStubPath($stub) @@ -75,4 +85,151 @@ { return str_replace(['App\\', '\\'], ['app/', '/'], $this->getModelNamespace().'/'.$this->model_name($this->getTableInput()).'.php'); } + + protected function gatherRelations() { + $relations = RelationshipNavigator::getRelations($this->getCurrentTable()); + + return $relations; + + } + + protected function renderFilters() { + $insert = ''; + foreach ($this->get_columns() as $column) { + if (in_array($column['name'], $this->columns_to_ignore)) { + continue; + } + $snippet = $this->getSnippet('filter'); + $tableName = $this->getCurrentTable(); + $value = 'value'; // TODO: this should be determined based on column type + $columnName = $column['name']; + $columnDisplay = Str::headline($columnName); + + // Replace placeholders with actual values + $string = str_replace( + ['{{value}}', '{{columnDisplay}}', '{{tableName}}', '{{columnName}}'], + [$value, $columnDisplay, $tableName, $columnName], + $snippet + ); + $insert .= sprintf("%s", $string); + } + + return $insert; + } + + protected function renderRelations($relations) { + $renders = [ + 'belongsTo' => [], + 'hasMany' => [], + 'belongsToMany' => [], + ]; + // Render belongsTo relations + foreach (($relations['belongsTo'] ?? []) as $relation) { + $renders['belongsTo'][] = $this->renderBelongsTo($relation); + } + + // Render hasMany relations + foreach (($relations['hasMany'] ?? []) as $relation) { + $renders['hasMany'][] = $this->renderHasMany($relation); + } + + // Render belongsToMany (many-to-many) via hasManyThrough pivot relations + foreach (($relations['hasManyThrough'] ?? []) as $relation) { + $renders['belongsToMany'][] = $this->renderBelongsToMany($relation); + } + return $renders; + } + + protected function renderBelongsTo($relationship) + { + $snippet = $this->getSnippet('belongs_to_relation'); + $relationName = Str::singular($relationship['table']); + $relatedModel = $this->getClassName($relationship['table']); + $columnName = $relationship['column']; + + // Replace placeholders with actual values + $string = str_replace( + ['{{relationName}}', '{{relatedModel}}', '{{columnName}}'], + [$relationName, $relatedModel, $columnName], + $snippet + ); + + return $string; + } + + /** + * Render a hasMany relation. + * + * @param array $relationship + * @return string + */ + protected function renderHasMany($relationship) + { + $snippet = $this->getSnippet('has_many_relation'); + // Method name uses camel case for plural relation + $relationName = Str::camel($relationship['table']); + $relatedModel = $this->getClassName($relationship['table']); + $columnName = $relationship['column']; + + // Replace placeholders with actual values + $string = str_replace( + ['{{relationName}}', '{{relatedModel}}', '{{columnName}}'], + [$relationName, $relatedModel, $columnName], + $snippet + ); + + return $string; + } + + protected function renderBelongsToMany($relationship) + { + $snippet = $this->getSnippet('belongs_to_many_relation'); + $relationName = $relationship['table']; + $relatedModel = $this->getClassName($relationship['table']); + $pivotTable = $relationship['through']['table']; + $foreignPivotKey = $relationship['through']['external_column']; + $relatedPivotKey = $relationship['through']['internal_column']; + + // Replace placeholders with actual values + $string = str_replace( + ['{{relationName}}', '{{relatedModel}}', '{{pivotTable}}', '{{foreignPivotKey}}', '{{relatedPivotKey}}'], + [$relationName, $relatedModel, $pivotTable, $foreignPivotKey, $relatedPivotKey], + $snippet + ); + + return $string; + } + + /** + * 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 + $relations = $this->gatherRelations(); + $rendered = $this->renderRelations($relations); + $filters = $this->renderFilters(); + + // Build code blocks for each relation type + $belongs = !empty($rendered['belongsTo']) ? implode("\n ", $rendered['belongsTo']) : ''; + $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; + $inserts['# {{ defaultFiltersInsertPoint }}'] = $filters; + + return $inserts; + } } diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Model/snippets/belongs_to_many_relation.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/Model/snippets/belongs_to_many_relation.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,6 @@ + +public function {{relationName}}() +{ + return $this->belongsToMany({{relatedModel}}::class, '{{pivotTable}}', '{{foreignPivotKey}}', '{{relatedPivotKey}}'); +} + diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Model/snippets/belongs_to_relation.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/Model/snippets/belongs_to_relation.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,6 @@ + +public function {{relationName}}() +{ + return $this->belongsTo({{relatedModel}}::class, '{{columnName}}'); +} + diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Model/snippets/filter.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/Model/snippets/filter.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,6 @@ +'{{columnName}}' => [ + 'column_name' => '{{columnName}}', + 'table' => '{{tableName}}', + 'display' => '{{columnDisplay}}', + 'type' => '{{value}}', +], diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Model/snippets/has_many_relation.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/Model/snippets/has_many_relation.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,4 @@ +public function {{relationName}}() +{ + return $this->hasMany({{relatedModel}}::class, '{{columnName}}'); +} diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Model/stubs/model.pivot.stub --- a/src/Generator/Model/stubs/model.pivot.stub Sat Dec 02 10:20:32 2023 -0500 +++ b/src/Generator/Model/stubs/model.pivot.stub Wed Sep 10 21:00:47 2025 -0400 @@ -10,12 +10,96 @@ /** * Indicates if the model should be timestamped. - * By default our pivots will not use timestamps + * By default our pivots will not use timestamps * * @var bool */ public $timestamps = false; + protected $default_relations = [ + # {{ defaultRelationsInsertPoint }} + ]; + //relations + + // BelongsTo + # {{ belongs_to_relationships }} + + // HasMany + # {{ has_many_relationships }} + + // 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(); + } } diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Model/stubs/model.stub --- a/src/Generator/Model/stubs/model.stub Sat Dec 02 10:20:32 2023 -0500 +++ b/src/Generator/Model/stubs/model.stub Wed Sep 10 21:00:47 2025 -0400 @@ -2,11 +2,10 @@ namespace {{ namespace }}; -use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Wizard\Framework\Models\BaseModel; -class {{ class }} extends Model +class {{ class }} extends BaseModel { //use HasFactory; use SoftDeletes; @@ -27,17 +26,67 @@ # {{ atributeInsertPoint }} ]; + protected $casts = [ + # {{ castInsertPoint }} + ]; - public static function boot() : void { - parent::boot(); + protected $fillable = [ + # {{ fillableInsertPoint }} + ]; + + protected $default_relations = [ + # {{ defaultRelationsInsertPoint }} + ]; + + protected static $filters = [ + # {{ defaultFiltersInsertPoint }} + ]; + + public static function get_filters() { + return static::filters; + } + + //relations + + // BelongsTo + # {{ belongs_to_relationships }} + + // HasMany + # {{ has_many_relationships }} + + // HasManyThrough + # {{ has_many_through_relationships }} + - self::creating(function ($item) { - $item->created_by = \Auth::user()->id; - $item->updated_by = \Auth::user()->id; - }); + /** + * Load the default relations for the model. + * + * @return $this + */ + public function load_relations() { + foreach($this->default_relations as $relation) { + $this->load($relation); + } + return $this; + } - self::saving(function ($item) { - $item->updated_by = \Auth::user()->id; - }); + /** + * Retrieve a query builder instance with default relations loaded. + * + * @return \Illuminate\Database\Eloquent\Builder + */ + public static function data_query() { + return parent::data_query(); } + + /** + * Retrieve a query builder instance with default relations loaded. + * + * @return \Illuminate\Database\Eloquent\Builder + */ + public static function get_data(array $validated = []) + { + return parent::get_data($validated); + } + } diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Requests/FilterRequestGenerator.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/Requests/FilterRequestGenerator.php Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,77 @@ +resolveStubPath('/stubs/request.stub'); + } + + /** + * Resolve the fully-qualified path to the stub. + * + * @param string $stub + * @return string + */ + protected function resolveStubPath($stub) + { + return is_file($customPath = $this->laravel->basePath(trim($stub, '/'))) + ? $customPath + : __DIR__.$stub; + } + + protected function getClassName($name) + { + return $this->filter_request_name($name); + } + + /** + * Get the stub file for the generator. + * + * @return string + */ + protected function getPath($name = null) + { + return str_replace(['App\\', '\\'], ['app/', '/'], $this->getRequestNamespace($this->getTableInput()).'/'.$this->filter_request_name($this->getTableInput()).'.php'); + } +} diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Requests/RequestGenerator.php --- a/src/Generator/Requests/RequestGenerator.php Sat Dec 02 10:20:32 2023 -0500 +++ b/src/Generator/Requests/RequestGenerator.php Wed Sep 10 21:00:47 2025 -0400 @@ -1,12 +1,12 @@ tableExists($this->getTableInput())) { + if (! $this->tableExists($this->getTableInput())) { $this->components->error('The table: "'.$this->getTableInput().'" does not exist in the database.'); return false; } if ($this->option('all')) { + $this->input->setOption('filter_request', true); $this->input->setOption('store_request', true); $this->input->setOption('update_request', true); } + if ($this->option('filter_request')) { + $this->createFilterRequest(); + } + if ($this->option('store_request')) { $this->createStoreRequest(); } @@ -60,13 +65,12 @@ /** * Get the console command options. - * - * @return array */ - protected function getOptions() + protected function getOptions(): array { return array_merge(parent::getOptions(), [ ['all', 'a', InputOption::VALUE_NONE, 'Generate all request classes for the table.'], + ['filter_request', 'i', InputOption::VALUE_NONE, 'Generate filter request class for the table.'], ['store_request', 's', InputOption::VALUE_NONE, 'Generate store request class for the table.'], ['update_request', 'u', InputOption::VALUE_NONE, 'Generate update request class for the table.'], ]); @@ -74,20 +78,19 @@ /** * Interact further with the user if they were prompted for missing arguments. - * - * @return void */ - protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) - { - } + protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void {} /** * Get the stub file for the generator. * * @return string */ - protected function getStub() + protected function getStub() {} + + protected function createFilterRequest() { + $this->call('mf:filter_request', ['table' => $this->getTableInput()]); } protected function createStoreRequest() diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Requests/StoreRequestGenerator.php --- a/src/Generator/Requests/StoreRequestGenerator.php Sat Dec 02 10:20:32 2023 -0500 +++ b/src/Generator/Requests/StoreRequestGenerator.php Wed Sep 10 21:00:47 2025 -0400 @@ -1,9 +1,9 @@ prefix('{{ tableName }}') - ->alias('{{ tableName }}.') - ->group( function () { - Route::get('/', 'index')->name('index'); - Route::get('/create', 'create')->name('create'); - Route::get('/edit', 'edit')->name('edit'); - - Route::post('/store', 'store')->name('store'); - Route::put('/udpate/{id}', 'update')->name('update'); - Route::delete('/delete/{id}', 'delete')->name('delete'); - }); diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/Route/stubs/routes.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/Route/stubs/routes.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,19 @@ +middleware(['web','auth']) + ->prefix('{{ tableName }}') + ->as('{{ tableName }}.') + ->group( function () { + Route::get('/', 'index')->name('index'); + Route::post('/get_data', 'get_data')->name('get_data'); + Route::get('/create', 'create')->name('create'); + Route::get('/{{{ modelVariable }}}/edit', 'edit')->name('edit'); + Route::get('/{{{ modelVariable }}}', 'show')->name('show'); + Route::get('/{{{ modelVariable }}}/load', 'load')->name('load'); + + Route::post('/store', 'store')->name('store'); + Route::put('/udpate/{{{ modelVariable }}}', 'update')->name('update'); + Route::delete('/destroy/{{{ modelVariable }}}', 'destroy')->name('destroy'); + }); diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/CreateEditViewGenerator.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/CreateEditViewGenerator.php Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,164 @@ +resolveStubPath('/stubs/create_edit.stub'); + } + + /** + * Resolve the fully-qualified path to the stub. + * + * @param string $stub + * @return string + */ + protected function resolveStubPath($stub) + { + return is_file($customPath = $this->laravel->basePath(trim($stub, '/'))) + ? $customPath + : __DIR__.$stub; + } + + protected function getClassName($name) + { + return $this->create_edit_view_name($name); + } + + /** + * Get the stub file for the generator. + * + * @return string + */ + protected function getPath($name = null) + { + 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; + } +} diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/IndexViewGenerator.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/IndexViewGenerator.php Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,173 @@ +resolveStubPath('/stubs/index.stub'); + } + + /** + * Resolve the fully-qualified path to the stub. + * + * @param string $stub + * @return string + */ + protected function resolveStubPath($stub) + { + return is_file($customPath = $this->laravel->basePath(trim($stub, '/'))) + ? $customPath + : __DIR__.$stub; + } + + protected function getClassName($name) + { + return $this->index_view_name($name); + } + + /** + * Get the stub file for the generator. + * + * @return string + */ + protected function getPath($name = null) + { + return str_replace(['Resources\\', '\\'], ['resources/', '/'], $this->getViewNamespace($this->getTableInput()).'index.blade.php'); + } + + protected function renderColumns() + { + $renders = []; + $values = []; + + $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), + '{{column_name}}' => $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 + if (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 + ); + + } + + return ['values' => $values]; + } + + /** + * Get available insertions including model relationships. + */ + 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)) { + $values = ! empty($rendered['values']) ? implode("\n ", $rendered['values']) : ''; + } + + // Assign to stub placeholders + $inserts['{{ columnInsertPoint }}'] = $values; + + return $inserts; + } +} diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/ShowViewGenerator.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/ShowViewGenerator.php Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,77 @@ +resolveStubPath('/stubs/show.stub'); + } + + /** + * Resolve the fully-qualified path to the stub. + * + * @param string $stub + * @return string + */ + protected function resolveStubPath($stub) + { + return is_file($customPath = $this->laravel->basePath(trim($stub, '/'))) + ? $customPath + : __DIR__.$stub; + } + + protected function getClassName($name) + { + return $this->show_view_name($name); + } + + /** + * Get the stub file for the generator. + * + * @return string + */ + protected function getPath($name = null) + { + return str_replace(['Resources\\', '\\'], ['resources/', '/'], $this->getViewNamespace($this->getTableInput()).'show.blade.php'); + } +} diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/ViewGenerator.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/ViewGenerator.php Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,105 @@ +tableExists($this->getTableInput())) { + $this->components->error('The table: "'.$this->getTableInput().'" does not exist in the database.'); + + return false; + } + + if ($this->option('all')) { + $this->input->setOption('index_view', true); + $this->input->setOption('create_edit_view', true); + $this->input->setOption('show_view', true); + } + + if ($this->option('index_view')) { + $this->createIndexView(); + } + + if ($this->option('create_edit_view')) { + $this->createCreateEditView(); + } + + if ($this->option('show_view')) { + $this->createShowView(); + } + } + + /** + * Get the console command options. + */ + protected function getOptions(): array + { + return array_merge(parent::getOptions(), [ + ['all', 'a', InputOption::VALUE_NONE, 'Generate all views for the table.'], + ['index_view', 'i', InputOption::VALUE_NONE, 'Generate index view for the table.'], + ['create_edit_view', 'c', InputOption::VALUE_NONE, 'Generate create_edit view for the table.'], + ['show_view', 's', InputOption::VALUE_NONE, 'Generate show view for the table.'], + ]); + } + + /** + * Interact further with the user if they were prompted for missing arguments. + */ + protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void {} + + /** + * Get the stub file for the generator. + * + * @return string + */ + protected function getStub() {} + + protected function createIndexView() + { + $this->call('mf:index_view', ['table' => $this->getTableInput(), '--fresh' => $this->option('fresh')]); + } + + protected function createCreateEditView() + { + $this->call('mf:create_edit_view', ['table' => $this->getTableInput(), '--fresh' => $this->option('fresh')]); + } + + protected function createShowView() + { + $this->call('mf:show_view', ['table' => $this->getTableInput(), '--fresh' => $this->option('fresh')]); + } +} diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/snippets/index/header.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/index/header.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,1 @@ +{{header}} diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/snippets/index/value.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/index/value.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,5 @@ +{ + name: '{{column_name}}', + label: '{{header}}', + class: '{{valueClass}}' +}, diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/snippets/input/checkbox.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/input/checkbox.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,5 @@ + diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/snippets/input/date.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/input/date.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,6 @@ + diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/snippets/input/select.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/input/select.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,7 @@ + diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/snippets/input/text.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/input/text.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,6 @@ + diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/snippets/input/textarea.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/snippets/input/textarea.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,6 @@ + diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/stubs/create_edit.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/stubs/create_edit.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,30 @@ + + +

+ {{ isset($item) ? 'Edit' : 'Create' }} {{ ucfirst('{{ modelVariable }}') }} +

+
+ +
+
+
+
+ @csrf + @if (isset($item)) + @method('PUT') + @endif + + {{ fieldsInsertPoint }} + +
+ Back + +
+
+
+
+
+
diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/stubs/index.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/stubs/index.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,83 @@ + + +

+ All {{ Str::plural(ucfirst('{{ modelVariable }}')) }} +

+
+ + + + @include('includes.ServerTable') + @pushOnce('scripts') + + @endpushOnce + +
diff -r a9ff874afdbd -r f65ab84ee47f src/Generator/View/stubs/show.stub --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Generator/View/stubs/show.stub Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,26 @@ + + +

+ {{ ucfirst('{{ modelVariable }}') }} Details +

+
+ +
+
+
+
+ @foreach($fields as $field) +
+ {{ ucfirst($field) }}: + {{ $item->$field }} +
+ @endforeach +
+
+ Edit + Back +
+
+
+
+
diff -r a9ff874afdbd -r f65ab84ee47f src/Helpers/FileModifier.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Helpers/FileModifier.php Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,73 @@ +get_file_contents($file_path); + $this->file_path = $file_path; + } + + public function get_file_contents($file_path): void + { + // TODO: there needs to be more/any error checking + $f = fopen($file_path, 'r'); + $this->contents = fread($f, filesize($file_path)); + fclose($f); + } + + public function write_to_path($file_path = null): void + { + $file_path = $file_path ?? $this->file_path; + + $f = fopen($file_path, 'w'); + fwrite($f, $this->contents); + fclose($f); + } + + /** + * Replaces the replacement point with the value in the current contents. + */ + public function replace($value, $replacement_point): void + { + $this->contents = str_replace($replacement_point, $value, $this->contents); + } + + /** + * Inserts the value above the insert point in the current contents. + */ + public function insert($value, $insert_point): void + { + // seperate on new lines into an array + $file_arr = explode("\n", $this->contents); + $temp_arr = []; + + foreach ($file_arr as $line) { + if (str_contains($line, $insert_point)) { + $temp_arr[] = $value; + } + $temp_arr[] = $line; + } + + $this->contents = implode("\n", $temp_arr); + } +} diff -r a9ff874afdbd -r f65ab84ee47f src/Helpers/RelationshipNavigator.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Helpers/RelationshipNavigator.php Wed Sep 10 21:00:47 2025 -0400 @@ -0,0 +1,232 @@ + current((array) $table), $tables); + + foreach ($tableNames as $table) { + echo "Table: $table \n"; + + $relations = self::getRelations($table); + echo "Relationships: \n"; + + foreach ($relations as $relation => $relatedTables) { + echo "$relation: \n"; + foreach ($relatedTables as $relatedTable) { + echo "\t"; + foreach ($relatedTable as $key => $value) { + if (is_array($value)) { + echo "\n\t\t" . implode("\n\t\t", array_map(fn($k, $v) => "$k: $v", array_keys($value), $value)); + } else { + echo "$key: $value "; + } + } + echo "\n"; + } + } + echo "\n --- \n"; + } + } + + /** + * Retrieves relationships of a specific table. + * + * @param string $table The table name. + * @return array An array containing 'belongsTo', 'hasMany', and 'hasManyThrough' relations. + */ + public static function getRelations($table) + { + $relations = [ + 'belongsTo' => [], + 'hasMany' => [], + 'hasManyThrough' => [], + ]; + + $foreignKeys = DB::select("SHOW KEYS FROM $table WHERE Key_name != 'PRIMARY'"); + $referencedTables = self::getAllReferencedTables($table); + + // Determine 'belongsTo' Relationships + foreach ($foreignKeys as $fk) { + $column = $fk->Column_name; + + // Skip certain columns + if (in_array($column, ['created_by', 'updated_by'])) { + continue; + } + + $referencedTable = $referencedTables[$column] ?? null; + + if ($referencedTable) { + $relations['belongsTo'][] = ['column' => $column, 'table' => $referencedTable->REFERENCED_TABLE_NAME]; + } + } + + // Determine 'hasMany' Relationships + if ($reverseRelation = self::findReverseRelation($table)) { + foreach ($reverseRelation as $relatedTable) { + $relations['hasMany'][] = $relatedTable; + } + } + + // Determine 'hasManyThrough' Relationships + if ($hasManyThroughRelations = self::findHasManyThroughRelations($table)) { + foreach ($hasManyThroughRelations as $relatedTable) { + $relations['hasManyThrough'][] = $relatedTable; + } + } + + return $relations; + } + + /** + * Retrieves all referenced tables for a given table. + * + * @param string $table The table name. + * @return array|null An associative array of referenced tables, keyed by column name. + */ + public static function getAllReferencedTables($table) + { + $results = DB::select(" + SELECT TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME + FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE + WHERE TABLE_SCHEMA = DATABASE() + AND REFERENCED_TABLE_NAME IS NOT NULL + AND TABLE_NAME = ? + ", [$table]); + + return self::re_key_array($results, 'COLUMN_NAME') ?: null; + } + + /** + * Finds 'hasMany' inverse relationships for a given table. + * + * @param string $table The table name. + * @return array|null An array of related tables with column names. + */ + public static function findReverseRelation($table) + { + $relations = DB::select(" + SELECT TABLE_NAME, COLUMN_NAME + FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE + WHERE TABLE_SCHEMA = DATABASE() + AND REFERENCED_TABLE_NAME = ? + AND REFERENCED_COLUMN_NAME = 'id' + AND COLUMN_NAME NOT IN ('created_by', 'updated_by') + ", [$table]); + + return array_map(fn($rel) => ['table' => $rel->TABLE_NAME, 'column' => $rel->COLUMN_NAME], $relations) ?: null; + } + + /** + * Finds 'hasManyThrough' relationships for a given table. + * + * @param string $table The table name. + * @return array An array of 'hasManyThrough' relationships. + */ + public static function findHasManyThroughRelations($table) + { + $relations = []; + $intermediaryTables = self::findReverseRelation($table); + + if ($intermediaryTables !== null) { + foreach ($intermediaryTables as $intermediary) { + if ($isPivot = self::isPivot($intermediary['table'])) { + $isPivot = current($isPivot); + + $potentialTables = array_keys($isPivot['tables']); + $externalTable = $potentialTables[0] === $table ? $isPivot['tables'][$potentialTables[1]] : $isPivot['tables'][$potentialTables[0]]; + $internalTable = $potentialTables[0] === $table ? $isPivot['tables'][$potentialTables[0]] : $isPivot['tables'][$potentialTables[1]]; + + $hasManyThrough = [ + 'table' => $externalTable['table_name'], + 'through' => [ + 'table' => $isPivot['table'], + 'external_column' => $externalTable['column'], + 'internal_column' => $internalTable['column'], + ], + ]; + $relations[] = $hasManyThrough; + } + } + } + + return $relations; + } + + /** + * Determines if a table is a pivot table. + * + * @param string $table The table name. + * @return array|null An array with pivot details or null if not a pivot. + */ + public static function isPivot($table) + { + $relations = []; + $pivotTables = DB::select(" + SELECT TABLE_NAME, TABLE_COMMENT + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = ? + AND TABLE_COMMENT != '' + ", [$table]); + + if (!is_null($pivotTables) && count($pivotTables) > 0) { + $ref = current($pivotTables); + $pivots = json_decode(str_replace('PIVOT:', '', $ref->TABLE_COMMENT), true); + $tables = []; + + if (count($pivots) > 0) { + $references = self::getAllReferencedTables($table); + $references = self::re_key_array($references, 'REFERENCED_TABLE_NAME'); + + foreach ($pivots as $key => $value) { + if ($refData = ($references[$value] ?? null)) { + $tables[$value] = [ + 'table_name' => $value, + 'column' => $refData->COLUMN_NAME, + ]; + } + } + } + $relations[] = ['table' => $ref->TABLE_NAME, 'tables' => $tables]; + } + + return !empty($relations) ? $relations : null; + } + + /** + * Re-keys an array of objects using a specific object's property. + * + * @param array $oldArray The original array of objects. + * @param string $key The key to re-index by. + * @return array The re-keyed array. + */ + public static function re_key_array($oldArray, $key) + { + $newArray = []; + if (count($oldArray) > 0) { + foreach ($oldArray as $array) { + $newArray[$array->$key] = $array; + } + } + return $newArray; + } +} diff -r a9ff874afdbd -r f65ab84ee47f src/MagicForgerServiceProvider.php --- a/src/MagicForgerServiceProvider.php Sat Dec 02 10:20:32 2023 -0500 +++ b/src/MagicForgerServiceProvider.php Wed Sep 10 21:00:47 2025 -0400 @@ -1,15 +1,20 @@ get_all_keywords($target); $available_replacements = $this->get_available_replacements(); - $target = str_replace( + return str_replace( array_keys($available_replacements), $available_replacements, $target ); - - return $target; } - public function get_available_replacements() + /** + * Get available replacements for string replacements. + */ + public function get_available_replacements(): array { $table_name = $this->getTableInput(); - $replacements = [ - '{{ class }}' => $this->getClassName($table_name), - '{{ controllerName }}' => $this->controller_name($table_name), - '{{ model }}' => $this->model_name($table_name), - '{{ modelVariable }}' => $this->model_variable($table_name), - '{{ namespace }}' => $this->{'get'.$this->type.'Namespace'}($table_name), - '{{ namespacedModel }}' => $this->getNamespacedModel($table_name), - '{{ requestUses }}' => $this->getRequestUses($table_name), - '{{ rootNamespace }}' => $this->getRootNamespace(), - '{{ storeRequest }}' => $this->store_request_name($table_name), - '{{ tableName }}' => $table_name, - '{{ updateRequest }}' => $this->update_request_name($table_name), + + return [ + '{{ class }}' => $this->getClassName($table_name), + '{{ controllerName }}' => $this->controller_name($table_name), + '{{ model }}' => $this->model_name($table_name), + '{{ modelVariable }}' => $this->model_variable($table_name), + '{{ namespace }}' => $this->{'get'.$this->type.'Namespace'}($table_name), + '{{ namespacedModel }}' => $this->getNamespacedModel($table_name), + '{{ requestUses }}' => $this->getRequestUses($table_name), + '{{ rootNamespace }}' => $this->getRootNamespace(), + '{{ storeRequest }}' => $this->store_request_name($table_name), + '{{ filterRequest }}' => $this->filter_request_name($table_name), + '{{ updateRequest }}' => $this->update_request_name($table_name), + '{{ tableName }}' => $table_name, ]; - - return $replacements; } - // ////////////////////////////////////////// - // Internals and Classes // - // ////////////////////////////////////////// + // Model and Controller Naming /** - * Model names are generated in uppercase first Camel case. + * Generate model name in Studly case. */ public function model_name(string $name): string { @@ -85,17 +82,15 @@ } /** - * Model variable is standardly just a singular version of the table name. + * Generate singular model variable name. */ public function model_variable(string $name): string { - /* return Str::singular($name); */ - return 'item'; + return Str::singular($name); } /** - * Controller names are generated in uppercase first Camel case - * and wrapped in the prefix and suffix. + * Generate controller name using prefix/suffix and studly case. */ public function controller_name(string $name): string { @@ -104,76 +99,192 @@ $this->controller_suffix; } + /** + * Generate the store request name. + */ public function store_request_name(string $name): string { return 'Store'.$this->model_name($name).'Request'; } + /** + * Generate the filter request name. + */ + public function filter_request_name(string $name): string + { + return 'Filter'.$this->model_name($name).'Request'; + } + + /** + * Generate the update request name. + */ public function update_request_name(string $name): string { return 'Update'.$this->model_name($name).'Request'; } - // ////////////////////////////////////////// - // Namespaces // - // ////////////////////////////////////////// + + /** + * Generate the index view name. + */ + public function index_view_name(string $name): string + { + return ''; + } + + /** + * Generate the create_edit view name. + */ + public function create_edit_view_name(string $name): string + { + return ''; + } - public function getRootNamespace() + /** + * Generate the show view name. + */ + public function show_view_name(string $name): string + { + return ''; + } + + + /** + * Generate route name in Studly case. + */ + public function routes_name(string $name): string + { + return Str::singular(Str::studly($name)); + } + + // Namespace Methods + // These methods handle the formation of various namespaces used within the replacements. + + /** + * Get the root namespace for the application. + */ + public function getRootNamespace(): string { return $this->laravel->getNamespace(); } - public function getModelNamespace(string $name = '') + /** + * Get the model namespace. + */ + public function getRouteNamespace(string $name = ''): string + { + return base_path() + . DIRECTORY_SEPARATOR . 'routes' + . DIRECTORY_SEPARATOR . 'resources' + ; + } + + /** + * Get the model namespace. + */ + public function getModelNamespace(string $name = ''): string { return $this->getRootNamespace().'Models'; } - public function getNamespacedModel(string $name = '') + /** + * Get the fully-qualified namespaced model class. + */ + public function getNamespacedModel(string $name = ''): string { return $this->getModelNamespace().'\\'.$this->model_name($name); } - public function getControllerNamespace(string $name = '') + /** + * Get the controller namespace. + */ + public function getControllerNamespace(string $name = ''): string { return $this->getRootNamespace().'Http\\Controllers'; } - public function getRequestNamespace(string $name) + /** + * Get the request namespace. + */ + public function getRequestNamespace(string $name): string { return $this->getRootNamespace().'Http\\Requests\\'.$this->model_name($name); } - public function getStoreRequestNamespace(string $name) + /** + * Get the store request namespace. + */ + public function getStoreRequestNamespace(string $name): string { return $this->getRequestNamespace($name); } - public function getUpdateRequestNamespace(string $name) + /** + * Get the filter request namespace. + */ + public function getFilterRequestNamespace(string $name): string + { + return $this->getRequestNamespace($name); + } + + /** + * Get the update request namespace. + */ + public function getUpdateRequestNamespace(string $name): string { return $this->getRequestNamespace($name); } - public function getRequestUses(string $name) + /** + * Get the view namespace. + */ + public function getViewNamespace(string $name): string + { + return $this->viewPath($name) . '\\'; + } + + /** + * Get the index view namespace. + */ + public function getIndexViewNamespace(string $name): string + { + return $this->getViewNamespace($name) . '\\'; + } + + /** + * Get the create_edit view namespace. + */ + public function getCreateEditViewNamespace(string $name): string + { + return $this->getViewNamespace($name) . '\\'; + } + + /** + * Get the show view namespace. + */ + public function getShowViewNamespace(string $name): string + { + return $this->getViewNamespace($name) . '\\'; + } + + + /** + * Get the request uses string for replacement. + */ + public function getRequestUses(string $name): string { return implode("\n", [ 'use '.$this->getRequestNamespace($name).'\\'.$this->store_request_name($name).';', + 'use '.$this->getRequestNamespace($name).'\\'.$this->filter_request_name($name).';', 'use '.$this->getRequestNamespace($name).'\\'.$this->update_request_name($name).';', ]); } - public function getRouteNamespace(string $name = '') - { - return $this->getRootNamespace().'Http\\Controllers'; - } - - // ////////////////////////////////////////// - // Language and Presentables // - // ////////////////////////////////////////// + // Text Manipulation /** - * Breaks up a string and makes it human readable. - * - * This function assumes that the inputted name is camel case + * Convert a string to a human-readable format. + * Assumes camel case input. */ public function human_readable(string $name): string { @@ -181,9 +292,8 @@ } /** - * Breaks up a string and makes it human readable and lowecase. - * - * This function assumes that the inputted name is camel case + * Convert a string to a lowercase human-readable format. + * Assumes camel case input. */ public function human_readable_lc(string $name): string { diff -r a9ff874afdbd -r f65ab84ee47f src/Replacer/TableReplacer.php --- a/src/Replacer/TableReplacer.php Sat Dec 02 10:20:32 2023 -0500 +++ b/src/Replacer/TableReplacer.php Wed Sep 10 21:00:47 2025 -0400 @@ -1,59 +1,144 @@ columns)) { - $this->columns = $this->getCurrentTable()->getColumns(); + $this->columns = $this->getTableColumns($this->getCurrentTable()); } return $this->columns; } - protected function get_attributes() + /** + * Get a string representation of values for creation. + */ + protected function getValuesForCreation(): string { + $insert = ''; + foreach ($this->get_columns() as $column) { + $column_name = $column['name']; + $insert .= sprintf('$item->%s = $validated["%s"] ?? NULL;', $column_name, $column_name)."\n"; + } + + return $insert; } - protected function getValuesForCreation() + /** + * Get a string representation of table attributes. + */ + protected function getCasts(): string { $insert = ''; foreach ($this->get_columns() as $column) { - $insert .= '$item->'.$column->getName().' = $validated["'.$column->getName().'"] ?? NULL;'."\n"; + if (in_array($column['name'], $this->columns_to_ignore)) { + continue; + } + $type = $column['type_name']; + //date + if(in_array($type, ['date'])) { + $insert .= sprintf("'%s' => 'date:Y-m-d',", $column['name'])."\n"; + } + //time + if(in_array($type, ['timestamp'])) { + $insert .= sprintf("'%s' => 'date:Y-m-d H:i',", $column['name'])."\n"; + } + } + + return $insert; + } + + /** + * Get a string representation of table attributes. + */ + protected function getAttributes(): string + { + $insert = ''; + foreach ($this->get_columns() as $column) { + if (in_array($column['name'], $this->columns_to_ignore)) { + continue; + } + $insert .= sprintf("'%s' => '',", $column['name'])."\n"; } return $insert; } + /** + * Get a string representation of table fillable columns. + */ + protected function getFillable(): string + { + $insert = ''; + foreach ($this->get_columns() as $column) { + if (in_array($column['name'], $this->columns_to_ignore)) { + continue; + } + $insert .= sprintf("'%s',", $column['name'])."\n"; + } + + return $insert; + } + + /** + * Get formatted validation rules for table columns. + */ + protected function getValuesForValidation(): string + { + $insert = ''; + foreach ($this->get_columns() as $column) { + if (in_array($column['name'], $this->columns_to_ignore)) { + continue; + } + $insert .= sprintf("'%s' => 'nullable',", $column['name'])."\n"; + } + + return $insert; + } + + /** + * Apply insertions in the target template. + */ public function apply_inserts(string $target): string { $inserts = $this->get_all_keywords($target); - $available_replacements = $this->get_available_inserts(); + $available_insertions = $this->get_available_inserts(); - $target = str_replace( - array_keys($available_replacements), - $available_replacements, + return str_replace( + array_keys($available_insertions), + $available_insertions, $target ); - - return $target; } - public function get_available_inserts() + /** + * Get available insertion points for the template. + */ + public function get_available_inserts(): array { - $table_name = $this->getTableInput(); - $replacements = [ - '// {{ valuesForCreation }}' => self::getValuesForCreation(), + return [ + '// {{ valuesForCreation }}' => $this->getValuesForCreation(), + '# {{ attributeInsertPoint }}' => $this->getAttributes(), + '# {{ castInsertPoint }}' => $this->getCasts(), + '# {{ fillableInsertPoint }}' => $this->getFillable(), + '// {{ valuesForValidation }}' => $this->getValuesForValidation(), ]; - - foreach ($replacements as $key => &$replacement) { - $replacement = $replacement."\n".$key; - } - - return $replacements; } }