Compare commits

...
14 Commits
Author SHA1 Message Date
Bruno Eckhardt 7a2d53d7b1 add documentaation 2026-05-22 14:53:34 +02:00
Bruno Eckhardt 307819681e docs 2026-05-22 14:51:19 +02:00
Bruno Eckhardt 27c11ba5ff changes and new login 2026-05-22 14:27:48 +02:00
Bruno Eckhardt c44b508689 new 2026-05-22 12:48:07 +02:00
Bruno Eckhardt 9d0863d65f fixes 2026-05-22 12:24:06 +02:00
Bruno Eckhardt bfd16c310d new 2026-05-22 11:43:59 +02:00
Bruno Eckhardt 85219fdb59 api 2026-05-22 11:28:51 +02:00
Bruno Eckhardt dc9ef406fc fix 2026-05-22 11:28:40 +02:00
Bruno Eckhardt 83f43b0149 add classes 2026-05-22 11:28:27 +02:00
Bruno Eckhardt d7cb5084ef fixes 2026-05-22 09:48:44 +02:00
Bruno Eckhardt 59eba3ef2b comment und duplication fixes 2026-05-22 09:09:29 +02:00
Bruno Eckhardt dbcf1228d8 puffer service 2026-05-22 08:49:56 +02:00
Bruno Eckhardt e341f4cfe1 del 2026-05-22 08:49:40 +02:00
Bruno Eckhardt 6ef6c15942 init 2026-05-22 08:16:42 +02:00
50 changed files with 1753 additions and 189 deletions

No files matched your search

