PageRenderTime 64ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/magmi/engines/magmi_productimportengine.php

https://bitbucket.org/jit_bec/shopifine
PHP | 1518 lines | 1136 code | 151 blank | 231 comment | 102 complexity | bd89ebf0f8273ec7ef8c575a8d68c5cf MD5 | raw file
Possible License(s): LGPL-3.0
  1. <?php
  2. /**
  3. * MAGENTO MASS IMPORTER CLASS
  4. *
  5. * version : 0.6
  6. * author : S.BRACQUEMONT aka dweeves
  7. * updated : 2010-10-09
  8. *
  9. */
  10. /* use external file for db helper */
  11. require_once("magmi_engine.php");
  12. /**
  13. *
  14. * Magmi Product Import engine class
  15. * This class handle product import
  16. * @author dweeves
  17. *
  18. */
  19. class Magmi_ProductImportEngine extends Magmi_Engine
  20. {
  21. public $attrinfo=array();
  22. public $attrbytype=array();
  23. public $store_ids=array();
  24. public $status_id=array();
  25. public $attribute_sets=array();
  26. public $prod_etype;
  27. public $sidcache=array();
  28. public $mode="update";
  29. private $_attributehandlers;
  30. private $_current_row;
  31. private $_optidcache=null;
  32. private $_curitemids=array("sku"=>null);
  33. private $_dstore=array();
  34. private $_same;
  35. private $_currentpid;
  36. private $_extra_attrs;
  37. private $_profile;
  38. private $_sid_wsscope=array();
  39. private $_sid_sscope=array();
  40. private $_prodcols=array();
  41. private $_stockcols=array();
  42. private $_skustats=array();
  43. public function addExtraAttribute($attr)
  44. {
  45. $attinfo=$this->attrinfo[$attr];
  46. $this->_extra_attrs[$attinfo["backend_type"]]["data"][]=$attinfo;
  47. }
  48. /**
  49. * constructor
  50. * @param string $conffile : configuration .ini filename
  51. */
  52. public function __construct()
  53. {
  54. $this->setBuiltinPluginClasses("itemprocessors",dirname(dirname(__FILE__))."/plugins/inc/magmi_defaultattributehandler.php::Magmi_DefaultAttributeItemProcessor");
  55. }
  56. public function getSkuStats()
  57. {
  58. return $this->_skustats;
  59. }
  60. public function getImportMode()
  61. {
  62. return $this->mode;
  63. }
  64. /**
  65. * (non-PHPdoc)
  66. * @see Magmi_Engine::getEngineInfo()
  67. */
  68. public function getEngineInfo()
  69. {
  70. return array("name"=>"Magmi Product Import Engine","version"=>"1.6","author"=>"dweeves");
  71. }
  72. /**
  73. * load properties
  74. * @param string $conf : configuration .ini filename
  75. */
  76. public function initProdType()
  77. {
  78. $tname=$this->tablename("eav_entity_type");
  79. $this->prod_etype=$this->selectone("SELECT entity_type_id FROM $tname WHERE entity_type_code=?","catalog_product","entity_type_id");
  80. }
  81. public function getPluginFamilies()
  82. {
  83. return array("datasources","general","itemprocessors");
  84. }
  85. public function registerAttributeHandler($ahinst,$attdeflist)
  86. {
  87. foreach($attdeflist as $attdef)
  88. {
  89. $ad=explode(":",$attdef);
  90. if(count($ad)!=2)
  91. {
  92. $this->log("Invalid registration string ($attdef) :".get_class($ahinst),"warning");
  93. }
  94. else
  95. {
  96. $this->_attributehandlers[$attdef]=$ahinst;
  97. }
  98. }
  99. }
  100. /**
  101. *
  102. * Return list of store codes that share the same website than the stores passed as parameter
  103. * @param string $scodes comma separated list of store view codes
  104. */
  105. public function getStoreIdsForWebsiteScope($scodes)
  106. {
  107. if(!isset($this->_sid_wsscope[$scodes]))
  108. {
  109. $this->_sid_wsscope[$scodes]=array();
  110. $wscarr=csl2arr($scodes);
  111. $qcolstr=$this->arr2values($wscarr);
  112. $cs=$this->tablename("core_store");
  113. $sql="SELECT csdep.store_id FROM $cs as csmain
  114. JOIN $cs as csdep ON csdep.website_id=csmain.website_id
  115. WHERE csmain.code IN ($qcolstr) ";
  116. $sidrows=$this->selectAll($sql,$wscarr);
  117. foreach($sidrows as $sidrow)
  118. {
  119. $this->_sid_wsscope[$scodes][]=$sidrow["store_id"];
  120. }
  121. }
  122. return $this->_sid_wsscope[$scodes];
  123. }
  124. public function getStoreIdsForStoreScope($scodes)
  125. {
  126. if(!isset($this->_sid_sscope[$scodes]))
  127. {
  128. $this->_sid_sscope[$scodes]=array();
  129. $scarr=csl2arr($scodes);
  130. $qcolstr=$this->arr2values($scarr);
  131. $cs=$this->tablename("core_store");
  132. $sql="SELECT csmain.store_id from $cs as csmain WHERE csmain.code IN ($qcolstr)";
  133. $sidrows=$this->selectAll($sql,$scarr);
  134. foreach($sidrows as $sidrow)
  135. {
  136. $this->_sid_sscope[$scodes][]=$sidrow["store_id"];
  137. }
  138. }
  139. return $this->_sid_sscope[$scodes];
  140. }
  141. /**
  142. * returns mode
  143. */
  144. public function getMode()
  145. {
  146. return $this->mode;
  147. }
  148. public function getProdCols()
  149. {
  150. if(count($this->_prodcols)==0)
  151. {
  152. $sql='DESCRIBE '.$this->tablename('catalog_product_entity');
  153. $rows=$this->selectAll($sql);
  154. foreach($rows as $row)
  155. {
  156. $this->_prodcols[]=$row['Field'];
  157. }
  158. }
  159. return $this->_prodcols;
  160. }
  161. public function getStockCols()
  162. {
  163. if(count($this->_stockcols)==0)
  164. {
  165. $sql='DESCRIBE '.$this->tablename('cataloginventory_stock_item');
  166. $rows=$this->selectAll($sql);
  167. foreach($rows as $row)
  168. {
  169. $this->_stockcols[]=$row['Field'];
  170. }
  171. }
  172. return $this->_stockcols;
  173. }
  174. /**
  175. * Initialize attribute infos to be used during import
  176. * @param array $cols : array of attribute names
  177. */
  178. public function checkRequired($cols)
  179. {
  180. $eav_attr=$this->tablename("eav_attribute");
  181. $sql="SELECT attribute_code FROM $eav_attr WHERE is_required=1
  182. AND frontend_input!='' AND frontend_label!='' AND entity_type_id=?";
  183. $required=$this->selectAll($sql,$this->prod_etype);
  184. $reqcols=array();
  185. foreach($required as $line)
  186. {
  187. $reqcols[]=$line["attribute_code"];
  188. }
  189. $required=array_diff($reqcols,$cols);
  190. return $required;
  191. }
  192. /**
  193. *
  194. * gets attribute metadata from DB and put it in attribute metadata caches
  195. * @param array $cols list of attribute codes to get metadata from
  196. * if in this list, some values are not attribute code, no metadata will be cached.
  197. */
  198. public function initAttrInfos($cols)
  199. {
  200. if($this->prod_etype==null)
  201. {
  202. //Find product entity type
  203. $tname=$this->tablename("eav_entity_type");
  204. $this->prod_etype=$this->selectone("SELECT entity_type_id FROM $tname WHERE entity_type_code=?","catalog_product","entity_type_id");
  205. }
  206. $toscan=array_values(array_diff($cols,array_keys($this->attrinfo)));
  207. if(count($toscan)>0)
  208. {
  209. //create statement parameter string ?,?,?.....
  210. $qcolstr=$this->arr2values($toscan);
  211. $tname=$this->tablename("eav_attribute");
  212. if($this->getMagentoVersion()!="1.3.x")
  213. {
  214. $extra=$this->tablename("catalog_eav_attribute");
  215. //SQL for selecting attribute properties for all wanted attributes
  216. $sql="SELECT `$tname`.*,$extra.is_global FROM `$tname`
  217. LEFT JOIN $extra ON $tname.attribute_id=$extra.attribute_id
  218. WHERE ($tname.attribute_code IN ($qcolstr)) AND (entity_type_id=?)";
  219. }
  220. else
  221. {
  222. $sql="SELECT `$tname`.* FROM `$tname` WHERE ($tname.attribute_code IN ($qcolstr)) AND (entity_type_id=?)";
  223. }
  224. $toscan[]=$this->prod_etype;
  225. $result=$this->selectAll($sql,$toscan);
  226. $attrinfs=array();
  227. //create an attribute code based array for the wanted columns
  228. foreach($result as $r)
  229. {
  230. $attrinfs[$r["attribute_code"]]=$r;
  231. }
  232. unset($result);
  233. //create a backend_type based array for the wanted columns
  234. //this will greatly help for optimizing inserts when creating attributes
  235. //since eav_ model for attributes has one table per backend type
  236. foreach($attrinfs as $k=>$a)
  237. {
  238. //do not index attributes that are not in header (media_gallery may have been inserted for other purposes)
  239. if(!in_array($k,$cols))
  240. {
  241. continue;
  242. }
  243. $bt=$a["backend_type"];
  244. if(!isset($this->attrbytype[$bt]))
  245. {
  246. $this->attrbytype[$bt]=array("data"=>array());
  247. }
  248. $this->attrbytype[$bt]["data"][]=$a;
  249. }
  250. //now add a fast index in the attrbytype array to store id list in a comma separated form
  251. foreach($this->attrbytype as $bt=>$test)
  252. {
  253. $idlist;
  254. foreach($test["data"] as $it)
  255. {
  256. $idlist[]=$it["attribute_id"];
  257. }
  258. $this->attrbytype[$bt]["ids"]=implode(",",$idlist);
  259. }
  260. $this->attrinfo=array_merge($this->attrinfo,$attrinfs);
  261. }
  262. $notattribs=array_diff($cols,array_keys($this->attrinfo));
  263. foreach($notattribs as $k)
  264. {
  265. $this->attrinfo[$k]=null;
  266. }
  267. /*now we have 2 index arrays
  268. 1. $this->attrinfo which has the following structure:
  269. key : attribute_code
  270. value : attribute_properties
  271. 2. $this->attrbytype which has the following structure:
  272. key : attribute backend type
  273. value : array of :
  274. data => array of attribute_properties ,one for each attribute that match
  275. the backend type
  276. ids => list of attribute ids of the backend type */
  277. }
  278. /**
  279. *
  280. * retrieves attribute metadata
  281. * @param string $attcode attribute code
  282. * @param boolean $lookup if set, this will try to get info from DB otherwise will get from cache and may return null if not cached
  283. * @return array attribute metadata info
  284. */
  285. public function getAttrInfo($attcode,$lookup=true)
  286. {
  287. $attrinf=isset($this->attrinfo[$attcode])?$this->attrinfo[$attcode]:null;
  288. if($attrinf==null && $lookup)
  289. {
  290. $this->initAttrInfos(array($attcode));
  291. }
  292. if(count($this->attrinfo[$attcode])==0)
  293. {
  294. $attrinf=null;
  295. unset($this->attrinfo[$attcode]);
  296. }
  297. else
  298. {
  299. $attrinf=$this->attrinfo[$attcode];
  300. }
  301. return $attrinf;
  302. }
  303. /**
  304. * retrieves attribute set id for a given attribute set name
  305. * @param string $asname : attribute set name
  306. */
  307. public function getAttributeSetId($asname)
  308. {
  309. if(!isset($this->attribute_sets[$asname]))
  310. {
  311. $tname=$this->tablename("eav_attribute_set");
  312. $asid=$this->selectone(
  313. "SELECT attribute_set_id FROM $tname WHERE attribute_set_name=? AND entity_type_id=?",
  314. array($asname,$this->prod_etype),
  315. 'attribute_set_id');
  316. $this->attribute_sets[$asname]=$asid;
  317. }
  318. return $this->attribute_sets[$asname];
  319. }
  320. /**
  321. * Retrieves product id for a given sku
  322. * @param string $sku : sku of product to get id for
  323. */
  324. public function getProductIds($sku)
  325. {
  326. $tname=$this->tablename("catalog_product_entity");
  327. $result=$this->selectAll(
  328. "SELECT sku,entity_id as pid,attribute_set_id as asid FROM $tname WHERE sku=?",
  329. $sku);
  330. if(count($result)>0)
  331. {
  332. return $result[0];
  333. }
  334. else
  335. {
  336. return false;
  337. }
  338. }
  339. /**
  340. * creates a product in magento database
  341. * @param array $item: product attributes as array with key:attribute name,value:attribute value
  342. * @param int $asid : attribute set id for values
  343. * @return : product id for newly created product
  344. */
  345. public function createProduct($item,$asid)
  346. {
  347. //force item type if not exists
  348. if(!isset($item["type"]))
  349. {
  350. $item["type"]="simple";
  351. }
  352. $tname=$this->tablename('catalog_product_entity');
  353. $item['type_id']=$item['type'];
  354. $item['attribute_set_id']=$asid;
  355. $item['entity_type_id']=$this->prod_etype;
  356. $item['created_at']=strftime("%Y-%m-%d %H:%M:%S");
  357. $item['updated_at']=strftime("%Y-%m-%d %H:%M:%S");
  358. $columns=array_intersect(array_keys($item), $this->getProdCols());
  359. $values=$this->filterkvarr($item, $columns);
  360. $sql="INSERT INTO `$tname` (".implode(",",$columns).") VALUES (".$this->arr2values($columns).")";
  361. $lastid=$this->insert($sql,array_values($values));
  362. return $lastid;
  363. }
  364. /**
  365. * Updateds product update time
  366. * @param unknown_type $pid : entity_id of product
  367. */
  368. public function touchProduct($pid)
  369. {
  370. $tname=$this->tablename('catalog_product_entity');
  371. $this->update("UPDATE $tname SET updated_at=? WHERE entity_id=?",array(strftime("%Y-%m-%d %H:%M:%S"),$pid));
  372. }
  373. /**
  374. * Get Option id for select attributes based on value
  375. * @param int $attid : attribute id to find option id from value
  376. * @param mixed $optval : value to get option id for
  377. * @return : array of lines (should be as much as values found),"opvd"=>option_id for value on store 0,"opvs" option id for value on current store
  378. */
  379. function getOptionsFromValues($attid,$store_id,$optvals)
  380. {
  381. $ovstr=substr(str_repeat("?,",count($optvals)),0,-1);
  382. $t1=$this->tablename('eav_attribute_option');
  383. $t2=$this->tablename('eav_attribute_option_value');
  384. $sql="SELECT optvals.option_id as opvs,optvals.value FROM $t2 as optvals";
  385. $sql.=" JOIN $t1 as opt ON opt.option_id=optvals.option_id AND opt.attribute_id=?";
  386. $sql.=" WHERE optvals.store_id=? AND optvals.value IN ($ovstr)";
  387. return $this->selectAll($sql,array_merge(array($attid,$store_id),$optvals));
  388. }
  389. /* create a new option entry for an attribute */
  390. function createOption($attid)
  391. {
  392. $t=$this->tablename('eav_attribute_option');
  393. $optid=$this->insert("INSERT INTO $t (attribute_id) VALUES (?)",$attid);
  394. return $optid;
  395. }
  396. /**
  397. * Creates a new option value for an option entry for a store
  398. * @param int $optid : option entry id
  399. * @param int $store_id : store id to add value for
  400. * @param mixed $optval : new option value to add
  401. * @return : option id for new created value
  402. */
  403. function createOptionValue($optid,$store_id,$optval)
  404. {
  405. $t=$this->tablename('eav_attribute_option_value');
  406. $optval_id=$this->insert("INSERT INTO $t (option_id,store_id,value) VALUES (?,?,?)",array($optid,$store_id,$optval));
  407. return $optval_id;
  408. }
  409. function getOptionIds($attid,$storeid,$values)
  410. {
  411. $optids=array();
  412. $svalues=array();
  413. $avalues=array();
  414. //Matching refstore value
  415. foreach($values as $val)
  416. {
  417. if(preg_match("|^(.*)::\[(.*)\]$|",$val,$matches))
  418. {
  419. $svalues[]=$matches[2];
  420. $avalues[]=$matches[1];
  421. }
  422. else
  423. {
  424. $svalues[]=$val;
  425. $avalues[]=$val;
  426. }
  427. }
  428. $existing=$this->getOptionsFromValues($attid,0,$avalues);
  429. $exvals=array();
  430. foreach($existing as $optdesc)
  431. {
  432. $exvals[]=$optdesc["value"];
  433. }
  434. $new=array_merge(array_diff($avalues,$exvals));
  435. if($storeid==0)
  436. {
  437. foreach($new as $nval)
  438. {
  439. $row=array("opvs"=>$this->createOption($attid),"value"=>$nval);
  440. $this->createOptionValue($row["opvs"],$storeid,$nval);
  441. $existing[]=$row;
  442. }
  443. $this->cacheOptIds($attid,$existing);
  444. }
  445. else
  446. {
  447. $brows=$this->getCachedOptIds($attid);
  448. if(count($brows)==0)
  449. {
  450. $existing=$this->getOptionsFromValues($attid,0,$avalues);
  451. $new=array_merge(array_diff($avalues,$exvals));
  452. foreach($new as $nval)
  453. {
  454. $row=array("opvs"=>$this->createOption($attid),"value"=>$nval);
  455. $this->createOptionValue($row["opvs"],$storeid,$nval);
  456. $existing[]=$row;
  457. }
  458. $this->cacheOptIds($attid,$existing);
  459. $brows=$this->getCachedOptIds($attid);
  460. }
  461. foreach($existing as $ex)
  462. {
  463. array_shift($brows);
  464. }
  465. for($i=0;$i<count($new);$i++)
  466. {
  467. $row=$brows[$i];
  468. if(!isset($row["opvs"]))
  469. {
  470. $row["opvs"]=$this->createOption($attid);
  471. $this->createOptionValue($row["opvs"],0,$new[$i]);
  472. }
  473. $this->createOptionValue($row["opvs"],$storeid,$new[$i]);
  474. $existing[]=$row;
  475. }
  476. }
  477. $optids=array();
  478. foreach($existing as $row)
  479. {
  480. $optids[]=$row["opvs"];
  481. }
  482. unset($existing);
  483. unset($exvals);
  484. return $optids;
  485. }
  486. function cacheOptIds($attid,$row)
  487. {
  488. $this->_optidcache[$attid]=$row;
  489. }
  490. function getCachedOptIds($attid)
  491. {
  492. if(isset($this->_optidcache[$attid]))
  493. {
  494. return $this->_optidcache[$attid];
  495. }
  496. else
  497. {
  498. return null;
  499. }
  500. }
  501. /**
  502. * returns tax class id for a given tax class value
  503. * @param $tcvalue : tax class value
  504. */
  505. public function getTaxClassId($tcvalue)
  506. {
  507. $t=$this->tablename('tax_class');
  508. $txid=$this->selectone("SELECT class_id FROM $t WHERE class_name=?",array($tcvalue),"class_id");
  509. //bugfix for tax class id, if not found set it to none
  510. if(!isset($txid))
  511. {
  512. $txid=0;
  513. }
  514. return $txid;
  515. }
  516. /**
  517. *
  518. * Return affected store ids for a given item given an attribute scope
  519. * @param array $item : item to get store for scope
  520. * @param string $scope : scope to get stores from.
  521. */
  522. public function getItemStoreIds($item,$scope)
  523. {
  524. switch($scope){
  525. //global scope
  526. case 1:
  527. $bstore_ids=$this->getStoreIdsForStoreScope("admin");
  528. break;
  529. //store scope
  530. case 0:
  531. $bstore_ids=$this->getStoreIdsForStoreScope($item["store"]);
  532. break;
  533. //website scope
  534. case 2:
  535. $bstore_ids=$this->getStoreIdsForWebsiteScope($item["store"]);
  536. break;
  537. }
  538. $itemstores=array_unique(array_merge($this->_dstore,$bstore_ids));
  539. sort($itemstores);
  540. return $itemstores;
  541. }
  542. /**
  543. * Create product attribute from values for a given product id
  544. * @param $pid : product id to create attribute values for
  545. * @param $item : attribute values in an array indexed by attribute_code
  546. */
  547. public function createAttributes($pid,&$item,$attmap,$isnew)
  548. {
  549. /**
  550. * get all store ids
  551. */
  552. $this->_extra_attrs=array();
  553. /* now is the interesring part */
  554. /* iterate on attribute backend type index */
  555. foreach($attmap as $tp=>$a)
  556. {
  557. /* for static types, do not insert into attribute tables */
  558. if($tp=="static")
  559. {
  560. continue;
  561. }
  562. //table name for backend type data
  563. $cpet=$this->tablename("catalog_product_entity_$tp");
  564. //data table for inserts
  565. $data=array();
  566. //inserts to perform on backend type eav
  567. $inserts=array();
  568. //deletes to perform on backend type eav
  569. $deletes=array();
  570. //use reflection to find special handlers
  571. $typehandler="handle".ucfirst($tp)."Attribute";
  572. //iterate on all attribute descriptions for the given backend type
  573. foreach($a["data"] as $attrdesc)
  574. {
  575. //get attribute id
  576. $attid=$attrdesc["attribute_id"];
  577. //get attribute value in the item to insert based on code
  578. $atthandler="handle".ucfirst($attrdesc["attribute_code"])."Attribute";
  579. $attrcode=$attrdesc["attribute_code"];
  580. //if the attribute code is no more in item (plugins may have come into the way), continue
  581. if(!in_array($attrcode,array_keys($item)))
  582. {
  583. continue;
  584. }
  585. //get the item value
  586. $ivalue=$item[$attrcode];
  587. //get item store id for the current attribute
  588. $store_ids=$this->getItemStoreIds($item,$attrdesc["is_global"]);
  589. //do not handle empty generic int values in create mode
  590. if($ivalue=="" && $this->mode!="update" && $tp=="int")
  591. {
  592. continue;
  593. }
  594. //for all store ids
  595. foreach($store_ids as $store_id)
  596. {
  597. //base output value to be inserted = base source value
  598. $ovalue=$ivalue;
  599. //check for attribute handlers for current attribute
  600. foreach($this->_attributehandlers as $match=>$ah)
  601. {
  602. $matchinfo=explode(":",$match);
  603. $mtype=$matchinfo[0];
  604. $mtest=$matchinfo[1];
  605. unset($matchinfo);
  606. unset($hvalue);
  607. if(preg_match("/$mtest/",$attrdesc[$mtype]))
  608. {
  609. //if there is a specific handler for attribute, use it
  610. if(method_exists($ah,$atthandler))
  611. {
  612. $hvalue=$ah->$atthandler($pid,$item,$store_id,$attrcode,$attrdesc,$ivalue);
  613. }
  614. else
  615. //use generic type attribute
  616. if(method_exists($ah,$typehandler))
  617. {
  618. $hvalue=$ah->$typehandler($pid,$item,$store_id,$attrcode,$attrdesc,$ivalue);
  619. }
  620. //if handlers returned a value that is not "__MAGMI_UNHANDLED__" , we have our output value
  621. if(isset($hvalue) && $hvalue!="__MAGMI_UNHANDLED__")
  622. {
  623. $ovalue=$hvalue;
  624. break;
  625. }
  626. }
  627. }
  628. //if __MAGMI_UNHANDLED__ ,don't insert anything
  629. if($ovalue=="__MAGMI_UNHANDLED__")
  630. {
  631. $ovalue=false;
  632. }
  633. //if handled value is a "DELETE"
  634. if($ovalue=="__MAGMI_DELETE__")
  635. {
  636. $deletes[]=$attid;
  637. }
  638. else
  639. //if we have something to do with this value
  640. if($ovalue!==false)
  641. {
  642. $data[]=$this->prod_etype;
  643. $data[]=$attid;
  644. $data[]=$store_id;
  645. $data[]=$pid;
  646. $data[]=$ovalue;
  647. $insstr="(?,?,?,?,?)";
  648. $inserts[]=$insstr;
  649. }
  650. //if one of the store in the list is admin
  651. if($store_id==0)
  652. {
  653. $sids=$store_ids;
  654. //remove all values bound to the other stores for this attribute,so that they default to "use admin value"
  655. array_pop($sids);
  656. if(count($sids)>0)
  657. {
  658. $sidlist=implode(",",$sids);
  659. $ddata=array($this->prod_etype,$attid,$pid);
  660. $sql="DELETE FROM $cpet WHERE entity_type_id=? AND attribute_id=? AND store_id IN ($sidlist) AND entity_id=?";
  661. $this->delete($sql,$ddata);
  662. unset($ddata);
  663. }
  664. unset($sids);
  665. break;
  666. }
  667. }
  668. }
  669. if(!empty($inserts))
  670. {
  671. //now perform insert for all values of the the current backend type in one
  672. //single insert
  673. $sql="INSERT INTO $cpet
  674. (`entity_type_id`, `attribute_id`, `store_id`, `entity_id`, `value`)
  675. VALUES ";
  676. $sql.=implode(",",$inserts);
  677. //this one taken from mysql log analysis of magento import
  678. //smart one :)
  679. $sql.=" ON DUPLICATE KEY UPDATE `value`=VALUES(`value`)";
  680. $this->insert($sql,$data);
  681. }
  682. if(!empty($deletes))
  683. {
  684. $sidlist=implode(",",$store_ids);
  685. $attidlist=implode(",",$deletes);
  686. $sql="DELETE FROM $cpet WHERE entity_type_id=? AND attribute_id IN ($attidlist) AND store_id IN ($sidlist) AND entity_id=?";
  687. $this->delete($sql,array($this->prod_etype,$pid));
  688. }
  689. if(empty($deletes) && empty($inserts) && $isnew)
  690. {
  691. if(!$this->_same)
  692. {
  693. $this->log("No $tp Attributes created for sku ".$item["sku"],"warning");
  694. }
  695. }
  696. unset($store_ids);
  697. unset($data);
  698. unset($inserts);
  699. unset($deletes);
  700. }
  701. return $this->_extra_attrs;
  702. }
  703. /**
  704. * update product stock
  705. * @param int $pid : product id
  706. * @param array $item : attribute values for product indexed by attribute_code
  707. */
  708. public function updateStock($pid,$item,$isnew)
  709. {
  710. $scols=$this->getStockCols();
  711. #take only stock columns that are in item
  712. $itstockcols=array_intersect(array_keys($item),$scols);
  713. #no stock columns set, item exists, no stock update needed.
  714. if(count($itstockcols)==0 && !$isnew)
  715. {
  716. return;
  717. }
  718. $csit=$this->tablename("cataloginventory_stock_item");
  719. $css=$this->tablename("cataloginventory_stock_status");
  720. #calculate is_in_stock flag
  721. if(isset($item["qty"]))
  722. {
  723. if(!isset($item["manage_stock"]))
  724. {
  725. $item["manage_stock"]=1;
  726. $item["use_config_manage_stock"]=0;
  727. }
  728. $mqty=(isset($item["min_qty"])?$item["min_qty"]:0);
  729. $is_in_stock=isset($item["is_in_stock"])?$item["is_in_stock"]:($item["qty"]>$mqty?1:0);
  730. if(!$is_in_stock && $item["qty"]>$mqty)
  731. {
  732. $is_in_stock=1;
  733. }
  734. $item["is_in_stock"]=$is_in_stock;
  735. }
  736. #take only stock columns that are in item after item update
  737. $common=array_intersect(array_keys($item),$scols);
  738. #create stock item line if needed
  739. $stock_id=(isset($item["stock_id"])?$item["stock_id"]:1);
  740. $sql="INSERT IGNORE INTO `$csit` (product_id,stock_id) VALUES (?,?)";
  741. $this->insert($sql,array($pid,$stock_id));
  742. if(count($common)>0)
  743. {
  744. $cols=$this->arr2columns($common);
  745. $stockvals=$this->filterkvarr($item,$common);
  746. #fill with values
  747. $svstr=$this->arr2update($stockvals);
  748. $relqty=NULL;
  749. //test for relative qty
  750. if($item["qty"][0]=="+" || $item["qty"][0]=="-")
  751. {
  752. $relqty=getRelative($item["qty"]);
  753. }
  754. //if relative qty
  755. if($relqty!=NULL)
  756. {
  757. //update UPDATE statement value affectation
  758. $svstr=preg_replace("/(^|,)qty=\?/","$1qty=qty$relqty?",$svstr);
  759. $stockvals["qty"]=$item["qty"];
  760. $svstr=str_replace("is_in_stock=?","is_in_stock=(qty>min_qty)",$svstr);
  761. unset($stockvals["is_in_stock"]);
  762. }
  763. $sql="UPDATE `$csit` SET $svstr WHERE product_id=? AND stock_id=?";
  764. $this->update($sql,array_merge(array_values($stockvals),array($pid,$stock_id)));
  765. }
  766. $data=array();
  767. $wsids=$this->getItemWebsites($item);
  768. $csscols=array("website_id","product_id","stock_id","qty","stock_status");
  769. $cssvals=$this->filterkvarr($item,$csscols);
  770. $stock_id=(isset($cssvals["stock_id"])?$cssvals["stock_id"]:1);
  771. $stock_status=(isset($cssvals["stock_status"])?$cssvals["stock_status"]:1);
  772. //new auto synchro on lat inserted stock item values for stock status.
  773. //also works for multiple stock ids.
  774. $sql="INSERT INTO `$css` SELECT csit.product_id,ws.website_id,cis.stock_id,csit.qty,? as stock_status
  775. FROM `$csit` as csit
  776. JOIN ".$this->tablename("core_website")." as ws ON ws.website_id IN (".$this->arr2values($wsids).")
  777. JOIN ".$this->tablename("cataloginventory_stock")." as cis ON cis.stock_id=?
  778. WHERE product_id=?
  779. ON DUPLICATE KEY UPDATE stock_status=VALUES(`stock_status`),qty=VALUES(`qty`)";
  780. $data[]=$stock_status;
  781. $data=array_merge($data,$wsids);
  782. $data[]=$stock_id;
  783. $data[]=$pid;
  784. $this->insert($sql,$data);
  785. unset($data);
  786. }
  787. /**
  788. * assign categories for a given product id from values
  789. * categories should already be created & csv values should be as the ones
  790. * given in the magento export (ie: comma separated ids, minus 1,2)
  791. * @param int $pid : product id
  792. * @param array $item : attribute values for product indexed by attribute_code
  793. */
  794. public function assignCategories($pid,$item)
  795. {
  796. $cce=$this->tablename("catalog_category_entity");
  797. $ccpt=$this->tablename("catalog_category_product");
  798. #handle assignment reset
  799. if(!isset($item["category_reset"]) || $item["category_reset"]==1)
  800. {
  801. $sql="DELETE $ccpt.*
  802. FROM $ccpt
  803. JOIN $cce ON $cce.entity_id=$ccpt.category_id
  804. WHERE product_id=?";
  805. $this->delete($sql,$pid);
  806. }
  807. $inserts=array();
  808. $data=array();
  809. $ddata=array();
  810. $catids=csl2arr($item["category_ids"]);
  811. foreach($catids as $catid)
  812. {
  813. $rel=getRelative($catid);
  814. if($rel=="-")
  815. {
  816. $ddata[]=$catid;
  817. }
  818. else
  819. {
  820. $inserts[]="(?,?)";
  821. $data[]=$catid;
  822. $data[]=$pid;
  823. }
  824. }
  825. #peform deletion of removed category affectation
  826. if(count($ddata)>0)
  827. {
  828. $sql="DELETE FROM $ccpt WHERE category_id IN (".$this->arr2values($ddata).") AND product_id=?";
  829. $ddata[]=$pid;
  830. $this->delete($sql,$ddata);
  831. unset($ddata);
  832. }
  833. #create new category assignment for products, if multi store with repeated ids
  834. #ignore duplicates
  835. if(count($inserts)>0)
  836. {
  837. $sql="INSERT IGNORE INTO $ccpt (`category_id`,`product_id`)
  838. VALUES ";
  839. $sql.=implode(",",$inserts);
  840. $this->insert($sql,$data);
  841. unset($data);
  842. }
  843. unset($deletes);
  844. unset($inserts);
  845. }
  846. public function getItemWebsites($item,$default=false)
  847. {
  848. $k=$item["store"];
  849. if(!isset($this->_wsids[$k]))
  850. {
  851. $this->_wsids[$k]=array();
  852. $cs=$this->tablename("core_store");
  853. if(trim($k)!="admin")
  854. {
  855. $scodes=csl2arr($k);
  856. $qcolstr=$this->arr2values($scodes);
  857. $rows=$this->selectAll("SELECT website_id FROM $cs WHERE code IN ($qcolstr) AND store_id!=0 GROUP BY website_id",$scodes);
  858. }
  859. else
  860. {
  861. $rows=$this->selectAll("SELECT website_id FROM $cs WHERE store_id!=0 GROUP BY website_id ");
  862. }
  863. foreach($rows as $row)
  864. {
  865. $this->_wsids[$k][]=$row['website_id'];
  866. }
  867. }
  868. return $this->_wsids[$k];
  869. }
  870. /**
  871. * set website of product if not exists
  872. * @param int $pid : product id
  873. * @param array $item : attribute values for product indexed by attribute_code
  874. */
  875. public function updateWebSites($pid,$item)
  876. {
  877. $wsids=$this->getItemWebsites($item);
  878. $qcolstr=$this->arr2values($wsids);
  879. $cpst=$this->tablename("catalog_product_website");
  880. $cws=$this->tablename("core_website");
  881. //associate product with all websites in a single multi insert (use ignore to avoid duplicates)
  882. $sql="INSERT IGNORE INTO `$cpst` (`product_id`, `website_id`) SELECT ?,website_id FROM $cws WHERE website_id IN ($qcolstr)";
  883. $this->insert($sql,array_merge(array($pid),$wsids));
  884. }
  885. public function clearOptCache()
  886. {
  887. unset($this->_optidcache);
  888. $this->_optidcache=array();
  889. }
  890. public function onNewSku($sku,$existing)
  891. {
  892. $this->clearOptCache();
  893. //only assign values to store 0 by default in create mode for new sku
  894. //for store related options
  895. if(!$existing)
  896. {
  897. $this->_dstore=array(0);
  898. }
  899. else
  900. {
  901. $this->_dstore=array();
  902. }
  903. $this->_same=false;
  904. }
  905. public function onSameSku($sku)
  906. {
  907. unset($this->_dstore);
  908. $this->_dstore=array();
  909. $this->_same=true;
  910. }
  911. public function getItemIds($item)
  912. {
  913. $sku=$item["sku"];
  914. if($sku!=$this->_curitemids["sku"])
  915. {
  916. //try to find item ids in db
  917. $cids=$this->getProductIds($sku);
  918. if($cids!==false)
  919. {
  920. //if found use it
  921. $this->_curitemids=$cids;
  922. }
  923. else
  924. {
  925. //only sku & attribute set id from datasource otherwise.
  926. $this->_curitemids=array("pid"=>null,"sku"=>$sku,"asid"=>isset($item["attribute_set"])?$this->getAttributeSetId($item["attribute_set"]):null);
  927. }
  928. //do not reset values for existing if non admin
  929. $this->onNewSku($sku,($cids!==false));
  930. unset($cids);
  931. }
  932. else
  933. {
  934. $this->onSameSku($sku);
  935. }
  936. return $this->_curitemids;
  937. }
  938. public function handleIgnore(&$item)
  939. {
  940. //filter __MAGMI_IGNORE__ COLUMNS
  941. foreach($item as $k=>$v)
  942. {
  943. if($v=="__MAGMI_IGNORE__")
  944. {
  945. unset($item[$k]);
  946. }
  947. }
  948. }
  949. public function findItemStores($pid)
  950. {
  951. $sql="SELECT cs.code FROM ".$this->tablename("catalog_product_website")." AS cpw".
  952. " JOIN ".$this->tablename("core_store")." as cs ON cs.website_id=cpw.website_id".
  953. " WHERE cpw.product_id=?";
  954. $result=$this->selectAll($sql,array($pid));
  955. $scodes=array();
  956. foreach($result as $row)
  957. {
  958. $scodes[]=$row["code"];
  959. }
  960. return implode(",",$scodes);
  961. }
  962. public function checkItemStores($scodes)
  963. {
  964. if($scodes=="admin")
  965. {
  966. return $scodes;
  967. }
  968. $scarr=explode(",",$scodes);
  969. trimarray($scarr);
  970. $rscode=array();
  971. $sql="SELECT code FROM ".$this->tablename("core_store")." WHERE code IN (".$this->arr2values($scarr).")";
  972. $result=$this->selectAll($sql,$scarr);
  973. $rscodes=array();
  974. foreach($result as $row)
  975. {
  976. $rscodes[]=$row["code"];
  977. }
  978. $diff=array_diff($scarr, $rscodes);
  979. $out="";
  980. if(count($diff)>0)
  981. {
  982. $out="Invalid store code(s) found:".implode(",",$diff);
  983. }
  984. if($out!="")
  985. {
  986. if(count($rscodes)==0)
  987. {
  988. $out.=", NO VALID STORE FOUND";
  989. }
  990. $this->log($out,"warning");
  991. }
  992. return implode(",",$rscodes);
  993. }
  994. public function checkstore(&$item,$pid,$isnew)
  995. {
  996. //we have store column set , just check
  997. if(isset($item["store"]) && trim($item["store"])!="")
  998. {
  999. $scodes=$this->checkItemStores($item["store"]);
  1000. }
  1001. else
  1002. {
  1003. $scodes="admin";
  1004. }
  1005. if($scodes=="")
  1006. {
  1007. return false;
  1008. }
  1009. $item["store"]=$scodes;
  1010. return true;
  1011. }
  1012. /**
  1013. * full import workflow for item
  1014. * @param array $item : attribute values for product indexed by attribute_code
  1015. */
  1016. public function importItem($item)
  1017. {
  1018. $this->handleIgnore($item);
  1019. if(Magmi_StateManager::getState()=="canceled")
  1020. {
  1021. exit();
  1022. }
  1023. //first step
  1024. if(!$this->callPlugins("itemprocessors","processItemBeforeId",$item))
  1025. {
  1026. return false;
  1027. }
  1028. //check if sku has been reset
  1029. if(!isset($item["sku"]) || trim($item["sku"])=='')
  1030. {
  1031. $this->log('No sku info found for record #'.$this->_current_row,"error");
  1032. return false;
  1033. }
  1034. //handle "computed" ignored columns
  1035. $this->handleIgnore($item);
  1036. $itemids=$this->getItemIds($item);
  1037. $pid=$itemids["pid"];
  1038. $asid=$itemids["asid"];
  1039. $isnew=false;
  1040. if(isset($pid) && $this->mode=="xcreate")
  1041. {
  1042. $this->log("skipping existing sku:{$item["sku"]} - xcreate mode set","skip");
  1043. return false;
  1044. }
  1045. if(!isset($pid))
  1046. {
  1047. if($this->mode!=='update')
  1048. {
  1049. if(!isset($asid))
  1050. {
  1051. $this->log("cannot create product sku:{$item["sku"]}, no attribute_set defined","error");
  1052. return false;
  1053. }
  1054. $pid=$this->createProduct($item,$asid);
  1055. $this->_curitemids["pid"]=$pid;
  1056. $isnew=true;
  1057. }
  1058. else
  1059. {
  1060. //mode is update, do nothing
  1061. $this->log("skipping unknown sku:{$item["sku"]} - update mode set","skip");
  1062. return false;
  1063. }
  1064. }
  1065. else
  1066. {
  1067. $this->updateProduct($item,$pid);
  1068. }
  1069. try
  1070. {
  1071. if(!$this->callPlugins("itemprocessors","processItemAfterId",$item,array("product_id"=>$pid,"new"=>$isnew,"same"=>$this->_same,"asid"=>$asid)))
  1072. {
  1073. return false;
  1074. }
  1075. if(count($item)==0)
  1076. {
  1077. return true;
  1078. }
  1079. //handle "computed" ignored columns from afterImport
  1080. $this->handleIgnore($item);
  1081. if(!$this->checkstore($item,$pid,$isnew))
  1082. {
  1083. $this->log("invalid store value, skipping item sku:".$item["sku"]);
  1084. return false;
  1085. }
  1086. //create new ones
  1087. $attrmap=$this->attrbytype;
  1088. do
  1089. {
  1090. $attrmap=$this->createAttributes($pid,$item,$attrmap,$isnew);
  1091. }
  1092. while(count($attrmap)>0);
  1093. if(!testempty($item,"category_ids"))
  1094. {
  1095. //assign categories
  1096. $this->assignCategories($pid,$item);
  1097. }
  1098. //update websites
  1099. if($this->mode!="update")
  1100. {
  1101. $this->updateWebSites($pid,$item);
  1102. }
  1103. if(!$this->_same)
  1104. {
  1105. //update stock
  1106. $this->updateStock($pid,$item,$isnew);
  1107. }
  1108. $this->touchProduct($pid);
  1109. //ok,we're done
  1110. if(!$this->callPlugins("itemprocessors","processItemAfterImport",$item,array("product_id"=>$pid,"new"=>$isnew,"same"=>$this->_same)))
  1111. {
  1112. return false;
  1113. }
  1114. }
  1115. catch(Exception $e)
  1116. {
  1117. $this->callPlugins(array("itemprocessors"),"processItemException",$item,array("exception"=>$e));
  1118. $this->logException($e,$this->_laststmt->queryString);
  1119. throw $e;
  1120. }
  1121. return true;
  1122. }
  1123. public function getProperties()
  1124. {
  1125. return $this->_props;
  1126. }
  1127. /**
  1128. * count lines of csv file
  1129. * @param string $csvfile filename
  1130. */
  1131. public function lookup()
  1132. {
  1133. $t0=microtime(true);
  1134. $this->log("Performing Datasouce Lookup...","startup");
  1135. $count=$this->datasource->getRecordsCount();
  1136. $t1=microtime(true);
  1137. $time=$t1-$t0;
  1138. $this->log("$count:$time","lookup");
  1139. $this->log("Found $count records, took $time sec","startup");
  1140. return $count;
  1141. }
  1142. public function updateProduct($item,$pid)
  1143. {
  1144. $tname=$this->tablename('catalog_product_entity');
  1145. if(isset($item['type']))
  1146. {
  1147. $item['type_id']=$item['type'];
  1148. }
  1149. $item['entity_type_id']=$this->prod_etype;
  1150. $item['updated_at']=strftime("%Y-%m-%d %H:%M:%S");
  1151. $columns=array_intersect(array_keys($item), $this->getProdCols());
  1152. $values=$this->filterkvarr($item, $columns);
  1153. $sql="UPDATE `$tname` SET ".$this->arr2update($values). " WHERE entity_id=?";
  1154. $this->update($sql,array_merge(array_values($values),array($pid)));
  1155. }
  1156. public function getProductEntityType()
  1157. {
  1158. return $this->prod_etype;
  1159. }
  1160. public function getCurrentRow()
  1161. {
  1162. return $this->_current_row;
  1163. }
  1164. public function setCurrentRow($cnum)
  1165. {
  1166. $this->_current_row=$cnum;
  1167. }
  1168. public function isLastItem($item)
  1169. {
  1170. return isset($item["__MAGMI_LAST__"]);
  1171. }
  1172. public function setLastItem(&$item)
  1173. {
  1174. $item["__MAGMI_LAST__"]=1;
  1175. }
  1176. public function engineInit($params)
  1177. {
  1178. $this->_profile=$this->getParam($params,"profile","default");
  1179. $this->initPlugins($this->_profile);
  1180. $this->mode=$this->getParam($params,"mode","update");
  1181. }
  1182. public function reportStats($nrow,&$tstart,&$tdiff,&$lastdbtime,&$lastrec)
  1183. {
  1184. $tend=microtime(true);
  1185. $this->log($nrow." - ".($tend-$tstart)." - ".($tend-$tdiff),"itime");
  1186. $this->log($this->_nreq." - ".($this->_indbtime)." - ".($this->_indbtime-$lastdbtime)." - ".($this->_nreq-$lastrec),"dbtime");
  1187. $lastrec=$this->_nreq;
  1188. $lastdbtime=$this->_indbtime;
  1189. $tdiff=microtime(true);
  1190. }
  1191. public function initImport($params)
  1192. {
  1193. $this->log("Import Mode:$this->mode","startup");
  1194. $this->log("MAGMI by dweeves - version:".Magmi_Version::$version,"title");
  1195. $this->log("step:".$this->getProp("GLOBAL","step",0.5)."%","step");
  1196. //intialize store id cache
  1197. $this->connectToMagento();
  1198. try
  1199. {
  1200. $this->initProdType();
  1201. $this->createPlugins($this->_profile,$params);
  1202. $this->_registerPluginLoopCallback("processItemAfterId", "onPluginProcessedItemAfterId");
  1203. $this->callPlugins("datasources,itemprocessors","startImport");
  1204. $this->resetSkuStats();
  1205. }
  1206. catch(Exception $e)
  1207. {
  1208. $this->disconnectFromMagento();
  1209. }
  1210. }
  1211. public function onPluginProcessedItemAfterId($plinst,&$item,$plresult)
  1212. {
  1213. $this->handleIgnore($item);
  1214. }
  1215. public function exitImport()
  1216. {
  1217. $this->callPlugins("datasources,general,itemprocessors","endImport");
  1218. $this->callPlugins("datasources,general,itemprocessors","afterImport");
  1219. $this->disconnectFromMagento();
  1220. }
  1221. public function updateSkuStats($res)
  1222. {
  1223. if(!$this->_same)
  1224. {
  1225. $this->_skustats["nsku"]++;
  1226. if($res["ok"])
  1227. {
  1228. $this->_skustats["ok"]++;
  1229. }
  1230. else
  1231. {
  1232. $this->_skustats["ko"]++;
  1233. }
  1234. }
  1235. }
  1236. public function getDataSource()
  1237. {
  1238. return $this->getPluginInstance("datasources");
  1239. }
  1240. public function processDataSourceLine($item,$rstep,&$tstart,&$tdiff,&$lastdbtime,&$lastrec)
  1241. {
  1242. //counter
  1243. $res=array("ok"=>0,"last"=>0);
  1244. $this->_current_row++;
  1245. if($this->_current_row%$rstep==0)
  1246. {
  1247. $this->reportStats($this->_current_row,$tstart,$tdiff,$lastdbtime,$lastrec);
  1248. }
  1249. try
  1250. {
  1251. if(is_array($item) && count($item)>0)
  1252. {
  1253. //import item
  1254. $this->beginTransaction();
  1255. $importedok=$this->importItem($item);
  1256. if($importedok)
  1257. {
  1258. $res["ok"]=true;
  1259. $this->commitTransaction();
  1260. }
  1261. else
  1262. {
  1263. $res["ok"]=false;
  1264. $this->rollbackTransaction();
  1265. }
  1266. }
  1267. else
  1268. {
  1269. $this->log("ERROR - RECORD #$this->_current_row - INVALID RECORD","error");
  1270. }
  1271. //intermediary measurement
  1272. }
  1273. catch(Exception $e)
  1274. {
  1275. $this->rollbackTransaction();
  1276. $res["ok"]=false;
  1277. $this->logException($e,"ERROR ON RECORD #$this->_current_row");
  1278. }
  1279. if($this->isLastItem($item))
  1280. {
  1281. unset($item);
  1282. $res["last"]=1;
  1283. }
  1284. unset($item);
  1285. $this->updateSkuStats($res);
  1286. return $res;
  1287. }
  1288. public function resetSkuStats()
  1289. {
  1290. $this->_skustats=array("nsku"=>0,"ok"=>0,"ko"=>0);
  1291. }
  1292. public function engineRun($params,$forcebuiltin=array())
  1293. {
  1294. $this->log("Import Mode:$this->mode","startup");
  1295. $this->log("MAGMI by dweeves - version:".Magmi_Version::$version,"title");
  1296. $this->log("step:".$this->getProp("GLOBAL","step",0.5)."%","step");
  1297. $this->createPlugins($this->_profile,$params);
  1298. $this->datasource=$this->getDataSource();
  1299. $this->callPlugins("datasources,general","beforeImport");
  1300. $nitems=$this->lookup();
  1301. Magmi_StateManager::setState("running");
  1302. //if some rows found
  1303. if($nitems>0)
  1304. {
  1305. $this->resetSkuStats();
  1306. //intialize store id cache
  1307. $this->callPlugins("datasources,itemprocessors","startImport");
  1308. //initializing item processors
  1309. $cols=$this->datasource->getColumnNames();
  1310. $this->log(count($cols),"columns");
  1311. $this->callPlugins("itemprocessors","processColumnList",$cols);
  1312. $this->log("Ajusted processed columns:".count($cols),"startup");
  1313. $this->initProdType();
  1314. //initialize attribute infos & indexes from column names
  1315. if($this->mode!="update")
  1316. {
  1317. $this->checkRequired($cols);
  1318. }
  1319. $this->initAttrInfos(array_values($cols));
  1320. //counter
  1321. $this->_current_row=0;
  1322. //start time
  1323. $tstart=microtime(true);
  1324. //differential
  1325. $tdiff=$tstart;
  1326. //intermediary report step
  1327. $this->initDbqStats();
  1328. $pstep=$this->getProp("GLOBAL","step",0.5);
  1329. $rstep=ceil(($nitems*$pstep)/100);
  1330. //read each line
  1331. $lastrec=0;
  1332. $lastdbtime=0;
  1333. while(($item=$this->datasource->getNextRecord())!==false)
  1334. {
  1335. $res=$this->processDataSourceLine($item, $rstep,$tstart,$tdiff,$lastdbtime,$lastrec);
  1336. //break on "forced" last
  1337. if($res["last"])
  1338. {
  1339. break;
  1340. }
  1341. }
  1342. $this->callPlugins("datasources,general,itemprocessors","endImport");
  1343. $this->reportStats($this->_current_row,$tstart,$tdiff,$lastdbtime,$lastrec);
  1344. $this->log("Skus imported OK:".$this->_skustats["ok"]."/".$this->_skustats["nsku"],"info");
  1345. if($this->_skustats["ko"]>0)
  1346. {
  1347. $this->log("Skus imported KO:".$this->_skustats["ko"]."/".$this->_skustats["nsku"],"warning");
  1348. }
  1349. }
  1350. else
  1351. {
  1352. $this->log("No Records returned by datasource","warning");
  1353. }
  1354. $this->callPlugins("datasources,general,itemprocessors","afterImport");
  1355. $this->log("Import Ended","end");
  1356. Magmi_StateManager::setState("idle");
  1357. }
  1358. public function onEngineException($e)
  1359. {
  1360. if(isset($this->datasource))
  1361. {
  1362. $this->datasource->onException($e);
  1363. }
  1364. $this->log("Import Ended","end");
  1365. Magmi_StateManager::setState("idle");
  1366. }
  1367. }