Menu
Coddy logo textTech

Vehicle Rental Service

Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 91 of 91.

challenge icon

Challenge

Easy

Let's build a Vehicle Rental Service system that manages a fleet of vehicles, tracks customer rentals, and calculates pricing based on vehicle type. This comprehensive challenge brings together inheritance, interfaces, encapsulation, and the State pattern to create a realistic rental business application.

You'll organize your code across seven files:

  • VehicleState.php — Define a VehicleState interface that represents the different states a vehicle can be in. Include two methods: getStatus(): string and canRent(): bool. Then create three classes implementing this interface:
    • AvailableState — returns "Available" and true
    • RentedState — returns "Rented" and false
    • MaintenanceState — returns "Maintenance" and false
  • Vehicle.php — Include the VehicleState file. Create an abstract Vehicle class with constructor promotion for licensePlate (string, public readonly), brand (string, public readonly), and dailyRate (float, protected). Store the current state in a private property, defaulting to AvailableState. Implement:
    • getState(): VehicleState — returns current state
    • setState(VehicleState $state): void — changes the state
    • getStatus(): string — delegates to state's getStatus()
    • canRent(): bool — delegates to state's canRent()
    • abstract calculateCost(int $days): float — each vehicle type calculates differently
    • getType(): string — returns the class name using static::class
  • Vehicles.php — Include the Vehicle file. Create three concrete vehicle classes:
    • Car — extends Vehicle; calculateCost() returns dailyRate * days
    • Motorcycle — extends Vehicle; calculateCost() returns dailyRate * days * 0.8 (20% discount)
    • Van — extends Vehicle with an additional $mileageRate (float) parameter; calculateCost(int $days, int $miles = 0) returns (dailyRate * days) + (mileageRate * miles)
  • Customer.php — Create a Customer class with constructor promotion for id (int, public readonly) and name (string, public readonly). Track rental history in a private array. Implement:
    • addRental(string $vehiclePlate, float $cost): void — adds to history
    • getRentalCount(): int — returns number of rentals
    • getTotalSpent(): float — returns sum of all rental costs
  • Rental.php — Include the Vehicle and Customer files. Create a Rental class that links a customer to a vehicle. Use constructor promotion for customer (Customer, public readonly), vehicle (Vehicle, public readonly), days (int, private), and optionally miles (int, private, default 0). Implement:
    • calculateTotal(): float — if vehicle is a Van, pass miles to calculateCost(); otherwise just pass days
    • complete(): string — calculates total, adds to customer's history, sets vehicle to AvailableState, returns "Rental completed: $[total]" (2 decimal places)
  • RentalService.php — Include the Rental and Vehicles files. Create a RentalService class that manages the fleet. Store vehicles in a private array indexed by license plate. Implement:
    • addVehicle(Vehicle $vehicle): void — adds to fleet
    • findVehicle(string $plate): ?Vehicle — returns vehicle or null
    • getAvailableVehicles(): array — returns vehicles where canRent() is true
    • rentVehicle(Customer $customer, string $plate, int $days, int $miles = 0): string|Rental — if vehicle not found, return "Vehicle not found"; if not available, return "Vehicle is not available"; otherwise set vehicle to RentedState and return a new Rental object
    • setMaintenance(string $plate): string — sets vehicle to MaintenanceState; return "[plate] set to maintenance" or "Vehicle not found"
  • main.php — Include the RentalService and Customer files. You'll receive three inputs: fleet data (JSON), customer data (JSON), and operations (JSON).

    Fleet JSON format:

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

    Customer JSON format:

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

    Operations JSON format:

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

    Create the rental service, add all vehicles to the fleet, and create the customer. Keep track of active rentals by plate. Process each operation:

    • "available" — Print "Available vehicles: [count]"
    • "rent" — Rent the vehicle; if successful, store the Rental and print "Rented [plate] for [days] days"; if Van, include miles from the operation; otherwise print the error message
    • "complete" — Complete the stored rental for that plate; print the result
    • "maintenance" — Print the result of setMaintenance()
    • "status" — Print "[plate]: [status]" for the given plate
    • "customer_stats" — Print "[name]: [count] rentals, $[total] spent" (2 decimal places)

    Print each result on a new line.

This system showcases how the State pattern elegantly handles vehicle availability without complex conditionals, how inheritance creates a clean vehicle hierarchy with polymorphic pricing, and how encapsulation protects rental data integrity throughout the workflow.

Try it yourself

<?php

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

// Read inputs
$fleetJson = trim(fgets(STDIN));
$customerJson = trim(fgets(STDIN));
$operationsJson = trim(fgets(STDIN));

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

// TODO: Create RentalService instance

// TODO: Add all vehicles to the fleet based on type:
// - "car" -> new Car(plate, brand, rate)
// - "motorcycle" -> new Motorcycle(plate, brand, rate)
// - "van" -> new Van(plate, brand, rate, mileage_rate)

// TODO: Create Customer instance from customerData

// TODO: Create array to track active rentals by plate
$activeRentals = [];

// TODO: Process each operation and print results:
// - "available": Print "Available vehicles: [count]"
// - "rent": Rent vehicle, store Rental if successful, print result
// - "complete": Complete the rental, print result
// - "maintenance": Set maintenance, print result
// - "status": Print "[plate]: [status]"
// - "customer_stats": Print "[name]: [count] rentals, $[total] spent"

foreach ($operations as $op) {
    $operation = (array)$op;
    $opType = $operation['op'];
    
    // TODO: Handle each operation type
    switch ($opType) {
        case 'available':
            // TODO: Print available vehicle count
            break;
        case 'rent':
            // TODO: Rent vehicle and print result
            break;
        case 'complete':
            // TODO: Complete rental and print result
            break;
        case 'maintenance':
            // TODO: Set maintenance and print result
            break;
        case 'status':
            // TODO: Print vehicle status
            break;
        case 'customer_stats':
            // TODO: Print customer statistics
            break;
    }
}

?>

All lessons in Object Oriented Programming