Menu
Coddy logo textTech

Servicio de alquiler de vehículos

Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy. Lección 91 de 91.

challenge icon

Desafío

Fácil

Construyamos un sistema de Servicio de alquiler de vehículos que gestione una flota de vehículos, registre los alquileres de los clientes y calcule los precios según el tipo de vehículo. Este desafío integral reúne herencia, interfaces, encapsulación y el patrón State para crear una aplicación realista de gestión de alquileres.

Organizarás tu código en siete archivos:

  • VehicleState.php: Define una interfaz VehicleState que represente los diferentes estados en los que puede encontrarse un vehículo. Incluye dos métodos: getStatus(): string y canRent(): bool. Después, crea tres clases que implementen esta interfaz:
    • AvailableState: devuelve "Available" y true
    • RentedState: devuelve "Rented" y false
    • MaintenanceState: devuelve "Maintenance" y false
  • Vehicle.php: Incluye el archivo VehicleState. Crea una clase abstracta Vehicle con promoción del constructor para licensePlate (string, public readonly), brand (string, public readonly) y dailyRate (float, protected). Almacena el estado actual en una propiedad privada, con el valor predeterminado de AvailableState. Implementa:
    • getState(): VehicleState: devuelve el estado actual
    • setState(VehicleState $state): void: cambia el estado
    • getStatus(): string: delega en el getStatus() del estado
    • canRent(): bool: delega en el canRent() del estado
    • abstract calculateCost(int $days): float: cada tipo de vehículo calcula el coste de forma diferente
    • getType(): string: devuelve el nombre de la clase mediante static::class
  • Vehicles.php: Incluye el archivo Vehicle. Crea tres clases concretas de vehículos:
    • Car: extiende Vehicle; calculateCost() devuelve dailyRate * days
    • Motorcycle: extiende Vehicle; calculateCost() devuelve dailyRate * days * 0.8 (20 % de descuento)
    • Van: extiende Vehicle con un parámetro adicional $mileageRate (float); calculateCost(int $days, int $miles = 0) devuelve (dailyRate * days) + (mileageRate * miles)
  • Customer.php: Crea una clase Customer con promoción del constructor para id (int, public readonly) y name (string, public readonly). Registra el historial de alquileres en un array privado. Implementa:
    • addRental(string $vehiclePlate, float $cost): void: añade un alquiler al historial
    • getRentalCount(): int: devuelve el número de alquileres
    • getTotalSpent(): float: devuelve la suma de todos los costes de alquiler
  • Rental.php: Incluye los archivos Vehicle y Customer. Crea una clase Rental que vincule un cliente con un vehículo. Usa la promoción del constructor para customer (Customer, public readonly), vehicle (Vehicle, public readonly), days (int, private) y, opcionalmente, miles (int, private, valor predeterminado 0). Implementa:
    • calculateTotal(): float: si el vehículo es un Van, pasa miles a calculateCost(); en caso contrario, solo pasa days
    • complete(): string: calcula el total, lo añade al historial del cliente, establece el vehículo en AvailableState y devuelve "Rental completed: $[total]" (con 2 decimales)
  • RentalService.php: Incluye los archivos Rental y Vehicles. Crea una clase RentalService que gestione la flota. Almacena los vehículos en un array privado indexado por la matrícula. Implementa:
    • addVehicle(Vehicle $vehicle): void: añade el vehículo a la flota
    • findVehicle(string $plate): ?Vehicle: devuelve el vehículo o null
    • getAvailableVehicles(): array: devuelve los vehículos para los que canRent() es true
    • rentVehicle(Customer $customer, string $plate, int $days, int $miles = 0): string|Rental: si no se encuentra el vehículo, devuelve "Vehicle not found"; si no está disponible, devuelve "Vehicle is not available"; de lo contrario, establece el vehículo en RentedState y devuelve un nuevo objeto Rental
    • setMaintenance(string $plate): string: establece el vehículo en MaintenanceState; devuelve "[plate] set to maintenance" o "Vehicle not found"
  • main.php: Incluye los archivos RentalService y Customer. Recibirás tres entradas: datos de la flota (JSON), datos del cliente (JSON) y operaciones (JSON).

    Formato del JSON de la flota:

    [{"type": "car", "plate": "CAR-001", "brand": "Toyota", "rate": 50}, {"type": "van", "plate": "VAN-001", "brand": "Ford", "rate": 80, "mileage_rate": 0.25}]

    Formato del JSON del cliente:

    {"id": 1, "name": "Alice"}

    Formato del JSON de operaciones:

    [{"op": "available"}, {"op": "rent", "plate": "CAR-001", "days": 3}, {"op": "complete", "plate": "CAR-001"}, {"op": "maintenance", "plate": "VAN-001"}, {"op": "customer_stats"}]

    Crea el servicio de alquiler, añade todos los vehículos a la flota y crea el cliente. Mantén un registro de los alquileres activos por matrícula. Procesa cada operación:

    • "available": imprime "Available vehicles: [count]"
    • "rent": alquila el vehículo; si la operación tiene éxito, guarda el Rental e imprime "Rented [plate] for [days] days"; si es un Van, incluye miles de la operación; de lo contrario, imprime el mensaje de error
    • "complete": completa el alquiler almacenado para esa matrícula; imprime el resultado
    • "maintenance": imprime el resultado de setMaintenance()
    • "status": imprime "[plate]: [status]" para la matrícula indicada
    • "customer_stats": imprime "[name]: [count] rentals, $[total] spent" (con 2 decimales)

    Imprime cada resultado en una línea nueva.

