Di default i profili di importazione di magento presuppongono una stretta interazione tra client e server. I dati in ingresso sono processati tramite un ciclo in javascript e l’importazione viene effettuata tramite chiamate AJAX.
Questo approccio presenta diverse problematiche, prima fra tutte la possibilità che, per problemi di rete tra client e server, l’operazione di importazione non arrivi a completamento.
Una possibile soluzione è ricreare in uno script esterno a Magento tutte le chiamate al controller effettuando il ciclo direttamente in php. Il controller deputato a gestire gli script di importazione è Mage_Adminhtml_System_Convert_ProfileController.
Per fare uno script esterno a magento che utilizzi le funzioni magento bisogna ricreare il contesto. Questa operazione è effettuata tramite:
1 2 3 | require_once 'app/Mage.php'; Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID); |
Bisogna fare molta attenzione soprattutto all’istruzione presente alla riga 3: in molti script che presenti in rete al suo posto si trova spesso un semplice Mage::app();. Non sono la stessa cosa! Un possibile problema introdotto dall’utilizzo di un semplice Mage->app(); è che, al momento in cui si carica un prodotto, l’oggetto non viene caricato completamente (la variabile _origData viene impostata a null) e di conseguenza le operazioni di update non vengono eseguite correttamente.
Il nome del file in cui sono contenuti i dati da importare e il l’id del profilo di importazione sono passati in get.
4 5 6 7 8 | $profileId = Mage::app()->getRequest()->getParam('profile_id'); $filename = Mage::app()->getRequest()->getParam('files'); if (!isset($filename)) { die("No file has been set!"); } |
Ultimo passaggio della fase di ricreazione del contesto è l’inizializzazione delle sessioni magento.
9 10 11 | $userModel = Mage::getModel('admin/user'); $userModel->setUserId(0); Mage::getSingleton('admin/session')->setUser($userModel); |
Dato che il nostro script verrà eseguito in background, i messaggi di notifica dello stato di avanzamento delle operazioni vengono inviati a un file di log piuttosto che a schermo (come invece fanno normalmente gli script di importazione). Il codice che segue serve a inizializzare il log (si ricorda che il log deve essere
12 13 14 15 | $logFileName= $filename.'.log'; $recordCount = 0; umask(0); Mage::log("Inizio dell'importazione di ".$filename,null,$logFileName); |
Nel momento in cui da backend si lancia uno script di importazione viene lanciato il metodo runAction() che a sua volta lancia tutte le operazioni di inizializzazione del profilo tramite _initProfile(). Riportiamo nel nostro script tali operazioni.
16 17 18 19 20 21 22 23 24 | $profile = Mage::getModel('dataflow/profile'); if ($profileId) { $profile->load($profileId); if (!$profile->getId()) { Mage::getSingleton('adminhtml/session')->addError('The profile you are trying to save no longer exists'); } } Mage::register('current_convert_profile', $profile); |
A questo punto l’esecuzione normale del profilo prevede la renderizzazione tramite toHtml() di Mage_Adminhtml_Block_System_Convert_Profile_Run. Come già accennato l’esecuzione del profilo viene effettuata tramite chiamate AJAX Eliminando tutto il codice che serve alla creazione della pagina HTML, nel nostro script viene mantenuto esclusivamente il codice che manda in esecuzione il profilo. In particolare il codice che importa effettivamente le righe è contenuto nella funzione batchRunAction() del controller Mage_Adminhtml_System_Convert_ProfileController.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | $profile->run(); $batchModel = Mage::getSingleton('dataflow/batch'); if ($batchModel->getId()) { if ($batchModel->getAdapter()) { $batchId = $batchModel->getId(); $batchImportModel = $batchModel->getBatchImportModel(); $importIds = $batchImportModel->getIdCollection(); $batchModel = Mage::getModel('dataflow/batch')->load($batchId); $adapter = Mage::getModel($batchModel->getAdapter()); $adapter->setBatchParams($batchModel->getParams()); foreach ($importIds as $importId) { $recordCount++; try { $batchImportModel->load($importId); if (!$batchImportModel->getId()) { $errors[] = Mage::helper('dataflow')->__('Skip undefined row'); continue; } $importData = $batchImportModel->getBatchData(); try { $adapter->saveRow($importData); } catch (Exception $e) { Mage::log($e->getMessage(),null,$logFileName); continue; } if ($recordCount%10 == 0) { Mage::log($recordCount . ' - Completed!! ',null,$logFileName); } } catch(Exception $ex) { Mage::log('Record# ' . $recordCount . ' - SKU = ' . $importData['sku']. ' - Error - ' . $ex->getMessage(),null,$logFileName); } } foreach ($profile->getExceptions() as $e) { Mage::log($e->getMessage(),null,$logFileName); } } } Mage::log("Importazione di $filename completata: $recordCount record inseriti",null,$logFileName); |
Per avere il controllo sull’avanzamento del processo si è scelto di scrivere un record nel file di log ogni 10 prodotti importati (vedi righe 53-55).
Lo script risultante è:
require_once 'app/Mage.php'; Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID); $profileId = Mage::app()->getRequest()->getParam('profile_id'); $filename = Mage::app()->getRequest()->getParam('files'); if (!isset($filename)) { die("No file has been set!"); } $userModel = Mage::getModel('admin/user'); $userModel->setUserId(0); Mage::getSingleton('admin/session')->setUser($userModel); $logFileName= $filename.'.log'; $recordCount = 0; umask(0); Mage::log("Inizio dell'importazione di ".$filename,null,$logFileName); $profile = Mage::getModel('dataflow/profile'); if ($profileId) { $profile->load($profileId); if (!$profile->getId()) { Mage::getSingleton('adminhtml/session')->addError('The profile you are trying to save no longer exists'); } } Mage::register('current_convert_profile', $profile); $profile->run(); $batchModel = Mage::getSingleton('dataflow/batch'); if ($batchModel->getId()) { if ($batchModel->getAdapter()) { $batchId = $batchModel->getId(); $batchImportModel = $batchModel->getBatchImportModel(); $importIds = $batchImportModel->getIdCollection(); $batchModel = Mage::getModel('dataflow/batch')->load($batchId); $adapter = Mage::getModel($batchModel->getAdapter()); $adapter->setBatchParams($batchModel->getParams()); foreach ($importIds as $importId) { $recordCount++; try { $batchImportModel->load($importId); if (!$batchImportModel->getId()) { $errors[] = Mage::helper('dataflow')->__('Skip undefined row'); continue; } $importData = $batchImportModel->getBatchData(); try { $adapter->saveRow($importData); } catch (Exception $e) { Mage::log($e->getMessage(),null,$logFileName); continue; } if ($recordCount%10 == 0) { Mage::log($recordCount . ' - Completed!! ',null,$logFileName); } } catch(Exception $ex) { Mage::log('Record# ' . $recordCount . ' - SKU = ' . $importData['sku']. ' - Error - ' . $ex->getMessage(),null,$logFileName); } } foreach ($profile->getExceptions() as $e) { Mage::log($e->getMessage(),null,$logFileName); } } } Mage::log("Importazione di $filename completata: $recordCount record inseriti",null,$logFileName);

