News:

Support the VirtueMart project and become a member

Main Menu

VM1 to VM2: Minor fixes to PortVm1RelatedProducts

Started by mbarry, January 16, 2014, 06:22:21 AM

Previous topic - Next topic

mbarry

VM 2.0.26a/d
Joomla 2.5.17

Scenario:
      Run the VM2 Migration tool with "everything" selected"
      Run the VM2 Migration tool with "Migrate related products into vm2" option selected
       
Issue:
      Although the migration works as expected; a debug step through the code identified a number of issues that would only reveal themselves in
      large shops with over a 1000 products with related items to process.
     
      Also worth noting that the code implements a single SQL INSERT at line 2145 of all products with related products,
       so the idea of time slicing the migration process at line 2149 does not work for each 1000 products processed.

Solution:
      The fixes relate to updating migration processing status
     
      $startLimit is always set to 0 since 'relatedproducts_start' does not exist in Table `#__virtuemart_migration_oldtonew_ids`.
        install.sql modified to include `relatedproducts_start` int(1),

        Call to $this->getMigrationProgress('relatedproducts'); always returns <uninitialised> since $alreadyKnownIds is never set
       
        $limitStartToStore never set correctly since insert was into 'relatedproducts' instead of 'relatedproducts_start'.
       
        Various comments added to help understand the code execution
        Additional checks added to break processing early and info messages added to provide status of processing

Affected files:
      administrator/components/com_virtuemart/helpers/migrator.php
      administrator/components/com_virturemart/install/install.sql  line 424 

code snip for PortVm1RelatedProducts() changes

   function portVm1RelatedProducts(){

      if($this->_stop || (microtime(true)-$this->starttime) >= ($this->maxScriptTime)){
         return;
      }
      vmSetStartTime('relatedproducts');

       $maxItems = $this->_getMaxItems('relatedproducts');
       // MJB this was a bug as relatedproducts_start did not exist in Database           
      $startLimit = $this->_getStartLimit('relatedproducts_start');   
      
      $i=0;
      $continue = true;
      $completionStatus = false;

      $alreadyKnownIds = $this->getMigrationProgress('relatedproducts');  // MJB Bug $alreadyKnownIds is never set later on so the store does nothing
      $productIdMapping = $this->getMigrationProgress('products');        // use this as easy way to translate VM1 to VM2 Product Ids
      $alreadyProcessed = count($alreadyKnownIds);
      
      if(empty ($productIdMapping)) {
         vmWarn("Port Related products: Migration of the VM1 products must be run first");
         return;
      }

      $out=array();
      $out2=array();

      while($continue){
         $q ='select * from #__vm_product_relations LIMIT '.$startLimit.','.$maxItems;

         $doneStart = $startLimit;
         $res = self::loadCountListContinue($q,$startLimit,$maxItems,'port Related products');
         $oldVm1relateds = $res[0];

         $startLimit = $res[1];
         $continue = $res[2];
         
         if(empty($oldVm1relateds)){
            break;
         }

         foreach($oldVm1relateds as $v){
            $pid=$v['product_id'];                   // parent id
            $ids=explode('|',$v['related_products']);   // related product ids
            $out=array_merge($ids,$out);
            $out[]=$pid;                        // list of all the old product Ids to translate includes the parent ids as well
            $out2[$pid]=$ids;                     // mapping of parent Id to related products
                        
            $newParentProductId = $productIdMapping[$pid];
            $alreadyKnownIds[$pid] = (int) $newParentProductId; // create mapping of VM1 to VM2 parent Ids to be processed
            $i++;                              // cont of items to be processed this run
         }
         // GET SkuS for Products from VM1 table
         $skus=array();
         $q="select product_id,product_sku from #__vm_product where product_id in (".implode(',',$out).") ";
         $this->_db->setQuery($q );
         $product_skus = $this->_db->loadAssocList();
         if (empty($product_skus)) {
            vmError("Port Related products: The following SKUs were not found ".implode(',',$out) );
            break;
         }
         // create array of id=>sku
         foreach ($product_skus as $v) {
            $skus[$v['product_id']]=$v['product_sku'];
         }
         
         // Create mapping of Parent SKU to array of related product SKUs; tacked onto the end of "out" array
         foreach($out2 as $k=>$v){
            $tmp=array();
            foreach($v as $vv){
               if(isset($skus[$vv]))
                  $tmp[]=$skus[$vv];
            }
            $out[$skus[$k]]=$tmp;
         }

         // GET virtuemart_product_id for those SKUs found in VM2
         $q="select virtuemart_product_id,product_sku from #__virtuemart_products where product_sku in ('".implode("','",$skus)."') ";
         $this->_db->setQuery($q);
         $out3=array();
         $products = $this->_db->loadAssocList();
         if (empty($products)) {
            vmError("Port Related products: Some of those SKUs were not found ".implode(',',$skus) );
            break;
         }
         // VM2 product mapping sku=>id
         foreach ($products as $v) {
            $out3[$v['product_sku']]=$v["virtuemart_product_id"];
         }
         
         $now=date('Y-m-d H:i:s',time());
         $sql='';
         foreach($out as $k => $v){  // foreach parent sku $k as array of related skus $v
            foreach($v as $vv){     // foreach related skus $v as related sku $vv 
               if(isset($out3[$k]) and isset($out3[$vv])) // if both the parent sku $k and the related sku $vv exists in VM2 products
                  $sql.=",({$out3[$k]},1,{$out3[$vv]},'".$now."')"; // build sql mapping foreach parent sku and related product sku. VM2 requires one entry per related sku
            }
         }
         if (empty($sql)) {
            vmError("Port Related products: Error while inserting new related products " );
            break;
         }
         
         // Insert multiple rows of related product skus for each parent sku
         $q="INSERT INTO #__virtuemart_product_customfields (virtuemart_product_id,virtuemart_custom_id,custom_value,modified_on) values ".substr($sql,1);
         $this->_db->setQuery($q) ;
         $this->_db->query();
         
         if((microtime(true)-$this->starttime) >= ($this->maxScriptTime)){
            vmdebug('Related products import breaked, you may rise the execution time, this is not an error, just a hint');
            $continue = false;
            break;
         }
      }
      if($out and count($out)==0){
         vmdebug ('no related products found');
         return;
      } else {
         vmdebug ('FOUND Related products ',count($out) );
      }

      $limitStartToStore = ', relatedproducts_start = "'.($doneStart+$i).'" ';   // MJB changed from relatedproducts to relatedproducts_start
      $this->storeMigrationProgress('relatedproducts',$alreadyKnownIds,$limitStartToStore);
      vmInfo('Migration: '.$i.' Related products processed processed this run, already processed '.$alreadyProcessed.' of '.($doneStart+$i));
      if(($alreadyProcessed+$i) == ($doneStart+$i)) $completionStatus = true;
      return $completionStatus;
   }