Helper Traits

Four traits provide common functionality for step classes. Use them instead of writing your own implementations.

#HasFormData

Manages form data and validation rules.

#initializeFormData()

Creates empty formData array from field configuration.

use HasFormData;
 
protected function mount(): void
{
$fields = config('installer-yourplugin.form_fields');
$this->initializeFormData($fields);
}

#buildRulesFromFields()

Generates validation rules from field configuration.

protected function rules(): array
{
return $this->buildRulesFromFields(
config('installer-yourplugin.form_fields')
);
}

Automatically removes database-dependent rules (unique, exists) since database doesn't exist yet during installation.

#removeDbValidationRules()

Removes database rules from a validation string or array.

// String format
$rules = 'required|email|unique:users,email';
$clean = $this->removeDbValidationRules($rules);
// Result: 'required|email'
 
// Array format
$rules = ['required', 'email', 'unique:users,email'];
$clean = $this->removeDbValidationRules($rules);
// Result: ['required', 'email']

Removes: unique, exists, unique_translation

#getDbData()

Extracts database-ready data from formData.

protected function execute(): void
{
$this->storeInSession('settings', $this->formData);
 
// Only store fields marked with 'store_in_db' => true
$dbData = $this->getDbData(
config('installer-yourplugin.form_fields')
);
 
Setting::insert($dbData);
}

Field configuration:

'form_fields' => [
[
'name' => 'api_key',
'store_in_db' => true,
'column' => 'key', // Optional, defaults to 'name'
],
[
'name' => 'temp_token',
'store_in_db' => false, // Exclude from database
],
],

#getFormFields()

Override to provide custom fields.

protected function getFormFields(): array
{
return [
['name' => 'email', 'validation' => 'required|email'],
['name' => 'password', 'validation' => 'required|min:8'],
];
}

Most steps load from config instead:

protected function getFormFields(): array
{
return config('installer-yourplugin.form_fields', []);
}

#ManagesSessionData

Session storage for installation data.

#storeInSession()

Save data to session.

use ManagesSessionData;
 
protected function execute(): void
{
$this->storeInSession('database', $this->formData);
// Stored as: session('installer_database')
}

All keys are automatically prefixed with installer_ to avoid conflicts.

#retrieveFromSession()

Load data from session.

protected function mount(): void
{
$databaseConfig = $this->retrieveFromSession('database');
 
if ($databaseConfig) {
$this->formData = $databaseConfig;
}
}

With default value:

$port = $this->retrieveFromSession('database.port', 3306);

#clearFromSession()

Remove data from session.

protected function execute(): void
{
// Clear temporary data
$this->clearFromSession('temp_verification');
}

#hasInSession()

Check if key exists in session.

if ($this->hasInSession('license')) {
// License was already verified
}

#storeStepData()

Save step-specific data.

$this->storeStepData('license', [
'key' => $this->formData['license_key'],
'verified_at' => now(),
]);
// Stored as: session('installer_step_license')

#retrieveStepData()

Load step-specific data.

$licenseData = $this->retrieveStepData('license');

#storeFormData() / retrieveFormData()

Convenience methods for form data.

$this->storeFormData($this->formData, 'myform');
$data = $this->retrieveFormData('myform', []);

#clearAllInstallerData()

Remove all installer session data.

protected function execute(): void
{
// Installation complete, clean up
$this->clearAllInstallerData();
}

Removes all keys starting with installer_.

#storeVerificationStatus() / isVerified()

Track verification state.

protected function execute(): void
{
$response = Http::post('https://api.example.com/verify', [...]);
 
if ($response->successful()) {
$this->storeVerificationStatus(true, 'license_verified');
}
}
 
if ($this->isVerified('license_verified')) {
// Already verified
}

#ValidatesRequirements

System requirements validation.

#checkPhpVersion()

Verify PHP version meets requirement.

use ValidatesRequirements;
 
protected function validateStep(): void
{
if (!$this->checkPhpVersion('8.2.0')) {
$this->errorMessage = __('requirements::requirements.php_version_failed');
return;
}
}

#checkExtension()

Check if PHP extension is loaded.

if (!$this->checkExtension('pdo_mysql')) {
$this->errorMessage = 'MySQL extension is required';
}

#checkExtensions()

Check multiple extensions at once.

$results = $this->checkExtensions([
'mbstring',
'openssl',
'pdo',
'tokenizer',
'xml',
]);
 
// Returns: ['mbstring' => true, 'openssl' => true, ...]

#getMissingExtensions()

Get list of missing extensions.

