8
votes

Yii insert ... sur la mise à jour duplicate

Je travaille sur un projet YII. Comment puis-je utiliser la fonctionnalité de mysql ( http: //dev.mysql.com/doc/refman/5.0/fr/insert-on-dupliquer.html ) Lorsque vous effectuez une sauvegarde () sur un modèle YII?

My MySQL est la suivante: xxx

mon PHP est un suivant: xxx

s'il y a un duplicaté dans ma clé primaire (sapce_id, jour), je Je ne veux pas qu'il se plaint, je veux juste qu'il met à jour avec les dernières données.

Je sais comment le faire dans SQL cru, je me demandais simplement s'il y a un moyen de Yii propice à le faire .


1 commentaires

6 Réponses :


4
votes

La fonctionnalité "sur la mise à jour de la clé en double" est spécifique à la dialecte de SQL de MySQL. Il est peu probable d'être mis en œuvre dans une couche d'abstraction de données. Zenddb et propulseur n'ont pas d'équivalent.

Vous pouvez simuler le comportement en tentative d'insertion dans un essai / capture et de mise à jour si l'insertion échoue avec le code d'erreur approprié. (Erreur clé en double).


6 commentaires

UPVOTE pour la réponse au problème, Downvote pour la solution. Essayez / Catch est vraiment pour la manipulation des erreurs, et cette situation n'est pas une erreur. Voir Stackoverflow.com/questions/1159665/...


Je vous donnerais un +1, mais un essai attrape pour mettre en œuvre la logique est une très mauvaise idée


Sous le capot, c'est exactement ce que fait MySQL. Il tente l'insertion, échoue et fait la mise à jour. Je ne vois pas le problème. Dans ce cas, la classe d'enregistrement active générera une exception sur insertion si la clé existe déjà.


@txyoji Si ce que vous dites est correct, quelqu'un devrait aller et gifler certains Devs MySQL. Essayez / Catch ne doit jamais être utilisé pour créer un flux logique dans votre code!


@txyoji Le problème est qu'il devient étroitement couplé à la mise en œuvre du modèle. Si le modèle s'arrête à l'aide d'ActiveRecord, ou pour une autre raison, il arrête de lancer une exception, le bloc Essayer / Catch ne fonctionnera plus.


@Blowski - Point pris. Merci pour les commentaires.



2
votes

Je suis d'accord avec l'analyse du problème @ TXYOJI, mais j'utiliserais une solution différente.

Vous pouvez étendre la méthode sauvegarder () du modèle pour rechercher un enregistrement existant et la mettre à jour, ou insérer une nouvelle ligne si elle ne le fait pas.


0 commentaires

4
votes

Je préconise avantValidate () où j'ai vérifié si un duplicate existe. Si on le fait, je définit $ ceci-> setisnewrecord (false);

semble fonctionner. Je ne sais pas à quel point c'est performant.


2 commentaires

Cela fonctionne, mais cela brise un peu le «principe du moins d'étonnement» - la validation ne consiste généralement pas à déterminer s'il faut faire une mise à jour ou une insertion. Il devrait simplement dire que «Cet ensemble de données transmet les règles de validation».


Mais c'est "avant" de valider. Donc, c'est juste un moyen de lancer du code avant que les choses soient vérifiées.



14
votes

Vous utilisez des modèles dans Yii, c'est assez simple .. Essayez de vous charger du modèle où vous pensez avoir des entrées en double, si vous trouvez l'entrée, le modèle est chargé d'autre que NULL est de retour. Maintenant, si votre modèle est NULL, créez simplement un nouveau modèle. Le repos est votre code normal pour insérer un nouvel enregistrement.

//try to load model with available id i.e. unique key
$model = someModel::model()->findByPk($id);  

//now check if the model is null
if(!$model) $model = new someModel();

//Apply you new changes
$model->attributes = $attributes;

//save
$model->save();


5 commentaires

Lors de la fabrication de FindbyPk Yii, c'est faire une requête «Select '», je ne pense pas que c'était le but de la question.


Oui, Yii crée un appel SELECT sur FindByPk () . Le but de la question était de mettre à jour une ligne si elle existe déjà ou insérez un nouveau. Et tout en utilisant des modèles, voici comment ça se passe. Bien sûr, sa pénalité de performance, mais si vous ne voulez pas remplacer Yii, c'est la meilleure option disponible. S'il vous plaît proposer une meilleure alternative, qui sera vraiment utile. Merci mec...


Malheureusement, cela ne fonctionne que pour les clés primaires Obtenir des entrées en double, pas pour vérifier la manipulation d'autres types de violations de contraintes par duplication.


Ce que @anand a dit est correct. Il demande comment mettre en œuvre une mise à jour sur duplication.


Je pense que Laravel pourrait aborder cette question beaucoup mieux que Yii 1



5
votes

Je répète deux points principaux des réponses précédentes, je pense que vous devriez garder:

  1. n'utilise pas (Essayez de) utiliser "sur la mise à jour de la clé en double" depuis son nom MySQL uniquement, comme le souligne TXyoji. P> LI>

  2. préférez le Select-> si code> non trouvé alors insertion-> ele code> insert démontré par Uday Sawant. P> Li> ol> BlockQuote>

    Il y a un autre point ici, bien que: concurrence. Bien que pour les applications de trafic bas de la circulation, la probabilité que vous ayez des problèmes est minimale (toujours jamais zéro), je pense que nous sommes toujours prudents à ce sujet. P>

    à partir d'un point de vue transactionnel, "Insérer .. sur la mise à jour en double « code> n'est pas équivalente à la sélection de la mémoire de votre application, puis de l'insérer ou de la mise à jour. La première est une transaction unique, puis la seconde n'est pas. P>

    Voici un mauvais scénario: P>

    1. Vous sélectionnez votre enregistrement à l'aide de Findbypk () code> qui retourne null li>
    2. Une autre transaction (à partir d'une autre demande d'utilisateur) insère un enregistrement avec l'ID que vous avez simplement échoué à sélectionner LI>
    3. Au prochain instant, vous essayez de l'insérer à nouveau li> ol> BlockQuote>

      Dans ce cas, vous obtenez une exception (si vous travaillez avec une clé unique, comme vous le faites ici) ou une entrée en double. Les entrées en double sont beaucoup plus difficiles à ramasser (généralement rien ne semble étrange jusqu'à ce que vos utilisateurs ne voient des enregistrements en double). P>

      La solution ici est de définir un niveau d'isolement strict, par exemple "sérialisable", puis de commencer une transaction. . P>

      Voici un exemple de Yii: P>

      Yii::app()->db->createCommand('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');
      
      $trn = Yii::app()->db->beginTransaction();
      
      try {
          // Try to load model with available id i.e. unique key
          // Since we're in serializable isolation level, even if
          // the record does not exist the RDBMS will lock this key
          // so nobody can insert it until you commit.
          // The same shold for the (most usual) case of findByAttributes()
          $model = someModel::model()->findByAttributes(array(
              'sapce_id' => $sapceId,
              'day' => $day
          ));  
      
          //now check if the model is null
          if (!$model) {
              $model = new someModel();
          }
      
          //Apply you new changes
          $model->attributes = $attributes;
      
          //save
          $model->save();
      
          // Commit changes
          $trn->commit();
      
      } catch (Exception $e) {
          // Rollback transaction
          $trn->rollback();
      
          echo $e->getMessage();
      }
      


1 commentaires

Aidez-moi avec la suite. S'il y a une requête de mise à jour après $ TRN-> COMMIT (); dans la transaction ci-dessus. Va-t-il exécuter?



1
votes

Vous devez utiliser Essayez d'attraper de la sorte:

                try{
                    $model->save();
                }
                catch(CDbException $e){
                    $model->isNewRecord = false;
                    $model->save();
                }


0 commentaires