Installation:
composer require ben182/laravel-ab
Publish the config file:
php artisan vendor:publish --provider="Ben182\LaravelAB\LaravelABServiceProvider" --tag="config"
Publish Migrations:
php artisan vendor:publish --provider="Ben182\LaravelAB\LaravelABServiceProvider" --tag="migrations"
Run migrations:
php artisan migrate
First Use Case:
Define an experiment in config/laravel-ab.php:
'experiments' => [
'homepage_button_color' => [
'variants' => [
'red' => ['weight' => 50],
'blue' => ['weight' => 50],
],
],
],
Use the experiment in a Blade view:
@php
$variant = \Ben182\LaravelAB\Experiment::get('homepage_button_color');
@endphp
<button style="background-color: {{ $variant }};">Click Me</button>
Experiment Definition:
config/laravel-ab.php with variants and weights.php artisan laravel-ab:create to scaffold new experiments via CLI.Variant Assignment:
$variant = \Ben182\LaravelAB\Experiment::get('experiment_key');
@laravelAB('experiment_key', 'default_variant')
$variant = \Ben182\LaravelAB\Experiment::get('experiment_key', request()->ip());
Tracking Conversions:
\Ben182\LaravelAB\Experiment::logConversion('experiment_key', $variant, 'user_id');
Middleware Integration:
public function handle($request, Closure $next) {
$request->merge(['ab_variant' => \Ben182\LaravelAB\Experiment::get('experiment_key')]);
return $next($request);
}
Dynamic Experiment Loading:
\Ben182\LaravelAB\Experiment::setExperiments($experimentsFromDB);
Cache Experiments:
Cache experiment definitions in AppServiceProvider for performance:
Cache::remember('laravel_ab_experiments', now()->addHours(1), function () {
return \Ben182\LaravelAB\Experiment::getExperiments();
});
Segmentation:
Use middleware to assign variants based on user segments (e.g., isPremiumUser):
$variant = \Ben182\LaravelAB\Experiment::get('experiment_key', $user->id, ['is_premium' => true]);
API Responses: Include variant info in API responses:
return response()->json([
'data' => $data,
'ab_variant' => \Ben182\LaravelAB\Experiment::get('experiment_key'),
]);
Weight Mismatch:
weights in config/laravel-ab.php sum to 100 (or use percentages). Otherwise, variants may not distribute as expected.laravel-ab:validate CLI command.Overwriting Variants:
?variant=red in URLs) can skew results. Use middleware to enforce consistency:
if ($request->has('variant')) {
\Ben182\LaravelAB\Experiment::forceVariant($request->variant);
}
Database Bloat:
ab_experiments and ab_conversions tables grow with usage. Archive old data periodically:
DB::table('ab_conversions')->where('created_at', '<', now()->subMonths(6))->delete();
Race Conditions:
DB::transaction(function () {
$variant = \Ben182\LaravelAB\Experiment::get('experiment_key', $userId);
});
Variant Assignment:
\Log::debug('Assigned variant', ['experiment' => 'key', 'variant' => $variant, 'user_id' => $userId]);
CLI Tools:
php artisan laravel-ab:list to inspect active experiments.php artisan laravel-ab:stats to view conversion stats.Testing:
\Ben182\LaravelAB\Experiment::setExperiments(['test_exp' => ['variants' => ['A' => 100]]]);
$this->assertEquals('A', \Ben182\LaravelAB\Experiment::get('test_exp'));
Custom Storage:
Experiment, Conversion) to use Redis or another storage backend.Event Listeners:
ab.variant.assigned or ab.conversion.logged events:
Event::listen('ab.variant.assigned', function ($experiment, $variant, $userId) {
// Send analytics event
});
Dynamic Weights:
WeightResolver interface to fetch weights dynamically (e.g., from an API):
\Ben182\LaravelAB\Experiment::setWeightResolver(new ApiWeightResolver());
Custom Variant Logic:
VariantResolver class to add logic like time-based or geo-based variant selection:
class CustomVariantResolver extends VariantResolver {
public function resolve($experiment, $context = []) {
if (now()->dayOfWeek === Carbon::SUNDAY) {
return 'weekend_special';
}
return parent::resolve($experiment, $context);
}
}
How can I help you explore Laravel packages today?