forked from fa/breadcrumb-the-shire
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:
@@ -2,17 +2,21 @@
|
||||
|
||||
namespace MintyPHP\Service\CustomField;
|
||||
|
||||
use MintyPHP\Repository\CustomField\TenantCustomFieldDefinitionRepository;
|
||||
use MintyPHP\Repository\CustomField\TenantCustomFieldOptionRepository;
|
||||
use MintyPHP\Repository\CustomField\UserCustomFieldValueOptionRepository;
|
||||
use MintyPHP\Repository\CustomField\UserCustomFieldValueRepository;
|
||||
use MintyPHP\Repository\CustomField\TenantCustomFieldDefinitionRepositoryInterface;
|
||||
use MintyPHP\Repository\CustomField\TenantCustomFieldOptionRepositoryInterface;
|
||||
use MintyPHP\Repository\CustomField\UserCustomFieldValueOptionRepositoryInterface;
|
||||
use MintyPHP\Repository\CustomField\UserCustomFieldValueRepositoryInterface;
|
||||
use MintyPHP\Repository\Support\RepoQuery;
|
||||
use MintyPHP\Service\Tenant\TenantScopeService;
|
||||
|
||||
class UserCustomFieldValueService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TenantScopeService $userScopeGateway
|
||||
private readonly TenantScopeService $userScopeGateway,
|
||||
private readonly TenantCustomFieldDefinitionRepositoryInterface $definitionRepository,
|
||||
private readonly TenantCustomFieldOptionRepositoryInterface $optionRepository,
|
||||
private readonly UserCustomFieldValueRepositoryInterface $valueRepository,
|
||||
private readonly UserCustomFieldValueOptionRepositoryInterface $valueOptionRepository
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -39,7 +43,7 @@ class UserCustomFieldValueService
|
||||
return [];
|
||||
}
|
||||
|
||||
$definitions = TenantCustomFieldDefinitionRepository::listByTenantIds($tenantIds, !$includeInactive);
|
||||
$definitions = $this->definitionRepository->listByTenantIds($tenantIds, !$includeInactive);
|
||||
if (!$definitions) {
|
||||
return [];
|
||||
}
|
||||
@@ -48,7 +52,7 @@ class UserCustomFieldValueService
|
||||
static fn (array $definition): int => (int) ($definition['id'] ?? 0),
|
||||
$definitions
|
||||
), static fn (int $id): bool => $id > 0));
|
||||
$options = TenantCustomFieldOptionRepository::listByDefinitionIds($definitionIds, !$includeInactive);
|
||||
$options = $this->optionRepository->listByDefinitionIds($definitionIds, !$includeInactive);
|
||||
$optionsByDefinition = [];
|
||||
foreach ($options as $option) {
|
||||
$definitionId = (int) ($option['definition_id'] ?? 0);
|
||||
@@ -85,7 +89,7 @@ class UserCustomFieldValueService
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = UserCustomFieldValueRepository::listByUserAndDefinitionIds($userId, $definitionIds);
|
||||
$rows = $this->valueRepository->listByUserAndDefinitionIds($userId, $definitionIds);
|
||||
if (!$rows) {
|
||||
return [];
|
||||
}
|
||||
@@ -94,7 +98,7 @@ class UserCustomFieldValueService
|
||||
static fn (array $row): int => (int) ($row['id'] ?? 0),
|
||||
$rows
|
||||
), static fn (int $id): bool => $id > 0));
|
||||
$optionIdsByValue = UserCustomFieldValueOptionRepository::listOptionIdsByValueIds($valueIds);
|
||||
$optionIdsByValue = $this->valueOptionRepository->listOptionIdsByValueIds($valueIds);
|
||||
|
||||
$map = [];
|
||||
foreach ($rows as $row) {
|
||||
@@ -250,7 +254,7 @@ class UserCustomFieldValueService
|
||||
$tenantIds = self::normalizeTenantIds($tenantIds);
|
||||
// Always clean values for tenants no longer assigned — happens even when !$canEdit,
|
||||
// because orphaned custom field data should not persist after tenant reassignment.
|
||||
if (!UserCustomFieldValueRepository::deleteByUserOutsideTenantIds($userId, $tenantIds)) {
|
||||
if (!$this->valueRepository->deleteByUserOutsideTenantIds($userId, $tenantIds)) {
|
||||
return ['ok' => false, 'errors' => [t('Custom field values can not be saved')]];
|
||||
}
|
||||
if (!$canEdit) {
|
||||
@@ -270,14 +274,14 @@ class UserCustomFieldValueService
|
||||
}
|
||||
foreach ($prepared['prepared'] as $definitionId => $valueData) {
|
||||
if (!empty($valueData['empty'])) {
|
||||
if (!UserCustomFieldValueRepository::deleteByUserAndDefinitionIds($userId, [(int) $definitionId])) {
|
||||
if (!$this->valueRepository->deleteByUserAndDefinitionIds($userId, [(int) $definitionId])) {
|
||||
return ['ok' => false, 'errors' => [t('Custom field values can not be saved')]];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($valueData['option_ids'])) {
|
||||
$valueId = UserCustomFieldValueRepository::upsertScalarValue($userId, (int) $definitionId, [
|
||||
$valueId = $this->valueRepository->upsertScalarValue($userId, (int) $definitionId, [
|
||||
'value_text' => null,
|
||||
'value_bool' => null,
|
||||
'value_date' => null,
|
||||
@@ -286,13 +290,13 @@ class UserCustomFieldValueService
|
||||
if (!$valueId) {
|
||||
return ['ok' => false, 'errors' => [t('Custom field values can not be saved')]];
|
||||
}
|
||||
if (!UserCustomFieldValueOptionRepository::replaceForValueId((int) $valueId, $valueData['option_ids'])) {
|
||||
if (!$this->valueOptionRepository->replaceForValueId((int) $valueId, $valueData['option_ids'])) {
|
||||
return ['ok' => false, 'errors' => [t('Custom field values can not be saved')]];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$valueId = UserCustomFieldValueRepository::upsertScalarValue($userId, (int) $definitionId, [
|
||||
$valueId = $this->valueRepository->upsertScalarValue($userId, (int) $definitionId, [
|
||||
'value_text' => $valueData['value_text'],
|
||||
'value_bool' => $valueData['value_bool'],
|
||||
'value_date' => $valueData['value_date'],
|
||||
@@ -301,7 +305,7 @@ class UserCustomFieldValueService
|
||||
if (!$valueId) {
|
||||
return ['ok' => false, 'errors' => [t('Custom field values can not be saved')]];
|
||||
}
|
||||
if (!UserCustomFieldValueOptionRepository::replaceForValueId((int) $valueId, [])) {
|
||||
if (!$this->valueOptionRepository->replaceForValueId((int) $valueId, [])) {
|
||||
return ['ok' => false, 'errors' => [t('Custom field values can not be saved')]];
|
||||
}
|
||||
}
|
||||
@@ -419,7 +423,7 @@ class UserCustomFieldValueService
|
||||
public function extractCustomFieldFilterSpec(array $query, int $currentUserId): array
|
||||
{
|
||||
$tenantIds = $this->userScopeGateway()->getUserTenantIds($currentUserId);
|
||||
$definitions = TenantCustomFieldDefinitionRepository::listFilterableByTenantIds($tenantIds);
|
||||
$definitions = $this->definitionRepository->listFilterableByTenantIds($tenantIds);
|
||||
if (!$definitions) {
|
||||
return ['definitions' => [], 'filters' => [], 'query' => []];
|
||||
}
|
||||
@@ -436,7 +440,7 @@ class UserCustomFieldValueService
|
||||
$definitionIds[] = $definitionId;
|
||||
}
|
||||
$definitionIds = array_values(array_unique($definitionIds));
|
||||
$options = TenantCustomFieldOptionRepository::listByDefinitionIds($definitionIds, true);
|
||||
$options = $this->optionRepository->listByDefinitionIds($definitionIds, true);
|
||||
$optionMapByDefinition = [];
|
||||
foreach ($options as $option) {
|
||||
$definitionId = (int) ($option['definition_id'] ?? 0);
|
||||
|
||||
Reference in New Issue
Block a user