Overview
The MeseroController (Waiter Controller) handles operations for restaurant waiters/servers, including table management and reservation assignment.
Location: app/Http/Controllers/MeseroController.php
Route Prefix: /mesero
Middleware: auth, role:mesero
Table Management
viewTables()
Display all tables in the restaurant.
Route: GET /mesero/tables
Route Name: mesero.tables
Implementation:
public function viewTables()
{
$tables = Table::all();
return view('mesero.meseromesas', compact('tables'));
}
Collection of all Table models with their current status, seats, and details
storeTable()
Create a new table.
Route: POST /mesero/tables
Route Name: mesero.tables.store
Implementation:
public function storeTable(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'number' => 'required|integer|unique:tables,number',
'type' => 'required|string',
'seats' => 'required|integer',
'status' => 'required|string',
]);
Table::create($request->all());
return redirect()->route('meseromesas')->with('success', 'Mesa agregada correctamente.');
}
Validation Rules:
Table name or identifier (max 255 characters)
Unique table number (must be unique across all tables)
Table type (e.g., “interior”, “exterior”, “VIP”, “terraza”)
Request Example:
{
"name": "Mesa Premium 5",
"number": 5,
"type": "VIP",
"seats": 6,
"status": "disponible"
}
updateTable()
Update an existing table’s information.
Route: PUT /mesero/tables/{id}
Route Name: mesero.tables.update
Implementation:
public function updateTable(Request $request, $id)
{
$request->validate([
'name' => 'required|string|max:255',
'number' => 'required|integer|unique:tables,number,' . $id,
'type' => 'required|string',
'seats' => 'required|integer',
'status' => 'required|string|in:available,occupied,reservada',
]);
$table = Table::findOrFail($id);
// Asegúrate de actualizar campos individuales para evitar errores
$table->name = $request->input('name');
$table->number = $request->input('number');
$table->type = $request->input('type');
$table->seats = $request->input('seats');
$table->status = $request->input('status');
$table->save();
return redirect()->route('meseromesas')->with('success', 'Mesa actualizada correctamente.');
}
Validation Rules:
Table ID to update (URL parameter)
Table name (max 255 characters)
Table number (unique, except for current table)
Table status - must be one of:
available - Table is available
occupied - Table is currently in use
reservada - Table is reserved
Request Example:
{
"name": "Mesa 5 - Actualizada",
"number": 5,
"type": "interior",
"seats": 4,
"status": "occupied"
}
deleteTable()
Delete a table from the system.
Route: DELETE /mesero/tables/{id}
Route Name: mesero.tables.delete
Implementation:
public function deleteTable($id)
{
$table = Table::findOrFail($id);
$table->delete();
return redirect()->route('meseromesas')->with('success', 'Mesa eliminada correctamente.');
}
Note: Deleting a table will fail if it has foreign key constraints (e.g., active reservations).
Reservation Management
meseroviewreservation()
Display all reservations with available tables for assignment.
Route: GET /mesero/reservations
Route Name: mesero.reservations
Implementation:
public function meseroviewreservation()
{
// Obtener todas las reservaciones y las mesas disponibles
$reservations = Reservation::with('table')->get();
$tables = Table::where('status', 'disponible')->get();
// Pasar las variables a la vista
return view('mesero.meseroreservation', compact('reservations', 'tables'));
}
Collection of all Reservation models with eager-loaded table relationshipsEach reservation includes:
id - Reservation ID
name - Guest name
email - Guest email
phone - Contact phone
guest - Number of guests
date - Reservation date
time - Reservation time
message - Special requests
table_id - Assigned table ID (nullable)
table - Related Table model (if assigned)
Collection of available tables (status = ‘disponible’) that can be assigned to reservations
assignTable()
Assign a table to a reservation. Validates that the number of guests matches the table’s seat capacity.
Route: POST /mesero/reservations/{reservationId}/assign-table
Route Name: mesero.reservations.assign
Implementation:
public function assignTable(Request $request, $reservationId)
{
$reservation = Reservation::findOrFail($reservationId);
$table = Table::findOrFail($request->table_id);
// Verificar si el número de invitados coincide con el número de asientos
if ($reservation->guest == $table->seats) {
// Asignar la mesa a la reservación
$reservation->table_id = $table->id;
$reservation->save();
// Marcar la mesa como reservada
$table->status = 'reservada';
$table->save();
return response()->json([
'success' => true,
'table_name' => $table->name,
'user_name' => $reservation->name,
]);
} else {
return response()->json([
'success' => false,
'message' => 'El número de invitados no coincide con los asientos de la mesa.',
]);
}
}
Parameters:
Reservation ID to assign table to (URL parameter)
Table ID to assign (request body)
Request Example:
Success Response:
{
"success": true,
"table_name": "Mesa VIP 5",
"user_name": "John Doe"
}
Indicates whether the assignment was successful
Name of the assigned table
Name of the guest who made the reservation
Error Response:
{
"success": false,
"message": "El número de invitados no coincide con los asientos de la mesa."
}
False when assignment fails
Error message explaining why the assignment failed
Business Logic:
- Validates that the reservation exists
- Validates that the table exists
- Critical validation: Checks if
reservation.guest (number of guests) exactly matches table.seats (table capacity)
- If match:
- Assigns
table_id to the reservation
- Changes table
status to 'reservada'
- Returns success with table and guest names
- If no match:
- Returns error message
- No changes are made to reservation or table
Additional Controllers
The system also includes a dashboard controller for waiters:
Mesero\DashboardController
Location: app/Http/Controllers/Mesero/DashboardController.php
Route: GET /admin/dashboard (unified panel)
Implementation:
public function index(): View
{
$tablesCount = Table::count();
$reservedCount = Table::where('status', 'reservada')->count();
$availableCount = Table::where('status', 'disponible')->count();
$reservationsToday = Reservation::whereDate('date', now()->toDateString())->count();
return view('mesero.meserohome', compact(
'tablesCount',
'reservedCount',
'availableCount',
'reservationsToday'
));
}
Dashboard Metrics:
Total number of tables in the restaurant
Number of currently reserved tables
Number of available tables
Number of reservations scheduled for today
Data Models
Table Model
class Table extends Model
{
protected $fillable = [
'name',
'number',
'type',
'seats',
'status',
];
public function reservations()
{
return $this->hasMany(Reservation::class);
}
}
Reservation Model
class Reservation extends Model
{
protected $fillable = [
'name',
'email',
'phone',
'guest',
'date',
'time',
'message',
'table_id',
];
protected $casts = [
'date' => 'date',
'time' => 'datetime',
];
public function table()
{
return $this->belongsTo(Table::class);
}
}
Table Status Flow
disponible
reservada
occupied
Available
- Table is free and ready for customers
- Can be assigned to new reservations
- Default status for new tables
Reserved
- Table has been assigned to a reservation
- Set automatically when
assignTable() is called
- Cannot be assigned to other reservations until freed
Occupied
- Table is currently in use by customers
- Manually set by waiters
- Should be set to
disponible after customers leave
Usage Examples
Assign Table via AJAX
fetch(`/mesero/reservations/${reservationId}/assign-table`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({ table_id: tableId })
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert(`Mesa ${data.table_name} asignada a ${data.user_name}`);
} else {
alert(data.message);
}
});
Update Table Status
<form action="{{ route('mesero.tables.update', $table->id) }}" method="POST">
@csrf
@method('PUT')
<input type="hidden" name="name" value="{{ $table->name }}">
<input type="hidden" name="number" value="{{ $table->number }}">
<input type="hidden" name="type" value="{{ $table->type }}">
<input type="hidden" name="seats" value="{{ $table->seats }}">
<select name="status">
<option value="available">Disponible</option>
<option value="occupied">Ocupada</option>
<option value="reservada">Reservada</option>
</select>
<button type="submit">Actualizar</button>
</form>