├── .editorconfig ├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── app ├── Commands │ ├── .gitkeep │ ├── CraftAll.php │ ├── CraftApi.php │ ├── CraftClass.php │ ├── CraftCommand.php │ ├── CraftController.php │ ├── CraftDefault.php │ ├── CraftDeveloper.php │ ├── CraftEvent.php │ ├── CraftFactory.php │ ├── CraftFormRequest.php │ ├── CraftInteractive.php │ ├── CraftListener.php │ ├── CraftMigration.php │ ├── CraftModel.php │ ├── CraftProvider.php │ ├── CraftPublish.php │ ├── CraftResource.php │ ├── CraftRule.php │ ├── CraftSeed.php │ ├── CraftTest.php │ ├── CraftViews.php │ └── CraftsmanResultCodes.php ├── CraftAssets.php ├── CraftsmanFileSystem.php ├── Exceptions │ └── Handler.php ├── Generators │ ├── ClassGenerator.php │ ├── GeneratorInterface.php │ ├── GeneratorTrait.php │ ├── MigrationGenerator.php │ └── ModelGenerator.php ├── Providers │ ├── AppServiceProvider.php │ └── TestProvider.php ├── Traits │ └── CommandDebugTrait.php └── helpers.php ├── bootstrap └── app.php ├── box.json ├── builds ├── config │ └── craftsman.php ├── laravel-craftsman └── templates │ ├── api-controller.mustache │ ├── binding-controller.mustache │ ├── class.mustache │ ├── command.mustache │ ├── controller.mustache │ ├── empty-controller.mustache │ ├── event.mustache │ ├── factory.mustache │ ├── factory_v8.mustache │ ├── form-request.mustache │ ├── invokable-controller.mustache │ ├── listener.mustache │ ├── migration.mustache │ ├── model.mustache │ ├── pest-test.mustache │ ├── provider.mustache │ ├── resource-controller.mustache │ ├── resource.mustache │ ├── rule.mustache │ ├── seed.mustache │ ├── test.mustache │ ├── view-create.mustache │ ├── view-edit.mustache │ ├── view-index.mustache │ └── view-show.mustache ├── commitlint.config.js ├── composer.json ├── config ├── app.php ├── commands.php ├── craftsman.php ├── database.php ├── insights.php └── logging.php ├── docs └── images │ ├── laravel-craftsman-cli.jpg │ └── laravel-craftsman.png ├── laravel-craftsman ├── laravel-craftsman.png ├── package-lock.json ├── package.json ├── phpunit-printer.yml ├── phpunit.ci.xml ├── phpunit.xml ├── tasks ├── build.sh ├── bumpBuild.js ├── clean.sh ├── deploy.sh ├── getVersion.js ├── messenger.sh ├── publish.sh ├── stress-test.sh ├── test-docker.sh └── todo.js ├── templates ├── api-controller.mustache ├── binding-controller.mustache ├── class.mustache ├── command.mustache ├── controller.mustache ├── empty-controller.mustache ├── event.mustache ├── factory.mustache ├── factory_v8.mustache ├── form-request.mustache ├── invokable-controller.mustache ├── listener.mustache ├── migration.mustache ├── model.mustache ├── pest-test.mustache ├── provider.mustache ├── resource-controller.mustache ├── resource.mustache ├── rule.mustache ├── seed.mustache ├── test.mustache ├── view-create.mustache ├── view-edit.mustache ├── view-index.mustache └── view-show.mustache └── tests ├── CraftsmanTestCase.php ├── CreatesApplication.php ├── Feature ├── CraftAllTest.php ├── CraftApiTest.php ├── CraftClassTest.php ├── CraftCommandTest.php ├── CraftControllerTest.php ├── CraftEventTest.php ├── CraftFactoryTest.php ├── CraftListenerTest.php ├── CraftMigrationTest.php ├── CraftModelTest.php ├── CraftProviderTest.php ├── CraftRequestTest.php ├── CraftResourceTest.php ├── CraftRouteModelBindingTest.php ├── CraftRuleTest.php ├── CraftSeedTest.php ├── CraftTestTest.php ├── CraftViewsTest.php └── VerifyTemplatesTest.php ├── TestCase.php ├── TestHelpersTrait.php ├── Unit └── CraftsmanFileSystemTest.php └── config └── craftsman.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [{*.js,*.ts}] 12 | indent_style = space 13 | indent_size = 2 14 | trim_trailing_whitespace = true 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | 19 | [{package.json,*.yml}] 20 | indent_style = space 21 | indent_size = 2 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | .travis.yml export-ignore 3 | .styleci.yml export-ignore 4 | .scrutinizer.yml export-ignore 5 | BACKERS.md export-ignore 6 | CONTRIBUTING.md export-ignore 7 | CHANGELOG.md export-ignore 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: mikeerickson 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Provide the exact command as you are typing in your terminal 16 | 2. What error is produced (if applicable) 17 | 3. What are the results the command 18 | 4. What is the expected behavior 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: mikeerickson 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /logs 3 | *.log 4 | /.idea 5 | /.vscode 6 | /.vagrant 7 | /database/database.sqlite 8 | /coverage 9 | .env 10 | /node_modules 11 | *.taskpaper 12 | *.cache 13 | .phpunit.result.cache 14 | /database/migrations 15 | box.json 16 | app/Http/Controllers 17 | app/Http/Resources 18 | app/Http/Utilities 19 | app/Models 20 | database/ 21 | NOTES.md 22 | app/Test/ 23 | TODO.md 24 | app/Http/ 25 | resources/ 26 | app/Post.php 27 | app/Test.php 28 | templates/custom.mustache 29 | templates/custom-request.mustache 30 | templates/custom-provider.mustache 31 | config.codekit3 32 | sandbox/ 33 | app/Events 34 | app/Listeners 35 | storage/ 36 | custom-templates/ 37 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "printWidth": 140, 4 | "singleQuote": true, 5 | "no-empty-interface": false 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Mike Erickson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /app/Commands/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeerickson/laravel-craftsman/fe2a3e5ca0517bb517d685b17d959cd8c43bd2ce/app/Commands/.gitkeep -------------------------------------------------------------------------------- /app/Commands/CraftApi.php: -------------------------------------------------------------------------------- 1 | Resource Name (ie Contact, Post, Comment) 38 | --model, -m Path to model [default: app/] 39 | --table, -t Desired tablename [default to pluarlized resource name] 40 | --rows, -r Number of rows for migration [default: 1] (passed to seeder) 41 | --current, -u Use --useCurrent for timestamps when creating migration 42 | --no-model, -o Skip crafting model 43 | --no-controller, -c Skip crafting controller 44 | --no-factory, -f Skip crafting factory 45 | --no-migration, -g Skip crafting migration 46 | --no-seed, -s Skip crafting seed 47 | 48 | --overwrite, -w Overwrite existing class 49 | '; 50 | 51 | public function __construct() 52 | { 53 | parent::__construct(); 54 | 55 | $this->setHelp($this->help); 56 | 57 | $this->fs = new CraftsmanFileSystem(); 58 | } 59 | 60 | public function handle() 61 | { 62 | $this->handleDebug(); 63 | 64 | // required options 65 | $name = $this->argument('name'); 66 | $model = $this->option('model'); 67 | if (!$model) { 68 | Messenger::error("When creating an API resources, you must supply model", "ERROR"); 69 | exit; 70 | } 71 | 72 | $tablename = $this->option('tablename'); 73 | $noModel = $this->option('no-model'); 74 | $noController = $this->option('no-controller'); 75 | $noFactory = $this->option('no-factory'); 76 | $noMigration = $this->option('no-migration'); 77 | $noSeed = $this->option('no-seed'); 78 | $rows = $this->option('rows') ?: '1'; 79 | 80 | // boolean options 81 | $overwrite = $this->option('overwrite') ? '--overwrite' : ''; 82 | $useCurrent = $this->option('current') ? '--current' : ''; 83 | 84 | if (strlen($tablename) === 0) { 85 | $tablename = Str::plural(strtolower($name)); 86 | } 87 | 88 | $this->info("\n"); 89 | 90 | $controllerName = "API/Api{$name}Controller"; 91 | 92 | if (!$noController) { 93 | Artisan::call("craft:controller $controllerName --api --model {$model} {$overwrite}"); 94 | } else { 95 | Messenger::info("▶︎ Controller crafting skipped\n"); 96 | } 97 | 98 | if (!$noFactory) { 99 | Artisan::call("craft:factory {$name}Factory --model {$model} {$overwrite}"); 100 | } else { 101 | Messenger::info("▶︎ Factory crafting skipped\n"); 102 | } 103 | 104 | if (!$noMigration) { 105 | Artisan::call("craft:migration create_{$tablename}_table --model {$model} --table {$tablename} {$useCurrent}"); 106 | } else { 107 | Messenger::info("▶︎ Migration crafting skipped\n"); 108 | } 109 | 110 | if (!$noModel) { 111 | Artisan::call("craft:model {$model} --table {$tablename} {$overwrite}"); 112 | } else { 113 | Messenger::info("▶︎ Model crafting skipped\n"); 114 | } 115 | 116 | if (!$noSeed) { 117 | Artisan::call("craft:seed {$name}sTableSeeder --model {$model} --rows {$rows} {$overwrite}"); 118 | } else { 119 | Messenger::info("▶︎ Seed crafting skipped\n"); 120 | } 121 | 122 | $skipAll = false; 123 | if ($noController && $noFactory && $noMigration && $noModel && $noSeed) { 124 | $skipAll = true; 125 | } else { 126 | Messenger::warning("\nNOTES: The following tasks need to be completed manually:\n"); 127 | } 128 | 129 | if (!$noFactory) { 130 | Messenger::warning(" ⚈ Complete {$name} factory configuration"); 131 | } 132 | 133 | if (!$noMigration) { 134 | Messenger::warning(" ⚈ Complete {$name} migrations"); 135 | } 136 | 137 | if (!$noSeed) { 138 | Messenger::warning(" ⚈ Update 'database/seeds/DatabaseSeed.php' to call {$name}sTableSeeder"); 139 | Messenger::warning(" ⚈ Run 'composer dump-autoload' after you have completed above steps"); 140 | } 141 | 142 | Messenger::info("\n================================================================================\n"); 143 | 144 | if ($skipAll) { 145 | Messenger::warning("You skipped all assets, nothing created", "WARNING"); 146 | } else { 147 | Messenger::info("Asset Crafting Complete", "COMPLETE"); 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /app/Commands/CraftClass.php: -------------------------------------------------------------------------------- 1 | Class Name 31 | --constructor, -c Include constructor method 32 | 33 | --template, -t Path to custom template (override config file) 34 | --overwrite, -w Overwrite existing class 35 | '; 36 | 37 | public function __construct() 38 | { 39 | parent::__construct(); 40 | 41 | $this->fs = new CraftsmanFileSystem(); 42 | 43 | $this->setHelp($this->help); 44 | } 45 | 46 | public function handle() 47 | { 48 | $this->handleDebug(); 49 | 50 | $result = (new ClassGenerator($this))->createFile(); 51 | 52 | if (!$this->option('quiet')) { 53 | if ($result["status"] === CraftsmanResultCodes::SUCCESS) { 54 | Messenger::success("{$result["message"]}\n", "SUCCESS"); 55 | } 56 | 57 | if ($result["status"] === CraftsmanResultCodes::FAIL) { 58 | Messenger::error("{$result["message"]}\n", "ERROR"); 59 | } 60 | } 61 | 62 | return $result["status"]; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/Commands/CraftCommand.php: -------------------------------------------------------------------------------- 1 | Command Name 32 | --signature, -s Command Signature 33 | --description, -d Command Description 34 | 35 | --template, -t Path to custom template (override config file) 36 | --overwrite, -w Overwrite existing class 37 | '; 38 | 39 | public function __construct() 40 | { 41 | parent::__construct(); 42 | 43 | $this->fs = new CraftsmanFileSystem(); 44 | 45 | $this->setHelp($this->help); 46 | } 47 | 48 | public function handle() 49 | { 50 | $this->handleDebug(); 51 | 52 | $className = $this->argument('name'); 53 | 54 | $signature = $this->option('signature') ?: 'command:name'; 55 | $description = $this->option('description') ?: 'Command description'; 56 | 57 | $data = [ 58 | "name" => $className, 59 | "signature" => $signature, 60 | "description" => $description, 61 | "template" => $this->option("template"), 62 | "overwrite" => $this->option("overwrite"), 63 | ]; 64 | 65 | $result = $this->fs->createFile('command', $className, $data); 66 | 67 | return $result["status"]; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/Commands/CraftController.php: -------------------------------------------------------------------------------- 1 | Controller Name 37 | --model, -m Use when creating controller 38 | --validation, -l Create validation blocks 39 | --api, -a Create API controller (skips create and update methods) 40 | --empty, -e Create empty controller 41 | --resource, -r Create resource controller 42 | --binding, -b Include Route Model Biding (requires model option) 43 | --collection, -c Use resource collection 44 | --invokable, -i Generate a single method, invokable controller class. 45 | 46 | --template, -t Template path (override configuration file) 47 | --overwrite, -w Overwrite existing controller 48 | '; 49 | 50 | public function __construct() 51 | { 52 | parent::__construct(); 53 | 54 | $this->fs = new CraftsmanFileSystem(); 55 | 56 | $this->setHelp($this->help); 57 | } 58 | 59 | public function handle() 60 | { 61 | $this->handleDebug(); 62 | 63 | $controllerName = $this->argument('name'); 64 | $model = $this->option('model'); 65 | $binding = $this->option('binding'); 66 | $api = $this->option('api'); 67 | $resource = $this->option('resource'); 68 | $invokable = $this->option('invokable'); 69 | if ($invokable) { 70 | $api = false; 71 | $resource = false; 72 | $binding = false; 73 | $model = ""; 74 | } 75 | 76 | $data = [ 77 | "model" => $model, 78 | "name" => $controllerName, 79 | "template" => $this->option('template'), 80 | "overwrite" => $this->option('overwrite'), 81 | "collection" => false, 82 | "binding" => $this->option('binding'), 83 | "invokable" => $invokable, 84 | ]; 85 | 86 | if ($api) { 87 | if (!$model) { 88 | Messenger::error("When creating an API controller, you must supply model", "ERROR"); 89 | exit; 90 | } 91 | $result = $this->fs->createFile('api-controller', $controllerName, $data); 92 | } elseif ($resource) { 93 | $data["collection"] = $this->option("collection"); 94 | $result = $this->fs->createFile('resource-controller', $controllerName, $data); 95 | } else { 96 | if ($binding) { 97 | if (strlen($data["model"]) === 0) { 98 | Messenger::warning("When creating binding controllers, you must supply model", "WARNING"); 99 | exit; 100 | } 101 | 102 | $result = $this->fs->createFile('binding-controller', $controllerName, $data); 103 | } else { 104 | if ($invokable) { 105 | $result = $this->fs->createFile('invokable-controller', $controllerName, $data); 106 | } else { 107 | if (strlen($model) === 0) { 108 | $result = $this->fs->createFile('empty-controller', $controllerName, $data); 109 | } else { 110 | $result = $this->fs->createFile('controller', $controllerName, $data); 111 | } 112 | } 113 | } 114 | } 115 | 116 | return $result["status"]; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/Commands/CraftDeveloper.php: -------------------------------------------------------------------------------- 1 | handleDebug(); 22 | 23 | $fs = new CraftsmanFileSystem(); 24 | 25 | debug("cwd: " . getcwd()); 26 | debug("local config: " . $fs->getLocalConfigFilename()); 27 | 28 | debug("config path: " . config_path()); 29 | 30 | $useCurrentDefault = $fs->getConfigValue("miscellaneous.useCurrentDefault"); 31 | debug("useCurrentDefault: {$useCurrentDefault}"); 32 | 33 | // echo "\nTemplates:\n"; 34 | // $templates = $fs->getConfigValue('templates'); 35 | 36 | // foreach ($templates as $key => $template) { 37 | // $template = getcwd() . DIRECTORY_SEPARATOR . $template; 38 | // $exists = file_exists($template) ? ' exists' : ' does not exist'; 39 | // echo ($template . $exists . PHP_EOL); 40 | // } 41 | 42 | // echo "\nPaths:\n"; 43 | // $paths = $fs->getConfigValue('paths'); 44 | // foreach ($paths as $key => $path) { 45 | // $path = getcwd() . DIRECTORY_SEPARATOR . $path; 46 | // echo (str_pad($key, 12) . " => " . $path . PHP_EOL); 47 | // } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/Commands/CraftEvent.php: -------------------------------------------------------------------------------- 1 | Class Name 32 | --no-broadcast, -b Skip broadcasting 33 | --listener, -l Generate Listener 34 | 35 | --template, -t Path to custom template (override config file) 36 | --overwrite, -w Overwrite existing class 37 | '; 38 | 39 | public function __construct() 40 | { 41 | parent::__construct(); 42 | 43 | $this->fs = new CraftsmanFileSystem(); 44 | 45 | $this->setHelp($this->help); 46 | } 47 | 48 | public function handle() 49 | { 50 | $this->handleDebug(); 51 | 52 | $className = $this->argument('name'); 53 | 54 | $data = [ 55 | "name" => $className, 56 | "no-broadcast" => $this->option("no-broadcast"), 57 | "listener" => $this->option("listener"), 58 | "template" => $this->option("template"), 59 | "overwrite" => $this->option("overwrite"), 60 | // "debug" => $this->option("debug") 61 | ]; 62 | 63 | $result = $this->fs->createFile('event', $className, $data); 64 | 65 | return $result["status"]; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/Commands/CraftFactory.php: -------------------------------------------------------------------------------- 1 | Factory Name 31 | --model, -m Use when creating controller 32 | 33 | --template, -t Template path (override configuration file) 34 | --overwrite, -w Overwrite existing factory 35 | '; 36 | 37 | public function __construct() 38 | { 39 | parent::__construct(); 40 | 41 | $this->fs = new CraftsmanFileSystem(); 42 | 43 | $this->setHelp($this->help); 44 | } 45 | 46 | public function handle() 47 | { 48 | $this->handleDebug(); 49 | 50 | $factoryName = $this->argument('name'); 51 | $model = $this->option('model'); 52 | 53 | if (strlen($model) === 0) { 54 | $this->error("Must supply model name"); 55 | } else { 56 | $data = [ 57 | "model" => $model, 58 | "name" => $factoryName, 59 | "template" => $this->option('template'), 60 | "overwrite" => $this->option('overwrite'), 61 | ]; 62 | 63 | $result = $this->fs->createFile('factory', $factoryName, $data); 64 | 65 | return $result["status"]; 66 | 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/Commands/CraftFormRequest.php: -------------------------------------------------------------------------------- 1 | Class Name 31 | --rules, -r List of rules (optional) 32 | eg. --rules title?required|unique:posts|max:255,body?required 33 | 34 | --template, -t Path to custom template (override config file) 35 | --overwrite, -w Overwrite existing class 36 | '; 37 | 38 | public function __construct() 39 | { 40 | parent::__construct(); 41 | 42 | $this->fs = new CraftsmanFileSystem(); 43 | 44 | $this->setHelp($this->help); 45 | } 46 | 47 | public function handle() 48 | { 49 | $this->handleDebug(); 50 | 51 | $className = $this->argument('name'); 52 | $rules = $this->option('rules'); 53 | 54 | $data = [ 55 | 'name' => $className, 56 | 'template' => $this->option('template'), 57 | 'overwrite' => $this->option('overwrite'), 58 | 'rules' => $rules, 59 | ]; 60 | 61 | $result = $this->fs->createFile('request', $className, $data); 62 | 63 | return $result["status"]; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/Commands/CraftListener.php: -------------------------------------------------------------------------------- 1 | Class Name 32 | --event, -e The event class be listener for 33 | --queued Indicates the event listener should be queued 34 | 35 | --template, -t Path to custom template (override config file) 36 | --overwrite, -w Overwrite existing class 37 | '; 38 | 39 | public function __construct() 40 | { 41 | parent::__construct(); 42 | 43 | $this->fs = new CraftsmanFileSystem(); 44 | 45 | $this->setHelp($this->help); 46 | } 47 | 48 | public function handle() 49 | { 50 | $this->handleDebug(); 51 | 52 | $className = $this->argument('name'); 53 | 54 | $data = [ 55 | "name" => $className, 56 | "event" => $this->option("event"), 57 | "queued" => $this->option("queued"), 58 | "template" => $this->option("template"), 59 | "overwrite" => $this->option("overwrite"), 60 | ]; 61 | 62 | $data["useEvent"] = strlen($data["event"]) > 0; 63 | 64 | $result = $this->fs->createFile('listener', $className, $data); 65 | 66 | return $result["status"]; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/Commands/CraftMigration.php: -------------------------------------------------------------------------------- 1 | Migration Name (will be appended with timestamp) 37 | --model, -m Path to model (required) 38 | --table, -t Desired tablename 39 | --update, -c Create update (change) migration 40 | --fields, -f List of fields (optional) 41 | eg. --fields "first_name:string@20:nullable, email:string@80:nullable:unique" 42 | --foreign, -r Add constraint (skipped by default) 43 | --pivot, -p Add pivot table information [composite unique, foreign fields] (skipped by default) 44 | eg. --pivot="contacts,tags" (will use contact_id, tag_id) 45 | eg. --pivot="index:false;contacts,tags" (no index; use contact_id, tag_id) 46 | eg. --pivot="contacts:my_contact_id,tags:my_tag_id" (defined table:field) 47 | Note: cascade on delete will always be added automatically 48 | --current, -u Use --useCurrent for timestamps (skipped by default) 49 | --down, -d Include down methods (skipped by default) 50 | 51 | --template, -a Template path (override configuration file)lc 52 | 53 | ======================================================================================================== 54 | Note: --overwrite flag is not supported as all migrations have current timestamp in filename 55 | ======================================================================================================== 56 | '; 57 | 58 | 59 | public function __construct() 60 | { 61 | parent::__construct(); 62 | 63 | $this->setHelp($this->help); 64 | } 65 | 66 | public function handle() 67 | { 68 | $this->handleDebug(); 69 | 70 | $result = (new MigrationGenerator($this))->createFile(); 71 | 72 | if (!$this->option('quiet')) { 73 | if ($result["status"] === CraftsmanResultCodes::SUCCESS) { 74 | Messenger::success("{$result["shortenFilename"]} created successfully\n", "SUCCESS"); 75 | } 76 | 77 | if ($result["status"] === CraftsmanResultCodes::FAIL) { 78 | Messenger::error("{$result["message"]}\n", "ERROR"); 79 | } 80 | } 81 | 82 | return $result["status"]; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/Commands/CraftModel.php: -------------------------------------------------------------------------------- 1 | Model Name (eg App\Models\Post) 43 | --all, -a Generate a migration, factory, and resource controller for the model 44 | --controller, -c Create a new controller for the model 45 | --table, -t Desired tablename 46 | --factory, -f Create a new factory for the mode 47 | --migration, -m Create a new migration file for the model 48 | --seed, -s Create a new seed file for the model 49 | 50 | --template, -l Template path (override configuration file) 51 | --overwrite, -w Overwrite existing model 52 | '; 53 | 54 | 55 | public function __construct() 56 | { 57 | parent::__construct(); 58 | 59 | $this->setHelp($this->help); 60 | 61 | $this->fs = new CraftsmanFileSystem(); 62 | } 63 | 64 | public function handle() 65 | { 66 | $this->handleDebug(); 67 | 68 | $modelName = $this->argument('name'); 69 | 70 | $overwrite = $this->option('overwrite'); 71 | $factory = $this->option('factory'); 72 | $seed = $this->option('seed'); 73 | 74 | $namespace = $this->fs->getNamespace("model", $modelName); 75 | $model = $this->fs->getModel($modelName); 76 | 77 | $tablename = $this->option("table"); 78 | $tablename = (is_null($tablename)) ? Str::plural(strtolower($model)) : $this->option("table"); 79 | 80 | $data = [ 81 | "model" => $model, 82 | "name" => $modelName, 83 | "className" => $model, 84 | "all" => $this->option('all'), 85 | "tablename" => $tablename, 86 | "factory" => $factory, 87 | "namespace" => $namespace, 88 | "overwrite" => $overwrite, 89 | "seed" => $seed, 90 | ]; 91 | 92 | $result = (new ModelGenerator($this))->createFile(); 93 | 94 | if (!$this->option('quiet')) { 95 | ($result["status"] === CraftsmanResultCodes::SUCCESS) 96 | ? Messenger::success("{$result["message"]}\n", "SUCCESS") 97 | : Messenger::error("{$result["message"]}\n", "ERROR"); 98 | } 99 | 100 | if ($this->option('migration') || $this->option('all')) { 101 | $useCurrent = config("craftsman.miscellaneous.useCurrentDefault") ? "--current" : ""; 102 | $command = "craft:migration create_{$tablename}_table --table {$tablename} {$useCurrent}"; 103 | Artisan::call($command); 104 | } 105 | 106 | if ($this->option('controller') || $this->option('all')) { 107 | $overwrite = $data["overwrite"] ? "--overwrite" : ""; 108 | $command = "craft:controller {$data["model"]}Controller {$overwrite}"; 109 | Artisan::call($command); 110 | } 111 | 112 | if ($this->option('seed') || $this->option('all')) { 113 | $model = $data["model"]; 114 | $overwrite = $data["overwrite"] ? "--overwrite" : ""; 115 | $command = "craft:seed {$model}sTableSeeder --model {$data['name']} {$overwrite}"; 116 | Artisan::call($command); 117 | } 118 | 119 | if ($this->option('factory') || $this->option('all')) { 120 | $model = $data["model"]; 121 | $overwrite = $data["overwrite"] ? "--overwrite" : ""; 122 | $command = "craft:factory {$model}Factory --model {$data['name']} {$overwrite}"; 123 | Artisan::call($command); 124 | } 125 | 126 | return $result["status"]; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /app/Commands/CraftProvider.php: -------------------------------------------------------------------------------- 1 | Class Name 31 | --facade, -f Craft Facade Class 32 | 33 | --template, -t Path to custom template (override config file) 34 | --overwrite, -w Overwrite existing class 35 | '; 36 | 37 | public function __construct() 38 | { 39 | parent::__construct(); 40 | 41 | $this->fs = new CraftsmanFileSystem(); 42 | 43 | $this->setHelp($this->help); 44 | } 45 | 46 | public function handle() 47 | { 48 | $this->handleDebug(); 49 | 50 | $className = $this->argument('name'); 51 | 52 | $data = [ 53 | "name" => $className, 54 | "facade" => $this->option("facade"), 55 | "template" => $this->option("template"), 56 | "overwrite" => $this->option("overwrite"), 57 | ]; 58 | 59 | $result = $this->fs->createFile('provider', $className, $data); 60 | 61 | return $result["status"]; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/Commands/CraftPublish.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 41 | 42 | $this->setHelp($this->help); 43 | } 44 | 45 | public function handle() 46 | { 47 | $this->handleDebug(); 48 | 49 | $overwrite = $this->option('overwrite'); 50 | $noConfig = $this->option('no-config'); 51 | $noTemplates = $this->option('no-templates'); 52 | 53 | echo "\n"; 54 | 55 | // copy templates 56 | if (!$noTemplates) { 57 | $src = $this->fs->getTemplatesDirectory(); 58 | $dest = $this->fs->path_join(getcwd(), "templates"); 59 | if (file_exists($dest) && !$overwrite) { 60 | $dest = $this->fs->tildify($dest); 61 | Messenger::error("{$dest} already exists", "ERROR"); 62 | } else { 63 | (new Filesystem())->copyDirectory($src, $dest); 64 | Messenger::success("Templates Published Successfully", "SUCCESS"); 65 | } 66 | } 67 | 68 | // copy config/craftsman 69 | if (!$noConfig) { 70 | $src = $this->fs->getAppConfigFilename(); 71 | $dest = $this->fs->path_join(getcwd(), "config", "craftsman.php"); 72 | if (file_exists($dest) && !$overwrite) { 73 | Messenger::error("{$dest} already exists", "ERROR"); 74 | } else { 75 | if ($src !== $dest) { 76 | copy($src, $dest); 77 | } 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/Commands/CraftResource.php: -------------------------------------------------------------------------------- 1 | Controller Name 30 | --collection, -c Use resource collection 31 | 32 | --template, -t Template path (override configuration file) 33 | --overwrite, -w Overwrite existing controller 34 | '; 35 | 36 | public function __construct() 37 | { 38 | parent::__construct(); 39 | 40 | $this->fs = new CraftsmanFileSystem(); 41 | 42 | $this->setHelp($this->help); 43 | } 44 | 45 | public function handle() 46 | { 47 | $this->handleDebug(); 48 | 49 | $controllerName = $this->argument('name'); 50 | 51 | $data = [ 52 | "name" => $controllerName, 53 | "template" => $this->option('template'), 54 | "overwrite" => $this->option('overwrite'), 55 | "collection" => $this->option('collection'), 56 | ]; 57 | 58 | $result = $this->fs->createFile('resource', $controllerName, $data); 59 | 60 | return $result["status"]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/Commands/CraftRule.php: -------------------------------------------------------------------------------- 1 | Rule Name 30 | --template, -t Path to custom template (override config file) 31 | --overwrite, -w Overwrite existing class 32 | '; 33 | 34 | public function __construct() 35 | { 36 | parent::__construct(); 37 | 38 | $this->fs = new CraftsmanFileSystem(); 39 | 40 | $this->setHelp($this->help); 41 | } 42 | 43 | public function handle() 44 | { 45 | $this->handleDebug(); 46 | 47 | $className = $this->argument('name'); 48 | 49 | $data = [ 50 | "name" => $className, 51 | "template" => $this->option("template"), 52 | "overwrite" => $this->option("overwrite"), 53 | ]; 54 | 55 | $result = $this->fs->createFile('rule', $className, $data); 56 | 57 | return $result["status"]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/Commands/CraftSeed.php: -------------------------------------------------------------------------------- 1 | Seed Name 36 | --model, -m Path to model 37 | --factory, -f Generate Factory (if not already created) 38 | --rows, -r Number of rows to use in factory call (Optional) 39 | 40 | --template, -t Template path (override configuration file) 41 | --overwrite, -w Overwrite existing seed 42 | '; 43 | 44 | public function __construct() 45 | { 46 | parent::__construct(); 47 | 48 | $this->fs = new CraftsmanFileSystem(); 49 | 50 | $this->setHelp($this->help); 51 | } 52 | 53 | public function handle() 54 | { 55 | $this->handleDebug(); 56 | 57 | try { 58 | $seedName = $this->argument('name'); 59 | $model = $this->option('model'); 60 | $factory = $this->option('factory'); 61 | $overwrite = $this->option('overwrite'); 62 | 63 | if (strlen($model) === 0) { 64 | $this->error("Must supply model name"); 65 | } else { 66 | $num_rows = (int) $this->option('rows') ?: 1; 67 | $data = [ 68 | "model" => $model, 69 | "name" => $seedName, 70 | "factory" => $factory, 71 | "num_rows" => $num_rows, 72 | "overwrite" => $overwrite, 73 | "template" => $this->option('template'), 74 | ]; 75 | 76 | $result = $this->fs->createFile('seed', $seedName, $data); 77 | 78 | return $result["status"]; 79 | } 80 | } catch (Exception $e) { 81 | $this->error($e->getMessage()); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/Commands/CraftTest.php: -------------------------------------------------------------------------------- 1 | Class Name 35 | --setup, -s Include setUp block 36 | --teardown, -d Include tearDown block 37 | --unit, -u Create unit test 38 | --pest, -p Create Pest test 39 | 40 | --template, -t Template path (override configuration file) 41 | --overwrite, -w Overwrite existing test 42 | '; 43 | 44 | public function __construct() 45 | { 46 | parent::__construct(); 47 | 48 | $this->fs = new CraftsmanFileSystem(); 49 | 50 | $this->setHelp($this->help); 51 | } 52 | 53 | public function handle() 54 | { 55 | $this->handleDebug(); 56 | 57 | $className = $this->argument('name'); 58 | $setup = $this->option("setup"); 59 | $teardown = $this->option("teardown"); 60 | $unit = $this->option("unit"); 61 | $pest = $this->option("pest"); 62 | $overwrite = $this->option("overwrite"); 63 | 64 | $namespace = "App\\Feature"; 65 | if ($unit) { 66 | $namespace = "App\\Unit"; 67 | } 68 | 69 | $data = [ 70 | "name" => $className, 71 | "pest" => $pest, 72 | "setup" => $setup, 73 | "teardown" => $teardown, 74 | "namespace" => $namespace, 75 | "overwrite" => $overwrite, 76 | ]; 77 | 78 | if (!Str::endsWith($className, "Test")) { 79 | $className .= "Test"; 80 | } 81 | 82 | 83 | if ($pest) { 84 | $filename = $this->fs->path_join($unit ? "Unit" : "Feature", $className); 85 | } else { 86 | $filename = $this->fs->path_join($unit ? "Unit" : "Feature", $className); 87 | } 88 | 89 | $result = $this->fs->createFile('test', $filename, $data); 90 | 91 | return $result["status"]; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/Commands/CraftViews.php: -------------------------------------------------------------------------------- 1 | )} 21 | {--x|extends= : Include extends block using supplied layout} 22 | {--s|section= : Include section block using supplied name} 23 | 24 | {--c|no-create : Don\'t craft create view} 25 | {--d|no-edit : Don\'t craft edit view} 26 | {--i|no-index : Don\'t craft index view} 27 | {--o|no-show : Don\'t craft show view} 28 | 29 | {--w|overwrite : Overwrite existing views} 30 | {--b|debug : Use Debug Interface} 31 | '; 32 | 33 | protected $description = "Craft Views (create, edit, index, show)"; 34 | 35 | protected $help = 'Craft Views 36 | Resource name (resources/views/) 37 | --extends, -e Include extends block using supplied layout 38 | --section, -s Include section block using supplied section name 39 | --overwrite, -w Overwrite existing views 40 | --no-create, -c Exclude create view 41 | --no-edit, -d Exclude edit view 42 | --no-index, -i Exclude index view 43 | --no-show, -w Exclude show view 44 | 45 | --overwrite, -w Overwrite existing model 46 | '; 47 | 48 | public function __construct() 49 | { 50 | parent::__construct(); 51 | 52 | $this->fs = new CraftsmanFileSystem(); 53 | 54 | $this->setHelp($this->help); 55 | } 56 | 57 | public function handle() 58 | { 59 | $this->handleDebug(); 60 | 61 | $assetName = strtolower($this->argument('name')); 62 | 63 | $data = [ 64 | "name" => strtolower($assetName), 65 | "extends" => $this->option("extends"), 66 | "section" => $this->option("section"), 67 | "noCreate" => $this->option('no-create'), 68 | "noEdit" => $this->option('no-edit'), 69 | "noIndex" => $this->option('no-index'), 70 | "noShow" => $this->option('no-show'), 71 | "overwrite" => $this->option("overwrite"), 72 | ]; 73 | 74 | $this->fs->createViewFiles($assetName, $data); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/Commands/CraftsmanResultCodes.php: -------------------------------------------------------------------------------- 1 | cfs = new CraftsmanFileSystem(); 14 | } 15 | 16 | public function create_controller() 17 | { 18 | } 19 | 20 | public function create_event() 21 | { 22 | } 23 | 24 | public function create_listener() 25 | { 26 | } 27 | 28 | public function create_test() 29 | { 30 | } 31 | 32 | public function create_model() 33 | { 34 | } 35 | 36 | public function create_migration() 37 | { 38 | } 39 | 40 | public function create_seed() 41 | { 42 | } 43 | 44 | public function create_class() 45 | { 46 | } 47 | 48 | public function create_command() 49 | { 50 | } 51 | 52 | public function create_factory() 53 | { 54 | } 55 | 56 | public function create_form_request() 57 | { 58 | } 59 | 60 | public function create_resource() 61 | { 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | getMessage()); 31 | $isDebug = strpos($exception->getMessage(), "--debug") > 0; 32 | if ($isDebug) { 33 | Messenger::warn($errMsg, " DEBUG "); 34 | exit; 35 | } 36 | 37 | $msg = $exception->getMessage() . " \n\nPlease review laravel-craftsman --help for details."; 38 | 39 | $ret = preg_match('/"([^"]+)"/', $msg, $matches); 40 | if ($ret > 0) { 41 | $command = $matches[$ret]; 42 | $st = $exception->getTrace()[2]["args"][0]; 43 | 44 | $str = (string) $st; 45 | $str = str_replace("craft:", "php artisan make:", $str); 46 | $str = str_replace("'", "", $str); 47 | $makeComand = str_replace("craft:", "make:", $command); 48 | echo "\n"; 49 | Messenger::info("${command} does not exist, using artisan {$makeComand}...", "INFO"); 50 | shell_exec($str); 51 | } else { 52 | echo "\n"; 53 | Messenger::error($msg, " ERROR "); 54 | } 55 | 56 | exit; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/Generators/ClassGenerator.php: -------------------------------------------------------------------------------- 1 | option('debug')) { 20 | $this->debug($args->options()); 21 | $this->debug($args->arguments()); 22 | } 23 | 24 | $this->type = "class"; 25 | $this->fs = new CraftsmanFileSystem(); 26 | 27 | $data = array_merge($args->arguments(), $args->options()); 28 | 29 | $this->vars = $this->setupCommandVariables($data); 30 | } 31 | 32 | public function setupCommandVariables(array $data): array 33 | { 34 | $vars = [ 35 | "name" => $data["name"], 36 | "className" => $this->fs->getClassName($data["name"]), 37 | "constructor" => isset($data["constructor"]) ? $data["constructor"] : false, 38 | "template" => isset($data["template"]) ? $data["template"] : "", 39 | "overwrite" => isset($data["overwrite"]) ? $data["overwrite"] : false, 40 | ]; 41 | 42 | return $vars; 43 | } 44 | 45 | public function createFile(): array 46 | { 47 | $templateFilename = (strlen($this->vars["template"]) === 0) 48 | ? $this->type : $this->vars["template"]; 49 | 50 | $src = $this->fs->getTemplateFilename($templateFilename); 51 | $templateResult = $this->fs->verifyTemplate($src); 52 | 53 | if ($templateResult["status"] === CraftsmanResultCodes::FILE_NOT_FOUND) { 54 | return $templateResult; 55 | } 56 | 57 | $path = $this->fs->getOutputPath($this->type); 58 | 59 | $this->vars["namespace"] = $this->fs->getNamespace("class", $this->vars["name"]);; 60 | $this->vars["name"] = str_replace("App/", "", $this->vars["name"]); 61 | 62 | $dest = $this->fs->path_join($path, $this->vars["name"].".php"); 63 | 64 | $result = $this->fs->mergeFile($src, $dest, $this->vars); 65 | 66 | return $result; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/Generators/GeneratorInterface.php: -------------------------------------------------------------------------------- 1 | option('debug')) { 21 | $this->debug($args->options()); 22 | $this->debug($args->arguments()); 23 | } 24 | 25 | $this->type = "migration"; 26 | $this->fs = new CraftsmanFileSystem(); 27 | 28 | $data = array_merge($args->arguments(), $args->options()); 29 | 30 | $this->vars = $this->setupCommandVariables($data); 31 | } 32 | 33 | public function setupCommandVariables(array $data): array 34 | { 35 | $vars = $data; 36 | 37 | $migrationName = $data["name"]; 38 | $tablename = $data["table"]; 39 | $model = $data["model"]; 40 | $className = "Create{$model}Table"; 41 | 42 | if (strlen($tablename) === 0 || (is_null($tablename))) { 43 | if (strlen($model) === 0) { 44 | $parts = explode("_", $migrationName); 45 | array_shift($parts); 46 | array_pop($parts); 47 | $tablename = Str::plural(implode("_", $parts)); 48 | $model = str_replace("_", "", Str::title($tablename)); 49 | } else { 50 | $parts = explode("/", $model); 51 | $tablename = Str::plural(array_pop($parts)); 52 | } 53 | } else { 54 | if (strlen($model) === 0) { 55 | $model = str_replace("_", "", Str::title($tablename)); 56 | } 57 | } 58 | 59 | $create = true; 60 | $update = false; 61 | $resourceParts = explode("_", $migrationName); 62 | 63 | if (count($resourceParts) >= 1) { 64 | if ($resourceParts[0] === 'update') { 65 | $update = true; 66 | $create = false; 67 | } 68 | } 69 | 70 | if ($vars["update"]) { 71 | $update = true; 72 | $create = false; 73 | } 74 | 75 | $className = Str::studly($migrationName); 76 | 77 | $vars = [ 78 | "name" => $migrationName, 79 | "className" => $className, 80 | "create" => $create, 81 | "update" => $update, 82 | "model" => $model, 83 | "table" => $tablename, 84 | "tablename" => $tablename, 85 | "fields" => $data["fields"], 86 | "foreign" => $data["foreign"], 87 | "pivot" => $data["pivot"], 88 | "current" => $data["current"], 89 | "down" => $data["down"], 90 | "template" => isset($data["template"]) ? $data["template"] : "", 91 | "overwrite" => false, 92 | ]; 93 | 94 | // format field data 95 | if ($vars["fields"]) { 96 | $vars["fields"] = $this->fs->buildFieldData($vars["fields"]); 97 | } 98 | 99 | $this->vars = $vars; 100 | 101 | // format foreign key values 102 | $vars = $this->parseForeign($vars); 103 | 104 | return $vars; 105 | } 106 | 107 | public function createFile(): array 108 | { 109 | $templateFilename = (strlen($this->vars["template"]) === 0) 110 | ? $this->type : $this->vars["template"]; 111 | 112 | $src = $this->fs->getTemplateFilename($templateFilename); 113 | 114 | $templateResult = $this->fs->verifyTemplate($src); 115 | if ($templateResult["status"] === -1) { 116 | return $templateResult; 117 | } 118 | 119 | $path = $this->fs->getOutputPath($this->type); 120 | 121 | // timestamp to be prepended to name 122 | $migrationFilename = Carbon::now()->format('Y_m_d_His') . "_" . $this->vars["name"] . ".php"; 123 | 124 | $dest = $this->fs->path_join($path, $migrationFilename); 125 | 126 | $result = $this->fs->mergeFile($src, $dest, $this->vars); 127 | 128 | return $result; 129 | } 130 | 131 | protected function parseForeign(array $vars): array 132 | { 133 | $vars = $this->vars; 134 | if (isset($vars["foreign"])) { 135 | $parts = explode(":", trim($vars["foreign"])); 136 | $vars["foreign"] = true; 137 | $fk = $parts[0]; 138 | $vars["fk"] = $fk; 139 | if (count($parts) >= 2) { 140 | $primaryInfo = explode(",", $parts[1]); 141 | [$pkid, $pktable] = $primaryInfo; 142 | $vars["pkid"] = $pkid; 143 | $vars["pktable"] = $pktable; 144 | } else { 145 | $primaryInfo = explode("_", $parts[0]); 146 | [$pktable, $pkid] = $primaryInfo; 147 | $vars["pkid"] = $pkid; 148 | $vars["pktable"] = Str::plural($pktable); 149 | } 150 | } 151 | return $vars; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /app/Generators/ModelGenerator.php: -------------------------------------------------------------------------------- 1 | option('debug')) { 21 | $this->debug(array_merge($args->arguments(), $args->options())); 22 | } 23 | 24 | $this->type = "model"; 25 | 26 | $this->fs = new CraftsmanFileSystem(); 27 | 28 | // $data = array_merge($args->arguments(), $args->options()); 29 | 30 | $this->vars = $this 31 | ->setupCommandVariables(array_merge($args->arguments(), $args->options())); 32 | } 33 | 34 | public function setupCommandVariables(array $data): array 35 | { 36 | $vars = [ 37 | "name" => $data["name"], 38 | "className" => $this->fs->getClassName($data["name"]), 39 | "table" => $data["table"], 40 | "tablename" => $data["table"], 41 | "all" => $data["all"], 42 | "controller" => $data["controller"], 43 | "factory" => $data["factory"], 44 | "migration" => $data["migration"], 45 | "seed" => $data["seed"], 46 | "template" => $data["template"], 47 | "overwrite" => $data["overwrite"], 48 | ]; 49 | 50 | return $vars; 51 | } 52 | 53 | public function createFile(): array 54 | { 55 | $templateFilename = (strlen($this->vars["template"]) === 0) 56 | ? $this->type 57 | : $this->vars["template"]; 58 | 59 | $src = $this->fs->getTemplateFilename($templateFilename); 60 | 61 | $templateResult = $this->fs->verifyTemplate($src); 62 | 63 | // if we have a bad template, short circut and bail out 64 | if ($templateResult["status"] === CraftsmanResultCodes::FILE_NOT_FOUND) { 65 | return $templateResult; 66 | } 67 | 68 | $modelPath = $this->fs->model_path("models"); 69 | 70 | if (file_exists($modelPath)) { 71 | if (strpos($this->vars["name"], "App/Models") === false) { 72 | $this->vars["name"] = "App/Models/" . $this->vars["name"]; 73 | } 74 | } 75 | 76 | $this->vars["namespace"] = $this->fs->getNamespace("model", $this->vars["name"]);; 77 | $this->vars["model"] = $this->fs->getModel($this->vars["name"]); 78 | 79 | // $this->vars["name"] = str_replace("App/Models/", "", $this->vars["name"]); 80 | $this->vars["name"] = str_replace("App/", "", $this->vars["name"]); 81 | 82 | // setup tablename, extract from model name 83 | $tablename = $this->vars["table"]; 84 | if (strlen($tablename) === 0) { 85 | $tablename = Str::plural(strtolower($this->vars["model"])); 86 | } 87 | $this->vars["table"] = $this->vars["tablename"] = $tablename; 88 | 89 | 90 | $path = $this->fs->getOutputPath($this->type); 91 | 92 | $dest = $this->fs->path_join($path, $this->vars["name"] . ".php"); 93 | $dest = str_replace("models/Models/", "Models/", $dest); 94 | 95 | return $this->fs->mergeFile($src, $dest, $this->vars); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | \Phar::running() 18 | ? dirname(\Phar::running(false)) . '/logs/laravel-craftsman.log' 19 | : storage_path('logs/laravel-craftsman.log') 20 | ]); 21 | } 22 | 23 | /** 24 | * Register any application services. 25 | * 26 | * @return void 27 | */ 28 | public function register() 29 | { 30 | // 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Providers/TestProvider.php: -------------------------------------------------------------------------------- 1 | isDebug() ? $this->debugMessage() : null; 13 | } 14 | 15 | function isDebug() 16 | { 17 | return (in_array("--debug", $_SERVER['argv'])); 18 | // return $this->option("debug"); 19 | } 20 | 21 | function debugMessage() 22 | { 23 | $data = array_merge($this->arguments(), $this->options()); 24 | $msg = ""; 25 | foreach ($data as $key => $value) { 26 | if ($value) { 27 | if (($key === "command") || ($key === "name")) { 28 | $msg .= $value . " "; 29 | } else { 30 | $msg .= "--" . $key; 31 | $msg .= (gettype($value) === "boolean") ? " " : "=" . "\"" . $value . "\" "; 32 | } 33 | } 34 | } 35 | 36 | $len = strlen($msg) + 2; 37 | 38 | Messenger::status("\n" . " +" . str_repeat("-", $len) . "+"); 39 | Messenger::status(" " . trim($msg)); 40 | Messenger::status(" +" . str_repeat("-", $len) . "+"); 41 | Messenger::status("\n"); 42 | } 43 | 44 | function debug($msg) 45 | { 46 | if ($this->isDebug()) { 47 | [$childClass, $caller] = debug_backtrace(false, 2); 48 | dump($caller["file"] . "::" . $caller["function"] . "." . $caller["line"]); 49 | dump($childClass["file"] . "::" . $childClass["function"] . "." . $childClass["line"]); 50 | echo ("\n"); 51 | dump($msg); 52 | echo ("\n"); 53 | } 54 | } 55 | 56 | function dump($msg) 57 | { 58 | $this->debug($msg); 59 | } 60 | 61 | function log($msg) 62 | { 63 | Log::info($msg); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/helpers.php: -------------------------------------------------------------------------------- 1 | 0; 77 | } 78 | } 79 | 80 | if (!function_exists("dlog")) { 81 | function dlog($msg = "") 82 | { 83 | if (!is_phar()) { 84 | app('log')->info($msg); 85 | } 86 | } 87 | } 88 | 89 | if (!function_exists("msg_info")) { 90 | function msg_info($msg = ""): void 91 | { 92 | Messenger::info($msg); 93 | } 94 | } 95 | 96 | if (!function_exists("msg_debug")) { 97 | function msg_debug($msg = ""): void 98 | { 99 | Messenger::debug($msg); 100 | } 101 | } 102 | 103 | if (!function_exists("debug")) { 104 | function debug($msg = ""): void 105 | { 106 | Messenger::debug($msg); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Console\Kernel::class, 31 | LaravelZero\Framework\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Debug\ExceptionHandler::class, 36 | App\Exceptions\Handler::class 37 | ); 38 | 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Return The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | This script returns the application instance. The instance is given to 46 | | the calling script so we can separate the building of the instances 47 | | from the actual running of the application and sending responses. 48 | | 49 | */ 50 | 51 | return $app; 52 | -------------------------------------------------------------------------------- /box.json: -------------------------------------------------------------------------------- 1 | {"chmod":"0755","directories":["app","bootstrap","config","vendor"],"files":["composer.json"],"exclude-composer-files":false,"compression":"GZ","compactors":["Herrera\\Box\\Compactor\\Php","Herrera\\Box\\Compactor\\Json"],"main":"laravel-craftsman"} -------------------------------------------------------------------------------- /builds/config/craftsman.php: -------------------------------------------------------------------------------- 1 | [ 16 | 'class' => 'app', 17 | 'controllers' => 'app/Http/Controllers', 18 | 'commands' => 'app/Console/Commands', 19 | 'events' => 'app/Events', 20 | 'listeners' => 'app/Listeners', 21 | 'resources' => 'app/Http/Resources', 22 | 'factories' => 'database/factories', 23 | 'migrations' => 'database/migrations', 24 | 'models' => 'app', 25 | 'providers' => 'app/Providers', 26 | 'requests' => 'app/Http/Requests', 27 | 'rules' => 'app/Rules', 28 | 'seeds' => 'database/seeds', 29 | 'templates' => 'templates', 30 | 'tests' => 'tests', 31 | 'views' => 'resources/views', 32 | ], 33 | 34 | /* 35 | |----------------------------------------------------------------------------- 36 | | Templates 37 | |------------------------------------------------------------------------------ 38 | | 39 | | This value determines the default template paths for each asset 40 | | If you wish to override any of these templates, you should create them 41 | | in using the following naming convention `templates/class.mustache.user.php` 42 | | accordingly. You should not modify the default templates directly as they 43 | | will be overwritten during any updates.. 44 | */ 45 | 46 | 'templates' => [ 47 | 'base' => 'templates', 48 | 'class' => 'templates/class.mustache', 49 | 'command' => 'templates/command.mustache', 50 | 'api-controller' => 'templates/api-controller.mustache', 51 | 'binding-controller' => 'templates/binding-controller.mustache', 52 | 'invokable-controller' => 'templates/invokable-controller.mustache', 53 | 'empty-controller' => 'templates/empty-controller.mustache', 54 | 'resource-controller' => 'templates/resource-controller.mustache', 55 | 'controller' => 'templates/controller.mustache', 56 | 'event' => 'templates/event.mustache', 57 | 'listener' => 'templates/listener.mustache', 58 | 'factory' => 'templates/factory.mustache', 59 | 'migration' => 'templates/migration.mustache', 60 | 'model' => 'templates/model.mustache', 61 | 'provider' => 'templates/provider.mustache', 62 | 'request' => 'templates/form-request.mustache', 63 | 'rule' => 'templates/rule.mustache', 64 | 'seed' => 'templates/seed.mustache', 65 | 'test' => 'templates/test.mustache', 66 | 'view-create' => 'templates/view-create.mustache', 67 | 'view-edit' => 'templates/view-edit.mustache', 68 | 'view-index' => 'templates/view-index.mustache', 69 | 'view-show' => 'templates/view-show.mustache', 70 | ], 71 | 72 | /* 73 | |----------------------------------------------------------------------------- 74 | | Miscellaneous 75 | |------------------------------------------------------------------------------ 76 | | 77 | | Miscellaneous crafting options: 78 | | useCurrentDefault - determines how migrations define timestamps (default false) 79 | | defaultModelPath - when using craft:model, this path will be used (default App) 80 | | quiet - when supplied nothing will echo'd to stdOut (default false) 81 | | defaultTestFormat - when tests created, determine test runner [phpunit|pest] (default phpunit) 82 | */ 83 | 84 | "miscellaneous" => [ 85 | "useCurrentDefault" => true, 86 | "defaultModelPath" => "App", // app/ 87 | "quiet" => false, 88 | "defaultTestFormat" => "phpunit", // pest | phpunit 89 | ], 90 | ]; 91 | -------------------------------------------------------------------------------- /builds/laravel-craftsman: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeerickson/laravel-craftsman/fe2a3e5ca0517bb517d685b17d959cd8c43bd2ce/builds/laravel-craftsman -------------------------------------------------------------------------------- /builds/templates/api-controller.mustache: -------------------------------------------------------------------------------- 1 | all()); 18 | } 19 | 20 | public function show($id) 21 | { 22 | return {{model}}::findOrFail($id); 23 | } 24 | 25 | public function update(Request $request, $id) 26 | { 27 | $data = {{model}}::findOrFail($id)->update($request->all()); 28 | } 29 | 30 | public function destroy($id) 31 | { 32 | $row = {{model}}::findOrFail($id); 33 | $row->delete(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /builds/templates/binding-controller.mustache: -------------------------------------------------------------------------------- 1 | {{model}}::all()]); 13 | } 14 | 15 | public function create() 16 | { 17 | return view('{{tablename}}.create'); 18 | } 19 | 20 | public function store(Request $request, {{binding}}) 21 | { 22 | $newData = $data->create($request->all()); 23 | return view('{{tablename}}.show', ["data" => $newData]); 24 | } 25 | 26 | public function show({{binding}}) 27 | { 28 | return view('{{tablename}}.detail', compact("data")); 29 | } 30 | 31 | public function edit({{binding}}) 32 | { 33 | return view('{{tablename}}.detail', compact($data)); 34 | } 35 | 36 | public function update(Request $request, {{binding}}) 37 | { 38 | $updateData = $data->update($request->all()); 39 | return view('{{tablename}}.detail', ["data" => $updateData]); 40 | } 41 | 42 | public function destroy({{binding}}) 43 | { 44 | $data->destroy(); 45 | return view('{{tablename}}.index',["data" => {{model}}::all()]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /builds/templates/class.mustache: -------------------------------------------------------------------------------- 1 | all()); 24 | return view('{{tablename}}.show', compact("data")); 25 | } 26 | 27 | public function show($id) 28 | { 29 | $data = {{model}}::findOrFail($id); 30 | return view('{{tablename}}.detail', compact("data")); 31 | } 32 | 33 | public function edit({{binding}}) 34 | { 35 | return view('{{tablename}}.detail', compact($data)); 36 | } 37 | 38 | public function update(Request $request, $id) 39 | { 40 | $data = {{model}}::find($id)->update($request->all()); 41 | return view('{{tablename}}.detail', compact("data")); 42 | } 43 | 44 | public function destroy($id) 45 | { 46 | $row = {{model}}::findOrFail($id); 47 | $row->delete(); 48 | $data = {{model}}::all(); 49 | return view('{{tablename}}.index', compact("data")); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /builds/templates/empty-controller.mustache: -------------------------------------------------------------------------------- 1 | define({{model}}::class, function (Faker $faker) { 8 | return [ 9 | 'key' => Str::slug($faker->sentence), 10 | 'value' => $faker->sentence, 11 | ]; 12 | }); 13 | -------------------------------------------------------------------------------- /builds/templates/factory_v8.mustache: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 19 | {{#foreign}} 20 | $table->unsignedBigInteger('{{fk}}'); 21 | {{/foreign}} 22 | {{{fields}}} 23 | {{#current}} 24 | $table->timestamp('created_at')->useCurrent(); 25 | $table->timestamp('updated_at')->useCurrent(); 26 | {{/current}} 27 | {{^current}} 28 | $table->timestamps(); 29 | {{/current}} 30 | 31 | {{#pivot}} 32 | $table->unique(['contact_id', 'tag_id']); 33 | 34 | $table->foreign('contact_id')->references('id')->on('contacts')->cascadeOnDelete(); 35 | $table->foreign('tag_id')->references('id')->on('tags')->cascadeOnDelete(); 36 | {{/pivot}} 37 | }); 38 | {{/create}} 39 | 40 | {{#foreign}} 41 | Schema::table('{{tablename}}', function ($table) { 42 | $table->foreign('{{{fk}}}')->references('{{{pkid}}}')->on('{{{pktable}}}')->onDelete('cascade'); 43 | }); 44 | {{/foreign}} 45 | } 46 | 47 | {{#down}} 48 | /** 49 | * Reverse the migrations. 50 | * 51 | * @return void 52 | */ 53 | public function down() 54 | { 55 | Schema::dropIfExists('{{tablename}}'); 56 | } 57 | {{/down}} 58 | } 59 | -------------------------------------------------------------------------------- /builds/templates/model.mustache: -------------------------------------------------------------------------------- 1 | create(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /builds/templates/test.mustache: -------------------------------------------------------------------------------- 1 | withoutExceptionHandling(); 16 | } 17 | 18 | {{/setup}} 19 | /** @test */ 20 | public function should_pass() 21 | { 22 | $this->assertTrue(true); 23 | } 24 | 25 | {{#teardown}} 26 | function tearDown(): void 27 | { 28 | parent::tearDown(); 29 | } 30 | {{/teardown}} 31 | } 32 | -------------------------------------------------------------------------------- /builds/templates/view-create.mustache: -------------------------------------------------------------------------------- 1 | {{#useExtends}} 2 | @extends('{{extends}}') 3 | 4 | {{/useExtends}} 5 | {{#useSection}} 6 | @section('{{section}}') 7 | {{/useSection}} 8 |

View Create

9 | {{#useSection}} 10 | @endsection 11 | {{/useSection}} 12 | -------------------------------------------------------------------------------- /builds/templates/view-edit.mustache: -------------------------------------------------------------------------------- 1 | {{#useExtends}} 2 | @extends('{{extends}}') 3 | 4 | {{/useExtends}} 5 | {{#useSection}} 6 | @section('{{section}}') 7 | {{/useSection}} 8 |

View Edit

9 | {{#useSection}} 10 | @endsection 11 | {{/useSection}} 12 | -------------------------------------------------------------------------------- /builds/templates/view-index.mustache: -------------------------------------------------------------------------------- 1 | {{#useExtends}} 2 | @extends('{{extends}}') 3 | 4 | {{/useExtends}} 5 | {{#useSection}} 6 | @section('{{section}}') 7 | {{/useSection}} 8 |

View Index

9 | {{#useSection}} 10 | @endsection 11 | {{/useSection}} 12 | -------------------------------------------------------------------------------- /builds/templates/view-show.mustache: -------------------------------------------------------------------------------- 1 | {{#useExtends}} 2 | @extends('{{extends}}') 3 | 4 | {{/useExtends}} 5 | {{#useSection}} 6 | @section('{{section}}') 7 | {{/useSection}} 8 |

View Show

9 | {{#useSection}} 10 | @endsection 11 | {{/useSection}} 12 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["@commitlint/config-conventional"] 3 | }; 4 | 5 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codedungeon/laravel-craftsman", 3 | "version": "1.12.0", 4 | "description": "Laravel Craftsman", 5 | "keywords": [ 6 | "cli", 7 | "make", 8 | "laravel", 9 | "console", 10 | "artisan", 11 | "generator", 12 | "framework", 13 | "laravel zero", 14 | "phpunit", 15 | "pest", 16 | "scaffold" 17 | ], 18 | "homepage": "https://laravel-craftsman.com", 19 | "type": "project", 20 | "license": "MIT", 21 | "support": { 22 | "issues": "https://github.com/mikeerickson/laravel-craftsman/issues", 23 | "source": "https://github.com/mikeerickson/laravel-craftsman" 24 | }, 25 | "authors": [ 26 | { 27 | "name": "Mike Erickson", 28 | "email": "codedungeon@gmail.com" 29 | } 30 | ], 31 | "require": { 32 | "php": "^7.1.3", 33 | "codedungeon/php-messenger": "^1.0", 34 | "codedungeon/phpunit-result-printer": "^0.27", 35 | "illuminate/log": "5.8.*", 36 | "laravel-zero/framework": "5.8.*", 37 | "mustache/mustache": "^2.12", 38 | "nunomaduro/laravel-console-menu": "^2.1", 39 | "nunomaduro/phpinsights": "^1.9", 40 | "padraic/phar-updater": "^1.0.6", 41 | "phpunit/phpunit": "^8.1" 42 | }, 43 | "require-dev": { 44 | "mockery/mockery": "^1.0", 45 | "phpstan/phpstan": "^0.12.10", 46 | "spatie/phpunit-watcher": "^1.8" 47 | }, 48 | "autoload": { 49 | "psr-4": { 50 | "App\\": "app/" 51 | }, 52 | "files": [ 53 | "app/helpers.php" 54 | ] 55 | }, 56 | "autoload-dev": { 57 | "psr-4": { 58 | "Tests\\": "tests/" 59 | } 60 | }, 61 | "config": { 62 | "preferred-install": "dist", 63 | "sort-packages": true, 64 | "optimize-autoloader": true 65 | }, 66 | "scripts": { 67 | "test:ci": "vendor/bin/phpunit -c phpunit.ci.xml", 68 | "test:feature": "vendor/bin/phpunit --testsuite Feature --colors=always", 69 | "test:unit": "vendor/bin/phpunit --testsuite Unit --colors=always", 70 | "post-create-project-cmd": [ 71 | "@php application app:rename" 72 | ] 73 | }, 74 | "minimum-stability": "dev", 75 | "prefer-stable": true, 76 | "bin": [ 77 | "builds/laravel-craftsman" 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /config/app.php: -------------------------------------------------------------------------------- 1 | 'Laravel Craftsman', 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Application Version 20 | |-------------------------------------------------------------------------- 21 | | 22 | | This value determines the "version" your application is currently running 23 | | in. You may want to follow the "Semantic Versioning" - Given a version 24 | | number MAJOR.MINOR.PATCH when an update happens: https://semver.org. 25 | | 26 | */ 27 | 28 | 'version' => get_version()." build ".get_build(), 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | Application Environment 33 | |-------------------------------------------------------------------------- 34 | | 35 | | This value determines the "environment" your application is currently 36 | | running in. This may determine how you prefer to configure various 37 | | services your application utilizes. Should be true in production. 38 | | 39 | */ 40 | 41 | 'production' => false, 42 | 43 | /* 44 | |-------------------------------------------------------------------------- 45 | | Autoloaded Service Providers 46 | |-------------------------------------------------------------------------- 47 | | 48 | | The service providers listed here will be automatically loaded on the 49 | | request to your application. Feel free to add your own services to 50 | | this array to grant expanded functionality to your applications. 51 | | 52 | */ 53 | 54 | 'providers' => [ 55 | App\Providers\AppServiceProvider::class, 56 | Codedungeon\PHPMessenger\MessengerServiceProvider::class, 57 | ], 58 | ]; 59 | -------------------------------------------------------------------------------- /config/commands.php: -------------------------------------------------------------------------------- 1 | NunoMaduro\LaravelConsoleSummary\SummaryCommand::class, 20 | // 'default' => CraftInteractive::class, 21 | 22 | /* 23 | |-------------------------------------------------------------------------- 24 | | Commands Paths 25 | |-------------------------------------------------------------------------- 26 | | 27 | | This value determines the "paths" that should be loaded by the console's 28 | | kernel. Foreach "path" present on the array provided below the kernel 29 | | will extract all "Illuminate\Console\Command" based class commands. 30 | | 31 | */ 32 | 33 | 'paths' => [app_path('Commands')], 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Added Commands 38 | |-------------------------------------------------------------------------- 39 | | 40 | | You may want to include a single command class without having to load an 41 | | entire folder. Here you can specify which commands should be added to 42 | | your list of commands. The console's kernel will try to load them. 43 | | 44 | */ 45 | 46 | 'add' => [ 47 | // .. 48 | ], 49 | 50 | /* 51 | |-------------------------------------------------------------------------- 52 | | Hidden Commands 53 | |-------------------------------------------------------------------------- 54 | | 55 | | Your application commands will always be visible on the application list 56 | | of commands. But you can still make them "hidden" specifying an array 57 | | of commands below. All "hidden" commands can still be run/executed. 58 | | 59 | */ 60 | 61 | 'hidden' => [ 62 | NunoMaduro\LaravelConsoleSummary\SummaryCommand::class, 63 | Symfony\Component\Console\Command\HelpCommand::class, 64 | Illuminate\Console\Scheduling\ScheduleRunCommand::class, 65 | Illuminate\Console\Scheduling\ScheduleFinishCommand::class, 66 | Illuminate\Foundation\Console\VendorPublishCommand::class, 67 | 68 | // this is only enabled during development 69 | // App\Commands\CraftDeveloper::class, // this is handled in command class `$hidden` property 70 | 71 | ], 72 | 73 | /* 74 | |-------------------------------------------------------------------------- 75 | | Removed Commands 76 | |-------------------------------------------------------------------------- 77 | | 78 | | Do you have a service provider that loads a list of commands that 79 | | you don't need? No problem. Laravel Zero allows you to specify 80 | | below a list of commands that you don't to see in your app. 81 | | 82 | */ 83 | 84 | 'remove' => [], 85 | 86 | ]; 87 | -------------------------------------------------------------------------------- /config/craftsman.php: -------------------------------------------------------------------------------- 1 | [ 16 | 'class' => 'app', 17 | 'controllers' => 'app/Http/Controllers', 18 | 'commands' => 'app/Console/Commands', 19 | 'events' => 'app/Events', 20 | 'listeners' => 'app/Listeners', 21 | 'resources' => 'app/Http/Resources', 22 | 'factories' => 'database/factories', 23 | 'migrations' => 'database/migrations', 24 | 'models' => 'app', 25 | 'providers' => 'app/Providers', 26 | 'requests' => 'app/Http/Requests', 27 | 'rules' => 'app/Rules', 28 | 'seeds' => 'database/seeds', 29 | 'templates' => 'templates', 30 | 'tests' => 'tests', 31 | 'views' => 'resources/views', 32 | ], 33 | 34 | /* 35 | |----------------------------------------------------------------------------- 36 | | Templates 37 | |------------------------------------------------------------------------------ 38 | | 39 | | This value determines the default template paths for each asset 40 | | If you wish to override any of these templates, you should create them 41 | | in using the following naming convention `templates/class.mustache.user.php` 42 | | accordingly. You should not modify the default templates directly as they 43 | | will be overwritten during any updates.. 44 | */ 45 | 46 | 'templates' => [ 47 | 'base' => 'templates', 48 | 'class' => 'templates/class.mustache', 49 | 'command' => 'templates/command.mustache', 50 | 'api-controller' => 'templates/api-controller.mustache', 51 | 'binding-controller' => 'templates/binding-controller.mustache', 52 | 'invokable-controller' => 'templates/invokable-controller.mustache', 53 | 'empty-controller' => 'templates/empty-controller.mustache', 54 | 'resource-controller' => 'templates/resource-controller.mustache', 55 | 'controller' => 'templates/controller.mustache', 56 | 'event' => 'templates/event.mustache', 57 | 'listener' => 'templates/listener.mustache', 58 | 'factory' => 'templates/factory.mustache', 59 | 'migration' => 'templates/migration.mustache', 60 | 'model' => 'templates/model.mustache', 61 | 'provider' => 'templates/provider.mustache', 62 | 'request' => 'templates/form-request.mustache', 63 | 'rule' => 'templates/rule.mustache', 64 | 'seed' => 'templates/seed.mustache', 65 | 'test' => 'templates/test.mustache', 66 | 'view-create' => 'templates/view-create.mustache', 67 | 'view-edit' => 'templates/view-edit.mustache', 68 | 'view-index' => 'templates/view-index.mustache', 69 | 'view-show' => 'templates/view-show.mustache', 70 | ], 71 | 72 | /* 73 | |----------------------------------------------------------------------------- 74 | | Miscellaneous 75 | |------------------------------------------------------------------------------ 76 | | 77 | | Miscellaneous crafting options: 78 | | useCurrentDefault - determines how migrations define timestamps (default false) 79 | | defaultModelPath - when using craft:model, this path will be used (default App) 80 | | quiet - when supplied nothing will echo'd to stdOut (default false) 81 | | defaultTestFormat - when tests created, determine test runner [phpunit|pest] (default phpunit) 82 | */ 83 | 84 | "miscellaneous" => [ 85 | "useCurrentDefault" => true, 86 | "defaultModelPath" => "App", // app/ 87 | "quiet" => false, 88 | "defaultTestFormat" => "phpunit", // pest | phpunit 89 | ], 90 | ]; 91 | -------------------------------------------------------------------------------- /config/database.php: -------------------------------------------------------------------------------- 1 | env('DB_CONNECTION', 'sqlite'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Database Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here are each of the database connections setup for your application. 24 | | Of course, examples of configuring each database platform that is 25 | | supported by Laravel is shown below to make development simple. 26 | | 27 | | 28 | | All database work in Laravel is done through the PHP PDO facilities 29 | | so make sure you have the driver for your particular database of 30 | | choice installed on your machine before you begin development. 31 | | 32 | */ 33 | 34 | 'connections' => [ 35 | 36 | 'sqlite' => [ 37 | 'driver' => 'sqlite', 38 | 'database' => env('DB_DATABASE', database_path('craftsman.sqlite')), 39 | 'prefix' => '', 40 | ], 41 | 42 | 'mysql' => [ 43 | 'driver' => 'mysql', 44 | 'host' => env('DB_HOST', '127.0.0.1'), 45 | 'port' => env('DB_PORT', '3306'), 46 | 'database' => env('DB_DATABASE', 'forge'), 47 | 'username' => env('DB_USERNAME', 'forge'), 48 | 'password' => env('DB_PASSWORD', ''), 49 | 'unix_socket' => env('DB_SOCKET', ''), 50 | 'charset' => 'utf8mb4', 51 | 'collation' => 'utf8mb4_unicode_ci', 52 | 'prefix' => '', 53 | 'strict' => true, 54 | 'engine' => null, 55 | ], 56 | 57 | 'pgsql' => [ 58 | 'driver' => 'pgsql', 59 | 'host' => env('DB_HOST', '127.0.0.1'), 60 | 'port' => env('DB_PORT', '5432'), 61 | 'database' => env('DB_DATABASE', 'forge'), 62 | 'username' => env('DB_USERNAME', 'forge'), 63 | 'password' => env('DB_PASSWORD', ''), 64 | 'charset' => 'utf8', 65 | 'prefix' => '', 66 | 'schema' => 'public', 67 | 'sslmode' => 'prefer', 68 | ], 69 | 70 | 'sqlsrv' => [ 71 | 'driver' => 'sqlsrv', 72 | 'host' => env('DB_HOST', 'localhost'), 73 | 'port' => env('DB_PORT', '1433'), 74 | 'database' => env('DB_DATABASE', 'forge'), 75 | 'username' => env('DB_USERNAME', 'forge'), 76 | 'password' => env('DB_PASSWORD', ''), 77 | 'charset' => 'utf8', 78 | 'prefix' => '', 79 | ], 80 | 81 | ], 82 | 83 | /* 84 | |-------------------------------------------------------------------------- 85 | | Migration Repository Table 86 | |-------------------------------------------------------------------------- 87 | | 88 | | This table keeps track of all the migrations that have already run for 89 | | your application. Using this information, we can determine which of 90 | | the migrations on disk haven't actually been run in the database. 91 | | 92 | */ 93 | 94 | 'migrations' => 'migrations', 95 | 96 | /* 97 | |-------------------------------------------------------------------------- 98 | | Redis Databases 99 | |-------------------------------------------------------------------------- 100 | | 101 | | Redis is an open source, fast, and advanced key-value store that also 102 | | provides a richer set of commands than a typical key-value systems 103 | | such as APC or Memcached. Laravel makes it easy to dig right in. 104 | | 105 | */ 106 | 107 | 'redis' => [ 108 | 109 | 'client' => 'predis', 110 | 111 | 'default' => [ 112 | 'host' => env('REDIS_HOST', '127.0.0.1'), 113 | 'password' => env('REDIS_PASSWORD', null), 114 | 'port' => env('REDIS_PORT', 6379), 115 | 'database' => env('REDIS_DB', 0), 116 | ], 117 | 118 | 'cache' => [ 119 | 'host' => env('REDIS_HOST', '127.0.0.1'), 120 | 'password' => env('REDIS_PASSWORD', null), 121 | 'port' => env('REDIS_PORT', 6379), 122 | 'database' => env('REDIS_CACHE_DB', 1), 123 | ], 124 | 125 | ], 126 | 127 | ]; 128 | -------------------------------------------------------------------------------- /config/insights.php: -------------------------------------------------------------------------------- 1 | 'laravel', 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Configuration 47 | |-------------------------------------------------------------------------- 48 | | 49 | | Here you may adjust all the various `Insights` that will be used by PHP 50 | | Insights. You can either add, remove or configure `Insights`. Keep in 51 | | mind that all added `Insights` must belong to a specific `Metric`. 52 | | 53 | */ 54 | 55 | 'exclude' => [ 56 | 'app/User.php', 57 | 'app/helpers.php', 58 | 'app/Exceptions/Handler.php', 59 | 'app/Providers', 60 | 'app/Http/Middleware', 61 | 'app/Console/Kernel.php', 62 | 'app/Http/Middleware/Authenticate.php', 63 | 'app/Http/Middleware/RedirectIfAuthenticated.php', 64 | ], 65 | 66 | 'add' => [ 67 | Classes::class => [ 68 | ForbiddenFinalClasses::class, 69 | ], 70 | ], 71 | 72 | 'remove' => [ 73 | AlphabeticallySortedUsesSniff::class, 74 | DeclareStrictTypesSniff::class, 75 | DisallowMixedTypeHintSniff::class, 76 | ForbiddenDefineFunctions::class, 77 | ForbiddenNormalClasses::class, 78 | ForbiddenTraits::class, 79 | TypeHintDeclarationSniff::class, 80 | EmptyCommentSniff::class, 81 | StaticClosureSniff::class, 82 | DocCommentSpacingSniff::class, 83 | ComposerMustBeValid::class, 84 | ForbiddenGlobals::class, 85 | MethodPerClassLimitSniff::class, 86 | CamelCapsMethodNameSniff::class, 87 | UselessParenthesesSniff::class, 88 | LanguageConstructWithParenthesesSniff::class, 89 | DisallowShortTernaryOperatorSniff::class, 90 | SpaceAfterNotSniff::class, 91 | ], 92 | 93 | 'config' => [ 94 | ForbiddenPrivateMethods::class => [ 95 | 'title' => 'The usage of private methods is not idiomatic in Laravel.', 96 | ], 97 | \PHP_CodeSniffer\Standards\Generic\Sniffs\Files\LineLengthSniff::class => [ 98 | 'lineLimit' => 200, 99 | 'absoluteLineLimit' => 200, 100 | 'ignoreComments' => false, 101 | ], 102 | \ObjectCalisthenics\Sniffs\Files\FunctionLengthSniff::class => [ 103 | 'maxLength' => 100, 104 | ], 105 | \ObjectCalisthenics\Sniffs\Metrics\MaxNestingLevelSniff::class => [ 106 | 'maxNestingLevel' => 5, 107 | ], 108 | \ObjectCalisthenics\Sniffs\NamingConventions\ElementNameMinimalLengthSniff::class => [ 109 | 'minLength' => 3, 110 | 'allowedShortNames' => ['i', 'id', 'to', 'up', 'e', 'fs', 'dt', 'fk'], 111 | ], 112 | ], 113 | 114 | ]; 115 | -------------------------------------------------------------------------------- /config/logging.php: -------------------------------------------------------------------------------- 1 | env('LOG_CHANNEL', 'single'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Log Channels 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Here you may configure the log channels for your application. Out of 27 | | the box, Laravel uses the Monolog PHP logging library. This gives 28 | | you a variety of powerful log handlers / formatters to utilize. 29 | | 30 | | Available Drivers: "single", "daily", "slack", "syslog", 31 | | "errorlog", "monolog", 32 | | "custom", "stack" 33 | | 34 | */ 35 | 36 | 'channels' => [ 37 | 'stack' => [ 38 | 'driver' => 'stack', 39 | 'channels' => ['stderr'], 40 | ], 41 | 42 | 'single' => [ 43 | 'driver' => 'single', 44 | 'path' => storage_path('logs/laravel.log'), 45 | 'level' => 'debug', 46 | ], 47 | 48 | 'daily' => [ 49 | 'driver' => 'daily', 50 | 'path' => storage_path('logs/laravel.log'), 51 | 'level' => 'debug', 52 | 'days' => 7, 53 | ], 54 | 55 | 'slack' => [ 56 | 'driver' => 'slack', 57 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 58 | 'username' => 'Laravel Log', 59 | 'emoji' => ':boom:', 60 | 'level' => 'critical', 61 | ], 62 | 63 | 'papertrail' => [ 64 | 'driver' => 'monolog', 65 | 'level' => 'debug', 66 | 'handler' => SyslogUdpHandler::class, 67 | 'handler_with' => [ 68 | 'host' => env('PAPERTRAIL_URL'), 69 | 'port' => env('PAPERTRAIL_PORT'), 70 | ], 71 | ], 72 | 73 | 'stderr' => [ 74 | 'driver' => 'monolog', 75 | 'handler' => StreamHandler::class, 76 | 'with' => [ 77 | 'stream' => 'php://stderr', 78 | ], 79 | ], 80 | 81 | 'syslog' => [ 82 | 'driver' => 'syslog', 83 | 'level' => 'debug', 84 | ], 85 | 86 | 'errorlog' => [ 87 | 'driver' => 'errorlog', 88 | 'level' => 'debug', 89 | ], 90 | ], 91 | 92 | ]; 93 | -------------------------------------------------------------------------------- /docs/images/laravel-craftsman-cli.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeerickson/laravel-craftsman/fe2a3e5ca0517bb517d685b17d959cd8c43bd2ce/docs/images/laravel-craftsman-cli.jpg -------------------------------------------------------------------------------- /docs/images/laravel-craftsman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeerickson/laravel-craftsman/fe2a3e5ca0517bb517d685b17d959cd8c43bd2ce/docs/images/laravel-craftsman.png -------------------------------------------------------------------------------- /laravel-craftsman: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 34 | 35 | $status = $kernel->handle( 36 | $input = new Symfony\Component\Console\Input\ArgvInput, 37 | new Symfony\Component\Console\Output\ConsoleOutput 38 | ); 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Shutdown The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once Artisan has finished running, we will fire off the shutdown events 46 | | so that any final work may be done by the application before we shut 47 | | down the process. This is the last thing to happen to the request. 48 | | 49 | */ 50 | 51 | $kernel->terminate($input, $status); 52 | 53 | exit($status); 54 | -------------------------------------------------------------------------------- /laravel-craftsman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeerickson/laravel-craftsman/fe2a3e5ca0517bb517d685b17d959cd8c43bd2ce/laravel-craftsman.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel-craftsman", 3 | "version": "1.12.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "colors": { 8 | "version": "1.4.0", 9 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", 10 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", 11 | "dev": true 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel-craftsman", 3 | "version": "1.12.0", 4 | "build": "353", 5 | "description": "Laravel Craftsman", 6 | "tagLine": "Laravel Artisan `make:xxx` on Steroids", 7 | "main": "index.js", 8 | "directories": { 9 | "test": "tests" 10 | }, 11 | "scripts": { 12 | "analyze": "vendor/bin/phpinsights", 13 | "bump": "./tasks/bumpBuild.js --verbose", 14 | "build": "./tasks/build.sh", 15 | "build:deploy": "./tasks/build.sh --deploy", 16 | "deploy": "./tasks/deploy.sh", 17 | "insights": "echo '\nPreparing Code Analysis...\n' && ./vendor/bin/phpinsights --config-path=\"config/insights.php\"", 18 | "publish": "./tasks/publish.sh", 19 | "test": "vendor/bin/phpunit --stop-on-failure", 20 | "test:all": "vendor/bin/phpunit --stop-on-failure", 21 | "test:coverage": "vendor/bin/phpunit --coverage-html coverage", 22 | "test:coverage-xdebug": "vendor/bin/phpunit --coverage-html coverage", 23 | "test:feature": "vendor/bin/phpunit --testsuite Feature", 24 | "test:unit": "vendor/bin/phpunit --testsuite Unit --stop-on-failure", 25 | "test:watch": "vendor/bin/phpunit-watcher watch", 26 | "test:stress": "./tasks/stress-test.sh 10", 27 | "test:docker": "./tasks/test-docker.sh", 28 | "test:ci": "./vendor/bin/phpunit -c phpunit.ci.xml --testsuite Unit", 29 | "test:travis": "./vendor/bin/phpunit -c phpunit.ci.xml --testsuite Unit", 30 | "pre-commit": "npm run test:travis", 31 | "todo": "./tasks/todo.js", 32 | "todo:file": "./tasks/todo.js > TODO.md", 33 | "todo:out": "./tasks/todo.js > TODO.md" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/mikeerickson/laravel-craftsman.git" 38 | }, 39 | "keywords": [ 40 | "cli", 41 | "craftsman", 42 | "generate", 43 | "generator", 44 | "laravel", 45 | "scaffold", 46 | "artisan", 47 | "artisan make:xxxx", 48 | "make", 49 | "phpunit", 50 | "pest" 51 | ], 52 | "author": "Mike Erickson (https://github.com/mikeerickson)", 53 | "license": "MIT", 54 | "bugs": { 55 | "url": "https://github.com/mikeerickson/laravel-craftsman/issues" 56 | }, 57 | "homepage": "https://github.com/mikeerickson/laravel-craftsman#readme", 58 | "devDependencies": { 59 | "colors": "1.4.0" 60 | } 61 | } -------------------------------------------------------------------------------- /phpunit-printer.yml: -------------------------------------------------------------------------------- 1 | options: 2 | cd-printer-hide-class: false 3 | cd-printer-simple-output: false 4 | cd-printer-show-config: true 5 | cd-printer-hide-namespace: true 6 | cd-printer-anybar: true 7 | cd-printer-anybar-port: 1738 8 | markers: 9 | cd-pass: "✔ " 10 | cd-fail: "✖ " 11 | cd-error: "⚈ " 12 | cd-skipped: "=> " 13 | cd-incomplete: "∅ " 14 | cd-risky: "⌽ " 15 | -------------------------------------------------------------------------------- /phpunit.ci.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/Feature 16 | 17 | 18 | ./tests/Unit 19 | 20 | 21 | 24 | 25 | 26 | ./app 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | ./tests/Feature 17 | 18 | 19 | ./tests/Unit 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ./app 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /tasks/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source ./tasks/messenger.sh 4 | 5 | BUILD=$(./tasks/bumpBuild.js) 6 | VERSION=$(./tasks/getVersion.js) 7 | BUILD_INFO="v$VERSION build $BUILD" 8 | 9 | info "Build number bumped to $BUILD ..." " INFO " 10 | 11 | php laravel-craftsman app:build 12 | rm -rf builds/templates 13 | rm -rf builds/config 14 | cp -r templates builds/templates 15 | mkdir builds/config 16 | cp config/craftsman.php builds/config 17 | 18 | printf "\n" 19 | success "Build Completed Successfully" " SUCCESS " 20 | 21 | if [[ "$@" == "--deploy" ]]; then 22 | ./tasks/deploy.sh "$BUILD_INFO" 23 | fi 24 | 25 | 26 | -------------------------------------------------------------------------------- /tasks/bumpBuild.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* global require */ 3 | 4 | const fs = require('fs') 5 | const colors = require('colors') 6 | 7 | const pkgInfo = require('../package.json') 8 | 9 | let currBuild = parseInt(pkgInfo.build) 10 | 11 | currBuild++ 12 | 13 | pkgInfo.build = currBuild.toString() 14 | 15 | fs.writeFileSync('./package.json', JSON.stringify(pkgInfo, null, 2)) 16 | 17 | // important, do not add anything other than build number as it supplies 18 | // return value which is used in the calling script (unless --verbose flag is supplied) 19 | 20 | if (process.argv.includes('--verbose')) { 21 | let versionStr = `v${pkgInfo.version} build ${currBuild}` 22 | console.log('\nUpdated To: ' + colors.cyan(versionStr)) 23 | } else { 24 | console.log(currBuild) 25 | } 26 | -------------------------------------------------------------------------------- /tasks/clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "./tasks/messenger.sh" 4 | 5 | rm -rf app/Http 6 | success " ✔︎ app/Http removed" 7 | rm -rf app/Models 8 | success " ✔︎ app/Models removed" 9 | rm -rf app/Test 10 | success " ✔︎ app/Test removed" 11 | rm -rf database/factories 12 | success " ✔︎ database/factories removed" 13 | rm -rf database/migrations 14 | success " ✔︎ database/migrations removed" 15 | rm -rf database/seeds 16 | success " ✔︎ database/seeds removed" 17 | rm app/Post.php 18 | rm app/TestClass.php 19 | 20 | printf "\n" 21 | success "Directories Cleaned Successfully" " SUCCESS " 22 | -------------------------------------------------------------------------------- /tasks/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source ./tasks/messenger.sh 4 | 5 | if [[ -d "~/laravel-craftsman" ]]; then 6 | mkdir ~/laravel-craftsman 7 | fi 8 | cp -r builds/ ~/laravel-craftsman/ 9 | #mv ~/laravel-craftsman/laravel-craftsman ~/laravel-craftsman/craftsman 10 | 11 | printf "\n" 12 | printf "==> deploying to: ~/laravel-craftsman\n" 13 | printf "\n" 14 | 15 | success "Laravel Craftsman $@ Deployed Successfully" " SUCCESS " 16 | -------------------------------------------------------------------------------- /tasks/getVersion.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* global require */ 4 | 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | 8 | let version = ''; 9 | 10 | let packageFilename = path.join(__dirname, '..', 'package.json'); 11 | if (fs.existsSync(packageFilename)) { 12 | let pkgInfo = require(packageFilename); 13 | if (pkgInfo.hasOwnProperty('version')) { 14 | version = pkgInfo.version; 15 | } 16 | } else { 17 | let composerFilename = path.join(__dirname, '..', 'composer.json'); 18 | let composerInfo = require(composerFilename); 19 | if (composerInfo.hasOwnProperty('version')) { 20 | version = composerInfo.version; 21 | } 22 | } 23 | 24 | console.log(version); 25 | 26 | -------------------------------------------------------------------------------- /tasks/messenger.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # messenger.sh 3 | 4 | # https://askubuntu.com/questions/558280/changing-colour-of-text-and-background-of-terminal 5 | # https://www.google.com/search?q=bash+colors+background+yellow 6 | 7 | test_messenger() { 8 | echo "\n" 9 | 10 | success "Success Message" " SUCCESS " 11 | success "Success Message" 12 | echo "\n" 13 | 14 | error "Error Message" " ERROR " 15 | error "Error Message" 16 | echo "\n" 17 | 18 | warning "Warning Message" " WARNING " 19 | warning "Warning Message" 20 | echo "\n" 21 | 22 | warn "Warn Message" " WARN " 23 | warn "Warn Message" 24 | echo "\n" 25 | 26 | info "Info Message" " INFO " 27 | info "Info Message" 28 | echo "\n" 29 | 30 | note "Note Message" " NOTE " 31 | note "Note Message" 32 | echo "\n" 33 | 34 | log "Log Message" " LOG " 35 | log "Log Message" 36 | echo "\n" 37 | 38 | status "Status Message" " STATUS " 39 | status "Status Message" 40 | echo "\n" 41 | 42 | important "Important Message" " IMPORTANT " 43 | important "Important Message" 44 | echo "\n" 45 | 46 | notice "Notice Message" " NOTICE " 47 | notice "Notice Message" 48 | echo "\n" 49 | } 50 | 51 | success() { 52 | if [[ -z "$2" ]]; then 53 | printf "\e[32m$1\e[39;49;00m\n"; 54 | else 55 | printf "\e[30;42;11;87m$2\e[39;49;00m\e[32m $1\e[39;49;00m\n"; 56 | fi 57 | } 58 | 59 | error() { 60 | if [[ -z "$2" ]]; then 61 | printf "\e[31m$1\e[39;49;00m\n"; 62 | else 63 | printf "\e[30;41;11;87m$2\e[39;49;00m\e[31m $1\e[39;49;00m\n"; 64 | fi 65 | } 66 | 67 | info() { 68 | if [[ -z "$2" ]]; then 69 | printf "\e[34;11m$1\e[39;49;00m\n"; 70 | else 71 | printf "\e[30;44;11;87m$2\e[39;49;00m\e[34m $1\e[39;49;00m\n"; 72 | fi 73 | } 74 | 75 | warning() { 76 | if [[ -z "$2" ]]; then 77 | printf "\e[33m$1\e[39;49;00m\n"; 78 | else 79 | printf "\e[30;43;11;87m$2\e[39;49;00m\e[33m $1\e[39;49;00m\n"; 80 | fi 81 | } 82 | 83 | warn() { 84 | if [[ -z "$2" ]]; then 85 | printf "\e[33m$1\e[39;49;00m\n"; 86 | else 87 | printf "\e[30;43;11;87m$2\e[39;49;00m\e[33m $1\e[39;49;00m\n"; 88 | fi 89 | } 90 | 91 | important() { 92 | if [[ -z "$2" ]]; then 93 | printf "\e[33m$1\e[39;49;00m\n"; 94 | else 95 | printf "\e[30;43;11;87m$2\e[39;49;00m\e[33m $1\e[39;49;00m\n"; 96 | fi 97 | } 98 | 99 | log() { 100 | if [[ -z "$2" ]]; then 101 | printf "$1\n"; 102 | else 103 | printf "\e[30;107;11;87m$2\e[39;49;00m $1\n"; 104 | fi 105 | } 106 | 107 | status() { 108 | if [[ -z "$2" ]]; then 109 | printf "\e[35m$1\e[39;49;00m\n"; 110 | else 111 | printf "\e[30;45;11;87m$2\e[39;49;00m\e[35m $1\e[39;49;00m\n"; 112 | fi 113 | } 114 | 115 | note() { 116 | # note: { fg: '38m', bg: '48m' }, 117 | if [[ -z "$2" ]]; then 118 | printf "$1\n"; 119 | else 120 | printf "\e[30;107;11;87m$2\e[39;49;00m $1\n"; 121 | fi 122 | } 123 | 124 | notice() { 125 | if [[ -z "$2" ]]; then 126 | printf "\e[34m$1\e[39;49;00m\n"; 127 | else 128 | printf "\e[30;44;11;87m$2\e[39;49;00m\e[34m $1\e[39;49;00m\n"; 129 | fi 130 | } 131 | -------------------------------------------------------------------------------- /tasks/publish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source ./tasks/messenger.sh 4 | 5 | VERSION=$(./tasks/getVersion.js) 6 | printf "\n" 7 | info "Publishing $VERSION ..." " INFO " 8 | 9 | printf "\n" 10 | 11 | success "✓ Creating Github tag $VERSION" 12 | 13 | git tag "$VERSION" && git push --tags --quiet 14 | 15 | printf "\n" 16 | success "Publishing $VERSION Completed Successfully " " SUCCESS " 17 | -------------------------------------------------------------------------------- /tasks/stress-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ESC_SEQ="\x1b[" 4 | COL_RESET=${ESC_SEQ}"39;49;00m" 5 | COL_LCYAN=${ESC_SEQ}"36;01m" 6 | 7 | ITERATIONS=${1:-5} 8 | 9 | for i in `seq 1 "$ITERATIONS"`; do 10 | echo "" 11 | printf "${COL_LCYAN}==> Running Stress Test: Iteration ${i} of ${ITERATIONS}...${COL_RESET}\n" 12 | vendor/bin/phpunit -c phpunit.ci.xml 13 | done 14 | -------------------------------------------------------------------------------- /tasks/test-docker.sh: -------------------------------------------------------------------------------- 1 | # test multiple php versions using docker 2 | source ./tasks/messenger.sh 3 | echo "\n" 4 | 5 | info "Testing PHP 7.2 (Alpine Distribution)" " INFO " 6 | docker run -v $(pwd):/app -w /app --rm php:7.2-alpine vendor/bin/phpunit -c phpunit.ci.xml --colors=always 7 | echo "\n" 8 | 9 | info "Testing PHP 7.3 (Alpine Distribution)" " INFO " 10 | docker run -v $(pwd):/app -w /app --rm php:7.3-alpine vendor/bin/phpunit -c phpunit.ci.xml --colors=always 11 | echo "\n" 12 | 13 | warn "Testing PHP 7.4 (Alpine Distribution) -- Currently Not Working" " WARNING " 14 | docker run -v $(pwd):/app -w /app --rm php:7.4-alpine vendor/bin/phpunit -c phpunit.ci.xml --colors=always 15 | echo "\n" 16 | 17 | success "Docker Testing Complete" " DONE " 18 | -------------------------------------------------------------------------------- /tasks/todo.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /*eslint no-console: "off"*/ 3 | 4 | const {spawnSync} = require('child_process'); 5 | 6 | spawnSync( 7 | 'leasot', 8 | ['-x', './**/*.{ts,js,vue,php}', '--ignore', './node_modules,./vendor', '--tags', 'review', '>', 'TODO.md'], 9 | {stdio: 'inherit'} 10 | ); 11 | -------------------------------------------------------------------------------- /templates/api-controller.mustache: -------------------------------------------------------------------------------- 1 | all()); 18 | } 19 | 20 | public function show($id) 21 | { 22 | return {{model}}::findOrFail($id); 23 | } 24 | 25 | public function update(Request $request, $id) 26 | { 27 | $data = {{model}}::findOrFail($id)->update($request->all()); 28 | } 29 | 30 | public function destroy($id) 31 | { 32 | $row = {{model}}::findOrFail($id); 33 | $row->delete(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /templates/binding-controller.mustache: -------------------------------------------------------------------------------- 1 | {{model}}::all()]); 13 | } 14 | 15 | public function create() 16 | { 17 | return view('{{tablename}}.create'); 18 | } 19 | 20 | public function store(Request $request, {{binding}}) 21 | { 22 | $newData = $data->create($request->all()); 23 | return view('{{tablename}}.show', ["data" => $newData]); 24 | } 25 | 26 | public function show({{binding}}) 27 | { 28 | return view('{{tablename}}.detail', compact("data")); 29 | } 30 | 31 | public function edit({{binding}}) 32 | { 33 | return view('{{tablename}}.detail', compact($data)); 34 | } 35 | 36 | public function update(Request $request, {{binding}}) 37 | { 38 | $updateData = $data->update($request->all()); 39 | return view('{{tablename}}.detail', ["data" => $updateData]); 40 | } 41 | 42 | public function destroy({{binding}}) 43 | { 44 | $data->destroy(); 45 | return view('{{tablename}}.index',["data" => {{model}}::all()]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /templates/class.mustache: -------------------------------------------------------------------------------- 1 | all()); 24 | return view('{{tablename}}.show', compact("data")); 25 | } 26 | 27 | public function show($id) 28 | { 29 | $data = {{model}}::findOrFail($id); 30 | return view('{{tablename}}.detail', compact("data")); 31 | } 32 | 33 | public function edit({{binding}}) 34 | { 35 | return view('{{tablename}}.detail', compact($data)); 36 | } 37 | 38 | public function update(Request $request, $id) 39 | { 40 | $data = {{model}}::find($id)->update($request->all()); 41 | return view('{{tablename}}.detail', compact("data")); 42 | } 43 | 44 | public function destroy($id) 45 | { 46 | $row = {{model}}::findOrFail($id); 47 | $row->delete(); 48 | $data = {{model}}::all(); 49 | return view('{{tablename}}.index', compact("data")); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /templates/empty-controller.mustache: -------------------------------------------------------------------------------- 1 | define({{model}}::class, function (Faker $faker) { 8 | return [ 9 | 'key' => Str::slug($faker->sentence), 10 | 'value' => $faker->sentence, 11 | ]; 12 | }); 13 | -------------------------------------------------------------------------------- /templates/factory_v8.mustache: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 19 | {{#foreign}} 20 | $table->unsignedBigInteger('{{fk}}'); 21 | {{/foreign}} 22 | {{{fields}}} 23 | {{#current}} 24 | $table->timestamp('created_at')->useCurrent(); 25 | $table->timestamp('updated_at')->useCurrent(); 26 | {{/current}} 27 | {{^current}} 28 | $table->timestamps(); 29 | {{/current}} 30 | 31 | {{#pivot}} 32 | $table->unique(['contact_id', 'tag_id']); 33 | 34 | $table->foreign('contact_id')->references('id')->on('contacts')->cascadeOnDelete(); 35 | $table->foreign('tag_id')->references('id')->on('tags')->cascadeOnDelete(); 36 | {{/pivot}} 37 | }); 38 | {{/create}} 39 | 40 | {{#foreign}} 41 | Schema::table('{{tablename}}', function ($table) { 42 | $table->foreign('{{{fk}}}')->references('{{{pkid}}}')->on('{{{pktable}}}')->onDelete('cascade'); 43 | }); 44 | {{/foreign}} 45 | } 46 | 47 | {{#down}} 48 | /** 49 | * Reverse the migrations. 50 | * 51 | * @return void 52 | */ 53 | public function down() 54 | { 55 | Schema::dropIfExists('{{tablename}}'); 56 | } 57 | {{/down}} 58 | } 59 | -------------------------------------------------------------------------------- /templates/model.mustache: -------------------------------------------------------------------------------- 1 | create(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /templates/test.mustache: -------------------------------------------------------------------------------- 1 | withoutExceptionHandling(); 16 | } 17 | 18 | {{/setup}} 19 | /** @test */ 20 | public function should_pass() 21 | { 22 | $this->assertTrue(true); 23 | } 24 | 25 | {{#teardown}} 26 | function tearDown(): void 27 | { 28 | parent::tearDown(); 29 | } 30 | {{/teardown}} 31 | } 32 | -------------------------------------------------------------------------------- /templates/view-create.mustache: -------------------------------------------------------------------------------- 1 | {{#useExtends}} 2 | @extends('{{extends}}') 3 | 4 | {{/useExtends}} 5 | {{#useSection}} 6 | @section('{{section}}') 7 | {{/useSection}} 8 |

View Create

9 | {{#useSection}} 10 | @endsection 11 | {{/useSection}} 12 | -------------------------------------------------------------------------------- /templates/view-edit.mustache: -------------------------------------------------------------------------------- 1 | {{#useExtends}} 2 | @extends('{{extends}}') 3 | 4 | {{/useExtends}} 5 | {{#useSection}} 6 | @section('{{section}}') 7 | {{/useSection}} 8 |

View Edit

9 | {{#useSection}} 10 | @endsection 11 | {{/useSection}} 12 | -------------------------------------------------------------------------------- /templates/view-index.mustache: -------------------------------------------------------------------------------- 1 | {{#useExtends}} 2 | @extends('{{extends}}') 3 | 4 | {{/useExtends}} 5 | {{#useSection}} 6 | @section('{{section}}') 7 | {{/useSection}} 8 |

View Index

9 | {{#useSection}} 10 | @endsection 11 | {{/useSection}} 12 | -------------------------------------------------------------------------------- /templates/view-show.mustache: -------------------------------------------------------------------------------- 1 | {{#useExtends}} 2 | @extends('{{extends}}') 3 | 4 | {{/useExtends}} 5 | {{#useSection}} 6 | @section('{{section}}') 7 | {{/useSection}} 8 |

View Show

9 | {{#useSection}} 10 | @endsection 11 | {{/useSection}} 12 | -------------------------------------------------------------------------------- /tests/CraftsmanTestCase.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 29 | } 30 | 31 | /** 32 | * setUpBeforeClass 33 | */ 34 | public static function setUpBeforeClass(): void 35 | { 36 | parent::setUpBeforeClass(); 37 | } 38 | 39 | /** 40 | * tearDownAfterClass 41 | */ 42 | public static function tearDownAfterClass(): void 43 | { 44 | parent::tearDownAfterClass(); 45 | 46 | rmdir("resources/views/coverage"); 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 19 | 20 | return $app; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Feature/CraftAllTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 30 | 31 | $this->withoutExceptionHandling(); 32 | } 33 | 34 | /** @test */ 35 | public function should_execute_craft_all_command() 36 | { 37 | $this->artisan('craft:all Author --model App/Models/Author --tablename authors --rows 44') 38 | ->assertExitCode(0); 39 | 40 | $this->fs->rmdir("app/Http"); 41 | // $this->fs->rmdir("app/Models"); 42 | $this->fs->rmdir("app/Test"); 43 | $this->fs->rmdir("resources/views/posts"); 44 | $this->fs->rmdir("database"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/Feature/CraftApiTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 24 | 25 | $this->withoutExceptionHandling(); 26 | } 27 | 28 | /** @test */ 29 | public function should_craft_all_api_assets_using_default() 30 | { 31 | $resource = 'Contact'; 32 | $model = "Contact"; 33 | 34 | $this->artisan("craft:api {$resource} --model {$model} --overwrite") 35 | ->assertExitCode(0); 36 | 37 | // create model 38 | $filename = $this->fs->path_join($this->fs->model_path(), "{$resource}.php"); 39 | // dd($filename); 40 | $this->assertFileExists($filename); 41 | unlink($filename); 42 | 43 | // create controller 44 | $filename = $this->fs->path_join($this->fs->controller_path(), "API", "Api{$resource}Controller.php"); 45 | $this->assertFileExists($filename); 46 | unlink($filename); 47 | 48 | // create factory 49 | $filename = $this->fs->path_join($this->fs->factory_path(), "{$resource}Factory.php"); 50 | $this->assertFileExists($filename); 51 | unlink($filename); 52 | 53 | // create migration 54 | $migrationName = "create_contacts_table"; 55 | $this->assertMigrationFileExists($migrationName); 56 | 57 | $this->cleanUp(); 58 | } 59 | 60 | public function cleanUp() 61 | { 62 | $this->fs->rmdir("app/Http"); 63 | $this->fs->rmdir("app/Models"); 64 | $this->fs->rmdir("app/Test"); 65 | $this->fs->rmdir("resources/views/posts"); 66 | $this->fs->rmdir("database/migrations"); 67 | $this->fs->rmdir("database/factories"); 68 | $this->fs->rmdir("database/seeds"); 69 | } 70 | 71 | /** @test */ 72 | public function should_create_all_api_assets_using_custom_model_path(): void 73 | { 74 | $resource = "Contact"; 75 | $migrationName = "create_contacts_table"; 76 | 77 | $this->artisan("craft:api {$resource} --model App/Models/{$resource}") 78 | ->assertExitCode(0); 79 | 80 | // create model 81 | $filename = $this->fs->path_join($this->fs->model_path(), "Models", "{$resource}.php"); 82 | $filename = str_replace("models/Models", "/Models", $filename); 83 | $this->assertFileExists($filename); 84 | unlink($filename); 85 | 86 | // check controller 87 | $controllerFilename = $this->fs->path_join($this->fs->controller_path(), "API", "Api{$resource}Controller.php"); 88 | $this->assertFileExists($controllerFilename); 89 | 90 | $factoryFilename = $this->fs->path_join($this->fs->factory_path(), "{$resource}Factory.php"); 91 | $this->assertFileExists($factoryFilename); 92 | unlink($factoryFilename); 93 | 94 | $migrationFilename = $this->fs->getLastMigrationFilename("database/migrations", $migrationName); 95 | $this->assertFileExists($migrationFilename); 96 | unlink($migrationFilename); 97 | 98 | $this->cleanUp(); 99 | } 100 | 101 | /** @test */ 102 | public function should_use_custom_tablename() 103 | { 104 | $resource = "Contact"; 105 | 106 | $this->artisan("craft:api {$resource} --model Contact --tablename contacters --overwrite") 107 | ->assertExitCode(0); 108 | 109 | $modelFilename = $this->fs->path_join($this->fs->model_path(), "{$resource}.php"); 110 | $this->assertFileContainsString($modelFilename, "contacters"); 111 | unlink($modelFilename); 112 | 113 | $this->assertTrue(true); 114 | 115 | $this->cleanUp(); 116 | } 117 | 118 | /** @test */ 119 | public function should_not_create_marked_resources(): void 120 | { 121 | $resource = 'Contact'; 122 | 123 | $this->artisan("craft:api {$resource} --model {$resource} --no-model --no-controller --no-factory --no-migration --no-seed") 124 | ->assertExitCode(0); 125 | 126 | // check controller 127 | $filename = $this->fs->path_join($this->fs->controller_path(), "{$resource}Controller.php"); 128 | $this->assertFileNotExists($filename); 129 | 130 | // check factory 131 | $filename = $this->fs->path_join($this->fs->factory_path(), "{$resource}Factory.php"); 132 | $this->assertFalse(file_exists($filename)); 133 | 134 | // check migration 135 | $migrationName = "create_contacts_table"; 136 | $migrationFilename = $this->fs->getLastMigrationFilename("database/migrations", $migrationName); 137 | $this->assertNull($migrationFilename); 138 | 139 | // check seed 140 | $filename = $this->fs->path_join($this->fs->seed_path(), "{$resource}TableSeeder.php"); 141 | $this->assertFalse(file_exists($filename)); 142 | 143 | $this->cleanUp(); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /tests/Feature/CraftClassTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 30 | 31 | $this->withoutExceptionHandling(); 32 | } 33 | 34 | /** @test */ 35 | public function should_create_simple_class_command() 36 | { 37 | $class = "Test"; 38 | 39 | $this->artisan("craft:class {$class}") 40 | ->assertExitCode(0); 41 | 42 | $filename = $this->pathJoin("app", "Test.php"); 43 | $this->assertFileExists($filename); 44 | 45 | $this->assertFileContainsString($filename, "class {$class}"); 46 | 47 | $this->fs->delete($filename); 48 | } 49 | 50 | /** @test */ 51 | public function should_create_class_with_namespace() 52 | { 53 | $class = "App/Test/SampleClass"; 54 | 55 | $this->artisan("craft:class {$class}") 56 | ->assertExitCode(0); 57 | 58 | $filename = $this->pathJoin("app/Test", "SampleClass.php"); 59 | $this->assertFileExists($filename); 60 | 61 | $this->assertFileContainsString($filename, "class SampleClass"); 62 | 63 | $this->fs->rmdir("app/Test"); 64 | } 65 | 66 | /** @test */ 67 | public function should_create_class_using_user_template() 68 | { 69 | $class = "App/Test/SampleClass"; 70 | 71 | $this->artisan("craft:class {$class} --template /custom-templates/class.mustache") 72 | ->assertExitCode(0); 73 | 74 | $filename = $this->pathJoin("app/Test", "SampleClass.php"); 75 | $this->assertFileExists($filename); 76 | 77 | $this->assertFileContainsString($filename, "class SampleClass"); 78 | $this->assertFileContainsString($filename, "testMethod"); 79 | 80 | $this->fs->rmdir("app/Test"); 81 | } 82 | 83 | /** ------------------------------------------------------------------------------------------------------ 84 | * Test Helpers 85 | * ------------------------------------------------------------------------------------------------------- */ 86 | 87 | } 88 | -------------------------------------------------------------------------------- /tests/Feature/CraftCommandTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 30 | 31 | $this->withoutExceptionHandling(); 32 | } 33 | 34 | /** @test */ 35 | public function should_create_console_command() 36 | { 37 | $class = "TestCommand"; 38 | 39 | $this->artisan("craft:command {$class}") 40 | ->assertExitCode(0); 41 | 42 | $filename = $this->pathJoin("app", "Console", "Commands", "{$class}.php"); 43 | 44 | $this->assertFileExists($filename); 45 | 46 | $this->assertFileContainsString($filename, "class {$class}"); 47 | 48 | unlink($filename); 49 | } 50 | 51 | /** @test */ 52 | public function should_create_command_with_namespace() 53 | { 54 | $class = "App/Test/SampleClass"; 55 | 56 | $this->artisan("craft:class {$class}") 57 | ->assertExitCode(0); 58 | 59 | $filename = $this->pathJoin("app/Test", "SampleClass.php"); 60 | $this->assertFileExists($filename); 61 | 62 | $this->assertFileContainsString($filename, "class SampleClass"); 63 | 64 | $this->fs->rmdir("app/Test"); 65 | } 66 | 67 | /** @test */ 68 | public function should_create_command_using_user_template() 69 | { 70 | $class = "SampleCommand"; 71 | 72 | $this->artisan("craft:command {$class} --template /custom-templates/command.mustache") 73 | ->assertExitCode(0); 74 | 75 | $filename = $this->pathJoin("App", "Console", "Commands", "${class}.php"); 76 | 77 | $this->assertFileExists($filename); 78 | 79 | $this->assertFileContainsString($filename, "class SampleCommand"); 80 | $this->assertFileContainsString($filename, "// custom-command"); 81 | 82 | $this->fs->rmdir("App/Console"); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/Feature/CraftControllerTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 30 | 31 | $this->withoutExceptionHandling(); 32 | } 33 | 34 | /** 35 | * 36 | */ 37 | function tearDown(): void 38 | { 39 | parent::tearDown(); 40 | 41 | $this->fs->rmdir("app/Http"); 42 | } 43 | 44 | /** @test */ 45 | public function should_create_empty_controller() 46 | { 47 | $class = "EmptyController"; 48 | 49 | $this->artisan("craft:controller EmptyController") 50 | ->assertExitCode(0); 51 | 52 | $controllerPath = $this->fs->controller_path(); 53 | $filename = $this->pathJoin($controllerPath, "{$class}.php"); 54 | $this->assertFileExists($filename); 55 | 56 | $this->assertFileContainsString($filename, "class {$class}"); 57 | } 58 | 59 | /** @test */ 60 | public function should_create_invokable_controller() 61 | { 62 | $class = "InvokableController"; 63 | 64 | $this->artisan("craft:controller ${class} --invokable") 65 | ->assertExitCode(0); 66 | 67 | $controllerPath = $this->fs->controller_path(); 68 | $filename = $this->pathJoin($controllerPath, "{$class}.php"); 69 | $this->assertFileExists($filename); 70 | 71 | $this->assertFileContainsString($filename, "public function __invoke"); 72 | } 73 | 74 | /** @test */ 75 | public function should_not_add_any_other_options_when_invokable_controller() 76 | { 77 | $class = "InvokableController"; 78 | 79 | $this->artisan("craft:controller ${class} --invokable --model App/Models/Post --api") 80 | ->assertExitCode(0); 81 | 82 | $controllerPath = $this->fs->controller_path(); 83 | $filename = $this->pathJoin($controllerPath, "{$class}.php"); 84 | $this->assertFileExists($filename); 85 | 86 | $this->assertFileContainsString($filename, "public function __invoke"); 87 | 88 | $this->assertFileNotContainString($filename, "Post"); 89 | } 90 | 91 | /** @test */ 92 | public function should_execute_craft_controller_command() 93 | { 94 | $model = "App/Models/Test"; 95 | $model_path = "App\\Models\\Test"; 96 | 97 | $this->artisan("craft:controller TestController --model {$model}") 98 | ->assertExitCode(0); 99 | 100 | $controllerPath = $this->fs->controller_path(); 101 | $filename = $this->fs->path_join($controllerPath, "TestController.php"); 102 | $this->assertFileExists($filename); 103 | 104 | $parts = explode("/", $model); 105 | $model_name = array_pop($parts); 106 | 107 | // spot check merged data 108 | $data = file_get_contents($filename); 109 | $this->assertStringContainsString("class {$model_name}", $data); 110 | $this->assertStringContainsString("public function index()", $data); 111 | $this->assertStringContainsString("use {$model_path};", $data); 112 | 113 | $this->assertStringContainsString("edit", $data); 114 | $this->assertStringContainsString("create", $data); 115 | } 116 | 117 | /** @test */ 118 | public function should_create_api_controller() 119 | { 120 | $model = "App/Models/Contact"; 121 | $model_path = "App\\Models\\Contact"; 122 | 123 | $this->artisan("craft:controller Api/ContactAPIController --model {$model} --api") 124 | ->assertExitCode(0); 125 | 126 | $controllerPath = $this->fs->controller_path(); 127 | $filename = $this->fs->path_join($controllerPath, "API", "ContactAPIController.php"); 128 | $this->assertFileExists($filename); 129 | 130 | $parts = explode("/", $model); 131 | $model_name = array_pop($parts); 132 | 133 | // spot check merged data 134 | $data = file_get_contents($filename); 135 | 136 | $this->assertStringContainsString("class {$model_name}", $data); 137 | $this->assertStringContainsString("return {$model_name}::all();", $data); 138 | $this->assertStringContainsString("use {$model_path};", $data); 139 | 140 | $this->assertStringNotContainsString("edit", $data); 141 | $this->assertStringNotContainsString("create", $data); 142 | } 143 | 144 | /** @test */ 145 | public function should_create_controller_using_custom_template() 146 | { 147 | $model = "App/Models/Contact"; 148 | 149 | $this->artisan("craft:controller CustomController --model {$model} --template /custom-templates/controller.mustache --overwrite") 150 | ->assertExitCode(0); 151 | 152 | $controllerPath = $this->fs->controller_path(); 153 | $filename = $this->fs->path_join($controllerPath, "CustomController.php"); 154 | $this->assertFileExists($filename); 155 | 156 | // spot check merged data 157 | $data = file_get_contents($filename); 158 | 159 | $this->assertStringContainsString("// custom-controller", $data); 160 | } 161 | 162 | /** @test */ 163 | public function should_create_resource_controller() 164 | { 165 | $this->artisan("craft:controller ResourceController --resource --overwrite") 166 | ->assertExitCode(0); 167 | 168 | $controllerPath = $this->fs->controller_path(); 169 | $filename = $this->fs->path_join($controllerPath, "ResourceController.php"); 170 | $this->assertFileExists($filename); 171 | 172 | // spot check merged data 173 | $data = file_get_contents($filename); 174 | 175 | $this->assertStringContainsString("ResourceController", $data); 176 | $this->assertStringNotContainsString("use Illuminate\Http\Resources\Json\ResourceCollection;", $data); 177 | } 178 | 179 | /** @test */ 180 | public function should_create_resource_controller_with_collection() 181 | { 182 | $this->artisan("craft:controller CustomResource --resource --collection --overwrite") 183 | ->assertExitCode(0); 184 | 185 | $resourcePath = $this->fs->controller_path(); 186 | $filename = $this->fs->path_join($resourcePath, "CustomResource.php"); 187 | $this->assertFileExists($filename); 188 | 189 | // spot check merged data 190 | $data = file_get_contents($filename); 191 | 192 | $this->assertStringContainsString("public function index()", $data); 193 | $this->assertStringContainsString("public function create()", $data); 194 | $this->assertStringContainsString("public function store(Request \$request)", $data); 195 | $this->assertStringContainsString("public function show(\$id)", $data); 196 | $this->assertStringContainsString("public function edit(\$id)", $data); 197 | $this->assertStringContainsString("public function update(Request \$request, \$id)", $data); 198 | $this->assertStringContainsString("public function destroy(\$id)", $data); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /tests/Feature/CraftEventTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 24 | 25 | $this->withoutExceptionHandling(); 26 | } 27 | 28 | /** @test */ 29 | public function should_craft_event_default() 30 | { 31 | $resource = 'MyEvent'; 32 | 33 | $this->artisan("craft:event {$resource} --overwrite") 34 | ->assertExitCode(0); 35 | 36 | // create event 37 | $filename = $this->fs->path_join($this->fs->event_path(), "{$resource}.php"); 38 | 39 | $this->assertFileExists($filename); 40 | 41 | $this->assertFileContainsString($filename, "broadcast"); 42 | 43 | $this->assertFileContainsString($filename, "class {$resource}"); 44 | 45 | $this->fs->delete($filename); 46 | 47 | $this->cleanUp(); 48 | } 49 | 50 | private function cleanUp() 51 | { 52 | $this->fs->rmdir("app/Events"); 53 | } 54 | 55 | /** @test */ 56 | public function should_create_event_using_custom_path(): void 57 | { 58 | $resource = "Products/ProductEvent"; 59 | 60 | $this->artisan("craft:event {$resource} --overwrite") 61 | ->assertExitCode(0); 62 | 63 | // create model 64 | $filename = $this->fs->path_join($this->fs->event_path(), "Products", "ProductEvent.php"); 65 | $this->assertFileExists($filename); 66 | unlink($filename); 67 | 68 | $this->cleanUp(); 69 | } 70 | 71 | /** @test */ 72 | public function should_create_event_skipping_broadcast() 73 | { 74 | $resource = "Product"; 75 | 76 | $this->artisan("craft:event {$resource} --no-broadcast --overwrite") 77 | ->assertExitCode(0); 78 | 79 | $filename = $this->fs->path_join($this->fs->event_path(), "{$resource}.php"); 80 | 81 | $this->assertFileNotContainString($filename, "broadcast"); 82 | 83 | unlink($filename); 84 | 85 | $this->cleanUp(); 86 | } 87 | 88 | /** @test */ 89 | public function should_create_event_listener(): void 90 | { 91 | 92 | $resource = "ProductEvent"; 93 | 94 | $this->artisan("craft:event {$resource} --no-broadcast --listener --overwrite") 95 | ->assertExitCode(0); 96 | 97 | $listenerFilename = $this->fs->path_join($this->fs->listener_path(), "{$resource}Listener.php"); 98 | 99 | $this->assertFileExists($listenerFilename); 100 | 101 | $this->assertFileContainsString($listenerFilename, "class ${resource}Listener"); 102 | 103 | unlink($listenerFilename); 104 | 105 | $this->cleanUp(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /tests/Feature/CraftFactoryTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 30 | 31 | $this->withoutExceptionHandling(); 32 | } 33 | 34 | /** @test */ 35 | public function should_execute_craft_factory_command() 36 | { 37 | $model = "App/Models/Test"; 38 | $model_path = "App\\Models\\Test"; 39 | 40 | $this->artisan("craft:factory TestFactory --model {$model}") 41 | ->assertExitCode(0); 42 | 43 | $factoryPath = $this->fs->factory_path(); 44 | $filename = $this->fs->path_join($factoryPath, "TestFactory.php"); 45 | $this->assertFileExists($filename); 46 | 47 | $this->assertFileContainsString($filename, "use {$model_path};"); 48 | 49 | $this->cleanUp(); 50 | } 51 | 52 | public function cleanUp() 53 | { 54 | $this->fs->rmdir("database"); 55 | } 56 | 57 | /** @test */ 58 | public function should_craft_factory_using_custom_template() 59 | { 60 | $model = "App/Models/Test"; 61 | $model_path = "App\\Models\\Test"; 62 | 63 | $this->artisan("craft:factory TestFactory --model {$model} --template /custom-templates/factory.mustache --overwrite") 64 | ->assertExitCode(0); 65 | 66 | $factoryPath = $this->fs->factory_path(); 67 | $filename = $this->fs->path_join($factoryPath, "TestFactory.php"); 68 | $this->assertFileExists($filename); 69 | 70 | $this->assertFileContainsString($filename, "// custom-factory"); 71 | 72 | $this->cleanUp(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /tests/Feature/CraftListenerTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 24 | 25 | $this->withoutExceptionHandling(); 26 | } 27 | 28 | /** @test */ 29 | public function should_craft_listener_default() 30 | { 31 | $resource = 'MyListener'; 32 | 33 | $this->artisan("craft:listener {$resource} --overwrite") 34 | ->assertExitCode(0); 35 | 36 | // create event 37 | $filename = $this->fs->path_join($this->fs->listener_path(), "{$resource}.php"); 38 | 39 | $this->assertFileExists($filename); 40 | 41 | $this->assertFileContainsString($filename, "class {$resource}"); 42 | 43 | $this->fs->delete($filename); 44 | 45 | $this->cleanUp(); 46 | } 47 | 48 | private function cleanUp() 49 | { 50 | $this->fs->rmdir("app/Listeners"); 51 | } 52 | 53 | /** @test */ 54 | public function should_create_listener_with_event() 55 | { 56 | $resource = "MyListener"; 57 | 58 | $this->artisan("craft:listener {$resource} --event MyEvent --overwrite") 59 | ->assertExitCode(0); 60 | 61 | $filename = $this->fs->path_join($this->fs->listener_path(), "{$resource}.php"); 62 | 63 | $this->assertFileContainsString($filename, "use App\Events\MyEvent"); 64 | $this->assertFileContainsString($filename, "public function handle(MyEvent"); 65 | 66 | unlink($filename); 67 | 68 | $this->cleanUp(); 69 | } 70 | 71 | /** @test */ 72 | public function should_craft_listener_using_custom_template() 73 | { 74 | $this->artisan("craft:listener TestListener --template /custom-templates/listener.mustache --overwrite") 75 | ->assertExitCode(0); 76 | 77 | $filename = $this->fs->path_join($this->fs->listener_path(), "TestListener.php"); 78 | $this->assertFileExists($filename); 79 | 80 | $this->assertFileContainsString($filename, "// custom-listener"); 81 | 82 | $this->cleanUp(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/Feature/CraftProviderTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 30 | 31 | $this->withoutExceptionHandling(); 32 | } 33 | 34 | /** @test */ 35 | public function should_create_simple_provider_command() 36 | { 37 | $class = "TestServiceProvider"; 38 | 39 | $this->artisan("craft:provider {$class} --overwrite") 40 | ->assertExitCode(0); 41 | 42 | $filename = $this->pathJoin($this->fs->provider_path(), "${class}.php"); 43 | 44 | $this->assertFileExists($filename); 45 | 46 | $this->assertFileContainsString($filename, "class {$class}"); 47 | 48 | unlink($filename); 49 | } 50 | 51 | /** @test */ 52 | public function should_return_error_when_filename_exists() 53 | { 54 | $class = "TestServiceProvider"; 55 | 56 | $this->artisan("craft:provider {$class}"); 57 | 58 | $this->artisan("craft:provider {$class}") 59 | ->assertExitCode(-1); 60 | 61 | $filename = $this->pathJoin($this->fs->provider_path(), "${class}.php"); 62 | 63 | unlink($filename); 64 | } 65 | 66 | /** @test */ 67 | public function should_create_provider_with_namespace() 68 | { 69 | $path = "App/MyProviders"; 70 | $class = "TestProvider"; 71 | 72 | $this->artisan("craft:provider ${path}/{$class} --overwrite") 73 | ->assertExitCode(0); 74 | 75 | $filename = $this->pathJoin($path, "${class}.php"); 76 | 77 | $this->assertFileExists($filename); 78 | 79 | $this->assertFileContainsString($filename, "namespace App\\MyProviders;"); 80 | $this->assertFileContainsString($filename, "class TestProvider"); 81 | 82 | unlink($filename); 83 | 84 | $this->fs->rmdir("app/MyProviders"); 85 | } 86 | 87 | /** @test */ 88 | public function should_create_provider_using_user_template() 89 | { 90 | $class = "CustomTestProvider"; 91 | 92 | $this->artisan("craft:provider {$class} --template /custom-templates/provider.mustache --overwrite") 93 | ->assertExitCode(0); 94 | 95 | $filename = $this->pathJoin($this->fs->provider_path(), "${class}.php"); 96 | $this->assertFileExists($filename); 97 | 98 | $this->assertFileContainsString($filename, "class ${class}"); 99 | $this->assertFileContainsString($filename, "// custom-provider"); 100 | 101 | unlink($filename); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /tests/Feature/CraftRequestTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 30 | 31 | $this->withoutExceptionHandling(); 32 | } 33 | 34 | public function tearDown(): void 35 | { 36 | parent::tearDown(); 37 | 38 | $this->fs->rmdir("app/Http"); 39 | } 40 | 41 | /** @test */ 42 | public function should_create_request_command() 43 | { 44 | $class = 'TestFormRequest'; 45 | 46 | $this->artisan("craft:request {$class}") 47 | ->assertExitCode(0); 48 | 49 | $filename = $this->pathJoin('app', 'Http', 'Requests', 'TestFormRequest.php'); 50 | $this->assertFileExists($filename); 51 | 52 | $this->assertFileContainsString($filename, "class {$class}"); 53 | } 54 | 55 | /** @test */ 56 | public function should_create_class_using_user_template() 57 | { 58 | $class = 'App/Http/Requests/TestFormRequest'; 59 | 60 | $this->artisan("craft:request {$class} --template /custom-templates/request.mustache") 61 | ->assertExitCode(0); 62 | 63 | $filename = $this->pathJoin('app', 'Http', 'Requests', 'TestFormRequest.php'); 64 | $this->assertFileExists($filename); 65 | 66 | $this->assertFileContainsString($filename, 'class TestFormRequest'); 67 | $this->assertFileContainsString($filename, 'public function rules()'); 68 | } 69 | 70 | /** @test */ 71 | public function should_return_error_when_filename_exists() 72 | { 73 | $class = 'TestFormRequest'; 74 | 75 | $this->artisan("craft:request {$class}"); 76 | 77 | $this->artisan("craft:request {$class}") 78 | ->assertExitCode(-1); 79 | 80 | $filename = $this->pathJoin($this->fs->request_path(), "${class}.php"); 81 | 82 | unlink($filename); 83 | } 84 | 85 | /** @test */ 86 | public function should_return_error_if_already_exists() 87 | { 88 | $class = 'TestFormRequest'; 89 | 90 | $data = [ 91 | 'name' => $class, 92 | ]; 93 | 94 | // create file twice to force error 95 | $this->fs->createFile('request', $class, $data); 96 | $result = $this->fs->createFile('request', $class, $data); 97 | 98 | $this->assertStringContainsString('already exists', $result['message']); 99 | 100 | // $this->assertFileExists($result); 101 | // $this->assertFileExists($result['filename']); 102 | } 103 | 104 | /** @test */ 105 | public function should_create_resource_with_supplied_rules() 106 | { 107 | $class = 'TestFormRequest'; 108 | 109 | $this->artisan("craft:request {$class} --rules title?required|unique:posts|max:255,body?required") 110 | ->assertExitCode(0); 111 | 112 | $filename = $this->pathJoin('app', 'Http', 'Requests', 'TestFormRequest.php'); 113 | $this->assertFileExists($filename); 114 | 115 | $this->assertFileContainsString($filename, 'class TestFormRequest'); 116 | 117 | $this->assertFileContainsString($filename, '"title" => "required|unique:posts|max:255"'); 118 | $this->assertFileContainsString($filename, '"body" => "required"'); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /tests/Feature/CraftResourceTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 30 | 31 | $this->withoutExceptionHandling(); 32 | } 33 | 34 | /** 35 | * 36 | */ 37 | function tearDown(): void 38 | { 39 | parent::tearDown(); 40 | 41 | $this->fs->rmdir("app/Http"); 42 | } 43 | 44 | /** @test */ 45 | public function should_create_resource_controller() 46 | { 47 | $this->artisan("craft:resource CustomResource --overwrite") 48 | ->assertExitCode(0); 49 | 50 | $controllerPath = $this->fs->resource_path(); 51 | $filename = $this->fs->path_join($controllerPath, "CustomResource.php"); 52 | $this->assertFileExists($filename); 53 | 54 | // spot check merged data 55 | $data = file_get_contents($filename); 56 | 57 | $this->assertStringContainsString("CustomResource", $data); 58 | $this->assertStringNotContainsString("use Illuminate\Http\Resources\Json\ResourceCollection;", $data); 59 | 60 | $this->fs->rmdir("app/Http/Resources"); 61 | } 62 | 63 | /** @test */ 64 | public function should_create_resource_controller_with_collection() 65 | { 66 | $this->artisan("craft:resource CustomResource --collection --overwrite") 67 | ->assertExitCode(0); 68 | 69 | $controllerPath = $this->fs->resource_path(); 70 | $filename = $this->fs->path_join($controllerPath, "CustomResource.php"); 71 | $this->assertFileExists($filename); 72 | 73 | // spot check merged data 74 | $data = file_get_contents($filename); 75 | 76 | $this->assertStringContainsString("use Illuminate\Http\Resources\Json\ResourceCollection;", $data); 77 | 78 | $this->fs->rmdir("app/Http/Resources"); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tests/Feature/CraftRouteModelBindingTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 30 | 31 | $this->withoutExceptionHandling(); 32 | } 33 | 34 | /** 35 | * 36 | */ 37 | function tearDown(): void 38 | { 39 | parent::tearDown(); 40 | 41 | $this->fs->rmdir("app/Http"); 42 | } 43 | 44 | /** @test */ 45 | public function should_create_route_model_binding_controller() 46 | { 47 | $class = "PostRouteController"; 48 | 49 | $this->artisan("craft:controller {$class} --binding --model App/Models/Post --overwrite") 50 | ->assertExitCode(0); 51 | 52 | $controllerPath = $this->fs->controller_path(); 53 | $filename = $this->pathJoin($controllerPath, "{$class}.php"); 54 | $this->assertFileExists($filename); 55 | 56 | $this->assertFileContainsString($filename, "class {$class}"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Feature/CraftRuleTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 24 | 25 | $this->withoutExceptionHandling(); 26 | } 27 | 28 | /** @test */ 29 | public function should_craft_rule_default() 30 | { 31 | $resource = 'MyRule'; 32 | 33 | $this->artisan("craft:rule {$resource} --overwrite") 34 | ->assertExitCode(0); 35 | 36 | // create event 37 | $filename = $this->fs->path_join($this->fs->rule_path(), "{$resource}.php"); 38 | 39 | $this->assertFileExists($filename); 40 | 41 | $this->assertFileContainsString($filename, "class {$resource}"); 42 | 43 | $this->fs->delete($filename); 44 | 45 | $this->cleanUp(); 46 | } 47 | 48 | private function cleanUp() 49 | { 50 | $this->fs->rmdir("app/Rules"); 51 | } 52 | 53 | /** @test */ 54 | public function should_craft_listener_using_custom_template() 55 | { 56 | $this->artisan("craft:rule TestRule --template /custom-templates/rule.mustache --overwrite") 57 | ->assertExitCode(0); 58 | 59 | $filename = $this->fs->path_join($this->fs->rule_path(), "TestRule.php"); 60 | $this->assertFileExists($filename); 61 | 62 | $this->assertFileContainsString($filename, "// custom-rule"); 63 | 64 | $this->cleanUp(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/Feature/CraftSeedTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 30 | 31 | $this->withoutExceptionHandling(); 32 | } 33 | 34 | /** @test */ 35 | public function should_execute_craft_seed_command() 36 | { 37 | $class = "TestsTableSeeder"; 38 | $model = "Test"; 39 | $rows = "25"; 40 | 41 | $this->artisan("craft:seed TestsTableSeeder --model App/Models/{$model} --rows {$rows}") 42 | ->assertExitCode(0); 43 | 44 | $seedPath = $this->fs->seed_path(); 45 | $filename = $this->pathJoin($seedPath, "{$class}.php"); 46 | 47 | $this->assertFileContainsString($filename, "class {$class} extends Seeder"); 48 | $this->assertFileContainsString($filename, "factory({$model}::class,{$rows})->create();"); 49 | 50 | $this->cleanUp(); 51 | } 52 | 53 | public function should_craft_seed_using_custom_template_error() 54 | { 55 | $this->markTestIncomplete("debugging to find reasy for error when checking `testMethod` "); 56 | 57 | $class = "TestsTableSeeder"; 58 | 59 | $this->artisan("craft:seed TestsTableSeeder --template /templates/custom.mustache --overwrite") 60 | ->assertExitCode(0); 61 | 62 | $seedPath = $this->fs->seed_path(); 63 | $filename = $this->pathJoin($seedPath, "{$class}.php"); 64 | var_dump($filename); 65 | 66 | // $this->assertFileContainsString($filename, "testMethod"); 67 | 68 | $this->cleanUp(); 69 | } 70 | 71 | public function cleanUp() 72 | { 73 | $this->fs->rmdir("database"); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/Feature/CraftTestTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 29 | // $this->withoutExceptionHandling(); 30 | } 31 | 32 | /** 33 | * 34 | */ 35 | function tearDown(): void 36 | { 37 | parent::tearDown(); 38 | } 39 | 40 | /** @test */ 41 | public function should_create_simple_test_command() 42 | { 43 | $class = "ExampleFeatureTest"; 44 | 45 | $this->artisan("craft:test {$class}") 46 | ->assertExitCode(0); 47 | 48 | $filename = $this->fs->path_join("tests", "Feature", "{$class}.php"); 49 | 50 | $this->assertFileExists($filename); 51 | 52 | $this->assertFileContainsString($filename, "class ${class}"); 53 | 54 | unlink($filename); 55 | } 56 | 57 | /** @test */ 58 | public function should_create_unit_test_command() 59 | { 60 | $class = "ExampleUnitTest"; 61 | 62 | $this->artisan("craft:test {$class} --unit") 63 | ->assertExitCode(0); 64 | 65 | $filename = $this->pathJoin("tests", "Unit", "{$class}.php"); 66 | 67 | $this->assertFileExists($filename); 68 | 69 | $this->assertFileContainsString($filename, "class ${class}"); 70 | $this->assertFileContainsString($filename, "namespace App\Unit;"); 71 | 72 | unlink($filename); 73 | } 74 | 75 | /** @test */ 76 | public function should_create_unit_test_with_setup_command() 77 | { 78 | $class = "ExampleUnitSetupTest"; 79 | 80 | $this->artisan("craft:test {$class} --unit --setup") 81 | ->assertExitCode(0); 82 | 83 | $filename = $this->fs->path_join("tests", "Unit", "{$class}.php"); 84 | 85 | $this->assertFileExists($filename); 86 | 87 | $data = file_get_contents($filename); 88 | $this->assertStringContainsString("function setUp()", $data); 89 | 90 | unlink($filename); 91 | } 92 | 93 | /** @test */ 94 | public function should_create_unit_test_with_teardown_command() 95 | { 96 | $class = "ExampleUnitTeardownTest"; 97 | 98 | $this->artisan("craft:test {$class} --unit --teardown") 99 | ->assertExitCode(0); 100 | 101 | $filename = $this->fs->path_join("tests", "Unit", "{$class}.php"); 102 | 103 | $this->assertFileExists($filename); 104 | 105 | $data = file_get_contents($filename); 106 | $this->assertStringContainsString("function tearDown()", $data); 107 | 108 | unlink($filename); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /tests/Feature/CraftViewsTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 30 | 31 | $this->withoutExceptionHandling(); 32 | } 33 | 34 | /** @test */ 35 | public function should_craft_create_view() 36 | { 37 | $resource = "contacts"; 38 | 39 | $this->artisan("craft:views {$resource} --extends partials.master --section content --no-index --no-edit --no-show") 40 | ->assertExitCode(0); 41 | 42 | $filename = $this->pathJoin("resources", "views", $resource, "create.blade.php"); 43 | 44 | $this->assertFileExists($filename); 45 | 46 | $this->assertFileContainsString($filename, "View Create"); 47 | $this->assertFileContainsString($filename, "@section('content')"); 48 | 49 | $this->fs->rmdir("resources/views/{$resource}"); 50 | } 51 | 52 | /** @test */ 53 | public function should_craft_edit_view() 54 | { 55 | $resource = "contacts"; 56 | 57 | $this->artisan("craft:views {$resource} --extends partials.master --section content --no-index --no-create --no-show") 58 | ->assertExitCode(0); 59 | 60 | $filename = $this->fs->path_join("resources", "views", $resource, "edit.blade.php"); 61 | 62 | $this->assertFileExists($filename); 63 | 64 | $this->assertFileContainsString($filename, "View Edit"); 65 | $this->assertFileContainsString($filename, "@section('content')"); 66 | 67 | $this->fs->rmdir("resources/views/{$resource}"); 68 | } 69 | 70 | /** @test */ 71 | public function should_craft_index_view() 72 | { 73 | $resource = "contacts"; 74 | 75 | $this->artisan("craft:views {$resource} --no-edit --no-create --no-show") 76 | ->assertExitCode(0); 77 | 78 | $filename = $this->fs->path_join("resources", "views", $resource, "index.blade.php"); 79 | 80 | $this->assertFileExists($filename); 81 | 82 | $this->assertFileContainsString($filename, "View Index"); 83 | 84 | $this->assertFileNotContainString($filename, "@section"); 85 | $this->assertFileNotContainString($filename, "@extends"); 86 | 87 | $this->fs->rmdir("resources/views/{$resource}"); 88 | 89 | } 90 | 91 | /** @test */ 92 | public function should_craft_show_view() 93 | { 94 | $resource = "contacts"; 95 | 96 | $this->artisan("craft:views {$resource} --extends partials.master --no-index --no-create --no-edit") 97 | ->assertExitCode(0); 98 | 99 | $filename = $this->pathJoin("resources", "views", $resource, "show.blade.php"); 100 | 101 | $this->assertFileExists($filename); 102 | 103 | $this->assertFileContainsString($filename, "View Show"); 104 | $this->assertFileContainsString($filename, "@extends('partials.master')"); 105 | $this->assertFileNotContainString($filename, "@section('content')"); 106 | 107 | $this->fs->rmdir("resources/views/{$resource}"); 108 | 109 | } 110 | 111 | /** @test */ 112 | public function should_create_all_views() 113 | { 114 | $resource = "customers"; 115 | 116 | $this->artisan("craft:views {$resource}") 117 | ->assertExitCode(0); 118 | 119 | $indexFilename = $this->fs->path_join("resources", "views", $resource, "index.blade.php"); 120 | $createFilename = $this->fs->path_join("resources", "views", $resource, "create.blade.php"); 121 | $editFilename = $this->fs->path_join("resources", "views", $resource, "edit.blade.php"); 122 | $showFilename = $this->fs->path_join("resources", "views", $resource, "show.blade.php"); 123 | 124 | $this->assertFileExists($createFilename); 125 | $this->assertFileExists($indexFilename); 126 | $this->assertFileExists($editFilename); 127 | $this->assertFileExists($showFilename); 128 | 129 | $this->fs->rmdir("resources/views/{$resource}"); 130 | 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /tests/Feature/VerifyTemplatesTest.php: -------------------------------------------------------------------------------- 1 | fs = new CraftsmanFileSystem(); 27 | 28 | $this->withoutExceptionHandling(); 29 | } 30 | 31 | /** @test */ 32 | public function should_verify_class_template_exists() 33 | { 34 | $filename = $this->fs->getTemplateFilename("class"); 35 | 36 | $this->assertFileExists($filename); 37 | } 38 | 39 | /** @test */ 40 | public function should_verify_controller_template_exists() 41 | { 42 | $filename = $this->fs->getTemplateFilename("controller"); 43 | 44 | $this->assertFileExists($filename); 45 | } 46 | 47 | /** @test */ 48 | public function should_verify_empty_controller_template_exists() 49 | { 50 | $filename = $this->fs->getTemplateFilename("empty-controller"); 51 | 52 | $this->assertFileExists($filename); 53 | } 54 | 55 | /** @test */ 56 | public function should_verify_api_controller_template_exists() 57 | { 58 | $filename = $this->fs->getTemplateFilename("api-controller"); 59 | 60 | $this->assertFileExists($filename); 61 | } 62 | 63 | /** @test */ 64 | public function should_verify_factory_template_exists() 65 | { 66 | $filename = $this->fs->getTemplateFilename("factory"); 67 | 68 | $this->assertFileExists($filename); 69 | } 70 | 71 | /** @test */ 72 | public function should_verify_migration_template_exists() 73 | { 74 | $filename = $this->fs->getTemplateFilename("migration"); 75 | 76 | $this->assertFileExists($filename); 77 | } 78 | 79 | /** @test */ 80 | public function should_verify_model_template_exists() 81 | { 82 | $filename = $this->fs->getTemplateFilename("model"); 83 | 84 | $this->assertFileExists($filename); 85 | } 86 | 87 | /** @test */ 88 | public function should_verify_seed_template_exists() 89 | { 90 | $filename = $this->fs->getTemplateFilename("seed"); 91 | 92 | $this->assertFileExists($filename); 93 | } 94 | 95 | /** @test */ 96 | public function should_verify_test_template_exists() 97 | { 98 | $filename = $this->fs->getTemplateFilename("test"); 99 | 100 | $this->assertFileExists($filename); 101 | } 102 | 103 | /** @test */ 104 | public function should_verify_view_create_template_exists() 105 | { 106 | $filename = $this->fs->getTemplateFilename("view-create"); 107 | 108 | $this->assertFileExists($filename); 109 | } 110 | 111 | /** @test */ 112 | public function should_verify_view_edit_template_exists() 113 | { 114 | $filename = $this->fs->getTemplateFilename("view-edit"); 115 | 116 | $this->assertFileExists($filename); 117 | } 118 | 119 | /** @test */ 120 | public function should_verify_view_index_template_exists() 121 | { 122 | $filename = $this->fs->getTemplateFilename("view-index"); 123 | 124 | $this->assertFileExists($filename); 125 | } 126 | 127 | /** @test */ 128 | public function should_verify_view_show_template_exists() 129 | { 130 | $filename = $this->fs->getTemplateFilename("view-show"); 131 | 132 | $this->assertFileExists($filename); 133 | } 134 | 135 | public function should_use_custom_template_overriding_default() 136 | { 137 | $filename = $this->fs->getTemplateFilename("/custom-templates/view.mustache"); 138 | $this->assertFileExists($filename); 139 | 140 | $this->assertFileContainsString($filename, "

Custom View Template

"); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | "Mike Erickson Test", 5 | "full" => [ 6 | "fname" => "Mike", 7 | "lname" => "Erickson", 8 | ], 9 | "migrations" => [ 10 | "useCurrentDefault" => true, 11 | ], 12 | ]; 13 | --------------------------------------------------------------------------------