Donnerstag, 18 Dezember 2025

Diese Woche am beliebtesten

Vertiefendes Material

Restaurant Tische Live

Mit unserem Tischzuweisungs System organisierst du Gäste in deinem Restaurant effizient und modern. Unser System bietet:

  • Gäste-Eintragung per QR-Code: Gäste scannen den Code, tragen sich selbst ein, einfach und kontaktlos.
  • Live Updates für Service & Küche: Jede Anmeldung wird sofort auf den Dashboards der Kellner und in der Küche angezeigt.
  • Tischzuweisung & Statusverwaltung: Service-Mitarbeiter können Tische zuweisen und den Status der Gäste aktualisieren.
  • Admin-Bereich mit PIN-Schutz: Kontrolliere alle Vorgänge zentral und sicher.
  • Statistiken & Monitoring: Behalte den Überblick über aktuelle Gäste und Auslastung.

Setup in einer Datei: Mit nur einem Klick werden alle benötigten Dateien und Ordner automatisch erstellt, inkl. WebSocket-Live-Update, QR-Code-Integration und modernen Dashboards.

Perfekt für Restaurants, Cafés oder Bars, die ihre Gästeverwaltung digital, live und ohne komplizierte Installation umsetzen wollen.

<?php
/**
Ein Dreamcodes.NET Codeschnipsel
 */

// -----------------------------
// Basisverzeichnisse
// -----------------------------
$baseDir = __DIR__;
$folders = [
    "$baseDir/public",
    "$baseDir/public/css",
    "$baseDir/public/js",
    "$baseDir/api",
    "$baseDir/data"
];
foreach ($folders as $f) if(!is_dir($f)) mkdir($f,0777,true);

