Hi All, Thank you for all your replies. Finally I was able to recreate the format I was looking for. Below is screenshot how it looks.

Below is the solution (Full Export Class) code for my works. It was actually really simple while I was figuring out how.
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Style\Border;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithDrawings;
use Maatwebsite\Excel\Concerns\WithHeadings;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithProperties;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use Maatwebsite\Excel\Concerns\WithCustomStartCell;
class ReservationsExport implements
FromCollection,
WithHeadings,
WithMapping,
WithStyles,
WithProperties,
WithDrawings,
WithCustomStartCell
{
use Exportable;
protected $reservations;
protected $restaurantName;
protected $mealTime;
protected $dateRange;
public function __construct($reservations, $restaurantName, $mealTime, $dateRange)
{
$this->reservations = $reservations;
$this->restaurantName = $restaurantName;
$this->mealTime = $mealTime;
$this->dateRange = $dateRange;
}
public function collection()
{
return $this->reservations->sortBy('reservation_time');
}
public function map($reservation): array
{
return [
$reservation->reservation_time,
implode(',', $reservation->table_no),
$reservation->room_number,
$reservation->guest_name,
$reservation->adults,
$reservation->kids,
$reservation->meal_plan,
$reservation->mealTime->name,
$reservation->user->username,
$reservation->reservation_date,
$reservation->restaurant->name,
$reservation->description,
$reservation->reservation_time,
];
}
public function headings(): array
{
return [
'Time',
'Table No.',
'Room',
'Name',
'Adults',
'Kids',
'Meal Plan',
'Meal Time',
'User',
'Date',
'Restaurant',
'Description',
'Time',
];
}
public function startCell(): string
{
return 'B11';
}
public function styles(Worksheet $sheet)
{
$sheet->setAutoFilter('B11:N11');
$sheet->getStyle(11)->getFont()->setBold(true);
$sheet->getStyle(11)->getFont()->setSize(12);
$sheet->getStyle('B11:N11')->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
$sheet->getStyle('B11:N11')->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
$sheet->getStyle('B11:N11')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
$sheet->getRowDimension('11')->setRowHeight(20);
$sheet->getColumnDimension('B')->setWidth(15);
$sheet->getColumnDimension('C')->setWidth(15);
$sheet->getColumnDimension('D')->setWidth(15);
$sheet->getColumnDimension('E')->setWidth(30);
$sheet->getColumnDimension('H')->setWidth(15);
$sheet->getColumnDimension('I')->setWidth(15);
$sheet->getColumnDimension('J')->setWidth(20);
$sheet->getColumnDimension('K')->setWidth(20);
$sheet->getColumnDimension('L')->setWidth(25);
$sheet->getColumnDimension('M')->setWidth(40);
$sheet->setShowGridlines(false);
$sheet->setCellValue('C5', 'Restaurant: ');
$sheet->setCellValue('D5', $this->restaurantName);
$sheet->setCellValue('C6', 'Total Covers: ');
$sheet->setCellValue('D6', $this->reservations->sum('adults') + $this->reservations->sum('kids'));
$sheet->getStyle('D6')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
$sheet->setCellValue('C7', 'Meal Time: ');
$sheet->setCellValue('D7', $this->mealTime);
$sheet->setCellValue('C8', 'Date Range: ');
$sheet->setCellValue('D8', $this->dateRange);
$sheet->setCellValue('C9', 'Exported At: ');
$sheet->setCellValue('D9', date('Y-m-d H:i:s'));
$reservations = $this->reservations->sortBy('reservation_time')->groupBy('reservation_time');
$currentRow = 12;
$lastRow = 0;
foreach ($reservations as $time => $group) {
$sheet->mergeCells("B{$currentRow}:B".($currentRow+count($group)-1));
$sheet->setCellValue("B{$currentRow}", $time);
$sheet->getStyle("B{$currentRow}")->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
$sheet->getStyle("B{$currentRow}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
foreach ($group as $reservation) {
$row = $currentRow++;
$sheet->getRowDimension($row)->setRowHeight(20);
$sheet->getStyle("B{$row}:N{$row}")->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
$sheet->getStyle("B{$row}:N{$row}")->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
$sheet->getStyle("B{$row}:N{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
}
$lastRow = $currentRow-1;
$sheet->getStyle("B{$lastRow}:N{$lastRow}")->getBorders()->getBottom()->setBorderStyle(Border::BORDER_DOUBLE);
}
}
public function drawings()
{
$drawing = new Drawing();
$drawing->setName('Logo');
$drawing->setDescription('This is my logo');
$drawing->setPath(public_path('/assets/img/logo/logo.png'));
$drawing->setHeight(90);
$drawing->setCoordinates('B5');
$drawing->setWidth(250);
$drawing->setHeight(100);
return $drawing;
}
}
And here is my Controller export method code.
public function export(Request $request)
{
$reservations = $this->reservationRepo->search($request->all())->get();
if (!$request->has('restaurant')) {
$restaurantId = null;
$restaurantNames = $reservations->unique('restaurant.name')->pluck('restaurant.name')->implode(', ');
$restaurantName = $restaurantNames;
} else {
$restaurantId = $request->input('restaurant');
$restaurantName = Restaurant::where('id', $restaurantId)->first()?->name;
}
if (!$request->has('meal_time')) {
$mealTimeId = null;
$mealTimeNames = $reservations->unique('mealTime.name')->pluck('mealTime.name')->implode(', ');
$mealTime = $mealTimeNames;
} else {
$mealTimeId = $request->input('meal_time');
$mealTime = MealTime::where('id', $mealTimeId)->first()->name;
}
if (!$request->has('date_range')) {
$dateFrom = now()->startOfDay();
$dateTo = now()->endOfDay();
$dateRange = $dateFrom->format('Y-m-d').' - '.$dateTo->format('Y-m-d');
} else {
$dateRange = $request->input('date_from').' - '.$request->input('date_to');
}
if ($reservations->isEmpty())
{
$restaurantName = '';
$mealTime = '';
$dateRange = '';
}
return Excel::download(new ReservationsExport($reservations, $restaurantName, $mealTime, $dateRange), 'Reservations Report (General) '.now().'.xlsx');
}
In case if anyone is wondering what is the "reservationRepo->search()" here is the code for ReservationRepository class
<?php
namespace App\Repositories\Reservations;
use App\Models\Reservation;
class ReservationRepository
{
public function search(array $params)
{
$reservations = Reservation::query();
if (isset($params['search'])) {
$reservations->search($params['search']);
}
if (isset($params['date_from'])) {
$reservations->where('reservation_date', '>=', $params['date_from']);
}
if (isset($params['date_to'])) {
$reservations->where('reservation_date', '<=', $params['date_to']);
}
if (isset($params['restaurant'])) {
$reservations->where('restaurant_id', $params['restaurant']);
}
if (isset($params['status'])) {
$reservations->where('status', $params['status']);
}
if (isset($params['meal_time'])) {
$reservations->where('meal_time_id', $params['meal_time']);
}
return $reservations;
}
}
And here is what I did on front end with the export button to get the requested parameters for filtering. (Additional for anyone else who is having same issue)
<button class="btn btn-warning d-flex align-items-center justify-content-between export" type="button" onclick="location.href='{{ route('admin.reservationsystem.reporting.export') }}?{{ http_build_query(request()->query()) }}'">
<i class="bx bx-export me-1 mt-0"></i>
Export
</button>
Let me know if anyone is having any questions about this. Also please suggest me the best practices if I'm doing anything wrong here.