refactor(customfield): interface-based DI for repositories
Align CustomField-Schicht mit core/Repository/Access-Muster. Vier Repositories (Tenant-Definition, Tenant-Option, User-Value, User-Value-Option) bekommen je ein Interface, werden von static auf instance umgestellt und ueber CustomFieldRepositoryFactory injiziert. TenantCustomFieldService und UserCustomFieldValueService injizieren die Repository-Interfaces statt statisch aufzurufen. CustomFieldServicesFactory reicht Instanzen durch. AddressBookService (Modul) bekommt TenantCustomFieldOptionRepositoryInterface per Container — Interface-Kopplung verbessert Modul-Isolation gegenueber dem vorherigen statischen Aufruf. Kein Verhaltenswechsel, keine SQL-Aenderung, keine Service-Signatur-Aenderung. Alle tenant_id-Parameter erhalten (GR-SEC-009, Security-Review SR-002). Oeffnet den Weg fuer Cluster B1 (TenantCustomFieldService-Tests) und B2 (UserCustomFieldValueService-Tests), die nun mit createMock() moeglich sind. Nebenbei zwei PHPStan-Folge-Findings korrigiert: - Veralteter Ignore-Eintrag fuer findById aus phpstan-baseline.neon entfernt - Redundanter is_array-Check in TenantCustomFieldOptionRepository entfernt (durch typisierte Interface-Signatur abgedeckt) Gates: QG-001 (1945 Tests, 28581 Assertions) / QG-002 / QG-003 / QG-006 pass. Workflow: .agents/runs/CUSTOMFIELD-DI-REFACTOR-001/ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ use MintyPHP\DB;
|
||||
use MintyPHP\Repository\Support\RepoQuery;
|
||||
|
||||
/** Reads and writes tenant-scoped custom field definitions with type and pagination filtering. */
|
||||
class TenantCustomFieldDefinitionRepository
|
||||
class TenantCustomFieldDefinitionRepository implements TenantCustomFieldDefinitionRepositoryInterface
|
||||
{
|
||||
private static function unwrap(?array $row): ?array
|
||||
{
|
||||
@@ -31,7 +31,7 @@ class TenantCustomFieldDefinitionRepository
|
||||
return $list;
|
||||
}
|
||||
|
||||
public static function listByTenantId(int $tenantId, bool $onlyActive = true): array
|
||||
public function listByTenantId(int $tenantId, bool $onlyActive = true): array
|
||||
{
|
||||
if ($tenantId <= 0) {
|
||||
return [];
|
||||
@@ -46,7 +46,7 @@ class TenantCustomFieldDefinitionRepository
|
||||
return self::unwrapList(DB::select($query, ...$params));
|
||||
}
|
||||
|
||||
public static function listByTenantIds(array $tenantIds, bool $onlyActive = true): array
|
||||
public function listByTenantIds(array $tenantIds, bool $onlyActive = true): array
|
||||
{
|
||||
$tenantIds = array_values(array_unique(array_map('intval', $tenantIds)));
|
||||
$tenantIds = array_values(array_filter($tenantIds, static fn ($id) => $id > 0));
|
||||
@@ -63,7 +63,7 @@ class TenantCustomFieldDefinitionRepository
|
||||
return self::unwrapList(call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $params)));
|
||||
}
|
||||
|
||||
public static function listFilterableByTenantIds(array $tenantIds): array
|
||||
public function listFilterableByTenantIds(array $tenantIds): array
|
||||
{
|
||||
$tenantIds = array_values(array_unique(array_map('intval', $tenantIds)));
|
||||
$tenantIds = array_values(array_filter($tenantIds, static fn ($id) => $id > 0));
|
||||
@@ -78,7 +78,7 @@ class TenantCustomFieldDefinitionRepository
|
||||
return self::unwrapList(call_user_func_array(['MintyPHP\\DB', 'select'], [$query, $tenantIds]));
|
||||
}
|
||||
|
||||
public static function findByUuid(string $uuid): ?array
|
||||
public function findByUuid(string $uuid): ?array
|
||||
{
|
||||
$row = DB::selectOne(
|
||||
'select id, uuid, tenant_id, field_key, label, type, is_required, is_filterable, active, sort_order, created_by, modified_by, created, modified ' .
|
||||
@@ -88,7 +88,7 @@ class TenantCustomFieldDefinitionRepository
|
||||
return self::unwrap($row);
|
||||
}
|
||||
|
||||
public static function findById(int $id): ?array
|
||||
public function findById(int $id): ?array
|
||||
{
|
||||
$row = DB::selectOne(
|
||||
'select id, uuid, tenant_id, field_key, label, type, is_required, is_filterable, active, sort_order, created_by, modified_by, created, modified ' .
|
||||
@@ -98,7 +98,7 @@ class TenantCustomFieldDefinitionRepository
|
||||
return self::unwrap($row);
|
||||
}
|
||||
|
||||
public static function findByTenantIdAndKey(int $tenantId, string $fieldKey): ?array
|
||||
public function findByTenantIdAndKey(int $tenantId, string $fieldKey): ?array
|
||||
{
|
||||
if ($tenantId <= 0 || $fieldKey === '') {
|
||||
return null;
|
||||
@@ -112,7 +112,7 @@ class TenantCustomFieldDefinitionRepository
|
||||
return self::unwrap($row);
|
||||
}
|
||||
|
||||
public static function create(array $data): int|false
|
||||
public function create(array $data): int|false
|
||||
{
|
||||
return DB::insert(
|
||||
'insert into tenant_custom_field_definitions ' .
|
||||
@@ -131,7 +131,7 @@ class TenantCustomFieldDefinitionRepository
|
||||
);
|
||||
}
|
||||
|
||||
public static function update(int $id, array $data): bool
|
||||
public function update(int $id, array $data): bool
|
||||
{
|
||||
if ($id <= 0) {
|
||||
return false;
|
||||
@@ -164,7 +164,7 @@ class TenantCustomFieldDefinitionRepository
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
public static function delete(int $id): bool
|
||||
public function delete(int $id): bool
|
||||
{
|
||||
if ($id <= 0) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Repository\CustomField;
|
||||
|
||||
interface TenantCustomFieldDefinitionRepositoryInterface
|
||||
{
|
||||
/** @return array<int, array<string, mixed>> */
|
||||
public function listByTenantId(int $tenantId, bool $onlyActive = true): array;
|
||||
|
||||
/**
|
||||
* @param int[] $tenantIds
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function listByTenantIds(array $tenantIds, bool $onlyActive = true): array;
|
||||
|
||||
/**
|
||||
* @param int[] $tenantIds
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function listFilterableByTenantIds(array $tenantIds): array;
|
||||
|
||||
public function findByUuid(string $uuid): ?array;
|
||||
|
||||
public function findById(int $id): ?array;
|
||||
|
||||
public function findByTenantIdAndKey(int $tenantId, string $fieldKey): ?array;
|
||||
|
||||
public function create(array $data): int|false;
|
||||
|
||||
public function update(int $id, array $data): bool;
|
||||
|
||||
public function delete(int $id): bool;
|
||||
}
|
||||
@@ -5,7 +5,7 @@ namespace MintyPHP\Repository\CustomField;
|
||||
use MintyPHP\DB;
|
||||
|
||||
/** Retrieves selectable options for tenant-scoped custom field definitions. */
|
||||
class TenantCustomFieldOptionRepository
|
||||
class TenantCustomFieldOptionRepository implements TenantCustomFieldOptionRepositoryInterface
|
||||
{
|
||||
private static function unwrapList($rows): array
|
||||
{
|
||||
@@ -22,7 +22,7 @@ class TenantCustomFieldOptionRepository
|
||||
return $list;
|
||||
}
|
||||
|
||||
public static function listByDefinitionIds(array $definitionIds, bool $onlyActive = true): array
|
||||
public function listByDefinitionIds(array $definitionIds, bool $onlyActive = true): array
|
||||
{
|
||||
$definitionIds = array_values(array_unique(array_map('intval', $definitionIds)));
|
||||
$definitionIds = array_values(array_filter($definitionIds, static fn ($id) => $id > 0));
|
||||
@@ -39,12 +39,12 @@ class TenantCustomFieldOptionRepository
|
||||
return self::unwrapList(call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $params)));
|
||||
}
|
||||
|
||||
public static function replaceForDefinition(int $definitionId, array $options): bool
|
||||
public function replaceForDefinition(int $definitionId, array $options): bool
|
||||
{
|
||||
if ($definitionId <= 0) {
|
||||
return false;
|
||||
}
|
||||
$existing = self::listByDefinitionIds([$definitionId], false);
|
||||
$existing = $this->listByDefinitionIds([$definitionId], false);
|
||||
$existingByKey = [];
|
||||
foreach ($existing as $row) {
|
||||
$key = (string) ($row['option_key'] ?? '');
|
||||
@@ -55,9 +55,6 @@ class TenantCustomFieldOptionRepository
|
||||
|
||||
$keepIds = [];
|
||||
foreach ($options as $option) {
|
||||
if (!is_array($option)) {
|
||||
continue;
|
||||
}
|
||||
$optionKey = trim((string) ($option['option_key'] ?? ''));
|
||||
$label = trim((string) ($option['label'] ?? ''));
|
||||
if ($optionKey === '' || $label === '') {
|
||||
@@ -117,7 +114,7 @@ class TenantCustomFieldOptionRepository
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function deleteByDefinitionId(int $definitionId): bool
|
||||
public function deleteByDefinitionId(int $definitionId): bool
|
||||
{
|
||||
if ($definitionId <= 0) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Repository\CustomField;
|
||||
|
||||
interface TenantCustomFieldOptionRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* @param int[] $definitionIds
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function listByDefinitionIds(array $definitionIds, bool $onlyActive = true): array;
|
||||
|
||||
/** @param array<int, array<string, mixed>> $options */
|
||||
public function replaceForDefinition(int $definitionId, array $options): bool;
|
||||
|
||||
public function deleteByDefinitionId(int $definitionId): bool;
|
||||
}
|
||||
@@ -5,9 +5,9 @@ namespace MintyPHP\Repository\CustomField;
|
||||
use MintyPHP\DB;
|
||||
|
||||
/** Manages selected option links for user custom field values (atomic replace). */
|
||||
class UserCustomFieldValueOptionRepository
|
||||
class UserCustomFieldValueOptionRepository implements UserCustomFieldValueOptionRepositoryInterface
|
||||
{
|
||||
public static function replaceForValueId(int $valueId, array $optionIds): bool
|
||||
public function replaceForValueId(int $valueId, array $optionIds): bool
|
||||
{
|
||||
if ($valueId <= 0) {
|
||||
return false;
|
||||
@@ -37,7 +37,7 @@ class UserCustomFieldValueOptionRepository
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function listOptionIdsByValueIds(array $valueIds): array
|
||||
public function listOptionIdsByValueIds(array $valueIds): array
|
||||
{
|
||||
$valueIds = array_values(array_unique(array_map('intval', $valueIds)));
|
||||
$valueIds = array_values(array_filter($valueIds, static fn ($id) => $id > 0));
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Repository\CustomField;
|
||||
|
||||
interface UserCustomFieldValueOptionRepositoryInterface
|
||||
{
|
||||
/** @param int[] $optionIds */
|
||||
public function replaceForValueId(int $valueId, array $optionIds): bool;
|
||||
|
||||
/**
|
||||
* @param int[] $valueIds
|
||||
* @return array<int, int[]>
|
||||
*/
|
||||
public function listOptionIdsByValueIds(array $valueIds): array;
|
||||
}
|
||||
@@ -5,7 +5,7 @@ namespace MintyPHP\Repository\CustomField;
|
||||
use MintyPHP\DB;
|
||||
|
||||
/** Reads and writes custom field values attached to individual users. */
|
||||
class UserCustomFieldValueRepository
|
||||
class UserCustomFieldValueRepository implements UserCustomFieldValueRepositoryInterface
|
||||
{
|
||||
private static function unwrapList($rows): array
|
||||
{
|
||||
@@ -22,7 +22,7 @@ class UserCustomFieldValueRepository
|
||||
return $list;
|
||||
}
|
||||
|
||||
public static function listByUserAndDefinitionIds(int $userId, array $definitionIds): array
|
||||
public function listByUserAndDefinitionIds(int $userId, array $definitionIds): array
|
||||
{
|
||||
if ($userId <= 0) {
|
||||
return [];
|
||||
@@ -41,7 +41,7 @@ class UserCustomFieldValueRepository
|
||||
return self::unwrapList($rows);
|
||||
}
|
||||
|
||||
public static function upsertScalarValue(int $userId, int $definitionId, array $typedValue): int|false
|
||||
public function upsertScalarValue(int $userId, int $definitionId, array $typedValue): int|false
|
||||
{
|
||||
if ($userId <= 0 || $definitionId <= 0) {
|
||||
return false;
|
||||
@@ -80,7 +80,7 @@ class UserCustomFieldValueRepository
|
||||
);
|
||||
}
|
||||
|
||||
public static function deleteByUserAndDefinitionIds(int $userId, array $definitionIds): bool
|
||||
public function deleteByUserAndDefinitionIds(int $userId, array $definitionIds): bool
|
||||
{
|
||||
if ($userId <= 0) {
|
||||
return false;
|
||||
@@ -98,7 +98,7 @@ class UserCustomFieldValueRepository
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
public static function deleteByUserOutsideTenantIds(int $userId, array $tenantIds): bool
|
||||
public function deleteByUserOutsideTenantIds(int $userId, array $tenantIds): bool
|
||||
{
|
||||
if ($userId <= 0) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Repository\CustomField;
|
||||
|
||||
interface UserCustomFieldValueRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* @param int[] $definitionIds
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function listByUserAndDefinitionIds(int $userId, array $definitionIds): array;
|
||||
|
||||
/** @param array<string, mixed> $typedValue */
|
||||
public function upsertScalarValue(int $userId, int $definitionId, array $typedValue): int|false;
|
||||
|
||||
/** @param int[] $definitionIds */
|
||||
public function deleteByUserAndDefinitionIds(int $userId, array $definitionIds): bool;
|
||||
|
||||
/** @param int[] $tenantIds */
|
||||
public function deleteByUserOutsideTenantIds(int $userId, array $tenantIds): bool;
|
||||
}
|
||||
Reference in New Issue
Block a user