# Performance Audit Report
## Executive Summary
This report identifies performance bottlenecks and optimization opportunities across controllers, models, and views in the ZapaziMe booking system.
---
## 1. Controller Performance Issues
### 1.1 BookingController.php
**Issue 1.1.1: N+1 Query in venue_objects loop (Line 107)**
- **Location**: `BookingController@store()`
- **Problem**: `VenueObject::find($objectId)` called inside foreach loop
- **Impact**: O(n) database queries where n = number of venue objects
- **Severity**: HIGH
- **Solution**:
```php
// Current (N+1)
foreach ($validated['venue_objects'] as $objectId => $quantity) {
$venueObject = \App\Models\VenueObject::find($objectId);
}
// Optimized (single query)
$objectIds = array_keys(array_filter($validated['venue_objects'], fn($q) => $q > 0));
$venueObjects = \App\Models\VenueObject::whereIn('id', $objectIds)->get()->keyBy('id');
foreach ($validated['venue_objects'] as $objectId => $quantity) {
$venueObject = $venueObjects[$objectId] ?? null;
}
```
**Issue 1.1.2: Similar N+1 in services loop (Line 151+)**
- **Location**: `BookingController@store()`
- **Problem**: `VenueService::find($serviceId)` inside loop
- **Impact**: O(n) database queries
- **Severity**: HIGH
- **Solution**: Same pattern as above - use `whereIn()` before loop
---
### 1.2 ReservationController.php
**Issue 1.2.1: N+1 Query in spots loop (Line 47)**
- **Location**: `ReservationController@store()`
- **Problem**: `VenueSpot::find($spotId)` inside foreach loop
- **Impact**: O(n) database queries
- **Severity**: HIGH
- **Solution**:
```php
// Current (N+1)
foreach ($validated['spots'] as $spotId) {
$spot = VenueSpot::find($spotId);
}
// Optimized (single query)
$spots = VenueSpot::whereIn('id', $validated['spots'])->get()->keyBy('id');
foreach ($validated['spots'] as $spotId) {
$spot = $spots[$spotId] ?? null;
}
```
**Issue 1.2.2: Inefficient spot status updates (Line 58)**
- **Location**: `ReservationController@store()`
- **Problem**: Individual UPDATE queries for each spot
- **Impact**: O(n) UPDATE queries
- **Severity**: MEDIUM
- **Solution**:
```php
// Optimized (single UPDATE)
VenueSpot::whereIn('id', $validated['spots'])->update(['status' => 'unavailable']);
```
---
### 1.3 ClientController.php
**Issue 1.3.1: Multiple Queries for Booking Stats (Lines 36-40)**
- **Location**: `ClientController@dashboard()`
- **Problem**: 5 separate COUNT queries for different statuses
- **Impact**: 5 database queries when 1 would suffice
- **Severity**: MEDIUM
- **Solution**:
```php
// Current (5 queries)
$bookingStats = [
'total' => Booking::where('user_id', $user->id)->count(),
'pending' => Booking::where('user_id', $user->id)->where('status', 'pending')->count(),
'confirmed' => Booking::where('user_id', $user->id)->where('status', 'confirmed')->count(),
'completed' => Booking::where('user_id', $user->id)->where('status', 'completed')->count(),
'cancelled' => Booking::where('user_id', $user->id)->where('status', 'cancelled')->count(),
];
// Optimized (1 query with conditional aggregation)
$bookingStats = Booking::where('user_id', $user->id)
->selectRaw('
COUNT(*) as total,
SUM(CASE WHEN status = "pending" THEN 1 ELSE 0 END) as pending,
SUM(CASE WHEN status = "confirmed" THEN 1 ELSE 0 END) as confirmed,
SUM(CASE WHEN status = "completed" THEN 1 ELSE 0 END) as completed,
SUM(CASE WHEN status = "cancelled" THEN 1 ELSE 0 END) as cancelled
')
->first()
->toArray();
```
**Issue 1.3.2: N+1 Query in Monthly Stats Loop (Lines 93-117)**
- **Location**: `ClientController@getMonthlyBookingStats()`
- **Problem**: Querying bookings for each month in a loop (12 queries)
- **Impact**: 12 database queries
- **Severity**: HIGH
- **Solution**:
```php
// Current (12 queries in loop)
for ($i = 11; $i >= 0; $i--) {
$month = now()->subMonths($i);
$bookings = Booking::where('user_id', $user->id)
->whereYear('created_at', $year)
->whereMonth('created_at', $month->format('m'))
->get();
}
// Optimized (1 query with GROUP BY)
$stats = Booking::where('user_id', $user->id)
->where('created_at', '>=', now()->subMonths(12)->startOfMonth())
->selectRaw('
YEAR(created_at) as year,
MONTH(created_at) as month,
COUNT(*) as bookings,
SUM(CASE WHEN payment_status = "paid" THEN total_price ELSE 0 END) as spent,
SUM(CASE WHEN status = "pending" THEN 1 ELSE 0 END) as pending,
SUM(CASE WHEN status = "confirmed" THEN 1 ELSE 0 END) as confirmed,
SUM(CASE WHEN status = "completed" THEN 1 ELSE 0 END) as completed
')
->groupBy('year', 'month')
->orderBy('year', 'desc')
->orderBy('month', 'desc')
->get()
->map(function($item) {
return [
'month' => \Carbon\Carbon::create($item->year, $item->month)->format('M'),
'year' => $item->year,
'bookings' => $item->bookings,
'spent' => $item->spent,
'pending' => $item->pending,
'confirmed' => $item->confirmed,
'completed' => $item->completed,
];
})->toArray();
```
**Issue 1.3.3: Duplicate Recent Activity Query (Lines 28-32, 67-71)**
- **Location**: `ClientController@dashboard()`
- **Problem**: Same query executed twice with minor differences
- **Impact**: Unnecessary duplicate query
- **Severity**: LOW
- **Solution**: Reuse the same dataset
---
### 1.4 VenueController.php
**Issue 1.4.1: Missing Eager Loading (Line 13)**
- **Location**: `VenueController@getSpots()`
- **Problem**: No eager loading on venueSpots relationship
- **Impact**: If spots have relationships, could cause N+1
- **Severity**: LOW-MEDIUM
- **Solution**:
```php
// Add eager loading if spots have relationships
$spots = $venue->venueSpots()->with(['venue'])->get();
```
---
## 2. Model Performance Issues
### 2.1 Common Patterns to Check
**Issue 2.1.1: Accessors with Database Queries**
- **Problem**: Accessors that execute database queries can cause N+1 issues
- **Severity**: HIGH
- **Recommendation**: Review all accessor methods in models for database queries
**Issue 2.1.2: Missing Casts**
- **Problem**: JSON fields not cast to array, causing JSON parsing on every access
- **Severity**: MEDIUM
- **Recommendation**: Add `$casts` for all JSON columns
**Issue 2.1.3: Missing Eager Loading in Relationships**
- **Problem**: Relationships not pre-loaded in common queries
- **Severity**: HIGH
- **Recommendation**: Add `with()` in common queries
---
## 3. View Performance Issues
### 3.1 Blade Templates
**Issue 3.1.1: Database Queries in Views**
- **Problem**: Direct database queries in Blade templates
- **Impact**: Difficult to cache, hard to optimize
- **Severity**: HIGH
- **Recommendation**: Move all data fetching to controllers
**Issue 3.1.2: Inefficient Loops**
- **Problem**: Nested loops or loops with database queries
- **Impact**: O(n²) or worse performance
- **Severity**: HIGH
- **Recommendation**: Pre-compute data in controllers
**Issue 3.1.3: Missing Caching**
- **Problem**: Static data queried on every request
- **Impact**: Unnecessary database load
- **Severity**: MEDIUM
- **Recommendation**: Cache venue types, categories, countries, etc.
---
## 4. General Recommendations
### 4.1 Immediate Actions (High Priority)
1. **Fix N+1 queries in BookingController** - Use `whereIn()` before loops
2. **Fix N+1 queries in ReservationController** - Use `whereIn()` before loops
3. **Optimize ClientController dashboard stats** - Use conditional aggregation
4. **Optimize monthly stats query** - Use GROUP BY instead of loop
### 4.2 Short-term Actions (Medium Priority)
1. **Add eager loading** to all relationship queries
2. **Add caching** for static/infrequently changing data
3. **Review all accessors** for database queries
4. **Add JSON casts** for all JSON columns
### 4.3 Long-term Actions (Low Priority)
1. **Implement query caching** with Redis
2. **Add database query monitoring** (Laravel Telescope or debugbar)
3. **Implement lazy loading** for images
4. **Add pagination** for large datasets
---
## 5. Performance Monitoring
### 5.1 Recommended Tools
1. **Laravel Telescope** - Monitor queries, requests, exceptions
2. **Laravel Debugbar** - Development performance profiling
3. **Clockwork** - Request timeline and query analysis
4. **MySQL Slow Query Log** - Identify slow queries
### 5.2 Key Metrics to Monitor
1. **Query count per request** - Target: < 50 for most pages
2. **Query execution time** - Target: < 100ms for most queries
3. **Response time** - Target: < 500ms for most pages
4. **Memory usage** - Target: < 128MB for most pages
---
## 6. Specific File Recommendations
### 6.1 Files Requiring Immediate Attention
1. `app/Http/Controllers/BookingController.php` - Lines 107, 151+
2. `app/Http/Controllers/ReservationController.php` - Lines 47, 58
3. `app/Http/Controllers/ClientController.php` - Lines 36-40, 93-117
### 6.2 Files Requiring Review
1. `app/Models/Booking.php` - Check accessors and relationships
2. `app/Models/VenueObject.php` - Check accessors and relationships
3. `app/Models/VenueSpot.php` - Check accessors and relationships
4. All Blade views in `resources/views/web/` - Check for database queries
---
## 7. Estimated Performance Improvements
Implementing the high-priority fixes should result in:
- **60-80% reduction** in database queries on dashboard pages
- **50-70% reduction** in query execution time for booking/reservation creation
- **40-60% improvement** in overall page load times
---
## 8. Next Steps
1. Review and fix high-priority controller issues
2. Add eager loading to common queries
3. Implement caching for static data
4. Set up performance monitoring tools
5. Create automated performance tests
---
## 9. View Performance Issues
### 9.1 location.blade.php
**Issue 9.1.1: @php blocks with random generation in loops (Lines 552-556, 572-574, 704-708, 724-727, 736-737, 765-768)**
- **Problem**: Random number generation in @php blocks inside foreach loops
- **Impact**: Unnecessary computation on every iteration
- **Severity**: MEDIUM
- **Solution**: Move random generation outside loops or pre-compute in controller
```php
// Current (inefficient)
@foreach($venues as $venue)
@php
$propertyTypes = ['Apartment', 'House', 'Villa', 'Studio'];
$selectedType = $propertyTypes[array_rand($propertyTypes)];
$bedrooms = rand(0, 4);
$guests = rand(2, 8);
@endphp
@endforeach
// Optimized (pre-compute in controller)
@foreach($venues as $venue)
<span class="px-3 py-1">{{ $venue->property_type }}</span>
<span>{{ $venue->bedrooms }} {{ __('bed') }}</span>
@endforeach
```
**Issue 9.1.2: Method calls in views without caching (Lines 598, 750)**
- **Problem**: `$venue->getLowestPrice()` called in loop
- **Impact**: Potential database queries in each iteration
- **Severity**: HIGH
- **Solution**: Pre-compute in controller with eager loading
```php
// In controller
$venues = Venue::with(['venueFacilities', 'place', 'location'])
->get()
->map(function($venue) {
$venue->lowest_price = $venue->getLowestPrice();
return $venue;
});
```
**Issue 9.1.3: Missing eager loading (Lines 528, 532)**
- **Problem**: Accessing `$venue->place`, `$venue->location`, `$venue->venueFacilities` in loop
- **Impact**: N+1 queries
- **Severity**: HIGH
- **Solution**: Add eager loading in controller
```php
// In controller
$venues = Venue::with(['place', 'location', 'venueFacilities'])->get();
```
### 9.2 bookings.blade.php
**Issue 9.2.1: Missing eager loading for relationships (Lines 91-100)**
- **Problem**: Accessing `$booking->venueServices` and `$booking->venuePackages` in loop
- **Impact**: N+1 queries
- **Severity**: HIGH
- **Solution**: Add eager loading in controller
```php
// In controller
$bookings = Booking::with(['venue', 'venueServices', 'venuePackages'])->get();
```
### 9.3 booking-details.blade.php
**Issue 9.3.1: Missing eager loading for venue (Lines 44-64)**
- **Problem**: Accessing `$booking->venue` without eager loading
- **Impact**: N+1 query
- **Severity**: MEDIUM
- **Solution**: Add eager loading in controller
```php
// In controller
$booking = Booking::with(['venue'])->findOrFail($id);
```
### 9.4 home.blade.php
**Issue 9.4.1: Large video file (Line 8)**
- **Problem**: Video background without lazy loading or optimization
- **Impact**: Slow initial page load
- **Severity**: MEDIUM
- **Solution**: Add lazy loading, use compressed version, or consider image fallback
```html
<!-- Add loading attribute -->
<video id="hero-video" autoplay muted loop playsinline preload="metadata" class="...">
<source src="{{ asset('public/media/videos/hero8.mp4') }}" type="video/mp4">
</video>
<!-- Or use poster image -->
<video poster="{{ asset('images/hero-poster.jpg') }}" ...>
```
### 9.5 company-registration-wizard.blade.php
**Issue 9.5.1: Debug info section exposed (Lines 50-82)**
- **Problem**: Debug information displayed in production view
- **Impact**: Security risk, performance overhead
- **Severity**: HIGH
- **Solution**: Remove debug section from production code
```php
// Remove this entire section
@if(session('debug_info'))
<div class="mb-6 p-4 bg-red-100 border border-red-300 rounded-lg">
...
</div>
@endif
```
---
## 10. View Performance Recommendations
### 10.1 Immediate Actions (High Priority)
1. **Add eager loading to all booking-related views**
- `bookings.blade.php`: Add `venueServices`, `venuePackages`
- `booking-details.blade.php`: Add `venue`
- `location.blade.php`: Add `place`, `location`, `venueFacilities`
2. **Remove debug code from production views**
- Remove `session('debug_info')` section from `company-registration-wizard.blade.php`
3. **Pre-compute random values in controller**
- Move random generation from view to controller for `location.blade.php`
### 10.2 Short-term Actions (Medium Priority)
1. **Optimize video loading**
- Add `preload="metadata"` to video
- Add poster image
- Consider using compressed version
2. **Cache view fragments**
- Cache static sections like footer, navigation
- Use `@cache` directive for expensive computations
3. **Lazy load images**
- Add `loading="lazy"` to all images
- Use placeholder images for initial load
### 10.3 Long-term Actions (Low Priority)
1. **Implement view component caching**
- Cache entire components that don't change frequently
- Use Laravel's fragment caching
2. **Use view composer for shared data**
- Move common data to view composers
- Avoid repeated queries in multiple views
3. **Implement CDN for static assets**
- Serve images, videos from CDN
- Use CloudFront or similar service
---
## 11. View-Specific Fixes
### Fix for location.blade.php Controller
```php
// In the controller that loads location.blade.php
public function show($locationId, Request $request)
{
$location = Location::findOrFail($locationId);
$venues = Venue::where('location_id', $locationId)
->with(['place', 'location', 'venueFacilities'])
->get()
->map(function($venue) {
$venue->lowest_price = $venue->getLowestPrice();
// Pre-compute random values if needed
$venue->property_type = $this->getRandomPropertyType($venue->id);
$venue->bedrooms = $this->getRandomBedrooms($venue->id);
$venue->guests = $this->getRandomGuests($venue->id);
$venue->rating = $this->getRandomRating($venue->id);
$venue->reviews_count = $this->getRandomReviews($venue->id);
return $venue;
});
return view('web.location', compact('location', 'venues', ...));
}
```
### Fix for bookings.blade.php Controller
```php
// Add eager loading
$bookings = Booking::with(['venue', 'venueServices', 'venuePackages'])
->where('user_id', Auth::id())
->orderBy('created_at', 'desc')
->paginate(20);
```
### Fix for booking-details.blade.php Controller
```php
// Add eager loading
$booking = Booking::with(['venue', 'venueServices', 'venuePackages'])
->findOrFail($id);
```
---
## 12. Estimated View Performance Improvements
Implementing the view optimizations should result in:
- **70-90% reduction** in database queries for listing pages
- **40-60% improvement** in initial page load time for home page
- **50-70% improvement** in rendering time for loops with relationships
- **30-50% improvement** in overall page performance