feat: performance improvements

This commit is contained in:
Björn Fromme committed 2026-03-16 11:59:11 +01:00
1 parent 4decaa6789
commit 1e464cef0a
6 files changed
+122 -103

No files matched your search

+42 -23
View File
@@ -10,20 +10,23 @@ use App\BusProNet\Traits\SortByPriceTrait;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use Carbon\Carbon;
use Spatie\Blink\Blink;
/**
* Consolidated service for all insurance-related operations.
*
* Handles insurance eligibility evaluation, filtering, caching, and assignment logic.
* Uses request-scoped caching via Blink to optimize performance for bookings with many participants.
* Uses request-scoped instance-level caching to optimize performance for bookings with many participants.
* This service is stateless and has no dependencies to avoid circular dependency issues.
*/
class InsuranceService
{
use SortByPriceTrait;
private const CACHE_KEY_PREFIX = 'selectable_insurances_';
/** @var array<string, array<Insurance>> Request-scoped cache for selectable insurances */
private array $selectableInsurancesCache = [];
/** @var array<string, array<Insurance>> Request-scoped cache for eligible insurances */
private array $eligibleInsurancesCache = [];
/**
* Returns selectable (non-complementary) insurances for a travel with request-scoped caching.
@@ -38,11 +41,9 @@ class InsuranceService
*/
public function getSelectableInsurances(Travel $travel): array
{
$cacheKey = self::CACHE_KEY_PREFIX.$travel->id;
$cacheKey = 'selectable_insurances_'.$travel->id;
return Blink::global()->once($cacheKey, function () use ($travel) {
return $this->filterNonComplementary($travel->insurances ?? []);
});
return $this->selectableInsurancesCache[$cacheKey] ??= $this->filterNonComplementary($travel->insurances ?? []);
}
/**
@@ -68,24 +69,42 @@ class InsuranceService
return []; // Cannot evaluate without travel dates
}
$bookingDate = Carbon::now()->toDateTimeImmutable();
$travelDurationDays = $travelStartDate->diff($travelEndDate)->days;
$eligibleInsurances = array_filter(
$insurances,
fn (Insurance $insurance) => $this->isInsuranceEligible(
$insurance,
$participant,
$booking,
$travelStartDate,
$travelEndDate,
$bookingDate,
$travelPrice,
$travelDurationDays
)
// Generate cache key based on all eligibility criteria
// Use spl_object_id() for insurance identification (not $insurance->id):
// - Works with test fixtures where IDs may be null
// - Negligible overhead (~0.002ms for 20 insurances vs ~50ms saved by caching)
// - Insurance objects are stable within a single request (loaded from session)
$insuranceHashes = array_map(fn (Insurance $i) => spl_object_id($i), $insurances);
$cacheKey = sprintf(
'eligible_insurances_%d_%s_%s_%s_%s_%s',
$participant->index,
number_format(round($travelPrice, 2), 2, '.', ''),
$booking->getMode(),
$travelStartDate->format('Y-m-d'),
$participant->dateOfBirth?->format('Y-m-d') ?? 'no_dob',
md5(implode('_', $insuranceHashes))
);
return $this->sortByPrice($eligibleInsurances);
return $this->eligibleInsurancesCache[$cacheKey] ??= (function () use ($insurances, $participant, $booking, $travelStartDate, $travelEndDate, $travelPrice) {
$bookingDate = Carbon::now()->toDateTimeImmutable();
$travelDurationDays = $travelStartDate->diff($travelEndDate)->days;
$eligibleInsurances = array_filter(
$insurances,
fn (Insurance $insurance) => $this->isInsuranceEligible(
$insurance,
$participant,
$booking,
$travelStartDate,
$travelEndDate,
$bookingDate,
$travelPrice,
$travelDurationDays
)
);
return $this->sortByPrice($eligibleInsurances);
})();
}
/**