2010-02-16 6 views
6

Ho una tabellaIgnora duplicati quando si utilizza INSERT in un database con Symfony e Dottrina

CREATE TABLE `sob_tags_articles` (
    `tag_id` int(11) NOT NULL, 
    `article_id` int(11) NOT NULL, 
    `id` int(11) NOT NULL auto_increment, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM AUTO_INCREMENT=112 

E triing per salvare un oggetto con Doctrine:

$sbTagsArticles = new SobTagsArticles(); 
$sbTagsArticles->article_id = $pubId; 
$sbTagsArticles->tag_id = $tagId; 
$sbTagsArticles->save(); 

Ma se il record esiste con la stessa $ pubId e $ tagId il nuovo record verrà inserito con il nuovo PK.

Come fare INSERIRE IGNORA nella tabella con symfony?

$sbTagsArticles->isNew(); 

rendimenti 1.

Thnx.

+0

Perché non usi '(tag_id, article_id)' come chiave primaria? –

+2

@ user274101: non è necessario modificare il titolo di una domanda risolta su StackOverflow. Dovresti accettare la risposta corretta. –

risposta

13
try 
{ 
    $record->save(); 
} 
catch(Doctrine_Exception $e) 
{ 
    if($e->getErrorCode() !== $duplicateKeyCode) 
    { 
     /** 
     * if its not the error code for a duplicate key 
     * value then rethrow the exception 
     */ 
     throw $e; 
    } 

    /** 
    * you might want to fetch the real record here instead 
    * so yure working with the persisted copy 
    */ 
} 

È necessario assicurarsi che lo stesso record non esista sul lato dell'applicazione e non sul lato SQL. Se non vuoi mai che lo stesso articolo/tag combo esista, aggiungi un indice univoco a (article_id, tag_id). Ciò dovrebbe generare un errore mysql che a sua volta genererà un'eccezione di dottrina che è possibile rilevare. Non esiste un flag di ignora per i salvataggi ... Potresti essere in grado di usarne uno operativo a un livello inferiore del DBAL (Doctrine_Query, Doctrine_Connection, ecc.) Ma non direttamente dal livello ORM.

Doctrine_Record::isNew() restituirà sempre true se si dispone di record istanziati per prelevarlo dal db altrimenti ha modo che non abbia modo di sapere che il record è/non è nuovo.

Inoltre, perché si utilizza il motore di archiviazione MyISAM? Sono abbastanza sicuro che ciò comporterà un sovraccarico maggiore quando si utilizza Doctrine poiché è necessario emulare i vincoli sul lato php. Normalmente lo schema sarebbe simile a questa:

CREATE TABLE `sob_tags_articles` (
    `tag_id` int(11) NOT NULL, 
    `article_id` int(11) NOT NULL, 
    `id` int(11) NOT NULL auto_increment, 
    PRIMARY KEY (`id`), 
    CONSTRAINT `some_unique_constraint_name_1` 
     FOREIGN KEY `article_id` 
     REFERENCES `article` (`id`) 
     ON DELETE CASCADE, 
    CONSTRAINT `some_unique_constraint_name_2` 
     FOREIGN KEY `tag_id` 
     REFERENCES `tag` (`id`) 
     ON DELETE CASCADE 
) ENGINE=InnoDB AUTO_INCREMENT=112 
+0

oh grazie! questo schema sarebbe ottimo per questa attività, ma non posso modificare il motore di tabella. E ho letto che Doctrine è migliore e più recente di Propel, quindi lo uso. Ho aggiunto un indice. E ora ho bisogno di prendere l'eccezione di Doctrine. sai come si fa? Io uso una query ajax per salvare i record, quindi non ho bisogno che tutta questa spazzatura sia visibile in firebug. – user274101

+0

tipico blocco Try/Catch con un po 'di logica per assicurarsi che sia l'errore che ci si aspetta ...potrebbe essere necessario controllare il codice di errore e non remebr se Doctrine genera un'eccezione Doctrine generica o qualcosa di più specifico. vedi sopra per esempio. – prodigitalson

+0

ok, grazie. Penso che il problema sia risolto – user274101

-1

Si potrebbe estendere il SobTagsArticles oggetto con un nuovo metodo di salvataggio, e verificare se il record esiste già:

public function exists() { 
    $q = Doctrine_Query::create() 
    ->from('sobtagsarticles ta') 
    ->where('ta.tag_id = ? and ta.article_id = ?', array($this->getTagId(), $this->getArticleId())); 

    if (!$result = $q->execute()) 
    { 
    parent::save(); 
    } 
} 

In questo modo verrà salvato l'oggetto solo se non esiste.

Si potrebbe anche impostare un indice univoco al vostro tavolo in questo modo:

UNIQUE INDEX `sb_tags_articles_unique` (`tag_id` ASC, `article_id` ASC) 

lo schema sarebbe simile a questa:

CREATE TABLE `sob_tags_articles` (
    `tag_id` int(11) NOT NULL, 
    `article_id` int(11) NOT NULL, 
    `id` int(11) NOT NULL auto_increment, 
    PRIMARY KEY (`id`), 
    UNIQUE INDEX `sb_tags_articles_unique` (`tag_id` ASC, `article_id` ASC), 
    CONSTRAINT `some_unique_constraint_name_1` 
     FOREIGN KEY `article_id` 
     REFERENCES `article` (`id`) 
     ON DELETE CASCADE, 
    CONSTRAINT `some_unique_constraint_name_2` 
     FOREIGN KEY `tag_id` 
     REFERENCES `tag` (`id`) 
     ON DELETE CASCADE 
) ENGINE=InnoDB AUTO_INCREMENT=112 
+0

Penso che sia meglio non interrogare mysql ancora una volta per verificare se il record esiste. – user274101

+0

Sì, è possibile utilizzare invece la coda memcached o redis –

6

Questo è il codice effettivo da utilizzare

try 
{ 
    $record->save(); 
} 
catch(Doctrine_Connection_Exception $e) 
{ 
    if($e->getPortableCode() != Doctrine::ERR_ALREADY_EXISTS) 
    { 
     /** 
     * if its not the error code for a duplicate key 
     * value then rethrow the exception 
     */ 
     throw $e; 
    } 
    /** 
    * you might want to fetch the real record here instead 
    * so yure working with the persisted copy 
    */ 
}