├── LICENSE.md ├── composer.json ├── config └── essentials.php ├── pint.json ├── src ├── Commands │ ├── EssentialsPintCommand.php │ ├── EssentialsRectorCommand.php │ └── MakeActionCommand.php ├── Configurables │ ├── AggressivePrefetching.php │ ├── AutomaticallyEagerLoadRelationships.php │ ├── FakeSleep.php │ ├── ForceScheme.php │ ├── ImmutableDates.php │ ├── PreventStrayRequests.php │ ├── ProhibitDestructiveCommands.php │ ├── SetDefaultPassword.php │ ├── ShouldBeStrict.php │ └── Unguard.php ├── Contracts │ └── Configurable.php └── EssentialsServiceProvider.php ├── stubs ├── action.stub ├── pint.stub └── rector.stub └── testbench.yaml /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Nuno Maduro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nunomaduro/essentials", 3 | "description": "Just better defaults for your Laravel projects.", 4 | "keywords": ["php", "laravel", "package", "essentials", "laravel-essentials"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Nuno Maduro", 9 | "email": "enunomaduro@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": "^8.3.0", 14 | "laravel/framework": "^11.44.2|^12.17.0" 15 | }, 16 | "require-dev": { 17 | "laravel/pint": "^1.22.1", 18 | "orchestra/testbench": "^10.3.0", 19 | "pestphp/pest": "^3.8.2", 20 | "pestphp/pest-plugin-type-coverage": "^3.5.1", 21 | "phpstan/phpstan": "^2.1.17", 22 | "rector/rector": "^2.0.17", 23 | "symfony/var-dumper": "^7.3.0" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "NunoMaduro\\Essentials\\": "src/" 28 | } 29 | }, 30 | "autoload-dev": { 31 | "psr-4": { 32 | "Tests\\": "tests/" 33 | } 34 | }, 35 | "minimum-stability": "stable", 36 | "prefer-stable": true, 37 | "config": { 38 | "sort-packages": true, 39 | "preferred-install": "dist", 40 | "allow-plugins": { 41 | "pestphp/pest-plugin": true 42 | } 43 | }, 44 | "scripts": { 45 | "refactor": "rector", 46 | "lint": "pint", 47 | "test:refactor": "rector --dry-run", 48 | "test:lint": "pint --test", 49 | "test:types": "phpstan analyse --ansi", 50 | "test:unit": "pest --colors=always --coverage --parallel", 51 | "test": [ 52 | "@test:refactor", 53 | "@test:lint", 54 | "@test:types", 55 | "@test:unit" 56 | ] 57 | }, 58 | "extra": { 59 | "laravel": { 60 | "providers": [ 61 | "NunoMaduro\\Essentials\\EssentialsServiceProvider" 62 | ] 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /config/essentials.php: -------------------------------------------------------------------------------- 1 | true, 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Automatically Eager Load Relationships 24 | |-------------------------------------------------------------------------- 25 | | 26 | | This option allows you to automatically eagerly load relationships 27 | | for your models. It reduces N+1 query issues and improves 28 | | performance without needing with() everywhere. 29 | | 30 | | Enabled by default. 31 | | 32 | | Note: This option is only available in Laravel 12.8 and above. 33 | | 34 | */ 35 | 36 | NunoMaduro\Essentials\Configurables\AutomaticallyEagerLoadRelationships::class => true, 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Fake Sleep when running tests 41 | |-------------------------------------------------------------------------- 42 | | 43 | | This option allows you to fake sleep when running tests. When 44 | | enabled, the framework will fake sleep for all tests. 45 | | 46 | | Enabled by default. 47 | | 48 | */ 49 | 50 | NunoMaduro\Essentials\Configurables\FakeSleep::class => true, 51 | 52 | /* 53 | |-------------------------------------------------------------------------- 54 | | Force HTTPS scheme 55 | |-------------------------------------------------------------------------- 56 | | 57 | | This option allows you to force the HTTPS scheme for your 58 | | application. When enabled, all URLs generated by the Laravel 59 | | will use the HTTPS scheme. 60 | | 61 | | Enabled by default. 62 | | 63 | */ 64 | 65 | NunoMaduro\Essentials\Configurables\ForceScheme::class => true, 66 | 67 | /* 68 | |-------------------------------------------------------------------------- 69 | | Immutable Carbon Dates 70 | |-------------------------------------------------------------------------- 71 | | 72 | | This option allows you to make all Carbon dates immutable in 73 | | your application. When enabled, all date functions will 74 | | return CarbonImmutable instances instead of Carbon. 75 | | 76 | | Enabled by default. 77 | | 78 | */ 79 | 80 | NunoMaduro\Essentials\Configurables\ImmutableDates::class => true, 81 | 82 | /* 83 | |-------------------------------------------------------------------------- 84 | | Prevent Stray Requests when running tests 85 | |-------------------------------------------------------------------------- 86 | | 87 | | This option allows you to prevent stray requests when running 88 | | tests. When enabled, the framework will prevent requests 89 | | from being sent during tests unless faked. 90 | | 91 | | Enabled by default. 92 | | 93 | */ 94 | 95 | NunoMaduro\Essentials\Configurables\PreventStrayRequests::class => true, 96 | 97 | /* 98 | |-------------------------------------------------------------------------- 99 | | Prohibit Destructive Commands 100 | |-------------------------------------------------------------------------- 101 | | 102 | | This option allows you to prohibit destructive commands 103 | | from being run in your application. When enabled, the 104 | | framework will prevent commands that could potentially 105 | | destroy data from being run in your application. 106 | | 107 | | Enabled by default. 108 | | 109 | */ 110 | 111 | NunoMaduro\Essentials\Configurables\ProhibitDestructiveCommands::class => true, 112 | 113 | /* 114 | |-------------------------------------------------------------------------- 115 | | Set Default Password complexity 116 | |-------------------------------------------------------------------------- 117 | | 118 | | This option sets the default password complexity for your 119 | | application to be at least 12 characters long, maximum 120 | | 255 characters long, and not compromised. 121 | | 122 | | Enabled by default. 123 | | 124 | */ 125 | 126 | NunoMaduro\Essentials\Configurables\SetDefaultPassword::class => true, 127 | 128 | /* 129 | |-------------------------------------------------------------------------- 130 | | Model should be strict 131 | |-------------------------------------------------------------------------- 132 | | 133 | | This option allows you to enable strict mode for your 134 | | application. It will prevent lazy loading, silently discarding 135 | | attributes and prevents accessing missing attributes. 136 | | 137 | | Enabled by default. 138 | | 139 | */ 140 | 141 | NunoMaduro\Essentials\Configurables\ShouldBeStrict::class => true, 142 | 143 | /* 144 | |-------------------------------------------------------------------------- 145 | | Unguard models 146 | |-------------------------------------------------------------------------- 147 | | 148 | | This option allows you to enable unguard mode for your 149 | | models. When enabled, the framework will unguard 150 | | all models, allowing you to mass assign any attributes. 151 | | 152 | | Disabled by default. 153 | | 154 | */ 155 | 156 | NunoMaduro\Essentials\Configurables\Unguard::class => false, 157 | 158 | ]; 159 | -------------------------------------------------------------------------------- /pint.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "laravel", 3 | "notPath": [ 4 | "tests/TestCase.php" 5 | ], 6 | "rules": { 7 | "array_push": true, 8 | "backtick_to_shell_exec": true, 9 | "date_time_immutable": true, 10 | "declare_strict_types": true, 11 | "lowercase_keywords": true, 12 | "lowercase_static_reference": true, 13 | "final_class": true, 14 | "final_internal_class": true, 15 | "final_public_method_for_abstract_class": true, 16 | "fully_qualified_strict_types": true, 17 | "global_namespace_import": { 18 | "import_classes": true, 19 | "import_constants": true, 20 | "import_functions": true 21 | }, 22 | "mb_str_functions": true, 23 | "modernize_types_casting": true, 24 | "new_with_parentheses": false, 25 | "no_superfluous_elseif": true, 26 | "no_useless_else": true, 27 | "no_multiple_statements_per_line": true, 28 | "ordered_class_elements": { 29 | "order": [ 30 | "use_trait", 31 | "case", 32 | "constant", 33 | "constant_public", 34 | "constant_protected", 35 | "constant_private", 36 | "property_public", 37 | "property_protected", 38 | "property_private", 39 | "construct", 40 | "destruct", 41 | "magic", 42 | "phpunit", 43 | "method_abstract", 44 | "method_public_static", 45 | "method_public", 46 | "method_protected_static", 47 | "method_protected", 48 | "method_private_static", 49 | "method_private" 50 | ], 51 | "sort_algorithm": "none" 52 | }, 53 | "ordered_interfaces": true, 54 | "ordered_traits": true, 55 | "protected_to_private": true, 56 | "self_accessor": true, 57 | "self_static_accessor": true, 58 | "strict_comparison": true, 59 | "visibility_required": true 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Commands/EssentialsPintCommand.php: -------------------------------------------------------------------------------- 1 | option('force') && ! $this->components->confirm('Do you wish to publish the Pint configuration file? This will override the existing [pint.json] file.', true)) { 33 | return 0; 34 | } 35 | 36 | $stub_path = __DIR__.'/../../stubs/pint.stub'; 37 | $destination_path = base_path('pint.json'); 38 | 39 | if (! file_exists($stub_path)) { 40 | $this->components->error('Pint configuration stub file not found.'); 41 | 42 | return 1; 43 | } 44 | 45 | if (file_exists($destination_path) && $this->option('backup')) { 46 | copy($destination_path, $destination_path.'.backup'); 47 | $this->components->info('Backup created at: '.$destination_path.'.backup'); 48 | } 49 | 50 | $this->components->info('Publishing Pint configuration file...'); 51 | 52 | if (! copy($stub_path, $destination_path)) { 53 | $this->components->error('Failed to publish the Pint configuration file.'); 54 | 55 | return 1; 56 | } 57 | 58 | $this->components->info('Pint configuration file published successfully at: '.$destination_path); 59 | 60 | return 0; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Commands/EssentialsRectorCommand.php: -------------------------------------------------------------------------------- 1 | option('force') && ! $this->components->confirm('Do you wish to publish the Rector configuration file? This will override the existing [rector.php] file.', true)) { 33 | return 0; 34 | } 35 | 36 | $stub_path = __DIR__.'/../../stubs/rector.stub'; 37 | $destination_path = base_path('rector.php'); 38 | 39 | if (! file_exists($stub_path)) { 40 | $this->components->error('Rector configuration stub file not found.'); 41 | 42 | return 1; 43 | } 44 | 45 | if (file_exists($destination_path) && $this->option('backup')) { 46 | copy($destination_path, $destination_path.'.backup'); 47 | $this->components->info('Backup created at: '.$destination_path.'.backup'); 48 | } 49 | 50 | $this->components->info('Publishing Rector configuration file...'); 51 | 52 | if (! copy($stub_path, $destination_path)) { 53 | $this->components->error('Failed to publish the Rector configuration file.'); 54 | 55 | return 1; 56 | } 57 | 58 | $this->components->info('Rector configuration file published successfully at: '.$destination_path); 59 | 60 | return 0; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Commands/MakeActionCommand.php: -------------------------------------------------------------------------------- 1 | alreadyExists($this->getNameInput())) { 42 | $this->error($this->type.' already exists!'); 43 | 44 | return 1; 45 | } 46 | 47 | return parent::handle(); 48 | } 49 | 50 | /** 51 | * Get the name input. 52 | */ 53 | protected function getNameInput(): string 54 | { 55 | /** @var string $name */ 56 | $name = $this->argument('name'); 57 | 58 | return Str::of(mb_trim($name)) 59 | ->replaceEnd('.php', '') 60 | ->replaceEnd('Action', '') 61 | ->append('Action') 62 | ->toString(); 63 | } 64 | 65 | /** 66 | * Get the stub file for the generator. 67 | */ 68 | protected function getStub(): string 69 | { 70 | return $this->resolveStubPath('/stubs/action.stub'); 71 | } 72 | 73 | /** 74 | * Get the default namespace for the class. 75 | */ 76 | protected function getDefaultNamespace($rootNamespace): string 77 | { 78 | return $rootNamespace.'\Actions'; 79 | } 80 | 81 | /** 82 | * Get the destination class path. 83 | */ 84 | protected function getPath($name): string 85 | { 86 | $name = Str::replaceFirst($this->rootNamespace(), '', $name); 87 | 88 | // Use the app_path helper to get the correct path 89 | return app_path(str_replace('\\', '/', $name).'.php'); 90 | } 91 | 92 | /** 93 | * Resolve the fully-qualified path to the stub. 94 | */ 95 | private function resolveStubPath(string $stub): string 96 | { 97 | $basePath = $this->laravel->basePath(mb_trim($stub, '/')); 98 | 99 | return file_exists($basePath) 100 | ? $basePath 101 | : __DIR__.'/../../'.$stub; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Configurables/AggressivePrefetching.php: -------------------------------------------------------------------------------- 1 | boolean(sprintf('essentials.%s', self::class), true); 18 | } 19 | 20 | /** 21 | * Run the configurable. 22 | */ 23 | public function configure(): void 24 | { 25 | Vite::useAggressivePrefetching(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Configurables/AutomaticallyEagerLoadRelationships.php: -------------------------------------------------------------------------------- 1 | boolean(sprintf('essentials.%s', self::class), true); 22 | } 23 | 24 | /** 25 | * Run the configurable. 26 | */ 27 | public function configure(): void 28 | { 29 | if (! method_exists($this->modelClass, 'automaticallyEagerLoadRelationships')) { 30 | return; 31 | } 32 | 33 | Model::automaticallyEagerLoadRelationships(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Configurables/FakeSleep.php: -------------------------------------------------------------------------------- 1 | boolean(sprintf('essentials.%s', self::class), true); 18 | $testing = app()->runningUnitTests(); 19 | 20 | return $enabled && $testing; 21 | } 22 | 23 | /** 24 | * Run the configurable. 25 | */ 26 | public function configure(): void 27 | { 28 | Sleep::fake(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Configurables/ForceScheme.php: -------------------------------------------------------------------------------- 1 | boolean(sprintf('essentials.%s', self::class), true); 18 | } 19 | 20 | /** 21 | * Run the configurable. 22 | */ 23 | public function configure(): void 24 | { 25 | URL::forceHttps(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Configurables/ImmutableDates.php: -------------------------------------------------------------------------------- 1 | boolean(sprintf('essentials.%s', self::class), true); 19 | } 20 | 21 | /** 22 | * Run the configurable. 23 | */ 24 | public function configure(): void 25 | { 26 | Date::use(CarbonImmutable::class); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Configurables/PreventStrayRequests.php: -------------------------------------------------------------------------------- 1 | boolean(sprintf('essentials.%s', self::class), true); 18 | $testing = app()->runningUnitTests(); 19 | 20 | return $enabled && $testing; 21 | } 22 | 23 | /** 24 | * Run the configurable. 25 | */ 26 | public function configure(): void 27 | { 28 | Http::preventStrayRequests(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Configurables/ProhibitDestructiveCommands.php: -------------------------------------------------------------------------------- 1 | boolean(sprintf('essentials.%s', self::class), true); 18 | } 19 | 20 | /** 21 | * Run the configurable. 22 | */ 23 | public function configure(): void 24 | { 25 | DB::prohibitDestructiveCommands( 26 | app()->isProduction(), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Configurables/SetDefaultPassword.php: -------------------------------------------------------------------------------- 1 | boolean(sprintf('essentials.%s', self::class), true); 18 | } 19 | 20 | /** 21 | * {@inheritDoc} 22 | */ 23 | public function configure(): void 24 | { 25 | Password::defaults(fn (): ?Password => app()->isProduction() ? Password::min(12)->max(255)->uncompromised() : null); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Configurables/ShouldBeStrict.php: -------------------------------------------------------------------------------- 1 | boolean(sprintf('essentials.%s', self::class), true); 18 | } 19 | 20 | /** 21 | * Run the configurable. 22 | */ 23 | public function configure(): void 24 | { 25 | Model::shouldBeStrict(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Configurables/Unguard.php: -------------------------------------------------------------------------------- 1 | boolean(sprintf('essentials.%s', self::class), false); 18 | } 19 | 20 | /** 21 | * Run the configurable. 22 | */ 23 | public function configure(): void 24 | { 25 | Model::unguard(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Contracts/Configurable.php: -------------------------------------------------------------------------------- 1 | > 20 | */ 21 | private array $configurables = [ 22 | Configurables\AggressivePrefetching::class, 23 | Configurables\AutomaticallyEagerLoadRelationships::class, 24 | Configurables\FakeSleep::class, 25 | Configurables\ForceScheme::class, 26 | Configurables\ImmutableDates::class, 27 | Configurables\PreventStrayRequests::class, 28 | Configurables\ProhibitDestructiveCommands::class, 29 | Configurables\SetDefaultPassword::class, 30 | Configurables\ShouldBeStrict::class, 31 | Configurables\Unguard::class, 32 | ]; 33 | 34 | /** 35 | * The list of commands. 36 | * 37 | * @var list> 38 | */ 39 | private array $commandsList = [ 40 | Commands\EssentialsRectorCommand::class, 41 | Commands\EssentialsPintCommand::class, 42 | Commands\MakeActionCommand::class, 43 | ]; 44 | 45 | /** 46 | * Bootstrap the application services. 47 | */ 48 | public function boot(): void 49 | { 50 | collect($this->configurables) 51 | ->map(fn (string $configurable) => $this->app->make($configurable)) 52 | ->filter(fn (Configurable $configurable): bool => $configurable->enabled()) 53 | ->each(fn (Configurable $configurable) => $configurable->configure()); 54 | 55 | if ($this->app->runningInConsole()) { 56 | $this->commands($this->commandsList); 57 | 58 | $this->publishes([ 59 | __DIR__.'/../stubs' => $this->app->basePath('stubs'), 60 | ], 'essentials-stubs'); 61 | 62 | $this->publishes([ 63 | __DIR__.'/../config/essentials.php' => config_path('essentials.php'), 64 | ]); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /stubs/action.stub: -------------------------------------------------------------------------------- 1 | withPaths([ 10 | __DIR__.'/app', 11 | __DIR__.'/bootstrap/app.php', 12 | __DIR__.'/database', 13 | __DIR__.'/public', 14 | ]) 15 | ->withSkip([ 16 | AddOverrideAttributeToOverriddenMethodsRector::class, 17 | ]) 18 | ->withPreparedSets( 19 | deadCode: true, 20 | codeQuality: true, 21 | typeDeclarations: true, 22 | privatization: true, 23 | earlyReturn: true, 24 | strictBooleans: true, 25 | ) 26 | ->withPhpSets(); -------------------------------------------------------------------------------- /testbench.yaml: -------------------------------------------------------------------------------- 1 | laravel: '@testbench' 2 | 3 | migrations: 4 | - workbench/database/migrations 5 | 6 | seeders: 7 | - Workbench\Database\Seeders\DatabaseSeeder 8 | 9 | workbench: 10 | start: '/' 11 | install: true 12 | health: false 13 | discovers: 14 | web: true 15 | api: true 16 | commands: true 17 | components: false 18 | factories: true 19 | views: false 20 | build: 21 | - asset-publish 22 | - create-sqlite-db 23 | - db-wipe 24 | - migrate-fresh 25 | assets: 26 | - laravel-assets 27 | sync: 28 | - from: storage 29 | to: workbench/storage 30 | reverse: true 31 | --------------------------------------------------------------------------------