Slim API deployment - composer install + full CRUD endpoints
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$bootstrap = require __DIR__ . '/../app/bootstrap.php';
|
||||
|
||||
use App\Support\Database;
|
||||
|
||||
$db = Database::connection();
|
||||
$migrations = glob(__DIR__ . '/../database/migrations/*.sql');
|
||||
sort($migrations);
|
||||
|
||||
foreach ($migrations as $migration) {
|
||||
$statements = array_filter(array_map('trim', explode(';', file_get_contents($migration))));
|
||||
foreach ($statements as $statement) {
|
||||
if (preg_match('/^ALTER\s+TABLE\s+([A-Za-z0-9_]+)\s+ADD\s+COLUMN\s+([A-Za-z0-9_]+)/i', $statement, $matches) === 1) {
|
||||
$columns = $db->query('PRAGMA table_info(' . $matches[1] . ')')->fetchAll();
|
||||
$columnNames = array_map(static fn (array $column): string => strtolower((string) $column['name']), $columns);
|
||||
if (in_array(strtolower($matches[2]), $columnNames, true)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$db->exec($statement);
|
||||
}
|
||||
fwrite(STDOUT, "Applied: " . basename($migration) . PHP_EOL);
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$bootstrap = require __DIR__ . '/../app/bootstrap.php';
|
||||
|
||||
use App\Repositories\InstructorRepository;
|
||||
use App\Repositories\ReferenceDataRepository;
|
||||
use App\Repositories\StudentRepository;
|
||||
use App\Repositories\TenantRepository;
|
||||
use App\Repositories\UserRepository;
|
||||
use App\Support\Database;
|
||||
|
||||
$db = Database::connection();
|
||||
|
||||
$tenantRepository = new TenantRepository();
|
||||
$userRepository = new UserRepository();
|
||||
$referenceRepository = new ReferenceDataRepository();
|
||||
$instructorRepository = new InstructorRepository();
|
||||
$studentRepository = new StudentRepository();
|
||||
|
||||
$existing = $db->query('SELECT COUNT(*) AS count FROM users')->fetch();
|
||||
if ((int) $existing['count'] > 0) {
|
||||
fwrite(STDOUT, "Seed skipped: data already exists." . PHP_EOL);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$tenant = $tenantRepository->create([
|
||||
'name' => 'Fahrschule Nordring',
|
||||
'timezone' => 'Europe/Berlin',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$db->prepare('INSERT INTO settings (tenant_id, key_name, value) VALUES (:tenant_id, :key_name, :value)')
|
||||
->execute([
|
||||
'tenant_id' => $tenant['id'],
|
||||
'key_name' => 'app.theme',
|
||||
'value' => 'signal',
|
||||
]);
|
||||
|
||||
$superadmin = $userRepository->create([
|
||||
'tenant_id' => null,
|
||||
'role' => 'superadmin',
|
||||
'email' => 'superadmin@example.com',
|
||||
'password' => 'secret123',
|
||||
'first_name' => 'System',
|
||||
'last_name' => 'Admin',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$tenantAdmin = $userRepository->create([
|
||||
'tenant_id' => $tenant['id'],
|
||||
'role' => 'tenant_admin',
|
||||
'email' => 'admin@nordring.test',
|
||||
'password' => 'secret123',
|
||||
'first_name' => 'Jana',
|
||||
'last_name' => 'Koester',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$dispatcher = $userRepository->create([
|
||||
'tenant_id' => $tenant['id'],
|
||||
'role' => 'dispatcher',
|
||||
'email' => 'planung@nordring.test',
|
||||
'password' => 'secret123',
|
||||
'first_name' => 'Mara',
|
||||
'last_name' => 'Dispo',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$instructorUser = $userRepository->create([
|
||||
'tenant_id' => $tenant['id'],
|
||||
'role' => 'instructor',
|
||||
'email' => 'fahrlehrer@nordring.test',
|
||||
'password' => 'secret123',
|
||||
'first_name' => 'Tobias',
|
||||
'last_name' => 'Fahr',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$instructors = [
|
||||
['user_id' => $instructorUser['id'], 'first_name' => 'Tobias', 'last_name' => 'Fahr', 'color' => '#0E5D80'],
|
||||
['user_id' => null, 'first_name' => 'Leonie', 'last_name' => 'Kraft', 'color' => '#A74826'],
|
||||
];
|
||||
|
||||
foreach ($instructors as $instructor) {
|
||||
$instructorRepository->create($tenant['id'], $instructor + ['notes' => '', 'is_active' => true]);
|
||||
}
|
||||
|
||||
$lessonTypes = [
|
||||
['name' => 'Einweisung', 'color' => '#0E5D80', 'default_duration' => 45, 'category' => 'student', 'requirement_key' => 'intro', 'is_billable' => 1, 'is_counted' => 1, 'is_rest_relevant' => 1, 'fixed_duration' => 0, 'sort_order' => 1],
|
||||
['name' => 'Uebungsstunde', 'color' => '#2C7A4B', 'default_duration' => 45, 'category' => 'student', 'requirement_key' => 'practice', 'is_billable' => 1, 'is_counted' => 1, 'is_rest_relevant' => 1, 'fixed_duration' => 0, 'sort_order' => 2],
|
||||
['name' => 'Bundes- / Landstrasse', 'color' => '#D99A1A', 'default_duration' => 45, 'category' => 'student', 'requirement_key' => 'rural', 'is_billable' => 1, 'is_counted' => 1, 'is_rest_relevant' => 1, 'fixed_duration' => 0, 'sort_order' => 3],
|
||||
['name' => 'Autobahn', 'color' => '#C86C1E', 'default_duration' => 45, 'category' => 'student', 'requirement_key' => 'highway', 'is_billable' => 1, 'is_counted' => 1, 'is_rest_relevant' => 1, 'fixed_duration' => 0, 'sort_order' => 4],
|
||||
['name' => 'Daemmerung / Dunkelheit', 'color' => '#4B356B', 'default_duration' => 45, 'category' => 'student', 'requirement_key' => 'night', 'is_billable' => 1, 'is_counted' => 1, 'is_rest_relevant' => 1, 'fixed_duration' => 0, 'sort_order' => 5],
|
||||
['name' => 'Pruefungsfahrt', 'color' => '#A83333', 'default_duration' => 70, 'category' => 'student', 'requirement_key' => null, 'is_billable' => 1, 'is_counted' => 0, 'is_rest_relevant' => 1, 'fixed_duration' => 1, 'sort_order' => 6],
|
||||
['name' => 'Theorieunterricht', 'color' => '#2B8793', 'default_duration' => 90, 'category' => 'other', 'requirement_key' => null, 'is_billable' => 0, 'is_counted' => 0, 'is_rest_relevant' => 0, 'fixed_duration' => 1, 'sort_order' => 7],
|
||||
['name' => 'Sonstige Arbeiten', 'color' => '#70747C', 'default_duration' => 60, 'category' => 'other', 'requirement_key' => null, 'is_billable' => 0, 'is_counted' => 0, 'is_rest_relevant' => 0, 'fixed_duration' => 1, 'sort_order' => 8],
|
||||
['name' => 'Privater Termin', 'color' => '#44444A', 'default_duration' => 60, 'category' => 'private', 'requirement_key' => null, 'is_billable' => 0, 'is_counted' => 0, 'is_rest_relevant' => 0, 'fixed_duration' => 1, 'sort_order' => 9],
|
||||
['name' => 'Sperrzeit / Blockierung', 'color' => '#111111', 'default_duration' => 60, 'category' => 'blocked', 'requirement_key' => null, 'is_billable' => 0, 'is_counted' => 0, 'is_rest_relevant' => 0, 'fixed_duration' => 1, 'sort_order' => 10],
|
||||
];
|
||||
|
||||
foreach ($lessonTypes as $lessonType) {
|
||||
$referenceRepository->createLessonType($tenant['id'], $lessonType);
|
||||
}
|
||||
|
||||
$templates = [
|
||||
[
|
||||
'code' => 'C',
|
||||
'name' => 'Klasse C',
|
||||
'is_combination' => false,
|
||||
'requirements' => [
|
||||
['requirement_key' => 'practice', 'label' => 'Uebungsstunden', 'required_units' => 8],
|
||||
['requirement_key' => 'rural', 'label' => 'Ueberland', 'required_units' => 5],
|
||||
['requirement_key' => 'highway', 'label' => 'Autobahn', 'required_units' => 4],
|
||||
['requirement_key' => 'night', 'label' => 'Nachtfahrt', 'required_units' => 3],
|
||||
],
|
||||
],
|
||||
[
|
||||
'code' => 'CE',
|
||||
'name' => 'Klasse CE',
|
||||
'is_combination' => false,
|
||||
'requirements' => [
|
||||
['requirement_key' => 'practice', 'label' => 'Uebungsstunden', 'required_units' => 10],
|
||||
['requirement_key' => 'rural', 'label' => 'Ueberland', 'required_units' => 5],
|
||||
['requirement_key' => 'highway', 'label' => 'Autobahn', 'required_units' => 5],
|
||||
['requirement_key' => 'night', 'label' => 'Nachtfahrt', 'required_units' => 3],
|
||||
],
|
||||
],
|
||||
[
|
||||
'code' => 'C+CE',
|
||||
'name' => 'Klasse C + CE',
|
||||
'is_combination' => true,
|
||||
'requirements' => [
|
||||
['requirement_key' => 'C::practice', 'phase_label' => 'C', 'label' => 'Uebungsstunden', 'required_units' => 8],
|
||||
['requirement_key' => 'C::rural', 'phase_label' => 'C', 'label' => 'Ueberland', 'required_units' => 5],
|
||||
['requirement_key' => 'C::highway', 'phase_label' => 'C', 'label' => 'Autobahn', 'required_units' => 4],
|
||||
['requirement_key' => 'C::night', 'phase_label' => 'C', 'label' => 'Nachtfahrt', 'required_units' => 3],
|
||||
['requirement_key' => 'CE::practice', 'phase_label' => 'CE', 'label' => 'Uebungsstunden', 'required_units' => 6],
|
||||
['requirement_key' => 'CE::rural', 'phase_label' => 'CE', 'label' => 'Ueberland', 'required_units' => 3],
|
||||
['requirement_key' => 'CE::highway', 'phase_label' => 'CE', 'label' => 'Autobahn', 'required_units' => 3],
|
||||
['requirement_key' => 'CE::night', 'phase_label' => 'CE', 'label' => 'Nachtfahrt', 'required_units' => 2],
|
||||
],
|
||||
],
|
||||
[
|
||||
'code' => 'C1',
|
||||
'name' => 'Klasse C1',
|
||||
'is_combination' => false,
|
||||
'requirements' => [
|
||||
['requirement_key' => 'practice', 'label' => 'Uebungsstunden', 'required_units' => 6],
|
||||
['requirement_key' => 'rural', 'label' => 'Ueberland', 'required_units' => 4],
|
||||
['requirement_key' => 'highway', 'label' => 'Autobahn', 'required_units' => 3],
|
||||
['requirement_key' => 'night', 'label' => 'Nachtfahrt', 'required_units' => 2],
|
||||
],
|
||||
],
|
||||
[
|
||||
'code' => 'C1E',
|
||||
'name' => 'Klasse C1E',
|
||||
'is_combination' => false,
|
||||
'requirements' => [
|
||||
['requirement_key' => 'practice', 'label' => 'Uebungsstunden', 'required_units' => 7],
|
||||
['requirement_key' => 'rural', 'label' => 'Ueberland', 'required_units' => 4],
|
||||
['requirement_key' => 'highway', 'label' => 'Autobahn', 'required_units' => 3],
|
||||
['requirement_key' => 'night', 'label' => 'Nachtfahrt', 'required_units' => 2],
|
||||
],
|
||||
],
|
||||
[
|
||||
'code' => 'C1+C1E',
|
||||
'name' => 'Klasse C1 + C1E',
|
||||
'is_combination' => true,
|
||||
'requirements' => [
|
||||
['requirement_key' => 'C1::practice', 'phase_label' => 'C1', 'label' => 'Uebungsstunden', 'required_units' => 6],
|
||||
['requirement_key' => 'C1::rural', 'phase_label' => 'C1', 'label' => 'Ueberland', 'required_units' => 4],
|
||||
['requirement_key' => 'C1::highway', 'phase_label' => 'C1', 'label' => 'Autobahn', 'required_units' => 3],
|
||||
['requirement_key' => 'C1::night', 'phase_label' => 'C1', 'label' => 'Nachtfahrt', 'required_units' => 2],
|
||||
['requirement_key' => 'C1E::practice', 'phase_label' => 'C1E', 'label' => 'Uebungsstunden', 'required_units' => 4],
|
||||
['requirement_key' => 'C1E::rural', 'phase_label' => 'C1E', 'label' => 'Ueberland', 'required_units' => 2],
|
||||
['requirement_key' => 'C1E::highway', 'phase_label' => 'C1E', 'label' => 'Autobahn', 'required_units' => 2],
|
||||
['requirement_key' => 'C1E::night', 'phase_label' => 'C1E', 'label' => 'Nachtfahrt', 'required_units' => 1],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$templateIds = [];
|
||||
foreach ($templates as $template) {
|
||||
$record = $referenceRepository->createTemplate($tenant['id'], $template);
|
||||
$templateIds[$record['code']] = $record['id'];
|
||||
}
|
||||
|
||||
$studentRepository->create($tenant['id'], [
|
||||
'template_id' => $templateIds['C+CE'],
|
||||
'first_name' => 'Mila',
|
||||
'last_name' => 'Hansen',
|
||||
'phone' => '0170 1234567',
|
||||
'notes' => 'Startet naechste Woche mit Nachtfahrten.',
|
||||
'status' => 'active',
|
||||
]);
|
||||
|
||||
$studentRepository->create($tenant['id'], [
|
||||
'template_id' => $templateIds['C1'],
|
||||
'first_name' => 'Jonas',
|
||||
'last_name' => 'Winter',
|
||||
'phone' => '0172 999000',
|
||||
'notes' => 'Braucht fruehe Termine.',
|
||||
'status' => 'active',
|
||||
]);
|
||||
|
||||
fwrite(STDOUT, "Seed complete." . PHP_EOL);
|
||||
Reference in New Issue
Block a user