2010-08-31 4 views
2

Sur le projet pour lequel je travaille actuellement, je dois lire un fichier Excel (avec plus de 1000 lignes), les extraire et les insérer/mettre à jour dans une table de base de données.Insérer/mettre à jour l'objet Doctrine depuis Excel

  1. en termes de performance, est préférable d'ajouter tous les enregistrements à un Doctrine_Collection et insérer/mettre à jour après avoir utilisé la méthode fromArray(), non? Une autre approche possible consiste à créer un nouvel objet pour chaque ligne (une ligne Excel sera un objet) et les enregistrer, mais je pense que c'est pire en termes de performance.

  2. Chaque fois que l'Excel est téléchargé, il est nécessaire de comparer ses lignes aux objets existants dans la base de données. Si la ligne n'existe pas en tant qu'objet, elle doit être insérée, sinon mise à jour. Ma première approche était de transformer les objets et les lignes en tableaux (ou Doctrine_Collections); comparez ensuite les deux tableaux avant de mettre en œuvre les opérations nécessaires.

Quelqu'un peut-il me suggérer une autre approche possible?

+0

est votre question concernant les opérations de base de données (manipulation d'insertion/mise à jour) ou la logique de code côté serveur (en comparant les tableaux) ou les deux? –

+0

C'est mieux à la fois. –

+0

Contrairement I Pensé sur un premier moment, le nombre de base est exactement le même en utilisant la méthode fromArray() de la Doctrine_Collection. N'est-ce pas de toute façon enregistrer tous les tableaux qui composent la collection sur la même requête? –

Répondre

1

Je n'ai jamais travaillé sur Doctrine_Collections, mais je peux répondre en termes de requêtes de base de données et de logique de code dans un sens plus large. J'applique la logique suivante: -

  1. Fetch toutes les lignes de la feuille Excel base de données en une seule requête et de les stocker dans un tableau $uploadedSheet.

  2. Créez un seul tableau de toutes les lignes de la feuille Excel téléchargée, appelez-le $storedSheet. Je suppose que les structures des Doctrine_Collections $uploadedSheet et $storedSheet seront similaires (les deux lignes bidimensionnelles, les cellules peuvent être identifiées et comparées).

3.Run foreach boucles sur le $uploadedSheet comme suit et seulement identifier les lignes qui doivent être insérées et qui doit être mis à jour (faire des requêtes réelles plus tard) -

$rowsToBeUpdated =array(); 
$rowsToBeInserted=array(); 
foreach($uploadedSheet as $row=>$eachRow) 
{ 
    if(is_array($storedSheet[$row])) 
    { 
     foreach($eachRow as $column=>$value) 
     { 
       if($value != $storedSheet[$row][$column]) 
       {//This is a representation of comparison 
        $rowsToBeUpdated[$row]=true; 
        break; //No need to check this row anymore - one difference detected. 
       } 
     } 
    } 
    else 
    { 
     $rowsToBeInserted[$row] = true; 
    } 
} 

4. De cette façon vous avez deux tableaux. Maintenant effectuer 2 requêtes de base de données -

  • en vrac insérer toutes les lignes de $uploadedSheet dont les numéros sont stockés dans le tableau $rowsToBeInserted.

  • mettre à jour en bloc toutes les lignes de $uploadedSheet dont les numéros sont stockés dans le tableau $rowsToBeUpdated.

Ces requêtes groupées sont essentielles pour accélérer les performances. Faites-moi savoir si cela a aidé, ou si vous vouliez savoir quelque chose d'autre.

+0

Je vais analyser attentivement votre proposition de solution. Merci d'avance pour l'hep. –

+0

vous êtes les bienvenus –

2

Nous en avons fait un peu dans un projet récemment, avec des données CSV. c'était assez indolore. Il y a un plugin symfony tmCsvPlugin, mais nous l'avons prolongé un peu car la version du plugin repo est plutôt obsolète.Il faut ajouter que la liste @TODO :)

Question 1:

Je ne sais pas explicitement sur la performance, mais je suppose que l'ajout des enregistrements à une Doctrine_Collection puis appeler Doctrine_Collection :: save() serait l'approche la plus soignée. Je suis sûr que ce serait pratique si une exception a été jeté quelque part et il fallait revenir sur votre dernière sauvegarde ..

Question 2:

Si vous pouvez utiliser un champ de ligne comme indentifier unique, , (supposons un nom d'utilisateur), vous pouvez rechercher un enregistrement existant. Si vous trouvez un enregistrement, et en supposant que votre ligne importée est un tableau, utilisez Doctrine_Record :: synchronizeWithArray() pour mettre à jour cet enregistrement; puis ajoutez-le à un Doctrine_Collection. Une fois terminé, il suffit d'appeler Doctrine_Collection :: save()

Une assez rude 'n' mise en œuvre prêt:

// set up a new collection 
$collection = new Doctrine_Collection('User'); 

// assuming $row is an associative 
// array representing one imported row. 

foreach ($importedRows as $row) { 

    // try to find an existing record 
    // based on a unique identifier. 
    $user = Doctrine_Core::getTable('User') 
     ->findOneByUsername($row['username']); 

    // create a new user record if 
    // no existing record is found. 
    if (!$user instanceof User) { 
     $user = new User(); 
    } 

    // sync record with current data. 
    $user->synchronizeWithArray($row); 

    // add to collection. 
    $collection->add($user); 

} 

// done. save collection. 
$collection->save(); 

assez rude, mais quelque chose comme ça a bien fonctionné pour moi. Cela suppose que vous pouvez utiliser vos données de ligne importées d'une manière ou d'une autre pour servir d'identificateur unique.

REMARQUE: méfiez-vous des synchronizeWithArray() si vous utilisez sf1.2/doctrine 1.0 - si je me souviens bien, il n'a pas été correctement mis en œuvre. cela fonctionne bien dans la doctrine 1.2 cependant.

+2

Pour l'instant, une petite correction. La méthode invoquée sur l'objet de table User doit être findOneByUsername(), sinon l'objet de retour sera un Doctrine_Collection au lieu d'un utilisateur. –

+0

désolé je le faisais du haut de ma tête. vous avez absolument raison. fixé. –

Questions connexes