asd
This commit is contained in:
445
BASE_FILES.md
Normal file
445
BASE_FILES.md
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
# Basisdateien fuer neue Projekte
|
||||||
|
|
||||||
|
Dieses Dokument enthaelt die Basisdateien, die bei einem neuen Projekt direkt angelegt werden sollen. Der Schwerpunkt liegt auf den Config-Dateien, weil diese fuer den ersten lauffaehigen Stand zwingend benoetigt werden.
|
||||||
|
|
||||||
|
Dieses Dokument ist dafuer gedacht, zusammen mit `GENERAL.md` in ein neues GitLab-Projekt kopiert zu werden. Danach kann die Projektstruktur inklusive Basisdateien direkt erstellt werden.
|
||||||
|
|
||||||
|
## Wichtige Regel vor der Erstellung
|
||||||
|
|
||||||
|
Ein neues Projekt darf nur erstellt werden, wenn beide Domains bekannt sind:
|
||||||
|
|
||||||
|
- Live-Domain
|
||||||
|
- Staging-Domain
|
||||||
|
|
||||||
|
Wenn eine oder beide Angaben fehlen, muss die Erstellung gestoppt werden. Vor dem Anlegen der Dateien ist dann explizit nach beiden Domains zu fragen.
|
||||||
|
|
||||||
|
Ohne beide Domains duerfen insbesondere diese Dateien nicht final erzeugt werden:
|
||||||
|
|
||||||
|
- `config/prod/domaindata.php`
|
||||||
|
- `config/prod/settings.php`
|
||||||
|
- `config/staging/domaindata.php`
|
||||||
|
- `config/staging/settings.php`
|
||||||
|
|
||||||
|
Vor der Neuerstellung ist das Repository ausserdem auf den neuen Projektstand zurueckzusetzen:
|
||||||
|
|
||||||
|
- alle bestehenden Dateien und Ordner loeschen
|
||||||
|
- `.gitlab-ci.yml` ausdruecklich behalten
|
||||||
|
- `.gitlab-ci.yml` anschliessend auf alte Domainreferenzen, Umgebungs-URLs und projektspezifische Angaben pruefen
|
||||||
|
- gefundene Domainreferenzen in `.gitlab-ci.yml` auf die neue Live- und Staging-Domain anpassen
|
||||||
|
|
||||||
|
## Platzhalter fuer neue Projekte
|
||||||
|
|
||||||
|
In den folgenden Vorlagen werden diese Platzhalter verwendet:
|
||||||
|
|
||||||
|
- `<LIVE_DOMAIN>` fuer die Produktiv-Domain
|
||||||
|
- `<STAGING_DOMAIN>` fuer die Staging-Domain
|
||||||
|
- `<APP_PREFIX>` fuer den Cookie- und App-Prefix
|
||||||
|
- `<APP_NAME>` fuer einen allgemeinen Projektnamen oder OIDC-Client-Namen
|
||||||
|
|
||||||
|
## Datei: `config/fileload.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
spl_autoload_register(function ($class) {
|
||||||
|
if (str_starts_with($class, 'App\\Repository\\')) {
|
||||||
|
$prefix = 'App\\Repository\\';
|
||||||
|
$baseDir = __DIR__ . '/../src/Repository/';
|
||||||
|
} elseif (str_starts_with($class, 'App\\')) {
|
||||||
|
$prefix = 'App\\';
|
||||||
|
$baseDir = __DIR__ . '/../src/App/';
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$len = strlen($prefix);
|
||||||
|
$relativeClass = substr($class, $len);
|
||||||
|
$file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
|
||||||
|
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require $file;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../src/App/functions.php';
|
||||||
|
|
||||||
|
$domainFile = __DIR__ . '/domaindata.php';
|
||||||
|
$settingsFile = __DIR__ . '/settings.php';
|
||||||
|
$configFile = __DIR__ . '/db.php';
|
||||||
|
$baseConfigFile = __DIR__ . '/base_db.php';
|
||||||
|
$fallbackBaseConfigStaging = __DIR__ . '/staging/db_settings_basic.php';
|
||||||
|
$fallbackBaseConfigProd = __DIR__ . '/prod/db_settings_basic.php';
|
||||||
|
|
||||||
|
if (file_exists($domainFile)) {
|
||||||
|
require_once $domainFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_exists($settingsFile)) {
|
||||||
|
require_once $settingsFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbConfig = [];
|
||||||
|
if (file_exists($configFile)) {
|
||||||
|
$dbConfig = require $configFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
$baseDbConfig = [];
|
||||||
|
if (file_exists($baseConfigFile)) {
|
||||||
|
$baseDbConfig = require $baseConfigFile;
|
||||||
|
}
|
||||||
|
if (empty($baseDbConfig) && file_exists($fallbackBaseConfigStaging)) {
|
||||||
|
$baseDbConfig = require $fallbackBaseConfigStaging;
|
||||||
|
}
|
||||||
|
if (empty($baseDbConfig) && file_exists($fallbackBaseConfigProd)) {
|
||||||
|
$baseDbConfig = require $fallbackBaseConfigProd;
|
||||||
|
}
|
||||||
|
|
||||||
|
global $appConfig;
|
||||||
|
$dbEnabled = defined('APP_DB_ENABLED') ? APP_DB_ENABLED : true;
|
||||||
|
$baseDbEnabled = defined('APP_BASE_DB_ENABLED') ? APP_BASE_DB_ENABLED : false;
|
||||||
|
$appConfig = new \App\Config($dbConfig, $dbEnabled, $baseDbConfig, $baseDbEnabled);
|
||||||
|
|
||||||
|
\App\App::init($appConfig);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `config/config.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
ini_set('display_errors', '1');
|
||||||
|
ini_set('display_startup_errors', '1');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
foreach (['domaindata.php', 'settings.php'] as $cfgFile) {
|
||||||
|
$rootPath = __DIR__ . '/' . $cfgFile;
|
||||||
|
|
||||||
|
if (file_exists($rootPath)) {
|
||||||
|
require_once $rootPath;
|
||||||
|
} else {
|
||||||
|
throw new \RuntimeException("Missing required config file: $cfgFile (expected $rootPath)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('ASSET_VERSION')) {
|
||||||
|
define('ASSET_VERSION', 'dev-' . date('Ymd-His'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('APP_DOMAIN_PRIMARY')) {
|
||||||
|
define('APP_DOMAIN_PRIMARY', APP_DOMAIN_NAME);
|
||||||
|
}
|
||||||
|
if (!defined('APP_URL_PRIMARY')) {
|
||||||
|
define('APP_URL_PRIMARY', 'https://' . APP_DOMAIN_PRIMARY);
|
||||||
|
}
|
||||||
|
if (!defined('APP_API_BASE')) {
|
||||||
|
define('APP_API_BASE', 'https://api.' . APP_DOMAIN_PRIMARY);
|
||||||
|
}
|
||||||
|
if (!defined('APP_DB_ENABLED')) {
|
||||||
|
define('APP_DB_ENABLED', false);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `config/base_db.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
$path = __DIR__ . '/db_settings_basic.php';
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
throw new RuntimeException('Missing base DB config: expected config/db_settings_basic.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
return require $path;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `config/staging/domaindata.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
if (!defined('APP_DOMAIN_NAME')) {
|
||||||
|
define('APP_DOMAIN_NAME', '<STAGING_DOMAIN>');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('APP_PREFIX')) {
|
||||||
|
define('APP_PREFIX', '<APP_PREFIX>');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `config/prod/domaindata.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
if (!defined('APP_DOMAIN_NAME')) {
|
||||||
|
define('APP_DOMAIN_NAME', '<LIVE_DOMAIN>');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('APP_PREFIX')) {
|
||||||
|
define('APP_PREFIX', '<APP_PREFIX>');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `config/staging/settings.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
define('ASSET_VERSION', 'dev-' . date('Ymd-His'));
|
||||||
|
define('APP_DOMAIN_PRIMARY', APP_DOMAIN_NAME);
|
||||||
|
define('APP_URL_PRIMARY', 'https://' . APP_DOMAIN_PRIMARY);
|
||||||
|
define('APP_API_BASE', 'https://api.' . APP_DOMAIN_PRIMARY);
|
||||||
|
|
||||||
|
define('APP_DB_ENABLED', false);
|
||||||
|
define('APP_DB_DEBUG', true);
|
||||||
|
define('APP_DB_AUTO_INIT', true);
|
||||||
|
define('APP_BASE_DB_ENABLED', true);
|
||||||
|
|
||||||
|
define('APP_BASIC_AUTH', true);
|
||||||
|
define('APP_AUTH_ENABLED', false);
|
||||||
|
define('APP_DEBUG_TOOL', true);
|
||||||
|
define('APP_AUTH_DEBUG', true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Optional fuer Projekte mit OIDC:
|
||||||
|
|
||||||
|
define('APP_OIDC_ISSUER', 'https://auth.example.tld/realms/<APP_NAME>');
|
||||||
|
define('APP_OIDC_AUTH_ENDPOINT', 'https://auth.example.tld/realms/<APP_NAME>/protocol/openid-connect/auth');
|
||||||
|
define('APP_OIDC_TOKEN_ENDPOINT', 'https://auth.example.tld/realms/<APP_NAME>/protocol/openid-connect/token');
|
||||||
|
define('APP_OIDC_USERINFO_ENDPOINT', 'https://auth.example.tld/realms/<APP_NAME>/protocol/openid-connect/userinfo');
|
||||||
|
define('APP_OIDC_LOGOUT_ENDPOINT', 'https://auth.example.tld/realms/<APP_NAME>/protocol/openid-connect/logout');
|
||||||
|
define('APP_OIDC_CLIENT_ID', '<APP_NAME>');
|
||||||
|
define('APP_OIDC_CLIENT_SECRET', 'CHANGE_ME');
|
||||||
|
define('APP_OIDC_REDIRECT_URI', 'https://<STAGING_DOMAIN>/auth/callback');
|
||||||
|
define('APP_OIDC_POST_LOGOUT_REDIRECT_URI', 'https://<STAGING_DOMAIN>/');
|
||||||
|
define('APP_OIDC_GROUP_CLAIM', 'groups');
|
||||||
|
define('APP_OIDC_ADMIN_GROUP', 'appadmin');
|
||||||
|
define('APP_OIDC_USER_GROUP', 'user');
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `config/prod/settings.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
define('ASSET_VERSION', 'dev-' . date('Ymd-His'));
|
||||||
|
define('APP_DOMAIN_PRIMARY', APP_DOMAIN_NAME);
|
||||||
|
define('APP_URL_PRIMARY', 'https://' . APP_DOMAIN_PRIMARY);
|
||||||
|
define('APP_API_BASE', 'https://api.' . APP_DOMAIN_PRIMARY);
|
||||||
|
|
||||||
|
define('APP_DB_ENABLED', false);
|
||||||
|
define('APP_DB_DEBUG', false);
|
||||||
|
define('APP_DB_AUTO_INIT', true);
|
||||||
|
define('APP_BASE_DB_ENABLED', true);
|
||||||
|
|
||||||
|
define('APP_BASIC_AUTH', false);
|
||||||
|
define('APP_AUTH_ENABLED', false);
|
||||||
|
define('APP_DEBUG_TOOL', false);
|
||||||
|
define('APP_AUTH_DEBUG', false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Optional fuer Projekte mit OIDC:
|
||||||
|
|
||||||
|
define('APP_OIDC_ISSUER', 'https://auth.example.tld/realms/<APP_NAME>');
|
||||||
|
define('APP_OIDC_AUTH_ENDPOINT', 'https://auth.example.tld/realms/<APP_NAME>/protocol/openid-connect/auth');
|
||||||
|
define('APP_OIDC_TOKEN_ENDPOINT', 'https://auth.example.tld/realms/<APP_NAME>/protocol/openid-connect/token');
|
||||||
|
define('APP_OIDC_USERINFO_ENDPOINT', 'https://auth.example.tld/realms/<APP_NAME>/protocol/openid-connect/userinfo');
|
||||||
|
define('APP_OIDC_LOGOUT_ENDPOINT', 'https://auth.example.tld/realms/<APP_NAME>/protocol/openid-connect/logout');
|
||||||
|
define('APP_OIDC_CLIENT_ID', '<APP_NAME>');
|
||||||
|
define('APP_OIDC_CLIENT_SECRET', 'CHANGE_ME');
|
||||||
|
define('APP_OIDC_REDIRECT_URI', 'https://<LIVE_DOMAIN>/auth/callback');
|
||||||
|
define('APP_OIDC_POST_LOGOUT_REDIRECT_URI', 'https://<LIVE_DOMAIN>/');
|
||||||
|
define('APP_OIDC_GROUP_CLAIM', 'groups');
|
||||||
|
define('APP_OIDC_ADMIN_GROUP', 'appadmin');
|
||||||
|
define('APP_OIDC_USER_GROUP', 'user');
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `config/staging/db_settings_basic.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dsn' => 'sqlite:' . __DIR__ . '/../../data/app_staging.sqlite',
|
||||||
|
'user' => null,
|
||||||
|
'password' => null,
|
||||||
|
'options' => [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `config/prod/db_settings_basic.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dsn' => 'sqlite:' . __DIR__ . '/../../data/app_prod.sqlite',
|
||||||
|
'user' => null,
|
||||||
|
'password' => null,
|
||||||
|
'options' => [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `public/index.php`
|
||||||
|
|
||||||
|
Minimaler Einstiegspunkt fuer den Webzugriff:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../config/fileload.php';
|
||||||
|
|
||||||
|
$uriPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
|
||||||
|
$uriPath = preg_replace('~/{2,}~', '/', $uriPath);
|
||||||
|
$uriPath = trim($uriPath, '/');
|
||||||
|
|
||||||
|
if (str_contains($uriPath, '..')) {
|
||||||
|
http_response_code(400);
|
||||||
|
exit('Bad request');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pagesBase = realpath(__DIR__ . '/../partials/landingpages') ?: (__DIR__ . '/../partials/landingpages');
|
||||||
|
$page404 = $pagesBase . '/404.php';
|
||||||
|
|
||||||
|
if (preg_match('~^module/([a-zA-Z0-9_-]+)(?:/(.+))?$~', $uriPath, $m)) {
|
||||||
|
$module = $m[1];
|
||||||
|
$page = isset($m[2]) && $m[2] !== '' ? trim($m[2], '/') : 'index';
|
||||||
|
$modulePage = app()->modules()->resolvePage($module, $page);
|
||||||
|
$target = $modulePage ?: $page404;
|
||||||
|
if (!$modulePage) {
|
||||||
|
http_response_code(404);
|
||||||
|
}
|
||||||
|
} elseif ($uriPath === '' || $uriPath === 'index' || $uriPath === 'index.php') {
|
||||||
|
$target = $pagesBase . '/index.php';
|
||||||
|
} else {
|
||||||
|
$base = $pagesBase . '/' . $uriPath;
|
||||||
|
if (is_dir($base) && is_file($base . '/index.php')) {
|
||||||
|
$target = $base . '/index.php';
|
||||||
|
} elseif (is_file($base . '.php')) {
|
||||||
|
$target = $base . '.php';
|
||||||
|
} else {
|
||||||
|
http_response_code(404);
|
||||||
|
$target = $page404;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
require $target;
|
||||||
|
$content = ob_get_clean();
|
||||||
|
|
||||||
|
tpl('layout_start', 'structure');
|
||||||
|
echo $content;
|
||||||
|
tpl('layout_end', 'structure');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `partials/landingpages/index.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<div class="card">
|
||||||
|
<h1>Projektstart</h1>
|
||||||
|
<p>Die Grundstruktur wurde erfolgreich erstellt.</p>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `partials/landingpages/404.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<div class="card">
|
||||||
|
<h1>404</h1>
|
||||||
|
<p>Die angeforderte Seite wurde nicht gefunden.</p>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `partials/structure/layout_start.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Projektbasis</title>
|
||||||
|
<link rel="stylesheet" href="/assets/css/app.css?v=<?= urlencode(app()->config()->assetVersion) ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="main-content">
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `partials/structure/layout_end.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
</main>
|
||||||
|
<script src="/assets/js/app.js?v=<?= urlencode(app()->config()->assetVersion) ?>" defer></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `public/assets/css/app.css`
|
||||||
|
|
||||||
|
```css
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: sans-serif;
|
||||||
|
background: #f5f5f5;
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
max-width: 960px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 32px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `public/assets/js/app.js`
|
||||||
|
|
||||||
|
```js
|
||||||
|
document.documentElement.classList.add('js');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datei: `public/.htaccess`
|
||||||
|
|
||||||
|
```apache
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteRule ^ index.php [QSA,L]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Empfohlene Startreihenfolge in einem neuen Projekt
|
||||||
|
|
||||||
|
1. Neues GitLab-Projekt anlegen.
|
||||||
|
2. Repository lokal mit VS Code verknuepfen.
|
||||||
|
3. Alle bestehenden Dateien und Ordner entfernen, `.gitlab-ci.yml` jedoch behalten.
|
||||||
|
4. `GENERAL.md` und `BASE_FILES.md` in das neue Repository kopieren.
|
||||||
|
5. `.gitlab-ci.yml` auf alte Domain- und Projektreferenzen pruefen und anpassen.
|
||||||
|
6. Pflichtstruktur aus `GENERAL.md` anlegen.
|
||||||
|
7. Basisdateien aus `BASE_FILES.md` anlegen.
|
||||||
|
8. Live- und Staging-Domain einsetzen.
|
||||||
|
9. Danach erst die eigentliche Projekterstellung oder Modulentwicklung starten.
|
||||||
|
|
||||||
|
## Kurzregel
|
||||||
|
|
||||||
|
Ohne `GENERAL.md`, ohne `BASE_FILES.md` oder ohne beide Domains ist die saubere Erstellung eines neuen Projekts auf dieser Basis nicht vollstaendig und soll nicht ausgefuehrt werden.
|
||||||
368
GENERAL.md
Normal file
368
GENERAL.md
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
# Projektbasis fuer neue Projekte
|
||||||
|
|
||||||
|
Dieses Dokument kombiniert die Erklaerung des allgemeinen Projektaufbaus mit einer konkreten Startstruktur fuer neue Projekte. Ziel ist, dass neue Projekte dieselbe technische Basis verwenden und dieselben Regeln fuer Routing, Verzeichnisse, Module, Assets und Konfiguration einhalten.
|
||||||
|
|
||||||
|
## Grundprinzip
|
||||||
|
|
||||||
|
Das Projekt basiert auf einer schlanken PHP-Struktur mit:
|
||||||
|
|
||||||
|
- einem zentralen Web-Einstiegspunkt in `public/index.php`
|
||||||
|
- einem zentralen Bootstrap in `config/fileload.php`
|
||||||
|
- globalen Seiten unter `partials/landingpages/`
|
||||||
|
- globalem Layout unter `partials/structure/`
|
||||||
|
- optionalen Fachmodulen unter `modules/`
|
||||||
|
- globalen Assets unter `public/assets/`
|
||||||
|
- einer Deployment-gesteuerten Config-Aufteilung mit `config/staging/` und `config/prod/`
|
||||||
|
|
||||||
|
## Verbindliche Startstruktur fuer neue Projekte
|
||||||
|
|
||||||
|
Die folgende Struktur soll als Basis fuer neue Projekte verwendet werden. Sie beruecksichtigt ausdruecklich auch die Basisverzeichnisse aus `.gitlab-ci.yml`.
|
||||||
|
|
||||||
|
```text
|
||||||
|
/
|
||||||
|
|-- .gitlab-ci.yml
|
||||||
|
|-- GENERAL.md
|
||||||
|
|-- api/
|
||||||
|
| `-- .gitkeep
|
||||||
|
|-- config/
|
||||||
|
| |-- .gitkeep
|
||||||
|
| |-- base_db.php
|
||||||
|
| |-- config.php
|
||||||
|
| |-- fileload.php
|
||||||
|
| |-- prod/
|
||||||
|
| | |-- .gitkeep
|
||||||
|
| | |-- db_settings_basic.php
|
||||||
|
| | |-- domaindata.php
|
||||||
|
| | `-- settings.php
|
||||||
|
| `-- staging/
|
||||||
|
| |-- .gitkeep
|
||||||
|
| |-- db_settings_basic.php
|
||||||
|
| |-- domaindata.php
|
||||||
|
| `-- settings.php
|
||||||
|
|-- data/
|
||||||
|
| `-- .gitkeep
|
||||||
|
|-- debug/
|
||||||
|
| `-- .gitkeep
|
||||||
|
|-- modules/
|
||||||
|
| `-- .gitkeep
|
||||||
|
|-- partials/
|
||||||
|
| |-- landingpages/
|
||||||
|
| | |-- .gitkeep
|
||||||
|
| | `-- index.php
|
||||||
|
| `-- structure/
|
||||||
|
| |-- .gitkeep
|
||||||
|
| |-- layout_end.php
|
||||||
|
| `-- layout_start.php
|
||||||
|
|-- public/
|
||||||
|
| |-- .gitkeep
|
||||||
|
| |-- .htaccess
|
||||||
|
| |-- index.php
|
||||||
|
| `-- assets/
|
||||||
|
| |-- css/
|
||||||
|
| | |-- .gitkeep
|
||||||
|
| | `-- app.css
|
||||||
|
| |-- images/
|
||||||
|
| | `-- .gitkeep
|
||||||
|
| `-- js/
|
||||||
|
| |-- .gitkeep
|
||||||
|
| `-- app.js
|
||||||
|
|-- src/
|
||||||
|
| |-- .gitkeep
|
||||||
|
| |-- App/
|
||||||
|
| | |-- .gitkeep
|
||||||
|
| | |-- App.php
|
||||||
|
| | |-- Assets.php
|
||||||
|
| | |-- Config.php
|
||||||
|
| | |-- Database.php
|
||||||
|
| | |-- ModuleManager.php
|
||||||
|
| | `-- functions.php
|
||||||
|
| `-- Repository/
|
||||||
|
| `-- .gitkeep
|
||||||
|
`-- tools/
|
||||||
|
`-- .gitkeep
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pflichtordner
|
||||||
|
|
||||||
|
Diese Ordner muessen als Basis immer vorhanden sein, weil sie entweder vom Projektaufbau oder vom Deployment vorausgesetzt werden:
|
||||||
|
|
||||||
|
- `api/`
|
||||||
|
- `config/`
|
||||||
|
- `data/`
|
||||||
|
- `debug/`
|
||||||
|
- `modules/`
|
||||||
|
- `partials/`
|
||||||
|
- `public/`
|
||||||
|
- `src/`
|
||||||
|
- `tools/`
|
||||||
|
|
||||||
|
Diese Liste entspricht der Deploy-Basis aus `.gitlab-ci.yml` plus den notwendigen Unterordnern fuer die Anwendung.
|
||||||
|
|
||||||
|
## Regel fuer leere Ordner
|
||||||
|
|
||||||
|
Wenn ein notwendiger Ordner anfangs noch keine echten Dateien enthaelt, muss er eine leere Datei mit dem Namen `.gitkeep` enthalten. Das gilt insbesondere fuer:
|
||||||
|
|
||||||
|
- `api/`
|
||||||
|
- `data/`
|
||||||
|
- `debug/`
|
||||||
|
- `modules/`
|
||||||
|
- `tools/`
|
||||||
|
- `public/assets/images/`
|
||||||
|
- `src/Repository/`
|
||||||
|
- weitere leere Unterordner, die im Template bereits vorgesehen sind
|
||||||
|
|
||||||
|
Damit bleiben die Verzeichnisse versionierbar und werden beim Kopieren und Deployment nicht versehentlich ausgelassen.
|
||||||
|
|
||||||
|
## Rollen der wichtigsten Verzeichnisse
|
||||||
|
|
||||||
|
### `public/`
|
||||||
|
|
||||||
|
`public/` ist der Webroot. Hier liegen:
|
||||||
|
|
||||||
|
- `index.php` als zentraler Router
|
||||||
|
- globale Assets unter `public/assets/`
|
||||||
|
- Webserver-Dateien wie `.htaccess`
|
||||||
|
|
||||||
|
### `src/`
|
||||||
|
|
||||||
|
`src/` enthaelt den PHP-Kern der Anwendung.
|
||||||
|
|
||||||
|
- `src/App/` fuer App-Klassen, Bootstrap-nahe Logik, Config, Assets, Datenbank, Request, Session und Modulverwaltung
|
||||||
|
- `src/Repository/` fuer Datenzugriffslogik
|
||||||
|
|
||||||
|
### `partials/`
|
||||||
|
|
||||||
|
`partials/` enthaelt globale Templates.
|
||||||
|
|
||||||
|
- `partials/landingpages/` fuer globale, direkt routbare Seiten
|
||||||
|
- `partials/structure/` fuer das globale Layout
|
||||||
|
|
||||||
|
### `modules/`
|
||||||
|
|
||||||
|
`modules/` enthaelt fachlich getrennte Erweiterungen. Jedes Modul bleibt in seinem eigenen Ordner und kapselt Seiten, Assets, Partials und optionale Bootstrap-Logik.
|
||||||
|
|
||||||
|
### `api/`
|
||||||
|
|
||||||
|
`api/` ist fuer API-Funktionen vorgesehen, falls das Projekt eigene API-Endpunkte anbieten soll. Wenn ein Projekt keine API bereitstellt, kann der Ordner leer bleiben, soll aber als Teil der Basisstruktur dennoch vorhanden sein.
|
||||||
|
|
||||||
|
### `config/`
|
||||||
|
|
||||||
|
`config/` enthaelt den Bootstrap der Konfiguration sowie die umgebungsspezifischen Quellverzeichnisse `staging` und `prod`.
|
||||||
|
|
||||||
|
### `tools/`
|
||||||
|
|
||||||
|
`tools/` ist fuer CLI-Skripte, Worker und Hilfsprogramme vorgesehen.
|
||||||
|
|
||||||
|
## Routing- und Renderstruktur
|
||||||
|
|
||||||
|
### `public/index.php`
|
||||||
|
|
||||||
|
`public/index.php` ist der einzige Web-Einstiegspunkt und uebernimmt:
|
||||||
|
|
||||||
|
- Laden von `config/fileload.php`
|
||||||
|
- Normalisierung des Request-Pfads
|
||||||
|
- Pruefung optionaler Authentifizierung oder Schutzregeln
|
||||||
|
- Routing auf globale Seiten unter `partials/landingpages/`
|
||||||
|
- Routing auf Modulseiten unter `modules/<modul>/pages/`
|
||||||
|
- 404-Behandlung
|
||||||
|
- Rendern des Inhalts innerhalb des globalen Layouts
|
||||||
|
|
||||||
|
### Globale Seiten
|
||||||
|
|
||||||
|
Globale Seiten liegen unter `partials/landingpages/`. Das Routing ist dateibasiert, also ohne externen Framework-Router.
|
||||||
|
|
||||||
|
Beispiele:
|
||||||
|
|
||||||
|
- `/` -> `partials/landingpages/index.php`
|
||||||
|
- `/users` -> `partials/landingpages/users/index.php`
|
||||||
|
|
||||||
|
### Layout
|
||||||
|
|
||||||
|
Die globale HTML-Struktur liegt unter `partials/structure/`.
|
||||||
|
|
||||||
|
- `layout_start.php` oeffnet das Seitenlayout
|
||||||
|
- `layout_end.php` schliesst es
|
||||||
|
|
||||||
|
### Module
|
||||||
|
|
||||||
|
Modulrouten folgen dem Schema:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/module/<modulname>
|
||||||
|
/module/<modulname>/<seite>
|
||||||
|
```
|
||||||
|
|
||||||
|
Diese Routen verweisen auf Dateien unter:
|
||||||
|
|
||||||
|
```text
|
||||||
|
modules/<modulname>/pages/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modulstruktur fuer neue Projekte
|
||||||
|
|
||||||
|
Wenn ein neues Projekt Module verwendet, sollte jedes Modul nach demselben Muster aufgebaut sein:
|
||||||
|
|
||||||
|
```text
|
||||||
|
modules/<modulname>/
|
||||||
|
|-- assets/
|
||||||
|
| `-- .gitkeep
|
||||||
|
|-- pages/
|
||||||
|
| |-- .gitkeep
|
||||||
|
| `-- index.php
|
||||||
|
|-- partials/
|
||||||
|
| `-- .gitkeep
|
||||||
|
|-- bootstrap.php
|
||||||
|
`-- module.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Regeln:
|
||||||
|
|
||||||
|
- modulspezifisches CSS und JavaScript bleibt unter `modules/<modulname>/assets/`
|
||||||
|
- globale Assets gehoeren nicht in Modulordner
|
||||||
|
- Modulcode gehoert nicht nach `public/assets/`
|
||||||
|
- Seiten eines Moduls liegen unter `pages/`
|
||||||
|
- wiederverwendbare Modul-Templates liegen unter `partials/`
|
||||||
|
|
||||||
|
## Asset-Struktur
|
||||||
|
|
||||||
|
### Globale Assets
|
||||||
|
|
||||||
|
Projektweite Dateien liegen unter:
|
||||||
|
|
||||||
|
- `public/assets/css/`
|
||||||
|
- `public/assets/js/`
|
||||||
|
- `public/assets/images/`
|
||||||
|
|
||||||
|
### Modul-Assets
|
||||||
|
|
||||||
|
Modulbezogene Dateien liegen unter:
|
||||||
|
|
||||||
|
- `modules/<modulname>/assets/`
|
||||||
|
|
||||||
|
Neue Projekte sollen diese Trennung konsequent beibehalten.
|
||||||
|
|
||||||
|
## Config-Aufteilung mit `staging` und `prod`
|
||||||
|
|
||||||
|
Dieser Teil ist fuer neue Projekte verpflichtend.
|
||||||
|
|
||||||
|
### Quellverzeichnisse im Repository
|
||||||
|
|
||||||
|
Im Repository liegen die umgebungsspezifischen Config-Quellen unter:
|
||||||
|
|
||||||
|
```text
|
||||||
|
config/staging/
|
||||||
|
config/prod/
|
||||||
|
```
|
||||||
|
|
||||||
|
Darin liegen typischerweise:
|
||||||
|
|
||||||
|
- `domaindata.php`
|
||||||
|
- `settings.php`
|
||||||
|
- `db_settings_basic.php`
|
||||||
|
|
||||||
|
### Laufzeitlogik
|
||||||
|
|
||||||
|
Die Anwendung selbst laedt zur Laufzeit nicht direkt aus `config/staging/` oder `config/prod/`, sondern aus dem Root von `config/`, also z. B.:
|
||||||
|
|
||||||
|
- `config/settings.php`
|
||||||
|
- `config/domaindata.php`
|
||||||
|
- `config/db_settings_basic.php`
|
||||||
|
|
||||||
|
`config/fileload.php` erwartet genau diese Root-Dateien.
|
||||||
|
|
||||||
|
### Deployment-Regel
|
||||||
|
|
||||||
|
Beim Deployment wird die passende Umgebung in das aktive `config/`-Root kopiert:
|
||||||
|
|
||||||
|
1. allgemeine Root-Dateien aus `config/` werden bereitgestellt
|
||||||
|
2. je nach Branch wird die Umgebungsvariante darueberkopiert
|
||||||
|
3. `develop` verwendet `config/staging/`
|
||||||
|
4. `main` verwendet `config/prod/`
|
||||||
|
5. im Zielsystem gelten die kopierten Dateien danach so, als waeren sie direkt normale Root-Configs
|
||||||
|
|
||||||
|
Neue Projekte muessen exakt dieses Prinzip uebernehmen.
|
||||||
|
|
||||||
|
## Domain-Pflicht fuer neue Projekterstellungen
|
||||||
|
|
||||||
|
Neue Projekte duerfen nur erstellt werden, wenn beide Domains vorliegen:
|
||||||
|
|
||||||
|
- Live-Domain
|
||||||
|
- Staging-Domain
|
||||||
|
|
||||||
|
Diese Domains muessen in den umgebungsspezifischen Config-Dateien verwendet und automatisch eingetragen werden, insbesondere in:
|
||||||
|
|
||||||
|
- `config/prod/domaindata.php`
|
||||||
|
- `config/prod/settings.php`
|
||||||
|
- `config/staging/domaindata.php`
|
||||||
|
- `config/staging/settings.php`
|
||||||
|
- `.gitlab-ci.yml`, falls dort Domainreferenzen oder Umgebungs-URLs hinterlegt sind
|
||||||
|
|
||||||
|
## Verbindliche Erstellungsregel
|
||||||
|
|
||||||
|
Wenn bei der Aufgabenstellung fuer ein neues Projekt keine Domains angegeben werden, muss vor der Projekterstellung explizit nach beiden Werten gefragt werden.
|
||||||
|
|
||||||
|
Die Erstellung ist in diesem Fall zu blockieren.
|
||||||
|
|
||||||
|
Sie darf erst fortgesetzt werden, wenn beide Angaben vorhanden sind:
|
||||||
|
|
||||||
|
1. Live-Domain
|
||||||
|
2. Staging-Domain
|
||||||
|
|
||||||
|
Fehlt mindestens eine der beiden Angaben, muss die Erstellung unterbunden werden mit einem klaren Hinweis, dass die Domains fehlen.
|
||||||
|
|
||||||
|
## Reset-Regel fuer neue Projekterstellungen
|
||||||
|
|
||||||
|
Vor der eigentlichen Erstellung eines neuen Projekts muessen alle bestehenden Dateien und Ordner im Repository entfernt werden, mit genau einer Ausnahme:
|
||||||
|
|
||||||
|
- `.gitlab-ci.yml`
|
||||||
|
|
||||||
|
Die bestehende `.gitlab-ci.yml` bleibt erhalten, weil sie die Deploy-Basis vorgibt. Sie muss jedoch im Zuge der neuen Projekterstellung geprueft und angepasst werden, wenn darin Domainreferenzen, Umgebungs-URLs oder projektspezifische Zielpfade enthalten sind.
|
||||||
|
|
||||||
|
Fuer neue Projekterstellungen gilt deshalb verbindlich:
|
||||||
|
|
||||||
|
1. vorhandene Projektdateien und Projektordner loeschen
|
||||||
|
2. `.gitlab-ci.yml` behalten
|
||||||
|
3. `.gitlab-ci.yml` auf alte Domain- oder Projektreferenzen pruefen
|
||||||
|
4. vorhandene Domainreferenzen in `.gitlab-ci.yml` auf die neue Live- und Staging-Domain anpassen
|
||||||
|
5. danach erst die neue Basisstruktur und die neuen Basisdateien erstellen
|
||||||
|
|
||||||
|
## Mindestinhalt der Config-Dateien
|
||||||
|
|
||||||
|
Die folgenden Dateien koennen als Basis aus diesem Projekttyp uebernommen werden und muessen pro neuem Projekt angepasst werden:
|
||||||
|
|
||||||
|
- `config/fileload.php`
|
||||||
|
- `config/config.php`
|
||||||
|
- `config/base_db.php`
|
||||||
|
- `config/prod/domaindata.php`
|
||||||
|
- `config/prod/settings.php`
|
||||||
|
- `config/staging/domaindata.php`
|
||||||
|
- `config/staging/settings.php`
|
||||||
|
|
||||||
|
Dabei gilt:
|
||||||
|
|
||||||
|
- die allgemeine Config-Logik kann uebernommen werden
|
||||||
|
- domainspezifische Werte muessen pro Projekt ersetzt werden
|
||||||
|
- staging und prod muessen immer unterschiedliche Zielwerte bekommen, sofern nicht ausdruecklich anders gefordert
|
||||||
|
|
||||||
|
## Basisdateien fuer neue Projekte
|
||||||
|
|
||||||
|
Neben der Ordnerstruktur werden fuer neue Projekte auch Basisdateien benoetigt, insbesondere im Config-Bereich. Die konkreten Inhalte sind in einer separaten Datei beschrieben:
|
||||||
|
|
||||||
|
- `BASE_FILES.md`
|
||||||
|
|
||||||
|
Diese Datei ist dafuer gedacht, nach dem Anlegen eines neuen GitLab-Projekts zusammen mit `GENERAL.md` in das neue Repository kopiert zu werden, damit die Erstellung der Grundstruktur und der Konfigurationsdateien direkt auf einer klaren Vorlage basiert.
|
||||||
|
|
||||||
|
## Kurzfassung fuer neue Projekte
|
||||||
|
|
||||||
|
Neue Projekte auf dieser Basis verwenden:
|
||||||
|
|
||||||
|
- einen zentralen Router in `public/index.php`
|
||||||
|
- einen zentralen Bootstrap in `config/fileload.php`
|
||||||
|
- globale Seiten in `partials/landingpages/`
|
||||||
|
- globale Layout-Dateien in `partials/structure/`
|
||||||
|
- optionale Module in `modules/`
|
||||||
|
- globale Assets in `public/assets/`
|
||||||
|
- modulspezifische Assets in `modules/<modulname>/assets/`
|
||||||
|
- eine Deploy-gesteuerte Config mit `config/staging/` und `config/prod/`
|
||||||
|
|
||||||
|
Leere Pflichtordner erhalten immer `.gitkeep`. Eine neue Projekterstellung ist nur zulaessig, wenn sowohl Live- als auch Staging-Domain vorab angegeben wurden.
|
||||||
Reference in New Issue
Block a user