$required = ['mbstring', 'openssl', 'pdo', 'tokenizer'];
$missing = $this->getMissingExtensions($required);
 
if (!empty($missing)) {
$this->errorMessage = 'Missing extensions: ' . implode(', ', $missing);
}

#checkFilePermissions()

Verify file/directory permissions.

if (!$this->checkFilePermissions(storage_path(), '775')) {
$this->errorMessage = 'Storage directory needs 775 permissions';
}

#checkPermissions()

Check multiple paths at once.

$paths = [
storage_path() => '775',
base_path('.env') => '644',
base_path('bootstrap/cache') => '775',
];
 
$results = $this->checkPermissions($paths);
 
foreach ($results as $path => $result) {
if (!$result['passed']) {
$this->addError('permissions', "Fix {$path}");
}
}

Result format:

[
'/path/to/storage' => [
'required' => '775',
'current' => '755',
'passed' => false,
],
]

#checkWritable() / checkReadable()

Quick permission checks.

if (!$this->checkWritable(storage_path('logs'))) {
$this->errorMessage = 'Logs directory must be writable';
}
 
if (!$this->checkReadable(base_path('.env'))) {
$this->errorMessage = '.env file must be readable';
}

#getFailedRequirements()

Filter results to show only failures.

$results = $this->checkExtensions(['mbstring', 'openssl', 'pdo']);
$failed = $this->getFailedRequirements($results);
 
// Returns only extensions that failed

#EmitsInstallerEvents

Event dispatching for step lifecycle.

#emitStepStarted()

Fire event when step begins.

use EmitsInstallerEvents;
 
protected function mount(): void
{
$this->emitStepStarted('license', [
'user_email' => $this->formData['email'] ?? null,
]);
}

Dispatches StepStarted event.

#emitStepCompleted()

Fire event when step succeeds.

protected function execute(): void
{
$this->storeInSession('license', $this->formData);
 
$this->emitStepCompleted('license', [
'license_key' => $this->formData['license_key'],
]);
}

Dispatches StepCompleted event.

#emitStepFailed()

Fire event when step fails.

protected function validateStep(): void
{
try {
$this->validate();
$this->verifyLicenseKey();
} catch (\Exception $e) {
$this->emitStepFailed($e, 'license');
$this->errorMessage = __('license::license.verification_failed');
}
}

Dispatches StepFailed event.

#executeWithEvents()

Wrap code with automatic event firing.

protected function execute(): void
{
$result = $this->executeWithEvents(function () {
// This code is wrapped with StepStarted and StepCompleted events
$this->verifyLicense();
$this->storeInSession('license', $this->formData);
 
return ['verified' => true];
}, 'license_verification');
 
if ($result['verified']) {
$this->successMessage = __('license::license.verified');
}
}

Automatically catches exceptions and emits StepFailed.

#emitCustomEvent()

Dispatch your own events.

protected function execute(): void
{
$this->storeInSession('license', $this->formData);
 
// Fire custom event
$this->emitCustomEvent(LicenseVerified::class, [
$this->formData['license_key'],
now(),
]);
}

Your event class:

namespace YourVendor\YourPlugin\Events;
 
class LicenseVerified
{
public function __construct(
public string $licenseKey,
public \Carbon\Carbon $verifiedAt
) {}
}

#Using multiple traits

Most steps use several traits together:

namespace YourVendor\YourPlugin\Steps;
 
use Olakunlevpn\Installer\Steps\BaseStep;
use Olakunlevpn\Installer\Concerns\HasFormData;
use Olakunlevpn\Installer\Concerns\ManagesSessionData;
use Olakunlevpn\Installer\Concerns\ValidatesRequirements;
use Olakunlevpn\Installer\Concerns\EmitsInstallerEvents;
 
class YourStep extends BaseStep
{
use HasFormData;
use ManagesSessionData;
use ValidatesRequirements;
use EmitsInstallerEvents;
 
protected function mount(): void
{
$this->emitStepStarted();
$this->initializeFormData(config('installer-yourplugin.form_fields'));
}
 
protected function rules(): array
{
return $this->buildRulesFromFields(
config('installer-yourplugin.form_fields')
);
}
 
protected function validateStep(): void
{
$this->validate();
 
if (!$this->checkPhpVersion('8.2.0')) {
$this->errorMessage = 'PHP 8.2+ required';
}
}
 
protected function execute(): void
{
$this->storeInSession('yourplugin', $this->formData);
$this->emitStepCompleted();
}
 
public function render()
{
return view('yourplugin::livewire.steps.your-step');
}
}

#Next steps