TL;DR
For my Software Development Workshop, I built a web-based Restaurant Management System with PHP, MySQL, JavaScript and Bootstrap. It covers menu management, order processing, inventory tracking and payments, with dedicated dashboards for administrators, waiters and chefs controlled through role-based access.
Many small restaurants still rely on paper order slips and verbal communication between the floor and the kitchen. Orders get lost, inventory runs out unexpectedly, and it is hard to know what is happening at any moment.
My goal for this project was to digitalise those daily operations. The most interesting challenge was not any single feature, but making sure each type of staff member could do their job without seeing or changing things they should not.
01 The Problem
A restaurant has several roles working on the same orders at the same time, each with different needs:
- Waiters take orders from customers and need to send them to the kitchen quickly.
- Chefs need a clear queue of what to cook and a way to mark dishes as ready.
- Administrators manage the menu, inventory, staff accounts and payments.
If everyone uses the same screen with the same permissions, the system becomes cluttered and risky. A waiter should not be able to change menu prices, and a chef does not need to see payment records.
02 Designing the Database
I started with the relational database, because every feature depends on it. The main entities and how they relate:
- users (with role)
- staff details
- login credentials
- menu_items
- categories
- inventory
- orders
- order_items
- payments
An order belongs to a table and is created by a waiter. Each order has many order items, each pointing to a menu item. A payment is linked to a completed order. Splitting orders and order items into separate tables keeps the design normalised and makes it easy to track the status of each dish.
03 Implementing Role-Based Access
Every user account has a role: admin, waiter or chef. When a user logs in, the role is stored in the session and used in two ways:
- Routing: after login, each user is sent to their own dashboard.
- Protection: every page checks the role before showing anything, so typing a URL directly does not bypass the rules.
A simplified version of the access check that runs at the top of each protected page:
<?php
session_start();
function requireRole(array $allowedRoles) {
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
if (!in_array($_SESSION['role'], $allowedRoles)) {
http_response_code(403);
exit('Access denied');
}
}
// Example: only admins can manage the menu
requireRole(['admin']);
Why check on every page?
Hiding a menu link is not security. Checking the role on the server for every request ensures users can only reach the pages and actions meant for them.
04 Real-Time Order Tracking
The feature that made the biggest difference was order tracking between the floor and the kitchen. Each order item moves through simple statuses, such as pending, cooking and ready.
- When a waiter submits an order, it appears in the chef dashboard queue.
- When a chef marks a dish as ready, the waiter dashboard shows it so the food can be served.
- The admin dashboard gives an overview of all orders, inventory levels and payments.
JavaScript refreshes the dashboards so staff see updates without reloading the page manually, which replaces the shouting and paper slips with a shared, up-to-date view.
05 Key Lessons & Takeaways
Start with the data model
A clear, normalised database made every later feature easier to build.
Security belongs on the server
Role checks must run on every request, not just in the interface.
Design dashboards around tasks
Each role sees only what helps them do their job, which keeps the interface simple.
Talk to the users
Thinking through how waiters and chefs actually work shaped the order statuses and screens.
What's Next?
This project taught me how much good database design and access control matter in real systems. If you'd like to discuss it, feel free to reach out.
Get In Touch