Un modo per trovare le istanze in base a criteri variabili, come suggerito da anteprime di scriptin è il modo giusto. Tuttavia, non è necessario il peso totale dei framework menzionati.
Ho recentemente programmato esattamente quello che hai descritto. Il repository necessita di meta-informazioni sui campi dell'oggetto che stai richiedendo - una classe di configurazione è una buona opzione e un modo per verificare se una richiesta è valida per un oggetto (un modo per convalidare i criteri - basato sulla meta-informazione ).
Nella mia implementazione, ho unificato il pattern del repository con il pattern identity-map. Il repository contiene una raccolta di entità, inizialmente vuota. Una richiesta di selectByCriteria
in entrata controllerà prima la raccolta e, se non viene trovata alcuna entità, i criteri verranno passati a un metodo per la trasformazione in una query db ed eseguiti. Le righe restituite vengono archiviate come entità nella raccolta-repository e quindi restituite al chiamante del metodo come propria "sotto-raccolta". Le potenziali future richieste a tali risorse nel corso della vita dell'applicazione verranno quindi soddisfatte dalla raccolta mantenuta in memoria.
Hai bisogno di un modo per normalizzare i criteri di selezione. Il tipo di richieste che un'API REST deve soddisfare può essere facilmente normalizzato in Disjunctive Normal Form (DNF) o Congiuntivo modulo normale (CNF) . Per semplicità di codice e logica, ho scelto DNF per la mia implementazione: leggero, come semplice array depth-3.
Un insieme di criteri di selezione sarebbe quindi simile a questo:
array(
array(
array('fieldName','comparator','fieldValue'),
**AND**
array('fieldName','comparator','fieldValue')
...
),
**OR**
array(
array('fieldName','comparator','fieldValue'),
**AND**
array('fieldName','comparator','fieldValue')
...
)
...
)
(I comparatori non devono essere stringhe, possono essere costanti di classe - i valori non devono necessariamente essere stringhe - anzi è più trasparente usare i tipi con cui vengono confrontati - specialmente se abbiamo bisogno per costruire una DB-Query in seguito)
Un oggetto di servizio prima convalida un insieme di criteri rispetto a un oggetto di configurazione per una classe di dati, controllando la struttura dell'array, se esistono nomi di campo, se i comparatori sono consentiti e se i tipi di campo possono essere significativamente confrontato con i valori dati e comparatori. Se l'array dei criteri è valido, viene verificato con foreach-loop contro la raccolta. Il vantaggio di DNF è che un ciclo può terminare controllando ogni disgiunzione una volta che il primo congiunto non è valido, e può terminare l'intero ciclo esterno (oltre i disgiunti) una volta che il primo disgiunto è stato trovato valido - semplificando il codice.
La selezione da una raccolta è implementata in questo modo:
$resultCollection = $this->getEmptyCollection;
if(!$this->criteriaValidationService->validateCriteria($dataObjectConfig,$criteria)) {
return $resultCollection;
}
foreach ($this->elements as $instance) {
foreach ($criteria as $disjunctCriteria) {
$isValidDisjunct = TRUE;
foreach ($disjunctCriteria as $conjunctCriterion) {
$fieldName = $conjunctCriterion[0];
$comparator = $conjunctCriterion[1];
if(isset($conjunctCriterion[2])) {
$compareValue = $conjunctCriterion[2];
}
if ($comparator == 'IS NULL') {
if(!(is_null($instance->$fieldName))) {
$isValidDisjunct = FALSE;
break;
}
} elseif ($comparator == 'IS NOT NULL') {
if(is_null($instance->$fieldName)) {
$isValidDisjunct = FALSE;
break;
}
} elseif ($comparator == '=') {
if (!(isset($instance->$fieldName) && $instance->$fieldName == $compareValue)) {
$isValidDisjunct = FALSE;
break;
}
} elseif ($comparator == '!=') {
if (!($instance->$fieldName != $compareValue)) {
$isValidDisjunct = FALSE;
break;
}
} elseif ($comparator == '>') {
if (!(isset($instance->$fieldName) && ($instance->$fieldName > $compareValue))) {
$isValidDisjunct = FALSE;
break;
}
} elseif ($comparator == '<') {
if (!(isset($instance->$fieldName) && ($instance->$fieldName < $compareValue))) {
$isValidDisjunct = FALSE;
break;
}
} elseif ($comparator == '<=') {
if (!(isset($instance->$fieldName) && !($instance->$fieldName <= $compareValue))) {
$isValidDisjunct = FALSE;
break;
}
} elseif ($comparator == '>=') {
if (!(isset($instance->$fieldName) || ($instance->$fieldName >= $compareValue))) {
$isValidDisjunct = FALSE;
break;
}
} elseif ($comparator == 'IN') {
if (!(in_array($instance->$fieldName, $compareValue))) {
$isValidDisjunct = FALSE;
break;
}
} elseif ($comparator == 'LIKE') {
//Replace SQL-Wildcard with fnmatch-wildcard
$compareValue = preg_replace('/(?<!\\)%/', '*', $compareValue);
if (!(fnmatch($compareValue, $instance->$fieldName))) {
$isValidDisjunct = FALSE;
break;
}
}
}
if ($isValidDisjunct) {
$resultCollection->add($instance, TRUE);
break;
}
}
}
return $resultCollection;