2016-03-16 16 views
5

Ho la seguente regola nel mio BlockedTable.phpCakePHP 3 isUnique multipla che consente NULL duplicare

public function buildRules(RulesChecker $rules) 
{ 
    $rules->add($rules->isUnique(['date', 'time']), 
     ['message' => 'unique error']); 

    return $rules; 
} 

Fino ad ora questo ha funzionato bene - se cerco di salvare un nuovo record con una data e ora già esistenti , mi impedisce di salvare.

Tuttavia, se il mio orario è NULL, ad esempio la voce in basso;

╔════╦══════════════╦═══════╗ 
║ ID ║ Date  ║ Time ║ 
╠════╬══════════════╬═══════╣ 
║ 1 ║ 22/08/1985 ║ NULL ║ 
╚════╩══════════════╩═══════╝ 

La regola consente comunque di salvare una nuova voce con gli stessi dati. Quindi, se provo a salvare $date = 22/08/1985 e $time = NULL, viene salvato e c'è un record duplicato nel mio database. Mi sarei aspettato che fosse fallito a causa della regola precedente?

Perché sta succedendo? E come posso evitare voci duplicate sui valori NULL?

Grazie in anticipo per il vostro aiuto.

risposta

2

Confrontando contro NULL utilizzando operatori di confronto ordinarie, cioè column = NULL (che regola unico fa), deve sempre essere NULL (AFAIK questo è almeno il caso in MySQL e Postgres), quindi non viene trovato, e di conseguenza l'unica il controllo passerà ogni volta che c'è un valore NULL coinvolto.

Se si desidera evitare questo comportamento, sarà necessario utilizzare una regola personalizzata, poiché quella incorporata semplicemente non la supporta. Penso che questo sia qualcosa che merita un miglioramento, quindi potresti voler aprire un ticket over at GitHub.

Ecco un esempio di base di una classe di sovresposta IsUnique regola, fondamentalmente solo aggiunge un IS operatore al tasto condizioni, in modo che NULL controlli finiscono come column IS NULL.

public function __invoke(EntityInterface $entity, array $options) 
{ 
    if (!$entity->extract($this->_fields, true)) { 
     return true; 
    } 

    $alias = $options['repository']->alias(); 
    $conditions = $this->_alias($alias, $entity->extract($this->_fields)); 
    if ($entity->isNew() === false) { 
     $keys = (array)$options['repository']->primaryKey(); 
     $keys = $this->_alias($alias, $entity->extract($keys)); 
     if (array_filter($keys, 'strlen')) { 
      $conditions['NOT'] = $keys; 
     } 
    } 

    // handle null values 
    foreach ($conditions as $key => $value) { 
     if ($value === null) { 
      $conditions[$key . ' IS'] = $value; 
      unset($conditions[$key]); 
     } 
    } 

    return !$options['repository']->exists($conditions); 
} 

In teoria si potrebbe fare in un metodo di IsUnique::_alias() sovresposta troppo, che avrebbe funzionato senza dover reimplementare il codice dalla classe regola originale, non è davvero il posto giusto però.

https://github.com/cakephp/cakephp/blob/3.2.5/src/ORM/Rule/IsUnique.php

Vedi anche

+0

Grazie per la risposta NDM in profondità, io considero le mie opzioni e nel frattempo creare un biglietto di valorizzazione . – JayIsTooCommon

+0

E ha appena fatto regola personalizzata, e funziona perfettamente. Grazie ancora – JayIsTooCommon