# IMVS - Architektur und Grundprinzipien ## Inhaltsverzeichnis 1. [Projektübersicht](#1-projektübersicht) 2. [Framework: MintyPHP](#2-framework-mintyphp) 3. [Verzeichnisstruktur](#3-verzeichnisstruktur) 4. [Datenbankarchitektur](#4-datenbankarchitektur) 5. [PHP-Architektur](#5-php-architektur) 6. [Frontend-Architektur](#6-frontend-architektur) 7. [Template-System](#7-template-system) 8. [Authentifizierung und Autorisierung](#8-authentifizierung-und-autorisierung) 9. [Internationalisierung](#9-internationalisierung) 10. [Best Practices](#10-best-practices) --- ## 1. Projektübersicht **Projektname:** IMVS **Framework:** MintyPHP v3+ **PHP-Version:** 8.5+ **Datenbank:** MySQL 5.7+ **Lizenz:** MIT ### Kernfeatures - Multi-Tenant-Architektur - Rollen- und Berechtigungssystem - Mehrsprachigkeit (DE/EN) - Dark/Light Theme - E-Mail-Integration mit Templates --- ## 2. Framework: MintyPHP ### Philosophie MintyPHP ist ein leichtgewichtiges PHP-Framework mit folgenden Prinzipien: - **Easy to learn** - Einfach für PHP-Entwickler zu erlernen - **Secure by design** - Sicherheit als Grundprinzip - **Lightweight** - Minimaler Overhead - **Single variable scope** - Eine Variable-Scope für alle Layer - **SQL required** - Direktes SQL statt ORM - **PHP as templating** - PHP selbst als Template-Sprache ### Kern-Komponenten | Komponente | Beschreibung | |------------|--------------| | `Router` | URL-Routing zu Pages | | `DB` | Datenbankzugriff mit Prepared Statements | | `Auth` | Basis-Authentifizierung | | `Session` | Session-Management | | `Buffer` | Output-Buffering für Templates | | `I18n` | Internationalisierung | | `Cache` | Memcached-Integration | | `Firewall` | DDoS-Schutz | ### Router-Konvention ``` URL: /admin/users/edit/abc-123 Datei: pages/admin/users/edit($id).php Variable: $id = 'abc-123' ``` --- ## 3. Verzeichnisstruktur ``` ImmobilienMaklerVerkaufssoftware/ ├── config/ # Konfigurationsdateien │ ├── config.php # Hauptkonfiguration │ ├── router.php # Route-Definitionen │ └── settings.php # App-Einstellungen (generiert) ├── db/ # Datenbank │ └── init/init.sql # Schema und Seeds ├── docker/ # Docker-Konfiguration ├── docs/ # Dokumentation ├── i18n/ # Sprachdateien │ ├── default_de.json │ └── default_en.json ├── lib/ # Geschäftslogik │ ├── Http/ # HTTP-Utilities │ ├── Repository/ # Datenbankschicht │ ├── Service/ # Business-Logic │ └── Support/ # Helper und Guards ├── pages/ # Seiten (Controller) │ ├── admin/ # Geschützte Admin-Seiten │ ├── auth/ # Authentifizierung │ ├── error/ # Fehlerseiten │ └── page/ # CMS-Seiten ├── storage/ # Dateiablage │ ├── branding/ # Logos, Favicons │ ├── tenants/ # Tenant-Dateien │ └── users/ # User-Avatare ├── templates/ # View-Templates │ ├── partials/ # Wiederverwendbare Teile │ └── emails/ # E-Mail-Templates ├── tests/ # PHPUnit Tests ├── vendor/ # Composer Dependencies └── web/ # Public Root ├── css/ # Stylesheets ├── js/ # JavaScript-Module ├── vendor/ # Frontend-Libraries └── index.php # Entry Point ``` --- ## 4. Datenbankarchitektur ### Entity-Relationship-Diagramm ``` ┌─────────────┐ ┌─────────────────┐ ┌─────────────┐ │ USERS │────<│ USER_TENANTS │>────│ TENANTS │ └─────────────┘ └─────────────────┘ └─────────────┘ │ │ │ ┌─────────────────┐ │ └────────────<│ USER_ROLES │ │ │ └─────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ ROLES │ │ │ └─────────────┘ │ │ │ │ │ ┌─────────────────┐ │ │ │ROLE_PERMISSIONS │ │ │ └─────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ PERMISSIONS │ │ │ └─────────────┘ │ │ │ │ ┌─────────────────┐ ┌──────┴──────┐ └────────────<│USER_DEPARTMENTS │>────│ DEPARTMENTS │ └─────────────────┘ └─────────────┘ │ ┌──────┴──────┐ │TENANT_DEPTS │ └─────────────┘ ``` ### Haupttabellen #### users ```sql id INT PRIMARY KEY AUTO_INCREMENT uuid VARCHAR(36) UNIQUE first_name VARCHAR(100) last_name VARCHAR(100) email VARCHAR(255) UNIQUE password VARCHAR(255) -- bcrypt hash locale VARCHAR(10) -- 'de', 'en' theme VARCHAR(20) -- 'light', 'dark' active TINYINT DEFAULT 1 primary_tenant_id INT FK -> tenants.id current_tenant_id INT FK -> tenants.id created_by INT FK -> users.id modified_by INT FK -> users.id created DATETIME modified DATETIME ``` #### tenants ```sql id INT PRIMARY KEY AUTO_INCREMENT uuid VARCHAR(36) UNIQUE description VARCHAR(255) -- Name/Firma address VARCHAR(255) postal_code VARCHAR(20) city VARCHAR(100) country VARCHAR(100) vat_id VARCHAR(50) -- MwSt-ID tax_number VARCHAR(50) phone VARCHAR(50) email VARCHAR(255) website VARCHAR(255) created_by INT FK -> users.id modified_by INT FK -> users.id created DATETIME modified DATETIME ``` #### roles ```sql id INT PRIMARY KEY AUTO_INCREMENT uuid VARCHAR(36) UNIQUE description VARCHAR(255) -- Rollenname created_by INT FK -> users.id modified_by INT FK -> users.id created DATETIME modified DATETIME ``` #### permissions ```sql id INT PRIMARY KEY AUTO_INCREMENT key VARCHAR(100) UNIQUE -- z.B. 'users.view' description VARCHAR(255) created DATETIME ``` #### departments ```sql id INT PRIMARY KEY AUTO_INCREMENT uuid VARCHAR(36) UNIQUE description VARCHAR(255) -- Abteilungsname created_by INT FK -> users.id modified_by INT FK -> users.id created DATETIME modified DATETIME ``` ### Verknüpfungstabellen | Tabelle | Beschreibung | |---------|--------------| | `user_tenants` | User ↔ Tenant (M:N) | | `user_roles` | User ↔ Role (M:N) | | `user_departments` | User ↔ Department (M:N) | | `role_permissions` | Role ↔ Permission (M:N) | | `tenant_departments` | Tenant ↔ Department (M:N) | ### Vordefinierte Permissions ``` users.view, users.create, users.update, users.delete users.self_update, users.update_assignments tenants.view, tenants.create, tenants.update, tenants.delete departments.view, departments.create, departments.update, departments.delete roles.view, roles.create, roles.update, roles.delete permissions.view, permissions.create, permissions.update, permissions.delete settings.view, settings.update mail_log.view stats.view ``` --- ## 5. PHP-Architektur ### Schichten-Architektur ``` ┌─────────────────────────────────────────────┐ │ Pages │ │ (Controller/Actions) │ ├─────────────────────────────────────────────┤ │ Services │ │ (Business Logic, Validation) │ ├─────────────────────────────────────────────┤ │ Repositories │ │ (Data Access, SQL) │ ├─────────────────────────────────────────────┤ │ DB │ │ (MintyPHP Database) │ └─────────────────────────────────────────────┘ ``` ### Service-Layer (`lib/Service/`) Services implementieren die Geschäftslogik: ```php namespace MintyPHP\Service; class UserService { // Listen public static function list(): array public static function listPaged(array $options): array // Finder public static function findByUuid(string $uuid): ?array public static function findById(int $id): ?array public static function findByEmail(string $email): ?array // CRUD public static function createFromAdmin(array $input, int $currentUserId): array public static function updateFromAdmin(int $userId, array $input, int $currentUserId): array public static function deleteByUuid(string $uuid, int $currentUserId): array // Bulk-Operationen public static function deleteByUuids(array $uuids, int $currentUserId): array public static function setActiveByUuids(array $uuids, bool $active, int $currentUserId): array } ``` **Wichtige Services:** | Service | Verantwortung | |---------|---------------| | `AuthService` | Login, Logout, Session-Management | | `UserService` | Benutzerverwaltung | | `TenantService` | Mandantenverwaltung | | `RoleService` | Rollenverwaltung | | `PermissionService` | Berechtigungsprüfung und -verwaltung | | `DepartmentService` | Abteilungsverwaltung | | `MailService` | E-Mail-Versand | | `SettingService` | App-Einstellungen | ### Repository-Layer (`lib/Repository/`) Repositories kapseln Datenbankzugriffe: ```php namespace MintyPHP\Repository; class UserRepository { public static function list(): array { return DB::select('SELECT * FROM users ORDER BY last_name, first_name'); } public static function find(int $id): ?array { return DB::selectOne('SELECT * FROM users WHERE id = ?', [$id]); } public static function create(array $data): int { return DB::insert('users', $data); } public static function update(int $id, array $data): bool { return DB::update('users', $data, ['id' => $id]) > 0; } } ``` ### Support-Layer (`lib/Support/`) #### Guard - Zugriffskontrolle ```php namespace MintyPHP\Support; class Guard { // Nur für angemeldete Benutzer public static function requireLogin(string $redirect = 'login'): void // Spezifische Berechtigung erforderlich public static function requirePermission(string $permissionKey, string $redirect = 'admin'): void // Berechtigung oder 403 (für AJAX) public static function requirePermissionOrForbidden(string $permissionKey): void } ``` #### Flash - Session-Nachrichten ```php namespace MintyPHP\Support; class Flash { public static function success(string $message, ?string $scope = null, ?string $key = null): string public static function error(string $message, ?string $scope = null, ?string $key = null): string public static function info(string $message, ?string $scope = null, ?string $key = null): string public static function peek(?string $scope = null): array public static function has(): bool } ``` ### Helper-Funktionen (`lib/Support/helpers/`) ```php // HTML-Escape e($string); // Asset-URL asset('css/style.css'); // Lokalisierte URL lurl('admin/users'); // Übersetzung t('Hello'); t('Welcome %s', $name); // App-Einstellung appSetting('app_title'); // Berechtigung prüfen can('users.view'); ``` --- ## 6. Frontend-Architektur ### CSS-Struktur (`web/css/`) ``` web/css/ ├── base/ │ ├── variables.base.css # CSS-Variablen (Basis + Light/Dark) │ ├── variables.theme-dark-green.css # Theme-Overrides │ └── variables.contrast.css # High-Contrast Overrides ├── layout/ │ ├── app-shell.css # Haupt-Layout │ ├── app-topbar.css # Header │ ├── app-sidebar.css # Navigation │ └── app-aside-icon-bar.css ├── components/ │ ├── app-buttons.css │ ├── app-forms.css │ ├── app-list-table.css │ ├── app-badges.css │ ├── app-tabs.css │ ├── app-flash.css │ └── vendor-*.css # Third-Party └── pages/ └── app-login.css ``` ### JavaScript-Module (`web/js/`) ES-Module-Architektur: ``` web/js/ ├── app-init.js # Haupt-Initialisierung ├── core/ │ └── app-grid-factory.js # Grid/Tabellen-Generator ├── components/ │ ├── app-tenant-switcher.js │ ├── app-theme-toggle.js │ ├── app-bulk-selection.js │ ├── app-flash-auto-dismiss.js │ └── ... └── pages/ ├── app-users-list.js └── app-list-utils.js ``` ### Modul-Pattern ```javascript // web/js/components/app-tenant-switcher.js const switchTenant = async (link) => { const tenantId = link.dataset.switchTenant; // ... }; const initTenantSwitcher = () => { const tenantLinks = document.querySelectorAll('[data-switch-tenant]'); tenantLinks.forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); switchTenant(link); }); }); }; // Auto-Init bei DOM Ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initTenantSwitcher); } else { initTenantSwitcher(); } ``` ### Import in app-init.js ```javascript // web/js/app-init.js import './components/app-tenant-switcher.js'; import './components/app-theme-toggle.js'; import './components/app-flash-auto-dismiss.js'; // ... ``` --- ## 7. Template-System ### Struktur ``` templates/ ├── default.phtml # Haupt-Layout ├── login.phtml # Login-Layout ├── error.phtml # Fehler-Layout ├── partials/ │ ├── app-topbar.phtml │ ├── app-sidebar.phtml │ ├── app-aside.phtml │ ├── app-flash.phtml │ └── app-footer.phtml └── emails/ ├── de/ │ ├── access_info.html │ └── reset_code.html └── en/ └── ... ``` ### Template-Rendering ```php <?php e(Buffer::get('title') ?? appTitle()); ?>
``` ### Page mit View ```php // pages/admin/users/index().php use MintyPHP\Buffer; use MintyPHP\Support\Guard; Guard::requirePermissionOrForbidden('users.view'); $users = UserService::list(); Buffer::set('title', t('Users')); // View wird automatisch geladen: pages/admin/users/index(default).phtml ``` --- ## 8. Authentifizierung und Autorisierung ### Login-Ablauf ``` 1. POST /login │ ├─> AuthService::login(email, password) │ ├─> Auth::login() - Passwort-Verifikation │ ├─> $_SESSION['user'] setzen │ ├─> PermissionService::getUserPermissions() │ │ └─> $_SESSION['permissions'] setzen │ └─> AuthService::loadTenantDataIntoSession() │ ├─> $_SESSION['available_tenants'] │ └─> $_SESSION['current_tenant'] │ └─> Router::redirect('admin') ``` ### Berechtigungsprüfung ```php // In Page Guard::requirePermissionOrForbidden(PermissionService::USERS_VIEW); // Im Template Neuer Benutzer ``` ### Session-Struktur ```php $_SESSION = [ 'user' => [ 'id' => 1, 'uuid' => 'abc-123', 'email' => 'user@example.com', 'first_name' => 'Max', 'last_name' => 'Mustermann', 'locale' => 'de', 'theme' => 'dark', ], 'permissions' => [ 'user_id' => 1, 'keys' => ['users.view', 'users.create', ...], ], 'available_tenants' => [ ['id' => 1, 'uuid' => '...', 'description' => 'Tenant A'], ['id' => 2, 'uuid' => '...', 'description' => 'Tenant B'], ], 'current_tenant' => [ 'id' => 1, 'uuid' => '...', 'description' => 'Tenant A', ], ]; ``` --- ## 9. Internationalisierung ### Sprachdateien ```json // i18n/default_de.json { "Login": "Anmelden", "Users": "Benutzer", "At least %d characters": "Mindestens %d Zeichen" } // i18n/default_en.json { "Login": "Login", "Users": "Users", "At least %d characters": "At least %d characters" } ``` ### Verwendung ```php // Einfache Übersetzung t('Login') // Mit Parametern t('At least %d characters', 12) // Im Template ``` ### Sprachauflösung Priorität: 1. URL-Präfix (`/de/admin`, `/en/admin`) 2. Session (`$_SESSION['user']['locale']`) 3. Cookie (`locale=de`) 4. Default (`APP_LOCALE`) --- ## 10. Best Practices ### Service-Rückgabewerte ```php // Erfolg return [ 'ok' => true, 'uuid' => $uuid, 'id' => $id, ]; // Fehler return [ 'ok' => false, 'error' => 'validation_error', 'errors' => ['email' => 'Email ist erforderlich'], 'form' => $sanitizedInput, ]; ``` ### Datenbankzugriff ```php // RICHTIG - Prepared Statements $user = DB::selectOne('SELECT * FROM users WHERE email = ?', [$email]); // FALSCH - SQL-Injection $user = DB::selectOne("SELECT * FROM users WHERE email = '$email'"); ``` ### Template-Ausgabe ```php // RICHTIG - Escape // FALSCH - XSS-Risiko ``` ### Passwort-Anforderungen - Mindestens 12 Zeichen - Mindestens 1 Großbuchstabe - Mindestens 1 Kleinbuchstabe - Mindestens 1 Ziffer - Mindestens 1 Sonderzeichen ### Code-Organisation 1. **Services** - Geschäftslogik und Validierung 2. **Repositories** - Nur Datenbankzugriff, keine Logik 3. **Guards** - Zugriffskontrolle 4. **Helpers** - Wiederverwendbare Funktionen 5. **Pages** - Koordination, keine Logik --- ## Weiterführende Dokumentation - [MintyPHP Framework](https://github.com/mevdschee/php-crud-api) - [Docker Setup](../docker-compose.yml) - [Database Schema](../db/init/init.sql)