docs: add inline documentation where missing

This commit is contained in:
Björn Fromme
2025-12-19 17:26:59 +01:00
parent 2dd5b7ad64
commit 1f1ac1a0cc
18 changed files with 150 additions and 0 deletions
@@ -4,6 +4,13 @@ declare(strict_types=1);
namespace App\BusProNet\DataProcessor; namespace App\BusProNet\DataProcessor;
/**
* Transforms raw pickup planning data into structured travel-indexed pickup information.
*
* Processes JSON payload from the CMS pickup planning endpoint, filtering for outbound
* trips (HIN direction) and grouping pickups by travel code. Each pickup includes city,
* location, and datetime, sorted chronologically per travel code.
*/
class PickupPlanningTransformer class PickupPlanningTransformer
{ {
/** /**
@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\EventListener; namespace App\EventListener;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@@ -9,6 +11,14 @@ use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AccessDeniedException;
/**
* Converts AccessDeniedException to user-friendly flash messages.
*
* Logs access denials and adds appropriate flash messages: "Please log in" for
* anonymous users, or "Access denied" for authenticated users. Clears target
* path for authenticated denials to prevent redirect loops. OAuth2 routes are
* logged but allowed to pass through without modification.
*/
class AccessDeniedListener implements EventSubscriberInterface class AccessDeniedListener implements EventSubscriberInterface
{ {
public function __construct( public function __construct(
@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\EventListener; namespace App\EventListener;
use App\Entity\User; use App\Entity\User;
@@ -13,6 +15,14 @@ use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Handles OAuth2 authorization code grant flow.
*
* Automatically approves authorization requests for authenticated users without
* consent prompts. Redirects anonymous users to login. For external OAuth2
* requests (flagged via session), logs out the user after authorization to
* prevent session hijacking.
*/
#[AsEventListener(event: OAuth2Events::AUTHORIZATION_REQUEST_RESOLVE, method: 'onAuthorizationRequestResolve')] #[AsEventListener(event: OAuth2Events::AUTHORIZATION_REQUEST_RESOLVE, method: 'onAuthorizationRequestResolve')]
class AuthorizationCodeListener class AuthorizationCodeListener
{ {
@@ -10,6 +10,18 @@ use App\Form\Model\BookingDto;
use App\Form\Service\Abstract\AbstractParticipantFieldHandler; use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
use App\Service\ServiceAvailabilityCalculator; use App\Service\ServiceAvailabilityCalculator;
/**
* Handles automatic replacement of discounted transportation based on booking rules.
*
* This virtual field handler enforces the business rule that discounted self-organized
* transportation (PKW with negative price) is only available when both outbound AND
* inbound travel use PKW. When inbound changes to BUS, the outbound PKW is automatically
* replaced with the regular (non-discounted) PKW variant.
*
* Conversely, when inbound changes back to PKW, this handler restores the discounted
* variant if availability permits. Baby participants (age <= 2) are excluded from
* discounted transportation restoration.
*/
class ParticipantTransportationDiscountReplacementFieldHandler extends AbstractParticipantFieldHandler class ParticipantTransportationDiscountReplacementFieldHandler extends AbstractParticipantFieldHandler
{ {
public function __construct( public function __construct(
+8
View File
@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Logger; namespace App\Logger;
use App\Entity\LogEntry; use App\Entity\LogEntry;
@@ -7,6 +9,12 @@ use Doctrine\ORM\EntityManagerInterface;
use Monolog\Handler\AbstractProcessingHandler; use Monolog\Handler\AbstractProcessingHandler;
use Monolog\LogRecord; use Monolog\LogRecord;
/**
* Monolog handler that persists log entries to the database.
*
* Writes log records to the LogEntry entity, replacing message placeholders
* with context values. Includes channel, context, and extra data for auditing.
*/
class DatabaseHandler extends AbstractProcessingHandler class DatabaseHandler extends AbstractProcessingHandler
{ {
public function __construct(private readonly EntityManagerInterface $entityManager) public function __construct(private readonly EntityManagerInterface $entityManager)
+8
View File
@@ -1,11 +1,19 @@
<?php <?php
declare(strict_types=1);
namespace App\Logger; namespace App\Logger;
use Monolog\Attribute\AsMonologProcessor; use Monolog\Attribute\AsMonologProcessor;
use Monolog\LogRecord; use Monolog\LogRecord;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
/**
* Enriches log records with HTTP request context.
*
* Adds the current request URI and method to the log record's extra data,
* enabling correlation of log entries with specific HTTP requests.
*/
#[AsMonologProcessor] #[AsMonologProcessor]
class RequestInfoProcessor class RequestInfoProcessor
{ {
+8
View File
@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Logger; namespace App\Logger;
use App\Entity\User; use App\Entity\User;
@@ -8,6 +10,12 @@ use Monolog\LogRecord;
use Symfony\Bundle\SecurityBundle\Security; use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
/**
* Enriches log records with current user information.
*
* Adds username and roles to log entries for audit trails. Handles CLI context
* (system user), anonymous requests, and user impersonation via SwitchUserToken.
*/
#[AsMonologProcessor] #[AsMonologProcessor]
class UserDataProcessor class UserDataProcessor
{ {
+9
View File
@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Security; namespace App\Security;
use App\BusProNet\ApiClient; use App\BusProNet\ApiClient;
@@ -22,6 +24,13 @@ use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPasspor
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait; use Symfony\Component\Security\Http\Util\TargetPathTrait;
/**
* Authenticates users against the BPN API.
*
* Validates credentials via BPN's getPersonalData endpoint, creates or updates
* local User entities, and retrieves CRM attributes (roles, hotel codes) for
* authorization. Passwords are stored encrypted with RSA for subsequent API calls.
*/
class BpnAuthenticator extends AbstractLoginFormAuthenticator implements AuthenticationEntryPointInterface class BpnAuthenticator extends AbstractLoginFormAuthenticator implements AuthenticationEntryPointInterface
{ {
use TargetPathTrait; use TargetPathTrait;
+9
View File
@@ -1,10 +1,19 @@
<?php <?php
declare(strict_types=1);
namespace App\Security; namespace App\Security;
use Spatie\Crypto\Rsa\PrivateKey; use Spatie\Crypto\Rsa\PrivateKey;
use Spatie\Crypto\Rsa\PublicKey; use Spatie\Crypto\Rsa\PublicKey;
/**
* RSA encryption/decryption utilities for secure credential storage.
*
* Uses asymmetric encryption to store BPN API passwords securely. Private key
* encrypts data for storage, public key decrypts for API calls. Also provides
* signing/verification for message integrity.
*/
class Crypt class Crypt
{ {
public function __construct(private readonly string $path) public function __construct(private readonly string $path)
+9
View File
@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Security\Voter; namespace App\Security\Voter;
use App\BusProNet\Model\Booking; use App\BusProNet\Model\Booking;
@@ -7,6 +9,13 @@ use App\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter; use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
* Determines access permissions for booking operations.
*
* Enforces ownership check via personId matching. VIEW access requires ownership,
* EDIT access additionally requires the booking to be in an editable state
* (determined by booking.isEditable()).
*/
class BookingVoter extends Voter class BookingVoter extends Voter
{ {
public const VIEW = 'VIEW'; public const VIEW = 'VIEW';
+9
View File
@@ -1,11 +1,20 @@
<?php <?php
declare(strict_types=1);
namespace App\Twig; namespace App\Twig;
use Twig\Extension\AbstractExtension; use Twig\Extension\AbstractExtension;
use Twig\TwigFilter; use Twig\TwigFilter;
use Twig\TwigFunction; use Twig\TwigFunction;
/**
* Twig extension registering booking-related filters and functions.
*
* Provides filters for formatting (file_size, format_money, format_service_price)
* and mapping (map_gender, map_status, map_country, map_nationality), plus
* functions for field state checking and participant eligibility.
*/
class AppExtension extends AbstractExtension class AppExtension extends AbstractExtension
{ {
public function getFilters(): array public function getFilters(): array
+9
View File
@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Twig; namespace App\Twig;
use App\BusProNet\DataProvider\CountryDataProvider; use App\BusProNet\DataProvider\CountryDataProvider;
@@ -14,6 +16,13 @@ use Twig\Environment;
use Twig\Extension\RuntimeExtensionInterface; use Twig\Extension\RuntimeExtensionInterface;
use Twig\Extra\Intl\IntlExtension; use Twig\Extra\Intl\IntlExtension;
/**
* Runtime implementation for AppExtension's filters and functions.
*
* Provides formatting utilities (money, file size, service prices), code-to-label
* mapping for display (gender, status, country), and field state introspection
* for conditional rendering in templates.
*/
class AppRuntime implements RuntimeExtensionInterface class AppRuntime implements RuntimeExtensionInterface
{ {
public function __construct( public function __construct(
@@ -1,9 +1,17 @@
<?php <?php
declare(strict_types=1);
namespace App\Validator\Constraints; namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraint;
/**
* Validates participant data for cross-field consistency.
*
* Applied as a class-level constraint on ParticipantDto to enforce business rules
* that depend on multiple fields (e.g., requiring pickup when bus transport is selected).
*/
#[\Attribute] #[\Attribute]
class Participant extends Constraint class Participant extends Constraint
{ {
@@ -1,11 +1,19 @@
<?php <?php
declare(strict_types=1);
namespace App\Validator\Constraints; namespace App\Validator\Constraints;
use App\Form\Model\ParticipantDto; use App\Form\Model\ParticipantDto;
use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates cross-field constraints on participant data.
*
* Enforces business rules that require examining multiple fields together,
* such as requiring a pickup location when bus transportation is selected.
*/
class ParticipantValidator extends ConstraintValidator class ParticipantValidator extends ConstraintValidator
{ {
public function validate(mixed $value, Constraint $constraint): void public function validate(mixed $value, Constraint $constraint): void
@@ -11,6 +11,13 @@ use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Validates promotional voucher codes against the BPN API.
*
* Verifies that promo codes are valid for the current travel, checks minimum price
* thresholds, and validates code availability. Uses the participant's calculated
* total price (including room and all selected services) for threshold comparison.
*/
class PromoVoucherValidator extends ConstraintValidator class PromoVoucherValidator extends ConstraintValidator
{ {
public function __construct( public function __construct(
@@ -10,6 +10,12 @@ use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Validates purchase voucher codes against the BPN API.
*
* Verifies that voucher codes exist and have remaining balance. Unlike promo
* vouchers, purchase vouchers do not require travel or price context validation.
*/
class PurchaseVoucherValidator extends ConstraintValidator class PurchaseVoucherValidator extends ConstraintValidator
{ {
public function __construct( public function __construct(
@@ -6,6 +6,13 @@ namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraint;
/**
* Validates room selection requirements for bookings.
*
* Applied as a class-level constraint on BookingDto to ensure valid room
* configurations: at least one room must be selected, and baby rooms
* cannot be booked without accompanying regular rooms.
*/
#[\Attribute] #[\Attribute]
class RoomSelection extends Constraint class RoomSelection extends Constraint
{ {
@@ -10,6 +10,12 @@ use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\UnexpectedTypeException;
/**
* Validates room selection business rules for bookings.
*
* Enforces that at least one room is selected and that baby rooms (BABY code)
* are only booked in combination with regular rooms, not standalone.
*/
class RoomSelectionValidator extends ConstraintValidator class RoomSelectionValidator extends ConstraintValidator
{ {
public function validate(mixed $value, Constraint $constraint): void public function validate(mixed $value, Constraint $constraint): void