Liam Hammett (Senior Tech Lead, Geological) gave a "50 Laravel Tips" talk at Laravel Live UK 2026, a trimmed version of the "101 Tips" talk he gave at the Dutch PHP Conference in March 2026. The slides are publicly available.
AppServiceProvider settings
A significant chunk of the talk covered useful things to register in
AppServiceProvider. These are easy wins that improve safety, consistency, and
developer experience across an app.
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
// Use immutable Carbon dates to prevent mutation bugs
Date::use(CarbonImmutable::class);
// Block destructive DB commands in production
DB::prohibitDestructiveCommands(app()->isProduction());
// Enforce strong passwords in production, relaxed locally
Password::defaults(fn (): ?Password => app()->isProduction()
? Password::min(12)
->mixedCase()
->letters()
->numbers()
->symbols()
->uncompromised()
: null,
);
// Force HTTPS in production
URL::forceHttps(app()->isProduction());
// Strict Eloquent mode (covers the three below in one call)
Model::shouldBeStrict();
// Or individually:
Model::preventLazyLoading();
Model::preventSilentlyDiscardingAttributes();
Model::preventAccessingMissingAttributes();
// Auto eager load relationships where possible
Model::automaticallyEagerLoadRelationships();
// Aggressive Vite prefetching
Vite::useAggressivePrefetching();
}
}
Date::use(CarbonImmutable::class) prevents a common mutation bug where calling
addDays() on a Carbon instance modifies the original variable rather than
returning a new one.
Model::shouldBeStrict() is a shorthand that enables all three strict model
behaviours at once: no lazy loading, no silently discarded attributes, no access
to missing attributes.
Password::defaults() lets you define password rules once and reference them
anywhere with Password::defaults(). The same rules can be passed to an HTML
passwordrules attribute via Password::defaults()->toPasswordRulesString().
Bladestan - static analysis for Blade templates
Bladestan is a PHPStan extension by Tomas Votruba that runs static analysis
inside Blade templates. Install it alongside Larastan and include its config in
phpstan.neon.
composer require tomasvotruba/bladestan --dev
includes:
- vendor/larastan/larastan/extension.neon
- vendor/nesbot/carbon/extension.neon
- vendor/tomasvotruba/bladestan/config/extension.neon
parameters:
paths:
- app/
- bootstrap/
- config/
- database/
- resources/views/
- routes/
level: 7
checkModelProperties: true
checkConfigTypes: true
checkOctaneCompatibility: true
Liam also covered a broader static analysis stack: Larastan for PHP, Bladestan for Blade views, Rector for automated refactoring and code quality enforcement, and Laravel Pint for formatting.
// rector.php
RectorConfig::configure()
->withPaths([__DIR__ . '/app', ...])
->withPhpSets()
->withPreparedSets(
deadCode: true,
codeQuality: true,
typeDeclarations: true,
privatization: true,
earlyReturn: true,
strictBooleans: true,
);
composer require driftingly/rector-laravel --dev
These can be combined into a single composer lint script and run in CI via
GitHub Actions.
References:
FilamentServiceProvider
Filament global defaults for tables, selects, columns, date pickers and
notifications should be stripped out of individual resources and centralised in
a dedicated FilamentServiceProvider. This removes repeated configuration
across every resource.
class FilamentServiceProvider extends ServiceProvider
{
public function boot(): void
{
Table::configureUsing(fn (Table $table): Table => $table
->striped()
->deferLoading()
->reorderableColumns()
->paginationPageOptions([10, 25, 50, 100])
->filtersTriggerAction(fn (Action $action): Action => $action
->button()->label('Filters')->slideOver()
)
);
Select::configureUsing(fn (Select $field): Select => $field
->searchable()
->preload()
);
DatePicker::configureUsing(fn (DatePicker $datePicker): DatePicker => $datePicker
->minDate(Date::createFromDate(1500, 1, 1))
->maxDate(now()->addYears(30))
);
TextColumn::configureUsing(fn (TextColumn $textColumn): TextColumn => $textColumn
->searchable()
->sortable()
);
Notification::configureUsing(fn (Notification $notification): Notification => $notification
->duration(10_000)
);
}
}
Other tips from the slides worth noting
Morph maps - storing full class names like App\Models\Post in polymorphic
columns is fragile. Register a morph map in AppServiceProvider to store short
keys like posts instead. spatie/laravel-morph-map-generator can automate
this using the model's table name.
Published stubs - run php artisan stub:publish to customise the templates
Laravel uses when generating files via artisan make:*. Editing stubs means
generated migrations, models, controllers etc. match your conventions from the
start.
Sleep facade - use Sleep::for(2)->seconds() instead of sleep(2). The
Pest beforeEach can call Sleep::fake() to make tests that involve sleep
instant and assertable.
Typed request helpers - use request()->string(), request()->date(),
request()->boolean(), request()->enum() instead of request()->get() for
everything. Returns cast values directly.
IDE Helper - barryvdh/laravel-ide-helper generates stubs that give IDEs
proper type awareness for Facades, models, and Eloquent methods. Run the artisan
commands in post-update-cmd so they stay current.
Frontend tooling - Liam uses Prettier with prettier-plugin-blade for Blade
formatting, ESLint for JS/TS, and Vitest for frontend unit tests. bun is
recommended over npm for speed. Node version is pinned via .nvmrc.
Custom starter kit - Liam published imliam/smarter-kit, a community starter
kit that bundles his recommended setup. Installable via
laravel new my-app --using=imliam/smarter-kit.
References:
Notes from Laravel Live UK 2026.