Ciao, grazie mille per l’articolo che proprio in questo momento mi è utilissimo!
Purtroppo però c’è un problema di cui non riesco a venirne a capo. Se utilizzo il profilo di importazione direttamente dall’area di admin di Magento funziona correttamente.
Se utilizzo il profilo da file php i prodotti vengono importati correttamente ma, non riesco a capire per quale motivo, non viene caricato lo stato (Abilitato o Disabilitato) e la visibilità.
Il file di log restituisce:
2010-10-08T15:47:45+00:00 DEBUG (7): Inizio dell’importazione di var/import/products.csv
2010-10-08T15:47:47+00:00 DEBUG (7): Starting Mage_Dataflow_Model_Convert_Adapter_Io :: load
2010-10-08T15:47:47+00:00 DEBUG (7): Loaded successfully: “/var/import/products.csv”.
2010-10-08T15:47:47+00:00 DEBUG (7): Starting Mage_Dataflow_Model_Convert_Parser_Csv :: parse
2010-10-08T15:47:47+00:00 DEBUG (7): Found 1 rows.
2010-10-08T15:47:47+00:00 DEBUG (7): Starting catalog/convert_adapter_product :: parse
2010-10-08T15:47:47+00:00 DEBUG (7): Importazione di var/import/products.csv completata: 1 record inseriti
Da cosa potrebbe dipendere il problema?
Ciao, per prima cosa volevo ringraziarvi per l’articolo di indubbia utilità, è raro trovare articoli interessanti su Magento in italiano
Ho notato che lo script di blocca con l’import dei prodotti configurabili sulla 1.4.1.1, io l’ho risolto aggiungendo prima della riga 47 (il saveRow), all’interno del try:
$adapter = Mage::getModel($batchModel->getAdapter());
$adapter->setBatchParams($batchModel->getParams());
ciao e grazie ancora per l’articolo
Daniele
@Simone D’Amico
molto probabilmente stai passando lo stato e la visibilità (ad es. “Abilitato” e “Catalogo, Ricerca”) in italiano, cosa che viene interpretata correttamente nell’esecuzione dal backend mentre tramite questo script si aspetta i valori in inglese (ad es. “Enabled”, “Catalogue, Search”).
N.B. se li imposti in inglese anche nell’esecuzione del profilo dal backend continuerà a funzionare correttamente
Ciao molto interessante tutto quanto, volevo sapere invece per quanto riguarda le grandi quantità di dati, leggevo su Internet che il codice potrebbe dare problemi e sarebbe meglio fare delle chiamate implicite a MySQL, inoltre dovrei aggiornare solo alcuni campi partendo dal codice SKU quali per esempio Price e Quantity, e volevo sapere se la procedura poteva essere corretta lo stesso… Premetto che l’ho realizzata solo che non saprei come intercettare il risultato della query e come scrivere il file di log….
Questo script è funzionante con Magento 1.5? Perchè ho provato ad eseguirlo e nonostante il risultato prodotto dai log, continua a non importare correttamente i prodotti.
Come dettaglio, ci tengo a precisare che il csv che ho usato per l’import è lo stesso csv prodotto dall’export dal pannello di amministrazione, a cui ho cambiato però il prezzo. Ho utilizzato lo script per vedere se l’upload di un nuovo csv aggiornava lo stato dei prodotti esistenti, ma così non è stato. Allego l’output sul file di log:
2011-06-30T18:03:24+00:00 DEBUG (7): Inizio dell’importazione di test_prodotti.csv
2011-06-30T18:03:25+00:00 DEBUG (7): Image does not exist.
2011-06-30T18:03:25+00:00 DEBUG (7): Image does not exist.
2011-06-30T18:03:25+00:00 DEBUG (7): Image does not exist.
2011-06-30T18:03:25+00:00 DEBUG (7): Starting Mage_Dataflow_Model_Convert_Parser_Csv :: parse
2011-06-30T18:03:25+00:00 DEBUG (7): Found 3 rows.
2011-06-30T18:03:25+00:00 DEBUG (7): Starting catalog/convert_adapter_product :: parse
2011-06-30T18:03:25+00:00 DEBUG (7): Importazione di test_prodotti.csv completata: 3 record inseriti