// -----------------------------
// CSS
// -----------------------------
$cssFile = "$baseDir/public/css/style.css";
if(!file_exists($cssFile)){
    file_put_contents($cssFile, <<<CSS
body { font-family:Arial,sans-serif; margin:0; padding:0; background:#f2f2f2; }
header { background:#333;color:#fff;padding:15px;font-size:24px; }
main { padding:20px; }
.card { background:#fff;padding:20px;margin-bottom:20px;border-radius:8px;box-shadow:0 4px 8px rgba(0,0,0,.1);}
button { padding:10px 14px;border:none;border-radius:6px;background:#007bff;color:white;cursor:pointer;}
table{width:100%;border-collapse:collapse;}
td,th{padding:10px;border-bottom:1px solid #ddd;}
#qrcode{margin-top:20px;}
CSS
);
}

// -----------------------------
// JS
// -----------------------------
$jsFile = "$baseDir/public/js/app.js";
if(!file_exists($jsFile)){
    file_put_contents($jsFile, <<<JS
let ws;
function connectWS(role){
    ws = new WebSocket('ws://localhost:8080');
    ws.onopen = ()=>ws.send(JSON.stringify({type:'join',role}));
    ws.onmessage = e=>{
        const msg = JSON.parse(e.data);
        if(msg.type==='newEntry') updateUI(msg.entry);
        if(msg.type==='updateEntry') updateUI(msg.entry);
    };
}

function ajax(action,data,callback){
    data.action=action;
    fetch('/api/api.php',{method:'POST',body:new URLSearchParams(data)})
        .then(r=>r.json()).then(callback);
}
JS
);
}

// -----------------------------
// API
// -----------------------------
$apiFile = "$baseDir/api/api.php";
if(!file_exists($apiFile)){
    file_put_contents($apiFile, <<<PHP
<?php
session_start();
header('Content-Type: application/json');

\$dataFile = __DIR__.'/../data/db.json';
if(!file_exists(\$dataFile)) file_put_contents(\$dataFile,'[]');
\$db = json_decode(file_get_contents(\$dataFile),true);

\$action = \$_GET['action']??'';

function save(\$db,\$file){ file_put_contents(\$file,json_encode(\$db,JSON_PRETTY_PRINT)); }

switch(\$action){
    case 'login':
        \$pin = \$_POST['pin']??'';
        if(\$pin==='1234'){ \$_SESSION['admin']=true; echo json_encode(['ok'=>true]); }
        else echo json_encode(['ok'=>false]);
        exit;

    case 'add':
        \$entry=[
            'id'=>uniqid(),
            'name'=>htmlspecialchars(\$_POST['name']??''),
            'guests'=>intval(\$_POST['guests']??0),
            'table'=>null,
            'status'=>'waiting',
            'time'=>date('H:i')
        ];
        \$db[]=\$entry;
        save(\$db,\$dataFile);
        file_put_contents(__DIR__.'/../data/ws_push.json',json_encode(['type'=>'newEntry','entry'=>\$entry]));
        echo json_encode(['ok'=>true,'entry'=>\$entry]);
        exit;

    case 'update':
        foreach(\$db as &\$e){
            if(\$e['id']===\$_POST['id']){
                if(isset(\$_POST['table'])) \$e['table']=$_POST['table'];
                if(isset(\$_POST['status'])) \$e['status']=$_POST['status'];
            }
        }
        save(\$db,\$dataFile);
        file_put_contents(__DIR__.'/../data/ws_push.json',json_encode(['type'=>'updateEntry','entry'=>$_POST]));
        echo json_encode(['ok'=>true]);
        exit;

    case 'list':
        echo json_encode(\$db); exit;
}
echo json_encode(['ok'=>false,'msg'=>'no action']);
PHP
);
}

// -----------------------------
// Gäste-Seite mit QR-Code
// -----------------------------
$guestFile = "$baseDir/public/guest.html";
$qrUrl = 'http://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']).'/public/guest.html';
if(!file_exists($guestFile)){
    file_put_contents($guestFile, <<<HTML
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="css/style.css">
<script src="js/app.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
</head>
<body>
<header>Gäste Anmeldung</header>
<main>
<div class="card">
<input id="name" placeholder="Name">
<input id="guests" type="number" placeholder="Anzahl Gäste">
<button onclick="addGuest()">Eintragen</button>
<div id="qrcode"></div>
</div>
<script>
connectWS('guest');
function addGuest(){
    let n=name.value, g=guests.value;
    ajax('add',{name:n,guests:g},function(res){
        if(res.ok){ alert('Danke, '+res.entry.name+'!'); name.value=''; guests.value=''; }
    });
}

// QR-Code erzeugen
new QRCode(document.getElementById("qrcode"), {
    text: "$qrUrl",
    width: 160,
    height: 160
});
</script>
</main>
</body>
</html>
HTML
);
}

// -----------------------------
// Service-Seite
// -----------------------------
$serviceFile = "$baseDir/public/service.html";
if(!file_exists($serviceFile)){
    file_put_contents($serviceFile, <<<HTML
<!DOCTYPE html>
<html>
<head><link rel="stylesheet" href="css/style.css"><script src="js/app.js"></script></head>
<body>
<header>Service Dashboard</header>
<main>
<table><thead><tr><th>Name</th><th>Gäste</th><th>Tisch</th><th>Status</th><th></th></tr></thead>
<tbody id="rows"></tbody></table>
</main>
<script>
connectWS('service');

function updateUI(e){
    let tbody=document.getElementById('rows');
    let row=document.getElementById(e.id);
    if(!row){
        tbody.innerHTML+=\`<tr id="\${e.id}"><td>\${e.name}</td><td>\${e.guests}</td>
        <td><input value="\${e.table??''}" onchange="update('\${e.id}','table',this.value)"></td>
        <td>\${e.status}</td>
        <td><button onclick="update('\${e.id}','status','seated')">✔</button></td></tr>\`;
    } else {
        row.cells[2].firstChild.value = e.table??'';
        row.cells[3].textContent = e.status;
    }
}

function update(id,field,val){
    let data={id}; data[field]=val;
    ajax('update',data,function(){ ws.send(JSON.stringify({type:'updateEntry',entry:data})); });
}

// Initial fetch
ajax('list',{},function(list){ list.forEach(updateUI); });
</script>
</body>
</html>
HTML
);
}

// -----------------------------
// Küchenansicht
// -----------------------------
$kitchenFile = "$baseDir/public/kitchen.html";
if(!file_exists($kitchenFile)){
    file_put_contents($kitchenFile, <<<HTML
<!DOCTYPE html>
<html>
<head><link rel="stylesheet" href="css/style.css"><script src="js/app.js"></script></head>
<body>
<header>Küchen Monitor</header>
<main>
<ul id="queue"></ul>
</main>
<script>
connectWS('kitchen');

function updateUI(e){
    let ul=document.getElementById('queue');
    let li=document.getElementById('li_'+e.id);
    if(!li && e.status==='waiting'){
        ul.innerHTML+='<li id="li_'+e.id+'">'+e.name+' ('+e.guests+')</li>';
    } else if(li && e.status!=='waiting'){
        li.remove();
    }
}

// Initial fetch
ajax('list',{},function(list){ list.forEach(updateUI); });
</script>
</body>
</html>
HTML
);
}

// -----------------------------
// Admin-Login
// -----------------------------
$adminFile = "$baseDir/public/admin.html";
if(!file_exists($adminFile)){
    file_put_contents($adminFile, <<<HTML
<!DOCTYPE html>
<html>
<head><link rel="stylesheet" href="css/style.css"><script src="js/app.js"></script></head>
<body>
<header>Admin Login</header>
<main>
<div class="card">
<input id="pin" placeholder="PIN">
<button onclick="login()">Login</button>
</div>
<script>
function login(){
    ajax('login',{pin:pin.value},function(res){
        if(res.ok) location='service.html';
        else alert('Falscher PIN');
    });
}
</script>
</main>
</body>
</html>
HTML
);
}

// -----------------------------
// Setup abgeschlossen
// -----------------------------
echo "<h1>Setup abgeschlossen!</h1>";
echo "<ul>
<li><a href='public/guest.html'>Gäste</a></li>
<li><a href='public/service.html'>Service/Kellner</a></li>
<li><a href='public/kitchen.html'>Küche</a></li>
<li><a href='public/admin.html'>Admin</a></li>
</ul>";
echo "<p>Datenbank: data/db.json</p>";
echo "<p>Bitte starten Sie zusätzlich den WebSocket-Server auf ws://localhost:8080</p>";

Schritte nach Setup:

  1. Datei Php Dateieinmal im Browser aufrufen → alles wird erstellt.
  2. WebSocket-Server starten (PHP Ratchet):
<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
require __DIR__.'/vendor/autoload.php';

class GuestFlowWS implements MessageComponentInterface {
    protected $clients;
    public function __construct(){ $this->clients = new \SplObjectStorage; }
    public function onOpen(ConnectionInterface $conn){ $this->clients->attach($conn); }
    public function onMessage(ConnectionInterface $from, $msg){
        foreach($this->clients as $client){ if($from!==$client) $client->send($msg); }
    }
    public function onClose(ConnectionInterface $conn){ $this->clients->detach($conn); }
    public function onError(ConnectionInterface $conn, \Exception $e){ $conn->close(); }
}

$app = new Ratchet\Server\IoServer(
    new Ratchet\Http\HttpServer(new Ratchet\WebSocket\WsServer(new GuestFlowWS())),
    new React\Socket\SocketServer('0.0.0.0:8080')
);
$app->run();
Dreamcodes Redaktion
Dreamcodes Redaktion
Jeder auf Dreamcodes bereitgestellte Codeschnipsel sowie jede Tutorial Anleitung basiert auf geprüften Best Practices und fundierter Praxiserfahrung. Ziel ist es, ein belastbares technisches Fundament bereitzustellen und keine unausgereiften oder experimentellen Lösungen zu veröffentlichen. Die konkrete Nutzung, Integration, Anpassung und Absicherung der Inhalte obliegt jedoch dem Anwender. Vor dem produktiven Einsatz sind sämtliche Inhalte eigenverantwortlich zu prüfen, zu testen und gegebenenfalls abzusichern. Dreamcodes stellt die technische Grundlage zur Verfügung, die finale Umsetzung und Verantwortung verbleibt beim Nutzer.
Vorheriges Tutorial

Vielleicht einen Blick WERT?