Este sistema muestra cómo el patrón State gestiona elegantemente la disponibilidad de los vehículos sin condicionales complejos, cómo la herencia crea una jerarquía de vehículos clara con precios polimórficos y cómo la encapsulación protege la integridad de los datos de alquiler durante todo el flujo de trabajo.

Pruébalo tú mismo

<?php

require_once 'RentalService.php';
require_once 'Customer.php';

// Leer entradas
$fleetJson = trim(fgets(STDIN));
$customerJson = trim(fgets(STDIN));
$operationsJson = trim(fgets(STDIN));

// Parsear datos JSON
$fleetData = (array)json_decode($fleetJson, true);
$customerData = (array)json_decode($customerJson, true);
$operations = (array)json_decode($operationsJson, true);

// TODO: Crear instancia de RentalService

// TODO: Añadir todos los vehículos a la flota según el tipo:
// - "car" -> new Car(plate, brand, rate)
// - "motorcycle" -> new Motorcycle(plate, brand, rate)
// - "van" -> new Van(plate, brand, rate, mileage_rate)

// TODO: Crear instancia de Customer a partir de customerData

// TODO: Crear array para rastrear alquileres activos por plate
$activeRentals = [];

// TODO: Procesar cada operación e imprimir resultados:
// - "available": Print "Available vehicles: [count]"
// - "rent": Alquilar vehículo, almacenar Rental si es exitoso, imprimir resultado
// - "complete": Completar el alquiler, imprimir resultado
// - "maintenance": Establecer mantenimiento, imprimir resultado
// - "status": Imprimir "[plate]: [status]"
// - "customer_stats": Imprimir "[name]: [count] rentals, $[total] spent"

foreach ($operations as $op) {
    $operation = (array)$op;
    $opType = $operation['op'];
    
    // TODO: Manejar cada tipo de operación
    switch ($opType) {
        case 'available':
            // TODO: Print available vehicle count
            break;
        case 'rent':
            // TODO: Alquilar vehículo e imprimir resultado
            break;
        case 'complete':
            // TODO: Completar el alquiler e imprimir resultado
            break;
        case 'maintenance':
            // TODO: Establecer mantenimiento e imprimir resultado
            break;
        case 'status':
            // TODO: Imprimir el estado del vehículo
            break;
        case 'customer_stats':
            // TODO: Imprimir las estadísticas del cliente
            break;
    }
}

?>

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de PHP online