Files
breadcrumb-the-shire/lib/Service/Tenant/TenantService.php
fs 25370a1a55 add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00

227 lines
9.0 KiB
PHP

<?php
namespace MintyPHP\Service\Tenant;
use MintyPHP\Repository\Org\DepartmentRepository;
use MintyPHP\Repository\Tenant\TenantRepository;
use MintyPHP\Service\Settings\SettingService;
class TenantService
{
public static function list(): array
{
return TenantRepository::list();
}
public static function listPaged(array $options): array
{
return TenantRepository::listPaged($options);
}
public static function findByUuid(string $uuid): ?array
{
return TenantRepository::findByUuid($uuid);
}
public static function findById(int $id): ?array
{
return TenantRepository::find($id);
}
public static function createFromAdmin(array $input, int $currentUserId = 0): array
{
$form = self::sanitizeBase($input);
$form['status'] = self::normalizeStatus($form['status'] ?? '');
$errors = self::validateBase($form);
if ($errors) {
return ['ok' => false, 'errors' => $errors, 'form' => $form];
}
$statusChangedAt = gmdate('Y-m-d H:i:s');
$createdId = TenantRepository::create([
'description' => $form['description'],
'address' => $form['address'],
'postal_code' => $form['postal_code'],
'city' => $form['city'],
'country' => $form['country'],
'region' => $form['region'],
'vat_id' => $form['vat_id'],
'tax_number' => $form['tax_number'],
'phone' => $form['phone'],
'fax' => $form['fax'],
'email' => $form['email'],
'support_email' => $form['support_email'],
'support_phone' => $form['support_phone'],
'billing_email' => $form['billing_email'],
'website' => $form['website'],
'privacy_url' => $form['privacy_url'],
'imprint_url' => $form['imprint_url'],
'primary_color' => $form['primary_color_use_default']
? null
: ($form['primary_color'] !== '' ? $form['primary_color'] : null),
'default_theme' => $form['default_theme'] !== '' ? $form['default_theme'] : null,
'allow_user_theme' => $form['allow_user_theme_mode'] === '' ? null : (int) $form['allow_user_theme_mode'],
'status' => $form['status'],
'status_changed_at' => $statusChangedAt,
'status_changed_by' => $currentUserId > 0 ? $currentUserId : null,
'created_by' => $currentUserId > 0 ? $currentUserId : null,
]);
if (!$createdId) {
return ['ok' => false, 'errors' => [t('Tenant can not be created')], 'form' => $form];
}
$createdTenant = TenantRepository::find((int) $createdId);
$uuid = $createdTenant['uuid'] ?? null;
if (!empty($input['is_default'])) {
SettingService::setDefaultTenantId((int) $createdId);
}
return ['ok' => true, 'form' => $form, 'uuid' => $uuid, 'id' => (int) $createdId];
}
public static function updateFromAdmin(int $tenantId, array $input, int $currentUserId = 0): array
{
$form = self::sanitizeBase($input);
$form['status'] = self::normalizeStatus($form['status'] ?? '');
$errors = self::validateBase($form);
if ($errors) {
return ['ok' => false, 'errors' => $errors, 'form' => $form];
}
$existing = TenantRepository::find($tenantId);
$statusChanged = false;
if ($existing && isset($existing['status'])) {
$statusChanged = (string) $existing['status'] !== $form['status'];
}
$updateData = [
'description' => $form['description'],
'address' => $form['address'],
'postal_code' => $form['postal_code'],
'city' => $form['city'],
'country' => $form['country'],
'region' => $form['region'],
'vat_id' => $form['vat_id'],
'tax_number' => $form['tax_number'],
'phone' => $form['phone'],
'fax' => $form['fax'],
'email' => $form['email'],
'support_email' => $form['support_email'],
'support_phone' => $form['support_phone'],
'billing_email' => $form['billing_email'],
'website' => $form['website'],
'privacy_url' => $form['privacy_url'],
'imprint_url' => $form['imprint_url'],
'primary_color' => $form['primary_color_use_default']
? null
: ($form['primary_color'] !== '' ? $form['primary_color'] : null),
'default_theme' => $form['default_theme'] !== '' ? $form['default_theme'] : null,
'allow_user_theme' => $form['allow_user_theme_mode'] === '' ? null : (int) $form['allow_user_theme_mode'],
'status' => $form['status'],
'modified_by' => $currentUserId > 0 ? $currentUserId : null,
];
if ($statusChanged) {
$updateData['status_changed_at'] = gmdate('Y-m-d H:i:s');
$updateData['status_changed_by'] = $currentUserId > 0 ? $currentUserId : null;
}
$updated = TenantRepository::update($tenantId, $updateData);
if (!$updated) {
return ['ok' => false, 'errors' => [t('Tenant can not be updated')], 'form' => $form];
}
return ['ok' => true, 'form' => $form];
}
public static function deleteByUuid(string $uuid): array
{
$uuid = trim($uuid);
if ($uuid === '') {
return ['ok' => false, 'status' => 404, 'error' => 'not_found'];
}
$tenant = TenantRepository::findByUuid($uuid);
if (!$tenant || !isset($tenant['id'])) {
return ['ok' => false, 'status' => 404, 'error' => 'not_found'];
}
$tenantId = (int) $tenant['id'];
if (DepartmentRepository::countByTenantId($tenantId) > 0) {
return ['ok' => false, 'status' => 409, 'error' => 'tenant_has_departments'];
}
$deleted = TenantRepository::delete($tenantId);
if (!$deleted) {
return ['ok' => false, 'status' => 500, 'error' => 'delete_failed'];
}
return ['ok' => true, 'tenant' => $tenant];
}
private static function sanitizeBase(array $input): array
{
return [
'description' => trim((string) ($input['description'] ?? '')),
'address' => trim((string) ($input['address'] ?? '')),
'postal_code' => trim((string) ($input['postal_code'] ?? '')),
'city' => trim((string) ($input['city'] ?? '')),
'country' => trim((string) ($input['country'] ?? '')),
'region' => trim((string) ($input['region'] ?? '')),
'vat_id' => trim((string) ($input['vat_id'] ?? '')),
'tax_number' => trim((string) ($input['tax_number'] ?? '')),
'phone' => trim((string) ($input['phone'] ?? '')),
'fax' => trim((string) ($input['fax'] ?? '')),
'email' => trim((string) ($input['email'] ?? '')),
'support_email' => trim((string) ($input['support_email'] ?? '')),
'support_phone' => trim((string) ($input['support_phone'] ?? '')),
'billing_email' => trim((string) ($input['billing_email'] ?? '')),
'website' => trim((string) ($input['website'] ?? '')),
'privacy_url' => trim((string) ($input['privacy_url'] ?? '')),
'imprint_url' => trim((string) ($input['imprint_url'] ?? '')),
'primary_color' => trim((string) ($input['primary_color'] ?? '')),
'primary_color_use_default' => !empty($input['primary_color_use_default']),
'default_theme' => strtolower(trim((string) ($input['default_theme'] ?? ''))),
'allow_user_theme_mode' => trim((string) ($input['allow_user_theme_mode'] ?? '')),
'status' => trim((string) ($input['status'] ?? '')),
];
}
private static function validateBase(array $form): array
{
$errors = [];
if ($form['description'] === '') {
$errors[] = t('Description cannot be empty');
}
if (!in_array($form['status'], ['active', 'inactive'], true)) {
$errors[] = t('Status is invalid');
}
if (
!$form['primary_color_use_default']
&& $form['primary_color'] !== ''
&& !preg_match('/^#([0-9a-f]{3}|[0-9a-f]{6})$/i', $form['primary_color'])
) {
$errors[] = t('Primary color is invalid');
}
$themes = function_exists('appThemes') ? appThemes() : [];
if ($form['default_theme'] !== '' && !isset($themes[$form['default_theme']])) {
$errors[] = t('Default theme is invalid');
}
if (!in_array($form['allow_user_theme_mode'], ['', '0', '1'], true)) {
$errors[] = t('User theme policy is invalid');
}
return $errors;
}
private static function normalizeStatus(string $status): string
{
$status = strtolower(trim($status));
if (!in_array($status, ['active', 'inactive'], true)) {
return 'active';
}
return $status;
}
}