-1
View File
@@ -1 +0,0 @@
<?php header('Content-Type: application/json'); echo json_encode(['status' => 'online']);
-33
View File
@@ -1,33 +0,0 @@
<?php
session_start();
require_once __DIR__ . '/src/MVC/autoloader.php';
$service = $_GET['service'] ?? 'home';
try {
$router = new \MVC\Core\Router();
$router->add('home', \MVC\Controller\HomeController::class, 'index');
$router->add('toggle_theme', \MVC\Controller\HomeController::class, 'toggle_theme');
$router->add('test1', \MVC\Controller\TestController::class, 'service1');
$router->add('test2', \MVC\Controller\TestController::class, 'service2');
$router->add('aussentemp', \MVC\Controller\AussentempController::class, 'aussentemp', ['auth']);
$layout = new \MVC\Layout\Layout('main');
$layout->setBlock('title', 'MVC Starter');
$layout->setBlock('header_title', '🚀 MVC Dashboard');
$layout->setBlock('header_actions', '<button class="btn-toggle" onclick="toggleTheme()">🌓 Theme umschalten</button>');
// Sidebar Block
ob_start();
?>
<nav>
<a href="?service=home">🏠 Home</a>
<a href="?service=test1">🛠️ Test 1</a>
<a href="?service=test2">📊 Test 2</a>
</nav>
<?php
$layout->setBlock('sidebar', ob_get_clean());
$layout->setBlock('content', $router->dispatch($service));
echo $layout->render();
} catch (\Exception $e) {
http_response_code(404); echo "<h1>404</h1><p>" . $e->getMessage() . "</p>";
}
@@ -1,18 +0,0 @@
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
// use MVC\Model\YourModel; // Importiere dein Model
class AussentempController extends Controller {
public function aussentemp() {
// Nutze Models für den Datenbankzugriff
// $data = YourModel::all();
return $this->render('aussentemp', [
'title' => 'Aussentemp Modul',
'message' => 'Willkommen in deinem neuen Modul Aussentemp!'
]);
}
}
@@ -1,27 +0,0 @@
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
class TestController extends Controller {
public function service1() {
return $this->render(
'test1',
[
'msg' => 'Service 1 ist bereit!'
]
);
}
public function service2() {
return $this->render(
'test2',
[
'data' =>
[
'Layout' => 'Header + Sidebar',
'Theme' => 'Auto-Toggle'
]
]
);
}
}
@@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="de" class="<?= ($_SESSION['theme'] ?? 'light') === 'dark' ? 'dark-mode' : '' ?>">
<head>
<meta charset="UTF-8">
<title><?= $this->getBlock('title') ?></title>
<style>
:root { --bg: #ffffff; --text: #333; --sidebar: #f4f4f4; --header: #007bff; --accent: #007bff; --border: #ddd; }
.dark-mode { --bg: #1a1a1a; --text: #f4f4f4; --sidebar: #252525; --header: #111; --accent: #3498db; --border: #333; }
body { font-family: 'Segoe UI', sans-serif; margin: 0; background: var(--bg); color: var(--text); transition: 0.3s; height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
.main-header { height: 60px; background: var(--header); color: white; display: flex; align-items: center; justify-content: space-between; padding: 0 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); z-index: 10; }
.main-container { flex: 1; display: flex; overflow: hidden; }
.sidebar { width: 260px; background: var(--sidebar); padding: 20px; border-right: 1px solid var(--border); overflow-y: auto; }
.content { flex: 1; padding: 40px; overflow-y: auto; }
.btn-toggle { padding: 8px 15px; border: 1px solid rgba(255,255,255,0.3); border-radius: 5px; cursor: pointer; background: transparent; color: white; font-size: 0.9rem; }
.btn-toggle:hover { background: rgba(255,255,255,0.1); }
nav a { display: block; padding: 12px; text-decoration: none; color: inherit; border-radius: 5px; margin-bottom: 5px; }
nav a:hover { background: rgba(0,0,0,0.05); }
.logo { font-size: 1.4rem; font-weight: bold; }
</style>
</head>
<body>
<header class="main-header">
<div class="logo"><?= $this->getBlock('header_title') ?></div>
<div class="header-actions">
<?= $this->getBlock('header_actions') ?>
</div>
</header>
<div class="main-container">
<div class="sidebar">
<?= $this->getBlock('sidebar') ?>
</div>
<div class="content">
<?= $this->getBlock('content') ?>
</div>
</div>
<script>
function toggleTheme() { fetch('?service=toggle_theme').then(() => location.reload()); }
</script>
</body>
</html>
@@ -1,22 +0,0 @@
<?php
namespace MVC\Model;
use MVC\Core\Model;
/**
* Model für die Tabelle 'messungen'
* Verfügbare Spalten: 'id', 'ts', 'aussentemp'
*/
class Messungen extends Model {
protected static $table = "messungen";
// Hier kannst du spezifische Methoden für dieses Model hinzufügen
/**
* Beispiel: Suche nach einer spezifischen Spalte
*/
public static function getByFirstColumn($value) {
return self::where("id = ?", $value);
}
}
@@ -1,14 +0,0 @@
<h2><?= htmlspecialchars($title) ?></h2>
<div class="card">
<p><?= htmlspecialchars($message) ?></p>
<p>Hier kannst du nun dein Projekt aufbauen.</p>
</div>
<style>
.card {
background: var(--card-bg);
padding: 20px;
border-radius: 8px;
border: 1px solid var(--card-border);
}
</style>
@@ -1 +0,0 @@
<h1>Dashboard</h1><p>Willkommen in deinem neuen MVC-System mit integriertem Header.</p>
@@ -1 +0,0 @@
<h1>Service 1</h1><p><?= $msg ?></p>
@@ -1 +0,0 @@
<h1>System Check</h1><ul><?php foreach($data as $k => $v): ?><li><b><?= $k ?>:</b> <?= $v ?></li><?php endforeach; ?></ul>
-6
View File
@@ -1,6 +0,0 @@
[database]
APP_ENV = "dev"
DB_DEV_HOST = "localhost"
DB_DEV_USERNAME = "root"
DB_DEV_PASSWORD = ""
DB_DEV_DATABASE = "mein_projekt"
File renamed without changes.
File renamed without changes.
+15
View File
@@ -0,0 +1,15 @@
<?php
use MVC\Classes\Api;
header('Content-Type: application/json');
require_once __DIR__ . '/../../src/MVC/autoloader.php';
use MVC\Controller\ApiController;
try {
Api::jsonResponse((new ApiController())->get());
} catch (\Exception $e) {
Api::jsonResponse($e->getMessage(), 404);
}
+65
View File
@@ -0,0 +1,65 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
session_start();
require_once __DIR__ . '/src/MVC/autoloader.php';
$service = $_GET['service'] ?? 'home';
try {
$router = new \MVC\Core\Router();
$router->add('home', \MVC\Controller\HomeController::class, 'index');
$router->add('toggle_theme', \MVC\Controller\HomeController::class, 'toggle_theme');
$router->add('abgas', \MVC\Controller\AbgasController::class, 'abgas');
$router->add('vorlauf', \MVC\Controller\VorlaufController::class, 'vorlauf', ['auth']);
$router->add('puffer', \MVC\Controller\PufferController::class, 'puffer', ['auth']);
$router->add('aussentemp', \MVC\Controller\AussentempController::class, 'aussentemp', ['auth']);
$router->add('docs', \MVC\Controller\DocsController::class, 'index');
$router->add('login', \MVC\Controller\AuthController::class, 'login');
$router->add('logout', \MVC\Controller\AuthController::class, 'logout');
$layout = new \MVC\Layout\Layout('main');
$layout->setBlock('title', 'MVC Starter');
$layout->setBlock('header_title', '🚀 MVC Dashboard');
$headerActions = '<button class="btn-toggle" onclick="toggleTheme()">🌓 Theme umschalten</button>';
if (isset($_SESSION['user_logged_in'])) {
$headerActions = '<span style="margin-right: 15px;">👤 ' . htmlspecialchars($_SESSION['username']) . '</span>' .
'<a href="?service=logout" class="btn-toggle" style="text-decoration: none; background: rgba(255,255,255,0.2);">🚪 Logout</a> ' .
$headerActions;
} else {
$headerActions = '<a href="?service=login" class="btn-toggle" style="text-decoration: none; background: var(--accent); color: white; margin-right: 10px;">🔑 Login</a> ' .
$headerActions;
}
$layout->setBlock('header_actions', $headerActions);
// Sidebar Block
ob_start();
$navItems = [
'home' => ['label' => '🏠 Home', 'auth' => false],
'abgas' => ['label' => '🔥 Abgas', 'auth' => false],
'vorlauf' => ['label' => '🏃‍♂️‍➡️ Vorlauf', 'auth' => true],
'puffer' => ['label' => '♨️ Puffer', 'auth' => true],
'aussentemp' => ['label' => '🌡️ Außentemperatur', 'auth' => true],
'docs' => ['label' => '📚 Dokumentation', 'auth' => false],
];
?>
<nav>
<?php foreach ($navItems as $navKey => $item):
if ($item['auth'] && !isset($_SESSION['user_logged_in'])) continue;
?>
<a href="?service=<?= $navKey ?>" class="<?= $service === $navKey ? 'active' : '' ?>"><?= $item['label'] ?></a>
<?php endforeach; ?>
<?php if (!isset($_SESSION['user_logged_in'])): ?>
<div style="margin-top: 20px; padding: 10px; font-size: 0.8rem; opacity: 0.5; border-top: 1px solid var(--border);">
🔒 Melden Sie sich an, um alle Funktionen zu sehen.
</div>
<?php endif; ?>
</nav>
<?php
$layout->setBlock('sidebar', ob_get_clean());
$layout->setBlock('content', $router->dispatch($service));
echo $layout->render();
} catch (\Throwable $e) {
http_response_code(500); echo "<h1>Error</h1><p>" . $e->getMessage() . "</p><pre>" . $e->getTraceAsString() . "</pre>";
};
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace MVC\Classes;
class Api {
public static function jsonResponse($data, int $statusCode = 200) {
header('Content-Type: application/json');
http_response_code($statusCode);
echo json_encode(
[
'status' => $statusCode === 200 ? 'success' : 'error',
'data' => $data,
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
);
exit;
}
}
@@ -0,0 +1,31 @@
<?php
namespace MVC\Classes;
class Client {
public static function ip() {
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ipList = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
return trim($ipList[0]);
} else if (!empty($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
} else {
return 'UNKNOWN';
}
}
public static function port() {
return $_SERVER['REMOTE_PORT'] ?? 'UNKNOWN';
}
public static function country() {
$response = file_get_contents('http://ip-api.com/json/{' . self::ip() . '}?fields=country,countryCode');
$data = json_decode($response);
return $data->country ?? 'UNKNOWN';
}
public static function userAgent() {
return $_SERVER['HTTP_USER_AGENT'] ?? 'UNKNOWN';
}
}
@@ -0,0 +1,20 @@
<?php
namespace MVC\Classes;
class Cookie {
public function set(string $CookieName, int $CookieValue, int $CookieTime) {
setcookie($CookieName, $CookieValue, time()+($CookieTime*60));
}
public function read(string $CookieName) {
if (isset($_COOKIE[$CookieName])) {
return $_COOKIE[$CookieName];
}
return 'Cookie not set';
}
public function Delete(string $CookieName) {
setcookie($CookieName, "", time()-(3600*24));
}
}
@@ -0,0 +1,33 @@
<?php
namespace MVC\Classes;
class Request {
public static function method() {
return $_SERVER['REQUEST_METHOD'] ?? 'UNKNOWN';
}
public static function uri() {
return $_SERVER['REQUEST_URI'] ?? 'UNKNOWN';
}
public static function headers() {
return getallheaders();
}
public static function body() {
return file_get_contents('php://input');
}
public static function queryParams() {
return $_GET;
}
public static function time() {
return $_SERVER['REQUEST_TIME'] ?? 'UNKNOWN';
}
public static function referrer() {
return $_SERVER['HTTP_REFERER'] ?? 'UNKNOWN';
}
}
@@ -0,0 +1,25 @@
<?php
namespace MVC\Classes;
class Server {
public static function documentRoot() {
return $_SERVER['DOCUMENT_ROOT'] ?? 'UNKNOWN';
}
public static function currentDir() {
return __DIR__;
}
public static function currentScript() {
return $_SERVER['SCRIPT_NAME'] ?? 'UNKNOWN';
}
public static function name() {
return $_SERVER['SERVER_NAME'] ?? 'UNKNOWN';
}
public static function port() {
return $_SERVER['SERVER_PORT'] ?? 'UNKNOWN';
}
}
@@ -0,0 +1,46 @@
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
use MVC\Model\Messungen;
class AbgasController extends Controller {
public function abgas() {
$day = $_GET['day'] ?? date('Y-m-d');
$datum = \DateTime::createFromFormat('Y-m-d', $day);
if (!$datum) {
$day = date('Y-m-d');
}
$array = Messungen::getAbgasByDay($day);
$labels = [];
$abgas = [];
foreach ($array as $row) {
$labels[] = date('H:i:s', strtotime($row['ts']));
$abgas[] = (float)$row['abgas'];
}
$prevDay = date('Y-m-d', strtotime($day . ' -1 day'));
$nextDay = date('Y-m-d', strtotime($day . ' +1 day'));
return $this->render('abgas', [
'title' => 'Abgas Modul',
'xtitle' => 'Zeit',
'ytitle' => 'Temperatur',
'array' => $array,
'labels' => $labels,
'abgas' => $abgas,
'day' => $day,
'prevDay' => $prevDay,
'nextDay' => $nextDay,
]);
}
}
@@ -0,0 +1,18 @@
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
use MVC\Model\Messungen;
use MVC\Classes\Api;
class ApiController extends Controller {
public function get() {
$day = "2026-05-22";
$abgas = Messungen::getAbgasByDay($day);
return Api::jsonResponse([
'abgas' => $abgas,
]);
}
}
@@ -0,0 +1,46 @@
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
use MVC\Model\Messungen;
class AussentempController extends Controller {
public function aussentemp() {
$day = $_GET['day'] ?? date('Y-m-d');
$datum = \DateTime::createFromFormat('Y-m-d', $day);
if (!$datum) {
$day = date('Y-m-d');
}
$array = Messungen::getAussentempByDay($day);
$labels = [];
$aussentemp = [];
foreach ($array as $row) {
$labels[] = date('H:i:s', strtotime($row['ts']));
$aussentemp[] = (float)$row['aussentemp'];
}
$prevDay = date('Y-m-d', strtotime($day . ' -1 day'));
$nextDay = date('Y-m-d', strtotime($day . ' +1 day'));
return $this->render('aussentemp', [
'title' => 'Aussentemperatur Modul',
'xtitle' => 'Zeit',
'ytitle' => 'Temperatur',
'array' => $array,
'labels' => $labels,
'aussentemp' => $aussentemp,
'day' => $day,
'prevDay' => $prevDay,
'nextDay' => $nextDay,
]);
}
}
@@ -0,0 +1,42 @@
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
class AuthController extends Controller {
public function login() {
if (isset($_SESSION['user_logged_in'])) {
header('Location: ?service=home');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
// Hardcoded logins
$users = [
'admin' => 'password123',
'user' => 'user123'
];
if (isset($users[$username]) && $users[$username] === $password) {
$_SESSION['user_logged_in'] = true;
$_SESSION['username'] = $username;
header('Location: ?service=home');
exit;
} else {
return $this->render('login', ['error' => 'Ungültige Anmeldedaten']);
}
}
return $this->render('login');
}
public function logout() {
session_destroy();
header('Location: ?service=login');
exit;
}
}
@@ -0,0 +1,13 @@
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
class DocsController extends Controller {
public function index() {
return $this->render('docs', [
'title' => '📚 System-Dokumentation'
]);
}
}
@@ -0,0 +1,58 @@
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
use MVC\Model\Messungen;
class PufferController extends Controller {
public function puffer() {
// Tag holen
$day = $_GET['day'] ?? date('Y-m-d');
// Validierung
$datum = \DateTime::createFromFormat('Y-m-d', $day);
if (!$datum) {
$day = date('Y-m-d');
}
// Daten laden
$array = Messungen::getPufferByDay($day);
// Chart Daten vorbereiten
$labels = [];
$pufferOben = [];
$pufferUnten = [];
foreach ($array as $row) {
// Zeit kürzen für Chart
$labels[] = date('H:i:s', strtotime($row['ts']));
$pufferOben[] = (float)$row['kessel_oben'];
$pufferUnten[] = (float)$row['kessel_unten'];
}
// Navigation
$prevDay = date('Y-m-d', strtotime($day . ' -1 day'));
$nextDay = date('Y-m-d', strtotime($day . ' +1 day'));
return $this->render('puffer', [
'title' => 'Puffer Übersicht',
'xtitle' => 'Zeit',
'ytitle' => 'Temperatur',
'array' => $array,
'labels' => $labels,
'pufferOben' => $pufferOben,
'pufferUnten' => $pufferUnten,
'day' => $day,
'prevDay' => $prevDay,
'nextDay' => $nextDay,
]);
}
}
@@ -0,0 +1,58 @@
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
use MVC\Model\Messungen;
class VorlaufController extends Controller {
public function vorlauf() {
// Tag holen
$day = $_GET['day'] ?? date('Y-m-d');
// Validierung
$datum = \DateTime::createFromFormat('Y-m-d', $day);
if (!$datum) {
$day = date('Y-m-d');
}
// Daten laden
$array = Messungen::getVorlaufByDay($day);
// Chart Daten vorbereiten
$labels = [];
$vorlaufIst = [];
$vorlaufSoll = [];
foreach ($array as $row) {
// Zeit kürzen für Chart
$labels[] = date('H:i:s', strtotime($row['ts']));
$vorlaufIst[] = (float)$row['vorlauf_ist'];
$vorlaufSoll[] = (float)$row['vorlauf_soll'];
}
// Navigation
$prevDay = date('Y-m-d', strtotime($day . ' -1 day'));
$nextDay = date('Y-m-d', strtotime($day . ' +1 day'));
return $this->render('vorlauf', [
'title' => 'Vorlauf Übersicht',
'xtitle' => 'Zeit',
'ytitle' => 'Temperatur',
'array' => $array,
'labels' => $labels,
'vorlaufIst' => $vorlaufIst,
'vorlaufSoll' => $vorlaufSoll,
'day' => $day,
'prevDay' => $prevDay,
'nextDay' => $nextDay,
]);
}
}
@@ -51,7 +51,7 @@ class Router {
if ($middleware === 'auth') { if ($middleware === 'auth') {
if (empty($_SESSION['user_logged_in'])) { if (empty($_SESSION['user_logged_in'])) {
header('Location: login.php'); header('Location: ?service=login');
exit; exit;
} }
} }
@@ -0,0 +1,112 @@
<!DOCTYPE html>
<html lang="de" class="<?= ($_SESSION['theme'] ?? 'light') === 'dark' ? 'dark-mode' : '' ?>">
<head>
<meta charset="UTF-8">
<title><?= $this->getBlock('title') ?></title>
<style>
:root {
--bg: #ffffff;
--text: #333;
--sidebar: #f4f4f4;
--header: #007bff;
--accent: #007bff;
--border: #ddd;
}
.dark-mode {
--bg: #1a1a1a;
--text: #f4f4f4;
--sidebar: #252525;
--header: #111;
--accent: #3498db;
--border: #333;
}
body {
font-family: 'Segoe UI', sans-serif;
margin: 0;
background: var(--bg);
color: var(--text);
transition: 0.3s;
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
.main-header {
height: 60px;
background: var(--header);
color: white;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
z-index: 10;
}
.main-container {
flex: 1;
display: flex;
overflow: hidden;
}
.sidebar {
width: 260px;
background: var(--sidebar);
padding: 20px;
border-right: 1px solid var(--border);
overflow-y: auto;
}
.content {
flex: 1;
padding: 40px;
overflow-y: auto;
}
.btn-toggle {
padding: 8px 15px;
border: 1px solid rgba(255,255,255,0.3);
border-radius: 5px;
cursor: pointer;
background: transparent;
color: white;
font-size: 0.9rem;
}
.btn-toggle:hover {
background: rgba(255,255,255,0.1);
}
nav a {
display: block;
padding: 12px;
text-decoration: none;
color: inherit;
border-radius: 5px;
margin-bottom: 5px;
}
nav a:hover {
background: rgba(0,0,0,0.05);
}
nav a.active {
background: rgba(0,0,0,0.25);
}
.logo {
font-size: 1.4rem; font-weight: bold;
}
</style>
</head>
<body>
<header class="main-header">
<div class="logo"><?= $this->getBlock('header_title') ?></div>
<div class="header-actions">
<?= $this->getBlock('header_actions') ?>
</div>
</header>
<div class="main-container">
<div class="sidebar">
<?= $this->getBlock('sidebar') ?>
</div>
<div class="content">
<?= $this->getBlock('content') ?>
</div>
</div>
<script>
function toggleTheme() { fetch('?service=toggle_theme').then(() => location.reload()); }
</script>
</body>
</html>
@@ -0,0 +1,42 @@
<?php
namespace MVC\Model;
use MVC\Core\Model;
/**
* Model für die Tabelle 'messungen'
* Verfügbare Spalten: 'id', 'ts', 'aussentemp', 'vorlauf_ist', 'vorlauf_soll', 'kessel_oben', 'kessel_unten', 'wohnung', 'abgas', 'stellung', 'stellung_proz', 'mischeron', 'abweichung', 'neigung', 'rtsoll', 'misch_begin', 'nachtabs'
*/
class Messungen extends Model {
protected static $table = "messungen";
// Hier kannst du spezifische Methoden für dieses Model hinzufügen
/**
* Beispiel: Suche nach einer spezifischen Spalte
*/
public static function getByFirstColumn($value) {
return self::where("id = ?", $value);
}
public static function getAbgasByDay($day) {
$sql = "SELECT ts, abgas FROM " . self::$table . " WHERE ts LIKE '$day%' ORDER BY ts ASC";
return self::query($sql)->fetch_all(MYSQLI_ASSOC);
}
public static function getVorlaufByDay($day) {
$sql = "SELECT ts, vorlauf_ist, vorlauf_soll FROM " . self::$table . " WHERE ts LIKE '$day%' ORDER BY ts ASC";
return self::query($sql)->fetch_all(MYSQLI_ASSOC);
}
public static function getPufferByDay($day) {
$sql = "SELECT ts, kessel_oben, kessel_unten FROM " . self::$table . " WHERE ts LIKE '$day%' ORDER BY ts ASC";
return self::query($sql)->fetch_all(MYSQLI_ASSOC);
}
public static function getAussentempByDay($day) {
$sql = "SELECT ts, aussentemp FROM " . self::$table . " WHERE ts LIKE '$day%' ORDER BY ts ASC";
return self::query($sql)->fetch_all(MYSQLI_ASSOC);
}
}
+126
View File
@@ -0,0 +1,126 @@
<?php
/**
* @var array $array
* @var string $prevDay
* @var string $nextDay
* @var string $day
* @var string $title
* @var string $xtitle
* @var string $ytitle
* @var string $labels
* @var string $abgas
*/
?>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
<div style="height:800px;">
<canvas id="logChart"></canvas>
</div>
<script>
const ctx = document.getElementById('logChart').getContext('2d');
const logChart = new Chart(ctx, {
type: 'line',
data: {
labels: <?php echo json_encode($labels); ?>,
datasets: [{
label: 'Abgas',
data: <?php echo json_encode($abgas); ?>,
borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
fill: true,
tension: 0.1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '<?= $title ?>'
},
legend: {
display: true,
position: 'top'
},
zoom: {
pan: {
enabled: true,
mode: 'x'
},
zoom: {
wheel: {
enabled: true
},
pinch: {
enabled: true
},
mode: 'x'
}
}
},
interaction: {
mode: 'index',
intersect: false
},
scales: {
x: {
title: {
display: true,
text: '<?= $xtitle ?>'
}
},
y: {
min: 0,
max: 600,
title: {
display: true,
text: '<?= $ytitle ?>'
},
ticks: {
stepSize: 20
},
beginAtZero: true
}
}
}
});
</script>
<div style="margin-bottom:20px;">
<a href="?service=abgas&day=<?php echo $prevDay; ?>">
← Vorheriger Tag
</a>
<strong style="margin:0 20px;">
<?php echo $day; ?>
</strong>
<a href="?service=abgas&day=<?php echo $nextDay; ?>">
Nächster Tag →
</a>
</div>
+126
View File
@@ -0,0 +1,126 @@
<?php
/**
* @var array $array
* @var string $prevDay
* @var string $nextDay
* @var string $day
* @var string $title
* @var string $xtitle
* @var string $ytitle
* @var string $labels
* @var string $aussentemp
*/
?>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
<div style="height:800px;">
<canvas id="logChart"></canvas>
</div>
<script>
const ctx = document.getElementById('logChart').getContext('2d');
const logChart = new Chart(ctx, {
type: 'line',
data: {
labels: <?php echo json_encode($labels); ?>,
datasets: [{
label: 'Aussentemperatur',
data: <?php echo json_encode($aussentemp); ?>,
borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
fill: true,
tension: 0.1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '<?= $title ?>'
},
legend: {
display: true,
position: 'top'
},
zoom: {
pan: {
enabled: true,
mode: 'x'
},
zoom: {
wheel: {
enabled: true
},
pinch: {
enabled: true
},
mode: 'x'
}
}
},
interaction: {
mode: 'index',
intersect: false
},
scales: {
x: {
title: {
display: true,
text: '<?= $xtitle ?>'
}
},
y: {
min: 0,
max: 50,
title: {
display: true,
text: '<?= $ytitle ?>'
},
ticks: {
stepSize: 20
},
beginAtZero: true
}
}
}
});
</script>
<div style="margin-bottom:20px;">
<a href="?service=aussentemp&day=<?php echo $prevDay; ?>">
← Vorheriger Tag
</a>
<strong style="margin:0 20px;">
<?php echo $day; ?>
</strong>
<a href="?service=aussentemp&day=<?php echo $nextDay; ?>">
Nächster Tag →
</a>
</div>
+127
View File
@@ -0,0 +1,127 @@
<div class="docs-container">
<h1>🏛️ Framework Architektur & Dokumentation</h1>
<div class="toc">
<h3>Inhalt</h3>
<ul>
<li><a href="#intro">1. Einführung</a></li>
<li><a href="#files">2. Datei-Referenz (Deep Dive)</a></li>
<li><a href="#api">3. API-System</a></li>
<li><a href="#database">4. Datenbank & Models</a></li>
<li><a href="#classes">5. Helper Klassen</a></li>
<li><a href="#generators">6. CLI-Generatoren</a></li>
<li><a href="#best-practices">7. Best Practices</a></li>
</ul>
</div>
<section id="intro">
<h2>1. Einführung</h2>
<p>Dieses Framework basiert auf dem <strong>Model-View-Controller (MVC)</strong> Entwurfsmuster und nutzt einen <strong>Front Controller</strong> (index.php). Alle Anfragen werden zentral verarbeitet, was globale Middleware (wie Authentifizierung) und ein einheitliches Layout ermöglicht.</p>
</section>
<section id="files">
<h2>2. Datei-Referenz (Deep Dive)</h2>
<h3>Framework Core</h3>
<ul>
<li><strong>Router.php:</strong> Verwaltet die Routen. Unterstützt Middlewares. Das Dispatching erfolgt über den <code>service</code> Parameter in der URL.</li>
<li><strong>Controller.php:</strong> Basisklasse für alle Controller. Bietet <code>render()</code> für Views und <code>json()</code> für API-Responses.</li>
<li><strong>Model.php:</strong> Abstrakte Basis für Datenbank-Entitäten. Kapselt die <code>databaseMethods.php</code> in ein objektorientiertes Interface.</li>
<li><strong>View.php:</strong> Behandelt das Extrahieren von Daten in HTML-Templates.</li>
</ul>
<h3>Konfiguration</h3>
<ul>
<li><strong>env.ini:</strong> Speichert Datenbank-Zugangsdaten für <code>dev</code> und <code>live</code> Umgebungen. <strong>Wichtig:</strong> Niemals in Git committen!</li>
<li><strong>autoloader.php:</strong> Registriert einen PSR-4 ähnlichen Autoloader für den <code>MVC\</code> Namespace.</li>
</ul>
</section>
<section id="api">
<h2>3. API-System</h2>
<p>APIs liegen im Verzeichnis <code>WEBROOT/api/v1/</code>. Sie nutzen die <code>ApiController</code>-Klasse.</p>
<pre>
// Beispiel: WEBROOT/api/v1/get.php
use MVC\Classes\Api;
use MVC\Controller\ApiController;
Api::jsonResponse((new ApiController())->get());
</pre>
<p>Die Klasse <code>MVC\Classes\Api</code> stellt die Methode <code>jsonResponse($data, $status)</code> bereit, die automatisch die korrekten Header setzt und die Antwort einheitlich formatiert.</p>
</section>
<section id="database">
<h2>4. Datenbank & Models</h2>
<p>Das Framework nutzt <code>mysqli</code> mit Prepared Statements für maximale Sicherheit.</p>
<h3>databaseMethods.php (Die Power-Tools)</h3>
<p>Diese Datei enthält prozedurale Funktionen für alle CRUD-Operationen:</p>
<ul>
<li><code>DatabaseShow($table, $columns, $where, ...$values)</code></li>
<li><code>DatabaseInsert($table, $columns, ...$values)</code></li>
<li><code>DatabaseUpdate($table, $set, $where, ...$values)</code></li>
<li><code>DatabaseDelete($table, $where, ...$values)</code></li>
<li><code>DatabaseShowWithFK($table, $fkMap, ...)</code>: Ermöglicht das automatische Auflösen von Fremdschlüsseln (Joins ohne SQL-Schreiben).</li>
</ul>
<h3>Models</h3>
<p>Models vereinfachen den Zugriff. Beispiel:</p>
<pre>
$user = User::find(1); // Holt User mit ID 1
$admins = User::where("role = ?", "admin");
</pre>
</section>
<section id="classes">
<h2>5. Helper Klassen (MVC\Classes)</h2>
<p>Nützliche Helfer, die in jedem Controller verfügbar sind:</p>
<ul>
<li><strong>Request:</strong> Zugriff auf <code>method()</code>, <code>queryParams()</code>, <code>headers()</code> und den <code>body()</code>.</li>
<li><strong>Client:</strong> Ermittelt <code>ip()</code>, <code>userAgent()</code> und sogar das <code>country()</code> via IP-API.</li>
<li><strong>Server:</strong> Informationen über den Host, Ports und Pfade.</li>
<li><strong>Cookie:</strong> Einfaches Interface zum <code>set()</code>, <code>read()</code> und <code>Delete()</code> von Cookies.</li>
</ul>
</section>
<section id="generators">
<h2>6. CLI-Generatoren</h2>
<p>Diese Scripte beschleunigen die Entwicklung massiv:</p>
<h3>create_module.php</h3>
<p>Erstellt ein komplettes Feature-Bundle bestehend aus einem Controller und einer View. Registriert danach den Service in der <code>index.php</code>.</p>
<h3>create_model.php</h3>
<p>Generiert eine Model-Klasse basierend auf einer vorhandenen Datenbank-Tabelle. Analysiert die Spalten und erstellt entsprechende PHP-Kommentare für die IDE.</p>
</section>
<section id="best-practices">
<h2>7. Best Practices</h2>
<div class="bp-grid">
<div class="bp-card">
<h4>Naming</h4>
<p>Controller immer mit <code>Controller</code>-Suffix (z.B. <code>UserController</code>). Models im Singular (z.B. <code>User</code>).</p>
</div>
<div class="bp-card">
<h4>Logic Placement</h4>
<p>Halte Controller "schlank". Komplexe SQL-Logik oder Berechnungen gehören in das jeweilige Model.</p>
</div>
<div class="bp-card">
<h4>Security</h4>
<p>Nutze <code>['auth']</code> Middleware konsequent für alle internen Seiten. Verwende niemals <code>$_GET</code> oder <code>$_POST</code> direkt, sondern die <code>Request</code>-Klasse.</p>
</div>
<div class="bp-card">
<h4>API First</h4>
<p>Wenn du Daten für das Frontend bereitstellst, nutze den <code>ApiController</code>. So bleibt die Geschäftslogik vom HTML getrennt.</p>
</div>
</div>
</section>
</div>
<style>
.docs-container { max-width: 1000px; color: var(--text); }
.toc { background: var(--sidebar); padding: 20px; border-radius: 8px; margin-bottom: 30px; border: 1px solid var(--border); }
.toc h3 { margin-top: 0; }
section { margin-bottom: 50px; padding-bottom: 20px; border-bottom: 1px solid var(--border); }
h2 { color: var(--accent); font-size: 1.8rem; margin-bottom: 20px; }
h3 { color: var(--text); opacity: 0.9; }
pre { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 5px; overflow-x: auto; border: 1px solid #333; font-size: 0.9rem; }
.bp-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px; }
.bp-card { background: var(--sidebar); padding: 20px; border-radius: 8px; border: 1px solid var(--border); }
.bp-card h4 { margin-top: 0; color: var(--accent); }
.bp-card p { font-size: 0.9rem; margin-bottom: 0; }
code { background: var(--sidebar); padding: 2px 5px; border-radius: 3px; font-family: monospace; }
</style>
+2
View File
@@ -0,0 +1,2 @@
<h1>Dashboard</h1>
<p>Willkommen in deinem neuen MVC-System mit integriertem Header.</p>
+60
View File
@@ -0,0 +1,60 @@
<style>
.login-container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--sidebar);
}
.login-container h2 {
text-align: center;
margin-top: 0;
}
.login-container input {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 1px solid var(--border);
border-radius: 4px;
box-sizing: border-box;
}
.login-container button {
width: 100%;
padding: 10px;
background: var(--header);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.login-container button:hover {
opacity: 0.9;
}
.error-msg {
color: #e74c3c;
background: rgba(231, 76, 60, 0.1);
padding: 10px;
border-radius: 4px;
margin-bottom: 15px;
text-align: center;
}
</style>
<div class="login-container">
<h2>🔑 Login</h2>
<?php if (isset($error)): ?>
<div class="error-msg"><?= $error ?></div>
<?php endif; ?>
<form method="POST">
<div>
<label>Benutzername</label>
<input type="text" name="username" required autofocus placeholder="Admin / User">
</div>
<div>
<label>Passwort</label>
<input type="password" name="password" required placeholder="********">
</div>
<button type="submit">Anmelden</button>
</form>
</div>
+116
View File
@@ -0,0 +1,116 @@
<?php
/**
* @var array $array
* @var string $prevDay
* @var string $nextDay
* @var string $day
* @var string $title
* @var string $xtitle
* @var string $ytitle
* @var string $labels
* @var string $pufferOben
* @var string $pufferUnten
*/
?>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
<div style="height:800px;">
<canvas id="logChart"></canvas>
</div>
<script>
const ctx = document.getElementById('logChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: <?php echo json_encode($labels); ?>,
datasets: [
{
label: 'Puffer Oben',
data: <?php echo json_encode($pufferOben); ?>,
borderColor: 'rgba(54, 162, 235, 1)',
backgroundColor: 'rgba(54, 162, 235, 0.2)',
tension: 0.2,
fill: false
},
{
label: 'Puffer Unten',
data: <?php echo json_encode($pufferUnten); ?>,
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
tension: 0.2,
fill: false
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '<?= $title ?>'
},
legend: {
display: true,
position: 'top'
},
zoom: {
pan: {
enabled: true,
mode: 'x'
},
zoom: {
wheel: {
enabled: true
},
pinch: {
enabled: true
},
mode: 'x'
}
}
},
interaction: {
mode: 'index',
intersect: false
},
scales: {
x: {
title: {
display: true,
text: '<?= $xtitle ?>'
}
},
y: {
title: {
display: true,
text: '<?= $ytitle ?>'
},
beginAtZero: false
}
}
}
});
</script>
<div style="margin-bottom:20px;">
<a href="?service=puffer&day=<?php echo $prevDay; ?>">← Vorheriger Tag</a>
<strong style="margin:0 20px;">
<?php echo $day; ?>
</strong>
<a href="?service=puffer&day=<?php echo $nextDay; ?>">Nächster Tag →</a>
</div>
+116
View File
@@ -0,0 +1,116 @@
<?php
/**
* @var array $array
* @var string $prevDay
* @var string $nextDay
* @var string $day
* @var string $title
* @var string $xtitle
* @var string $ytitle
* @var string $labels
* @var string $vorlaufIst
* @var string $vorlaufSoll
*/
?>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
<div style="height:800px;">
<canvas id="logChart"></canvas>
</div>
<script>
const ctx = document.getElementById('logChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: <?php echo json_encode($labels); ?>,
datasets: [
{
label: 'Vorlauf IST',
data: <?php echo json_encode($vorlaufIst); ?>,
borderColor: 'rgba(54, 162, 235, 1)',
backgroundColor: 'rgba(54, 162, 235, 0.2)',
tension: 0.2,
fill: false
},
{
label: 'Vorlauf SOLL',
data: <?php echo json_encode($vorlaufSoll); ?>,
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
tension: 0.2,
fill: false
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: 'Vorlauf IST vs SOLL'
},
legend: {
display: true,
position: 'top'
},
zoom: {
pan: {
enabled: true,
mode: 'x'
},
zoom: {
wheel: {
enabled: true
},
pinch: {
enabled: true
},
mode: 'x'
}
}
},
interaction: {
mode: 'index',
intersect: false
},
scales: {
x: {
title: {
display: true,
text: '<?= $xtitle ?>'
}
},
y: {
title: {
display: true,
text: '<?= $ytitle ?>'
},
beginAtZero: false
}
}
}
});
</script>
<div style="margin-bottom:20px;">
<a href="?service=vorlauf&day=<?php echo $prevDay; ?>">← Vorheriger Tag</a>
<strong style="margin:0 20px;">
<?php echo $day; ?>
</strong>
<a href="?service=vorlauf&day=<?php echo $nextDay; ?>">Nächster Tag →</a>
</div>
+6
View File
@@ -0,0 +1,6 @@
[database]
APP_ENV = "dev"
DB_DEV_HOST = "blackpi.ipv64.net"
DB_DEV_USERNAME = "worker"
DB_DEV_PASSWORD = "bBHofurUJWKzPE8Z"
DB_DEV_DATABASE = "HEIZUNG"
File renamed without changes.
File renamed without changes.
File renamed without changes.
+432 -24
View File
@@ -1,7 +1,7 @@
<?php <?php
/** /**
* Ultimate MVC Project Bootstrapper (Fixed Version with Header) * Ultimate MVC Project Bootstrapper (Fixed Version with Header & Auth)
* Erstellt ein voll funktionsfähiges Projekt inkl. Dev-Server, Header, DarkMode & freiem Zugriff. * Erstellt ein voll funktionsfähiges Projekt inkl. Dev-Server, Header, DarkMode & Auth-System.
*/ */
if (php_sapi_name() !== 'cli') { if (php_sapi_name() !== 'cli') {
@@ -34,6 +34,7 @@ $dirs = [
'WEBROOT/src/utils', 'WEBROOT/src/utils',
'WEBROOT/src/MVC', 'WEBROOT/src/MVC',
'WEBROOT/src/MVC/Core', 'WEBROOT/src/MVC/Core',
'WEBROOT/src/MVC/Classes',
'WEBROOT/src/MVC/Controller', 'WEBROOT/src/MVC/Controller',
'WEBROOT/src/MVC/Model', 'WEBROOT/src/MVC/Model',
'WEBROOT/src/MVC/View', 'WEBROOT/src/MVC/View',
@@ -369,10 +370,16 @@ PHP;
$files['WEBROOT/src/config/env.ini'] = <<<INI $files['WEBROOT/src/config/env.ini'] = <<<INI
[database] [database]
APP_ENV = "dev" APP_ENV = "dev"
DB_DEV_HOST = "localhost"
DB_DEV_USERNAME = "root" DB_DEV_HOST = ""
DB_DEV_USERNAME = ""
DB_DEV_PASSWORD = "" DB_DEV_PASSWORD = ""
DB_DEV_DATABASE = "mein_projekt" DB_DEV_DATABASE = ""
DB_LIVE_HOST = ""
DB_LIVE_USERNAME = ""
DB_LIVE_PASSWORD = ""
DB_LIVE_DATABASE = ""
INI; INI;
// --- DB: databaseConnect.php --- // --- DB: databaseConnect.php ---
@@ -398,6 +405,148 @@ function db_connect() {
} }
} }
PHP; PHP;
// -- CLASSES --
$files['WEBROOT/src/MVC/Classes/Api.php'] = <<<'PHP'
<?php
namespace MVC\Classes;
class Api {
public static function jsonResponse($data, int $statusCode = 200) {
header('Content-Type: application/json');
http_response_code($statusCode);
echo json_encode(
[
'status' => $statusCode === 200 ? 'success' : 'error',
'data' => $data,
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
);
exit;
}
}
PHP;
$files['WEBROOT/src/MVC/Classes/Client.php'] = <<<'PHP'
<?php
namespace MVC\Classes;
class Client {
public static function ip() {
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ipList = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
return trim($ipList[0]);
} else if (!empty($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
} else {
return 'UNKNOWN';
}
}
public static function port() {
return $_SERVER['REMOTE_PORT'] ?? 'UNKNOWN';
}
public static function country() {
$response = file_get_contents('http://ip-api.com/json/{' . self::ip() . '}?fields=country,countryCode');
$data = json_decode($response);
return $data->country ?? 'UNKNOWN';
}
public static function userAgent() {
return $_SERVER['HTTP_USER_AGENT'] ?? 'UNKNOWN';
}
}
PHP;
$files['WEBROOT/src/MVC/Classes/Cookie.php'] = <<<'PHP'
<?php
namespace MVC\Classes;
class Cookie {
public function set(string $CookieName, int $CookieValue, int $CookieTime) {
setcookie($CookieName, $CookieValue, time()+($CookieTime*60));
}
public function read(string $CookieName) {
if (isset($_COOKIE[$CookieName])) {
return $_COOKIE[$CookieName];
}
return 'Cookie not set';
}
public function Delete(string $CookieName) {
setcookie($CookieName, "", time()-(3600*24));
}
}
PHP;
$files['WEBROOT/src/MVC/Classes/Request.php'] = <<<'PHP'
<?php
namespace MVC\Classes;
class Request {
public static function method() {
return $_SERVER['REQUEST_METHOD'] ?? 'UNKNOWN';
}
public static function uri() {
return $_SERVER['REQUEST_URI'] ?? 'UNKNOWN';
}
public static function headers() {
return getallheaders();
}
public static function body() {
return file_get_contents('php://input');
}
public static function queryParams() {
return $_GET;
}
public static function time() {
return $_SERVER['REQUEST_TIME'] ?? 'UNKNOWN';
}
public static function referrer() {
return $_SERVER['HTTP_REFERER'] ?? 'UNKNOWN';
}
}
PHP;
$files['WEBROOT/src/MVC/Classes/Server.php'] = <<<'PHP'
<?php
namespace MVC\Classes;
class Server {
public static function documentRoot() {
return $_SERVER['DOCUMENT_ROOT'] ?? 'UNKNOWN';
}
public static function currentDir() {
return __DIR__;
}
public static function currentScript() {
return $_SERVER['SCRIPT_NAME'] ?? 'UNKNOWN';
}
public static function name() {
return $_SERVER['SERVER_NAME'] ?? 'UNKNOWN';
}
public static function port() {
return $_SERVER['SERVER_PORT'] ?? 'UNKNOWN';
}
}
PHP;
// --- CORE --- // --- CORE ---
$files['WEBROOT/src/MVC/Core/Controller.php'] = <<<'PHP' $files['WEBROOT/src/MVC/Core/Controller.php'] = <<<'PHP'
@@ -581,7 +730,7 @@ class Router {
if ($middleware === 'auth') { if ($middleware === 'auth') {
if (empty($_SESSION['user_logged_in'])) { if (empty($_SESSION['user_logged_in'])) {
header('Location: login.php'); header('Location: ?service=login');
exit; exit;
} }
} }
@@ -666,6 +815,7 @@ $files['WEBROOT/src/MVC/Layout/Templates/main.php'] = <<<'HTML'
.btn-toggle:hover { background: rgba(255,255,255,0.1); } .btn-toggle:hover { background: rgba(255,255,255,0.1); }
nav a { display: block; padding: 12px; text-decoration: none; color: inherit; border-radius: 5px; margin-bottom: 5px; } nav a { display: block; padding: 12px; text-decoration: none; color: inherit; border-radius: 5px; margin-bottom: 5px; }
nav a:hover { background: rgba(0,0,0,0.05); } nav a:hover { background: rgba(0,0,0,0.05); }
nav a.active { background: rgba(0,0,0,0.1); }
.logo { font-size: 1.4rem; font-weight: bold; } .logo { font-size: 1.4rem; font-weight: bold; }
</style> </style>
</head> </head>
@@ -692,10 +842,31 @@ $files['WEBROOT/src/MVC/Layout/Templates/main.php'] = <<<'HTML'
HTML; HTML;
// --- CONTROLLERS --- // --- CONTROLLERS ---
$files['WEBROOT/src/MVC/Controller/ApiController.php'] = <<<'PHP'
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
use MVC\Classes\Api;
class ApiController extends Controller {
public static function get(){
return Api::jsonResponse([
'data' => 'DummyData',
]);
}
}
PHP;
$files['WEBROOT/src/MVC/Controller/HomeController.php'] = <<<'PHP' $files['WEBROOT/src/MVC/Controller/HomeController.php'] = <<<'PHP'
<?php <?php
namespace MVC\Controller; namespace MVC\Controller;
use MVC\Core\Controller; use MVC\Core\Controller;
use MVC\Classes\Client;
use MVC\Classes\Cookie;
use MVC\Classes\Request;
use MVC\Classes\Server;
class HomeController extends Controller { class HomeController extends Controller {
public function index() { public function index() {
@@ -714,10 +885,75 @@ class HomeController extends Controller {
} }
PHP; PHP;
$files['WEBROOT/src/MVC/Controller/AuthController.php'] = <<<'PHP'
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
class AuthController extends Controller {
public function login() {
if (isset($_SESSION['user_logged_in'])) {
header('Location: ?service=home');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
// Hardcoded logins
$users = [
'admin' => 'password123',
'user' => 'user123'
];
if (isset($users[$username]) && $users[$username] === $password) {
$_SESSION['user_logged_in'] = true;
$_SESSION['username'] = $username;
header('Location: ?service=home');
exit;
} else {
return $this->render('login', ['error' => 'Ungültige Anmeldedaten']);
}
}
return $this->render('login');
}
public function logout() {
session_destroy();
header('Location: ?service=login');
exit;
}
}
PHP;
$files['WEBROOT/src/MVC/Controller/DocsController.php'] = <<<'PHP'
<?php
namespace MVC\Controller;
use MVC\Core\Controller;
class DocsController extends Controller {
public function index() {
return $this->render('docs', [
'title' => '📚 System-Dokumentation'
]);
}
}
PHP;
$files['WEBROOT/src/MVC/Controller/TestController.php'] = <<<'PHP' $files['WEBROOT/src/MVC/Controller/TestController.php'] = <<<'PHP'
<?php <?php
namespace MVC\Controller; namespace MVC\Controller;
use MVC\Core\Controller; use MVC\Core\Controller;
use MVC\Classes\Client;
use MVC\Classes\Cookie;
use MVC\Classes\Request;
use MVC\Classes\Server;
class TestController extends Controller { class TestController extends Controller {
public function service1() { public function service1() {
@@ -744,13 +980,151 @@ class TestController extends Controller {
} }
PHP; PHP;
// --- API ---
$files['WEBROOT/api/v1/get.php'] = <<<'PHP'
<?php
header('Content-Type: application/json');
require_once __DIR__ . '/../../src/MVC/autoloader.php';
use MVC\Controller\ApiController;
$api = (new ApiController());
try {
echo ($api->get());
} catch (\Exception $e) {
echo ($e->getMessage());
}
PHP;
// --- VIEWS --- // --- VIEWS ---
$files['WEBROOT/src/MVC/View/home.php'] = "<h1>Dashboard</h1><p>Willkommen in deinem neuen MVC-System mit integriertem Header.</p>"; $files['WEBROOT/src/MVC/View/home.php'] = "<h1>Dashboard</h1><p>Willkommen in deinem neuen MVC-System mit integriertem Header.</p>";
$files['WEBROOT/src/MVC/View/test1.php'] = "<h1>Service 1</h1><p><?= \$msg ?></p>"; $files['WEBROOT/src/MVC/View/test1.php'] = "<h1>Service 1</h1><p><?= \$msg ?></p>";
$files['WEBROOT/src/MVC/View/test2.php'] = "<h1>System Check</h1><ul><?php foreach(\$data as \$k => \$v): ?><li><b><?= \$k ?>:</b> <?= \$v ?></li><?php endforeach; ?></ul>"; $files['WEBROOT/src/MVC/View/test2.php'] = "<h1>System Check</h1><ul><?php foreach(\$data as \$k => \$v): ?><li><b><?= \$k ?>:</b> <?= \$v ?></li><?php endforeach; ?></ul>";
// --- API --- $files['WEBROOT/src/MVC/View/docs.php'] = <<<'HTML'
$files['WEBROOT/api/v1/get.php'] = "<?php header('Content-Type: application/json'); echo json_encode(['status' => 'online']);"; <div class="docs-container">
<h1>🏛️ Framework Architektur & Dokumentation</h1>
<div class="toc">
<h3>Inhalt</h3>
<ul>
<li><a href="#intro">1. Einführung</a></li>
<li><a href="#files">2. Datei-Referenz (Deep Dive)</a></li>
<li><a href="#api">3. API-System</a></li>
<li><a href="#database">4. Datenbank & Models</a></li>
<li><a href="#classes">5. Helper Klassen</a></li>
<li><a href="#generators">6. CLI-Generatoren</a></li>
<li><a href="#best-practices">7. Best Practices</a></li>
</ul>
</div>
<section id="intro">
<h2>1. Einführung</h2>
<p>Dieses Framework basiert auf dem <strong>Model-View-Controller (MVC)</strong> Entwurfsmuster und nutzt einen <strong>Front Controller</strong> (index.php). Alle Anfragen werden zentral verarbeitet, was globale Middleware (wie Authentifizierung) und ein einheitliches Layout ermöglicht.</p>
</section>
<section id="files">
<h2>2. Datei-Referenz (Deep Dive)</h2>
<h3>Framework Core</h3>
<ul>
<li><strong>Router.php:</strong> Verwaltet die Routen. Unterstützt Middlewares. Das Dispatching erfolgt über den <code>service</code> Parameter in der URL.</li>
<li><strong>Controller.php:</strong> Basisklasse für alle Controller. Bietet <code>render()</code> für Views und <code>json()</code> für API-Responses.</li>
<li><strong>Model.php:</strong> Abstrakte Basis für Datenbank-Entitäten. Kapselt die <code>databaseMethods.php</code> in ein objektorientiertes Interface.</li>
<li><strong>View.php:</strong> Behandelt das Extrahieren von Daten in HTML-Templates.</li>
</ul>
<h3>Konfiguration</h3>
<ul>
<li><strong>env.ini:</strong> Speichert Datenbank-Zugangsdaten für <code>dev</code> und <code>live</code> Umgebungen.</li>
<li><strong>autoloader.php:</strong> Registriert einen PSR-4 ähnlichen Autoloader für den <code>MVC\</code> Namespace.</li>
</ul>
</section>
<section id="api">
<h2>3. API-System</h2>
<p>APIs liegen im Verzeichnis <code>WEBROOT/api/v1/</code>. Sie nutzen die <code>ApiController</code>-Klasse.</p>
<pre>
// Beispiel: WEBROOT/api/v1/get.php
use MVC\Classes\Api;
use MVC\Controller\ApiController;
Api::jsonResponse((new ApiController())->get());
</pre>
</section>
<section id="database">
<h2>4. Datenbank & Models</h2>
<p>Das Framework nutzt <code>mysqli</code> mit Prepared Statements für maximale Sicherheit.</p>
<h3>databaseMethods.php (Die Power-Tools)</h3>
<ul>
<li><code>DatabaseShow($table, $columns, $where, ...$values)</code></li>
<li><code>DatabaseInsert($table, $columns, ...$values)</code></li>
<li><code>DatabaseUpdate($table, $set, $where, ...$values)</code></li>
<li><code>DatabaseDelete($table, $where, ...$values)</code></li>
</ul>
</section>
<section id="classes">
<h2>5. Helper Klassen (MVC\Classes)</h2>
<ul>
<li><strong>Request:</strong> Zugriff auf <code>method()</code>, <code>queryParams()</code>, <code>headers()</code>.</li>
<li><strong>Client:</strong> Ermittelt <code>ip()</code>, <code>userAgent()</code>, <code>country()</code>.</li>
<li><strong>Cookie:</strong> Interface zum <code>set()</code>, <code>read()</code>, <code>Delete()</code> von Cookies.</li>
</ul>
</section>
<section id="generators">
<h2>6. CLI-Generatoren</h2>
<p><code>php create_module.php [Name]</code> - Erstellt Controller & View.</p>
<p><code>php create_model.php [Tabelle] [Spalten]</code> - Erstellt ein Model.</p>
</section>
<section id="best-practices">
<h2>7. Best Practices</h2>
<p><strong>Naming:</strong> Controller immer mit <code>Controller</code>-Suffix. Models im Singular.</p>
<p><strong>Security:</strong> Nutze <code>['auth']</code> Middleware konsequent für alle internen Seiten.</p>
</section>
</div>
<style>
.docs-container { max-width: 1000px; color: var(--text); }
.toc { background: var(--sidebar); padding: 20px; border-radius: 8px; margin-bottom: 30px; border: 1px solid var(--border); }
.toc h3 { margin-top: 0; }
section { margin-bottom: 50px; padding-bottom: 20px; border-bottom: 1px solid var(--border); }
h2 { color: var(--accent); font-size: 1.8rem; margin-bottom: 20px; }
pre { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 5px; overflow-x: auto; border: 1px solid #333; font-size: 0.9rem; }
</style>
HTML;
$files['WEBROOT/src/MVC/View/login.php'] = <<<'HTML'
<style>
.login-container { max-width: 400px; margin: 50px auto; padding: 30px; border: 1px solid var(--border); border-radius: 8px; background: var(--sidebar); box-shadow: 0 4px 15px rgba(0,0,0,0.1); }
.login-container h2 { text-align: center; margin-top: 0; color: var(--text); }
.login-container input { width: 100%; padding: 12px; margin: 10px 0; border: 1px solid var(--border); border-radius: 4px; box-sizing: border-box; background: var(--bg); color: var(--text); }
.login-container button { width: 100%; padding: 12px; background: var(--header); color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; font-weight: bold; }
.login-container button:hover { opacity: 0.9; }
.error-msg { color: #e74c3c; background: rgba(231, 76, 60, 0.1); padding: 10px; border-radius: 4px; margin-bottom: 15px; text-align: center; font-size: 0.9rem; }
</style>
<div class="login-container">
<h2>🔑 Login</h2>
<?php if (isset($error)): ?>
<div class="error-msg"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<form method="POST">
<div>
<label>Benutzername</label>
<input type="text" name="username" required autofocus placeholder="Admin / User">
</div>
<div>
<label>Passwort</label>
<input type="password" name="password" required placeholder="********">
</div>
<button type="submit">Anmelden</button>
</form>
</div>
HTML;
// --- ENTRY POINT --- // --- ENTRY POINT ---
$files['WEBROOT/index.php'] = <<<'PHP' $files['WEBROOT/index.php'] = <<<'PHP'
@@ -760,31 +1134,62 @@ require_once __DIR__ . '/src/MVC/autoloader.php';
$service = $_GET['service'] ?? 'home'; $service = $_GET['service'] ?? 'home';
try { try {
$router = new \MVC\Core\Router(); $router = new \MVC\Core\Router();
// LOGIN EIN/AUSSCHALTEN: Einfach ['auth'] als 4. Parameter hinzufügen oder entfernen
$router->add('home', \MVC\Controller\HomeController::class, 'index'); $router->add('home', \MVC\Controller\HomeController::class, 'index');
$router->add('toggle_theme', \MVC\Controller\HomeController::class, 'toggle_theme'); $router->add('toggle_theme', \MVC\Controller\HomeController::class, 'toggle_theme');
$router->add('test1', \MVC\Controller\TestController::class, 'service1'); $router->add('docs', \MVC\Controller\DocsController::class, 'index');
$router->add('test2', \MVC\Controller\TestController::class, 'service2'); $router->add('test1', \MVC\Controller\TestController::class, 'service1', ['auth']);
$router->add('test2', \MVC\Controller\TestController::class, 'service2', ['auth']);
$router->add('login', \MVC\Controller\AuthController::class, 'login');
$router->add('logout', \MVC\Controller\AuthController::class, 'logout');
$layout = new \MVC\Layout\Layout('main'); $layout = new \MVC\Layout\Layout('main');
$layout->setBlock('title', 'MVC Starter'); $layout->setBlock('title', 'MVC Starter');
$layout->setBlock('header_title', '🚀 MVC Dashboard'); $layout->setBlock('header_title', '🚀 MVC Dashboard');
$layout->setBlock('header_actions', '<button class="btn-toggle" onclick="toggleTheme()">🌓 Theme umschalten</button>');
$headerActions = '<button class="btn-toggle" onclick="toggleTheme()">🌓 Theme umschalten</button>';
if (isset($_SESSION['user_logged_in'])) {
$headerActions = '<span style="margin-right: 15px; font-size: 0.9rem;">👤 ' . htmlspecialchars($_SESSION['username']) . '</span>' .
'<a href="?service=logout" class="btn-toggle" style="text-decoration: none; background: rgba(255,255,255,0.2); margin-right: 10px;">🚪 Logout</a> ' .
$headerActions;
} else {
$headerActions = '<a href="?service=login" class="btn-toggle" style="text-decoration: none; background: var(--accent); color: white; margin-right: 10px;">🔑 Login</a> ' .
$headerActions;
}
$layout->setBlock('header_actions', $headerActions);
// Sidebar Block // Sidebar Block
ob_start(); ob_start();
$navItems = [
'home' => ['label' => '🏠 Home', 'auth' => false],
'test1' => ['label' => '🛠️ Test 1', 'auth' => true],
'test2' => ['label' => '📊 Test 2', 'auth' => true],
'docs' => ['label' => '📚 Dokumentation', 'auth' => false],
];
?> ?>
<nav> <nav>
<a href="?service=home">🏠 Home</a> <?php foreach ($navItems as $navKey => $item):
<a href="?service=test1">🛠️ Test 1</a> if ($item['auth'] && !isset($_SESSION['user_logged_in'])) continue;
<a href="?service=test2">📊 Test 2</a> ?>
<a href="?service=<?= $navKey ?>" class="<?= $service === $navKey ? 'active' : '' ?>"><?= $item['label'] ?></a>
<?php endforeach; ?>
<?php if (!isset($_SESSION['user_logged_in'])): ?>
<div style="margin-top: 20px; padding: 10px; font-size: 0.8rem; opacity: 0.5; border-top: 1px solid var(--border);">
🔒 Melden Sie sich an, um alle Funktionen zu sehen.
</div>
<?php endif; ?>
</nav> </nav>
<?php <?php
$layout->setBlock('sidebar', ob_get_clean()); $layout->setBlock('sidebar', ob_get_clean());
$layout->setBlock('content', $router->dispatch($service)); $layout->setBlock('content', $router->dispatch($service));
echo $layout->render(); echo $layout->render();
} catch (\Exception $e) { } catch (\Throwable $e) {
http_response_code(404); echo "<h1>404</h1><p>" . $e->getMessage() . "</p>"; http_response_code(500);
echo "<h1>Error</h1><p>" . htmlspecialchars($e->getMessage()) . "</p>";
if (ini_get('display_errors')) echo "<pre>" . htmlspecialchars($e->getTraceAsString()) . "</pre>";
} }
PHP; PHP;
@@ -909,6 +1314,10 @@ $controllerContent = <<<PHPCTRL
namespace MVC\Controller; namespace MVC\Controller;
use MVC\Core\Controller; use MVC\Core\Controller;
use MVC\Classes\Client;
use MVC\Classes\Cookie;
use MVC\Classes\Request;
use MVC\Classes\Server;
// use MVC\Model\YourModel; // Importiere dein Model // use MVC\Model\YourModel; // Importiere dein Model
class {$className}Controller extends Controller { class {$className}Controller extends Controller {
@@ -958,11 +1367,9 @@ if (!file_exists($viewFile)) {
// 3. Anleitung ausgeben // 3. Anleitung ausgeben
echo "\n--- NÄCHSTE SCHRITTE ---\n"; echo "\n--- NÄCHSTE SCHRITTE ---\n";
echo "1. Registriere das Modul in WEBROOT/index.php beim Router:\n"; echo " Registriere das Modul in WEBROOT/index.php beim Router:\n";
echo " \$router->add('{$key}', \\MVC\\Controller\\{$className}Controller::class, '{$key}', ['auth']);\n\n"; echo " \$router->add('{$key}', \\MVC\\Controller\\{$className}Controller::class, '{$key}', ['auth']);\n";
echo "2. Füge den Service in der Seitenverwaltung (Admin) oder in config.php hinzu:\n"; echo " (Lösche ['auth'] wenn kein Login erforderlich ist)\n\n";
echo " ID: {$key}\n";
echo " Name: " . ucfirst($name) . "\n";
echo "------------------------\n"; echo "------------------------\n";
PHP; PHP;
@@ -973,6 +1380,7 @@ foreach ($files as $file => $content) {
echo "📄 Datei erstellt: $file\n"; echo "📄 Datei erstellt: $file\n";
} }
echo "\n✨ Projekt '$projectName' wurde mit HEADER initialisiert!\n"; echo "\n✨ Projekt '$projectName' wurde initialisiert!\n";
echo "1. Starten: php server.php\n"; echo "1. Richte deine Datenbankverbindung in WEBROOT/src/config/env.ini ein.\n";
echo "2. Öffnen: http://localhost:8000\n"; echo "2. Starten: php server.php\n";
echo "3. Öffnen: http://localhost:8000\n";