Question

I am writing a module that will extract objects, such as websites, group, categories, and products from one Magento instance, serialize their properties and write everything to a text file, to be de-serialized and on another server. These properties are then used to programatically re-create these objects on the new server. The idea is that we will be able to extract all of the objects that make up a Magento web store, and move them to another server. (No, we don't want to move the whole instance to another server. We just want to be able to move a store and it's related objects.)

Obviously, since we are creating categories on a new server, their entity_id's will change. I have worked that part out, as well as making sure that sub-categories have the proper parent id. This project has been mostly straightforward until I tried to recreate category and sub-category object. I am having all manner of problems. The new category objects save t the database. However, sometimes they don't show up in the category tree, sometimes their parent_id's change to 0, sometimes the whole category tree goes away. I been working on this for about a week. I have read that you have to set the 'path' property to the path of the parent before saving. I have read that you have to use the 'move' method to set the category to be a child of it's parent. There is lots of theory, but nobody seems to have an answer.

So my question: How do you create category and sub-category records that actually work, are properly linked to their parent categories, show up in the category tree, and don't beak things?? I have the following attributes from the original source category stored in an array called $aryData().

[entity_id] => 127       //This usually changes on new server
[parent_id] => 1         //Lookup NEW entity_id of parent and use it
[path] =>                //Not sure how to properly set this. Tried a few things
[position] => 8          //Leave this alone, hope for the best
[children_count] => 0    //Have to zero this out when you create new category object
[name] => Best Test      
[url_key] => best-test
[is_active] => 1
[include_in_menu] => 1

And here is generally what I am doing, in a simplified fashion:

$objNewCat = Mage::getModel('catalog/category'); //Create new object to populate

$parent_id = getNewParent($data['name'], $data['url-key']; //Get new parent id by name and URL key. (This works)
$objParentCat = Mage::getModel('catalog/category')->load($parent_id);

$aryData(['parent_id']) = $parent_id;  //Update parent ID in data array
$aryData(['children_count']) = 0;  //Must set to 0. Updated as children are added


$objNewCat->setData($data); //Set all data parameters from our save array
$objNewCat->setPath($objParentCat->getPath()); //Is this correct? Read you have to do this  
$objNewCat->save();  //Save object to populate entity_id field

//--- Now assign object to be child of the parent.
$objCat = Mage::getModel('catalog/category')->load($newCat->getId()); //reloading to set 'current category'
Mage::unregister('category');
Mage::unregister('current_category');
Mage::register('category', $objCat);
Mage::register('current_category', $objCat);
$objCat->move($parent_id);
$objCat->save();

Yes, some of this code is kind of rough. It is a simplified, and I have been trying many things to get it to work. It's very frustrating. Help me Obi-Wan Knobi. Your my only hope.

Était-ce utile?

La solution

Don!, I have spent many hours researching and struggling with this problem. There are lots of references to the problem on the Internet, but nobody has come up with a reliable solution, until now.

Magento uses an object model to handle all database I/O. This is supposed to insulate you from having to do tedious work, such as updating related fields. For example, if you add a new child category, the Magento object model will automatically update the 'children_count' in the parent record. The Magento object model basically lets you concern yourself with the data that you wish to write, while taking care of the housekeeping for you. However, when the object model does not do what it is supposed to, things really suck. That is the situation with the 'parent_id' field, and the 'path' field. Magento just does not update them the way it should.

The solution is to make the changes that you need to via the object model, save the object, and then FORCE the correct values for 'parent_id' and 'path' via a database query. I have found that the 'parent_id', and 'path' fields seem to be saved properly when the record is first created, but then they get messed up if the object is updated. This code presumes you are updating an existing category object.

$catId = 127; //ID of category record you wish to edit
$objCat = Mage::getModel('catalog/category')->load($catId); 
if(!is_object($objCat)) {
    throw new Exception("ERROR: Could not find category id: " .$catId);
}

//--- Make sure we have proper parent_id and path values
$parentId = $objCatParent->getId();
$path =  $objCatParent->getPath() ."/" .$objCat->getId();

$objCatParent = $objCat->getParentCategory();  //We will need this parent category
$data = array('name'     => 'My Cool Category',
              'parent_id => $parentId,
              'path'     => $path;
$objCat->addData($data);  //Use 'addData' rather than 'setData'
$objCat->save(); //Save to update record and break parent_id, path fields

//--- Now we get a connection to the database, build a query, and update record
$resource = Mage::getSingleton('core/resource');
$objCon = $resource->getConnection('core_write');
$tableName = "catalog_category_entity";  //Well, it's the name of the Category table
$query = "UPDATE  {$table} SET parent_id = {$parentId}, path = '{$path}' "
       . " WHERE entity_id = " .$catId;
$objCon->query($query); //Force proper values directly into table

Yes, yes, yes, you have to be very careful when you bypass the object model and make changes directly to database records. However, the Magento object model just does not seem to work here, so we have little choice. And since we are saving the record via the object's 'save()' method before it is doing whatever 'behind the scenes' work it is supposed to do. We are just correcting an error.

I hope this helps you out. This was a sticky problem. I wish there was a better solution, but when they fix the API I will stop playing with the database. And to those of you who would say that answering your own question in the third person is akward, I say "HA!"

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top