2010-11-03 3 views
4

Mon objet hôte contient plusieurs objets d'option associés. Dans le formulaire d'édition, les utilisateurs peuvent (de) sélectionner des options et enregistrer ce nouvel ensemble d'associations. Ceci est implémenté en utilisant saveAll() sur les données publiées. Le résultat est queExiste-t-il un moyen de faire en sorte que saveAll() supprime les objets superflus?

  • l'hôte (principal) objet est mis à jour,
  • l'option
  • (associé) les objets qui sont inclus à la fois dans la précédente et la nouvelle association sont mis à jour, et
  • objets d'options qui ne sont pas inclus dans l'association précédente, mais sont inclus dans le nouveau sont créés.

Mais ce qui ne se produit pas est

  • que les objets d'options qui ont été inclus dans l'association avant, mais pas dans le nouveau sont supprimés.

Question: Can saveAll() faire aussi bien, et comment la structure de données aurait Ressembler pour obtenir cet effet?

Informations connexes:

Mon code pour gérer le formulaire de modification est en fait plus complexe (et donc je ne l'ai pas cité ici), mais il en résulte la structure de données comme décrit dans le livre:

([Host] => (... host object fields ...), 
    [Option] => ([0] => (... first option object fields ...), 
       ... 
       [n] => (... nth option object fields ...) 
      ) 
) 

Maintenant, si l'hôte d'origine avait une option associée qui n'est pas incluse dans le tableau 0..n alors saveAll() ne le détectera pas et ne supprimera pas cet objet associé.

Je ne sais pas si cela est pertinent, mais j'utilise CakePHP 1.3.

Répondre

0

HABTM supprime tous les enregistrements associés, puis recrée ce qui est nécessaire. Comme le suggère PawelMysior, vous pouvez réaliser ceci avec votre hasMany en supprimant manuellement les enregistrements associés immédiatement avant la sauvegarde. Le danger, cependant, est que la sauvegarde échoue, vous perdez l'état précédent.

Je voudrais aller avec une solution de GJ et les supprimer après une sauvegarde réussie, mais à la place boucle sur un tableau d'ID redondants et utiliser la méthode Model-> del() de Cake. De cette façon, vous conservez toute la gestion des erreurs intégrée.

+0

Merci pour la clarification. Je vais supprimer les enregistrements supplémentaires manuellement et probablement ajouter manuellement aussi, en éliminant la configuration de saveAll() alltogether (la source de données ne supporte pas les changements atomiques, de sorte que le point en faveur de saveAll() est discutable). –

+0

J'utilise rarement saveAll - Je préfère garder le contrôle. – Leo

2

Pas vraiment une solution élégante mais fonctionne pour moi.

if ($this->Main->saveAll($this->data)) 
{ 
    $this->Main->query(sprintf(
     'DELETE ' 
     . 'FROM extraneous ' 
     . 'WHERE main_id = \'%s\' AND modified < (SELECT modified FROM main WHERE id = \'%1$s\')' 
     , mysql_real_escape_string($this->Main->id) 
    )); 
} 

Notez que vos tables doivent avoir un champ modified.

1

saveAll() ne supprime rien de votre base de données.

Je suppose que le meilleur moyen est de supprimer les options liées à l'hôte actuel avant de les sauvegarder, puis de les ajouter. Si toutefois vous devez mettre à jour ceux qui existent déjà (et vous?) Pour une raison quelconque (comme: les options étant liées à d'autres modèles), je suppose que vous pouvez essayer d'écrire un morceau de code qui supprimera les options non sélectionnées.

2

Vous pouvez vous assurer que tout est exécuté de manière atomique si vous en enveloppez manuellement tout dans une transaction.

Cela peut être fait avec les begin(), rollback() et commit() méthodes de la source de données:

$this->Main->begin(); 

if (!$this->Main->save(...)) { 
    $this->Main->rollback(); 
    return false; 
} 

// Perform saves in related models... 
if (!$this->Main->MainRelatedModel->save(...)) { 
    $this->Main->rollback(); 
    return false; 
} 

// Perform deletes in extraneous records... 
if (!$this->Main->MainRelatedModel->delete(...)) { 
    $this->Main->rollback(); 
    return false; 
} 

// Everything went well, commit and close the transaction 
$this->Main->commit(); 

Le principal inconvénient est que les transactions ne peuvent pas être imbriquées, vous pouvez donc pas utiliser saveAll(). Vous devez enregistrer/supprimer tout étape par étape, au lieu de le faire en un seul appel.

+0

Cette réponse est ancienne mais m'aide encore à résoudre mes problèmes avec le gâteau. Merci @elitalon! –

1

En cherchant cela, j'ai remarqué qu'il n'y a toujours pas de solution intégrée CakePHP. Pour ce faire, j'ai ajouté le code suivant à mon modèle:

private $oldBarIds = array(); 
public function beforeSave($options = array() { 
    parent::beforeSave($options); 

    $this->oldBarIds = array(); 
    if ($this->id && $this->exists() && isset($this->data['Bar'])) { 
     $oldBars = $this->Bar->find('all', array(
      'fields' => array('id'), 
      'conditions' => array(
       'Bar.foo_id' => $this->id 
      ) 
     )); 
     $this->oldBarIds = Hash::extract($oldBars, '{n}.id'); 
    } 
} 

Ce vérifie si Bar existe dans les données d'économie. Si c'est le cas, il obtiendra les identifiants actuels des actuels, en les mettant à $this->oldBarIds. Puis, quand la sauvegarde réussit, il faut supprimer les anciens:

public function afterSave($created, $options = array()) { 
    parent::afterSave($created, $options); 

    if (!$created && $this->oldBarIds) { 
     $this->Bar->deleteAll(array(
      'Bar' => $this->oldBarIds 
     )); 
    } 
} 

De cette façon, la suppression est gérée par le modèle, et ne se produit que lorsque la sauvegarde a réussi. Devrait être en mesure d'ajouter cela à un comportement, pourrait le faire un jour.

Questions connexes