jand3v / laravel-opinionated-essentials
Opinionated essentials for Laravel applications.
Package info
github.com/jand3v/laravel-opionated-essentials
pkg:composer/jand3v/laravel-opinionated-essentials
Requires
- php: ^8.3
- illuminate/contracts: ^13.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^11.0
- pestphp/pest: ^4.0
This package is not auto-updated.
Last update: 2026-06-06 08:44:25 UTC
README
Opinionated essentials for Laravel applications. Applies sensible framework defaults automatically and provides generators for common patterns.
Installation
composer require jand3v/laravel-opinionated-essentials
The service provider is auto-discovered — no manual registration required.
Configuration
Publish the config file to adjust or disable individual defaults:
php artisan vendor:publish --tag=opinionated-essentials-config
This creates config/opinionated-essentials.php in your application. Every default can be toggled via environment variables.
Opinionated Defaults
The following behaviors are applied automatically when the package is installed:
| Behavior | Config key | Env variable | Default |
|---|---|---|---|
| Eloquent lazy-loading prevention (non-production) | models.prevent_lazy_loading |
OPINIONATED_ESSENTIALS_PREVENT_LAZY_LOADING |
true |
| Carbon immutable dates | dates.immutable |
OPINIONATED_ESSENTIALS_IMMUTABLE_DATES |
true |
| Secure password rules in production | passwords.secure |
OPINIONATED_ESSENTIALS_SECURE_PASSWORDS |
true |
| Prohibit destructive DB commands in production | database.prevent-destruction |
OPINIONATED_ESSENTIALS_PREVENT_DB_DESTRUCTION |
true |
To disable a default, set the env variable to false or update config/opinionated-essentials.php.
Lazy Loading Prevention
Outside of production, accessing an unloaded Eloquent relationship throws a LazyLoadingViolationException, forcing eager loading and preventing N+1 queries:
// ✗ Throws LazyLoadingViolationException outside production $post->comments; // ✓ Always safe Post::with('comments')->get();
Immutable Dates
All date instances returned by Eloquent are CarbonImmutable, preventing accidental mutation:
$user->created_at->addDay(); // returns a new instance, original unchanged
Secure Passwords (production only)
In production, Password::defaults() requires a minimum of 12 characters, mixed case, letters, numbers, symbols, and checks the password against the HaveIBeenPwned database:
// Use the default password rule in your form requests 'password' => ['required', Password::default()],
In non-production environments Password::default() returns null (no extra constraints), making registration easy during development.
Prohibit Destructive Commands (production only)
DB::prohibitDestructiveCommands(true) is called in production, preventing migrate:fresh, migrate:reset, and db:wipe from running accidentally.
Generators
make:action
Creates a new Action class in app/Actions:
php artisan make:action PlaceOrder
Generates app/Actions/PlaceOrderAction.php:
<?php declare(strict_types=1); namespace App\Actions; final readonly class PlaceOrderAction { public function __construct() {} public function execute(): void { // } }
The
Actionsuffix is added automatically. PassingPlaceOrderActionproduces the same result — no double suffix.
--dto option
Pass --dto to generate an accompanying DTO and wire it into execute():
php artisan make:action PlaceOrder --dto
Generates both app/Actions/PlaceOrderAction.php and app/DTOs/PlaceOrderData.php:
// app/Actions/PlaceOrderAction.php final readonly class PlaceOrderAction { public function __construct() {} public function execute(PlaceOrderData $data): void { // } }
// app/DTOs/PlaceOrderData.php final readonly class PlaceOrderData { use MapsFromArray; public function __construct( // Add your properties here ) {} }
make:dto
Creates a standalone DTO in app/DTOs without an Action:
php artisan make:dto PlaceOrder
Generates app/DTOs/PlaceOrderData.php (same structure as above). The Data suffix is added automatically — passing PlaceOrderData produces the same result.
DTOs and MapsFromArray
Every generated DTO uses the MapsFromArray trait, which maps an array (e.g. from $request->validated()) to constructor properties.
Defining a DTO
use Jand3v\OpinionatedEssentials\Dto\Attributes\MapFrom; use Jand3v\OpinionatedEssentials\Dto\Concerns\MapsFromArray; final readonly class PlaceOrderData { use MapsFromArray; public function __construct( public int $customerId, public string $customerName, // mapped from 'customer_name' #[MapFrom('order_total')] public int $totalAmount, // mapped from 'order_total' public ?string $note = null, // optional, defaults to null ) {} }
Creating instances
From a controller with a validated form request:
public function store(Request $request): Response { $data = PlaceOrderData::from($request->validated()); // $data->customerId, $data->customerName, $data->totalAmount }
Or from any array:
$data = PlaceOrderData::from([ 'customer_id' => 42, 'customer_name' => 'Ada Lovelace', 'order_total' => 9900, ]);
Mapping resolution order
For each constructor parameter, the trait resolves the value in this order:
#[MapFrom('key')]— explicit array key (highest priority).- Exact camelCase name — e.g.
customerNamematches array keycustomerName. - snake_case fallback — e.g.
customerNamealso matchescustomer_name. - Default value or nullable — uses the parameter's default, or
nullif nullable. InvalidArgumentException— thrown when a required key is missing.
Testing
composer install vendor/bin/pest
License
The MIT License (MIT). See LICENSE for details.