Magento: Rebuilding Flat Catalog Programmatically - magento

I am using a cron to import inventory changes nightly. When I try to change a product's information (price, etc) I get the following error:
Column not found: 1054 Unknown column 'e.display_price_group_0' in 'field list'
I can fix this by clicking "Rebuild Flat Catalog Product" in the Cache Management panel. I setup a cron to do this programmatically using the following code:
Mage :: getResourceModel( 'catalog/product_flat_indexer' ) -> rebuild();
I don't get any errors when I run the script, but the "Column not found" error persists.
Does anyone know how I can rebuild the flat catalog other than through the admin interface?

Previous I said to do this:
Mage::getModel('catalog/product_flat_indexer')->rebuild();
Note: it's getModel and NOT getResourceModel.
This is not true. Either works. However, I have found through a rather painful trial and error process that the flat product tables don't rebuild correctly unless I also rebuild the entire catalog. This is how I finally solved my problem:
Mage::getSingleton('catalog/index')->rebuild();
Mage::getResourceModel('catalog/product_flat_indexer')->rebuild();
Mage::getSingleton('catalog/url')->refreshRewrites();
Mage::getModel('catalog/product_image')->clearCache();
Mage::getSingleton('catalogsearch/fulltext')->rebuildIndex();
Mage::getSingleton('cataloginventory/stock_status')->rebuild();
$flag = Mage::getModel('catalogindex/catalog_index_flag')->loadSelf();
if ($flag->getState() == Mage_CatalogIndex_Model_Catalog_Index_Flag::STATE_RUNNING) {
$kill = Mage::getModel('catalogindex/catalog_index_kill_flag')->loadSelf();
$kill->setFlagData($flag->getFlagData())->save();
}
$flag->setState(Mage_CatalogIndex_Model_Catalog_Index_Flag::STATE_QUEUED)->save();
Mage::getSingleton('catalogindex/indexer')->plainReindex();
Basically, just rebuild everything. Don't worry about optimization. Like someone once said, "Premature optimization is the root of all evil."

I've found that there's a more efficient way to update only specific product attributes.
Mage::getModel('catalog/product_flat_indexer')->updateAttribute($attributeCode, null, $productIds);
Or you can update the entire product in the flat table:
Mage::getModel('catalog/product_flat_indexer')->updateProduct($productIds, null);
Where $productIds is an array of the product entity ids to update. These functions will also update other indexed data related to the products you update. Hope this helps.

See this script.
I personally had some trouble with it, but others seem to be quite happy with it.
If you don't want the whole thing, you can easily pull out the part that rebuilds the flat catalog product and point a cron job at it.

* Rebuild Catalog Index
Mage::getSingleton('catalog/index')->rebuild();
* Rebuild Flat Catalog Product
Mage::getResourceModel('catalog/product_flat_indexer')->rebuild();
* Inventory Stock
Mage::getSingleton('cataloginventory/stock_status')->rebuild();

public function rebuildIndexes(){
$processes = array();
$collection = Mage::getSingleton('index/indexer')->getProcessesCollection();
foreach ($collection as $process) {
try {
$process->reindexEverything();
$this->_message($process->getIndexer()->getName() . " index was rebuilt successfully");
} catch (Mage_Core_Exception $e) {
$this->_throwException($e->getMessage());
} catch (Exception $e) {
$this->_throwException($process->getIndexer()->getName() . " index process unknown error:\n" . $e);
}
}
}
people why dont you research a little bit before post somethig that doesn't work, open the shell/indexer.php, inside you will find all the answers related to indexing.

I also can't get it to work correctly.
When I rebuild the Rebuild Flat Catalog Product from the Admin it works fine and I don't get the SQL column error, but when I do it programmatically it doesn't work via:
Mage::getResourceModel('catalog/product_flat_indexer')->rebuild();

I have just written this code, based on the Shell reindex script.
I have tested it in Magento 1.5.1 using a web script (with long max_execution_time).
if ( !empty($_SERVER['HTTP_HOST']) )
{
header('Content-Type: text/plain');
}
$oIndexer = Mage::getSingleton('index/indexer');
/* #var $oIndexer Mage_Index_Model_Indexer */
$oProcessCollection = $oIndexer->getProcessesCollection();
/* #var $oProcessCollection Mage_Index_Model_Mysql4_Process_Collection */
foreach ( $oProcessCollection as $oProcess )
{
/* #var $oProcess Mage_Index_Model_Process */
echo 'Rebuilding ' . $oProcess->getIndexer()->getName() . ' index...';
outputFlush();
$oProcess->reindexEverything();
}
echo 'Done.';
outputFlush()
function outputFlush()
{
while ( ob_get_length() )
{
ob_end_flush();
}
if ( !empty($_SERVER['HTTP_HOST']) )
{
echo str_repeat(' ',4096);
}
echo "\n";
flush();
}

Related

Update Magento product meta description for different store

i am trying to update meta description for different store but magento keep showing error :
Product with URL key already exist.
what i am trying to do i have two stores on one magento. so we want seprate meta information for different stores. so when i try to update meta info for specific store then shows me error. any one facing same problem :
$pid = Mage::getModel('catalog/product')->getResource()->getIdBySku($sku);
$product = Mage::getModel('catalog/product')->load($pid);
if($product)
{
$product->setStoreId(6);
$product->setmeta_keyword($newKeyWord);
$product->setmeta_description($newmeta_metadescription);
try {
$product->save();
echo 'Product Updated successfully --- '.$sku."\n";
}
catch (Exception $ex) {
echo $ex->getMessage();
}
There are lot of issue in your code:
Setter function is wrong:
$product->setMetaKeyword($newKeyWord);
$product->setMetaDescription($newmeta_metadescription);
Best way to do this:
$product = Mage::getModel('catalog/product')->load($pid);
Please use below format,which more faster then your code
$product->addAttributeUpdate($Attributecode, $value, $storeId)
then
$product->addAttributeUpdate('meta_keyword', $newKeyWord, $storeId=6);
$product->addAttributeUpdate('meta_description', $newmeta_metadescription, $storeId=6);
No need use save() function in this case.

Magento - Can't edit categories anymore after deleting and adding categories programmatically

I wanted to clear all test categories to start with a clean database. I removed all categories with the following piece of code
Mage::app()->setCurrentStore(Mage::getModel('core/store')>load(Mage_Core_Model_App::ADMIN_STORE_ID));
// 1 is default store_id
$rootCategoryId = Mage::app()->getStore(1)->getRootCategoryId();
$resource = Mage::getSingleton('core/resource');
$db_read = $resource->getConnection('core_read');
$categories = $db_read->fetchCol("SELECT entity_id FROM " . $resource->getTableName("catalog_category_entity") . " WHERE entity_id>1 ORDER BY entity_id DESC");
foreach ($categories as $category_id) {
if ($category_id == $rootCategoryId)
continue;
try {
Mage::getModel("catalog/category")->load($category_id)->delete();
} catch (Exception $e) {
return $e->getMessage() . "\n";
}
}
It removed everything except the root category. Once this was done, I added the categories through the API, that went pretty well. Now here comes the problem: When i tried to edit one of the categories in the Magento Admin, he only shows the form to create a new root category.
What could be the cause of this? I found another issue on SO which looked similar, but the solution mentioned there (disabling mod_security) didn't do the job for me. So i guess there is something else going on. It's very well possible that i haven't removed the categories in a good way.
I am pretty sure it can't be the API because i have added categories before that way, and i was able to edit them in the admin afterwards.
Can't wait for your help.

Reindex Product after Cronjob in Magento - Configurable Products showing Out Of Stock

I have written a Extension that updates some custom shipping value Attributes on save for Magento 1.7. All working fine, when saving the product all is updated as it should. However I also have the need for a cronjob to update them each night in case I need to change shipping costs throught the board.
Is all working, and is updating the attribute values correctly, however on the frontend all configurable products are showing as Out of Stock, Simple Products are fine.
If I go to the admin, just click in the master product and save it without doing anything it shows back as In Stock on the frontend. Also if I go to Indexes and reindex Product Attributes it again shows as In stock on the frontend. I presume then that my cronjob needs to update the indexer on saving each product.
Looking around I used the following code, however it doesn't seem to update the product, and wondered if anyone could help. I have tried different variations around the Mage_Catalog_Model_Product and TYPE_SAVE but can't find what I am supposed to use!
$updateProduct = Mage::getModel('catalog/product')->load($_product->getId());
$updateProduct->setShippingLabel($shippData['delivery_type']);
$updateProduct->setShippingPrice($shippData['price']);
$updateProduct->setShippingNote($shippData['notes']);
try {
$updateProduct->save();
$updateProduct->setForceReindexRequired(true);
Mage::getSingleton('index/indexer')->processEntityAction(
$updateProduct,
Mage_Catalog_Model_Product::ENTITY,
Mage_Index_Model_Event::TYPE_SAVE
);
echo $updateProduct->getId()." Successfully Updated \n";
} catch(Exception $e){
echo $e->getMessage().$updateProduct->getId()."\n";
}
Update 17/5/2013 20:28
Have been playing with the code and this amendment seems to work, if it is totally useless and a stupid way of doing it please let me know
$updateProduct = Mage::getModel('catalog/product')->load($_product->getId());
$updateProduct->setShippingLabel($shippData['delivery_type']);
$updateProduct->setShippingPrice($shippData['price']);
$updateProduct->setShippingNote($shippData['notes']);
try {
$updateProduct->save();
$stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($_product->getId());
$stockItem->setForceReindexRequired(true);
Mage::getSingleton('index/indexer')->processEntityAction(
$stockItem,
Mage_CatalogInventory_Model_Stock_Item::ENTITY,
Mage_Index_Model_Event::TYPE_SAVE
);
echo $updateProduct->getId()." Successfully Updated \n";
} catch(Exception $e){
echo $e->getMessage().$updateProduct->getId()."\n";
}
After the execution of your cron job you can update your indexer:
$indexingProcesses = Mage::getSingleton('index/indexer')->getProcessesCollection();
foreach ($indexingProcesses as $process) {
$process->reindexEverything();
}
After the execution of your cron job you can update your indexer:
$indexingProcesses = Mage::getSingleton('index/indexer')->getProcessesCollection();
foreach ($indexingProcesses as $process) {
$process->reindexEverything();
}
hello dhawal if i have to run a cron job for to re-index product on site, I have to placed the code that you have mention with this code
$updateProduct = Mage::getModel('catalog/product')->load($_product->getId());
$updateProduct->`setShippingLabel`($shippData['delivery_type']);
$updateProduct->setShippingPrice($shippData['price']);
$updateProduct->setShippingNote($shippData['notes']);
try {
$updateProduct->save();
$stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($_product->getId());
$stockItem->setForceReindexRequired(true);
Mage::getSingleton('index/indexer')->processEntityAction(
$stockItem,
Mage_CatalogInventory_Model_Stock_Item::ENTITY,
Mage_Index_Model_Event::TYPE_SAVE
);
echo $updateProduct->getId()." Successfully Updated \n";
} catch(Exception $e){
echo $e->getMessage().$updateProduct->getId()."\n";
}
at same page ??

Magento mass-assign products to category

As the title says,i need to mass-assign products to a category and from the admin i can only edit one product at a time; i dont know why it just doesnt work to mass add them from the "category products" tab in the category page.
Thats why i need another method that's fast,like using phpMyAdmin or something alike.
Any help?
Thanks in advance!
I created a simple script to do this outside of Magento. Be sure to test this first on a single product and make sure it looks as you'd expect.
// Load Magento
require_once 'path/to/app/Mage.php';
Mage::app();
// $productIds is an array of the products you want to modify.
// Create it however you want, I did it like this...
$productsIds = Mage::getModel('catalog/product')->getCollection()
->addAttributeToFilter('sku', array('like' => 'something'))
->getAllIds();
// Array of category_ids to add.
$newCategories = array(20);
foreach ($productIds as $id) {
$product = Mage::getModel('catalog/product')->load($id);
$product->setCategoryIds(
array_merge($product->getCategoryIds(), $newCategories)
);
$product->save();
}
If you wish to overwrite a product's existing categories, change array_merge(...) to just $newCategories.
I would shy away from tackling this problem from the database side of things. If you do go that direction make sure and take lots of backups and do it during low usage.
The following thread on the Magento forum identifies the very same problem. One poster recommends a raw sql approach with example. Again, I would be careful - make sure you take backups.
The answer I like best from the thread (posted by Magento MVP):
Go into the category you don’t want them in, find the product list.
Click the check boxes on the products you want to remove and select
delete from the little dropdown.
Now go into the category where you
do want them, go to the product list. Select the NO dropdown so it
shows items not in the category. You might have to do a selective
search to limit stuff and do it in a couple iterations. Click the
check boxes and tell it to add stuff.
You may as well do this using the magento API
This is the script I use for mass adding products. sku.txt contains one sku per line.
<?php
$wsdlUrl = "magento-root/index.php/api/soap/?wsdl";
$proxy = new SoapClient($wsdlUrl);
$sessionId = $proxy->login('apiuser', 'apipasswd');
$listOfDiscountedSKUFile = "sku.txt";
function readinFile($filePath)
{
$fp = fopen($filePath,'r') or exit("Unable to open file!");
$dataItems = array();
while(!feof($fp))
{
$dataItems[] = trim(fgets($fp));
}
fclose($fp);
var_dump($dataItems);
return $dataItems;
}
function addToCategory($sku,$categoryId)
{
global $proxy,$sessionId;
$proxy->call($sessionId, 'category.assignProduct', array($categoryId, $sku));
}
function IsNullOrEmptyString($question){
return (!isset($question) || trim($question)==='');
}
$categoryId = 82;//e.g.
$listOfSKU = readinFile($listOfDiscountedSKUFile);
foreach($listOfSKU as $sku)
{
addToCategory($sku,$category);
}
?>
I managed to resolve the problem with the following code :
$write = Mage::getSingleton('core/resource')->getConnection('core_write');
$x = 1171;
$y = 2000;
$categoryID = 4;
$productPosition = 0;
while($x <= $y) {
$write->query("REPLACE INTO `catalog_category_product` (`category_id`, `product_id`, `position`) VALUES ($categoryID, $x++, $productPosition)");
}
echo "The job is done";
?>
I hope the code is clear for everyone,if it's not,reply and i'll try to explain it.
#nachito : here it is.

Magento - re-importing of products

I have a script that pulls in data from a 3rd party file. My import simply parses and inserts rows, which is working fine.
The problem comes with images.
When the import script runs, it first deletes all the current items and then the import begins, inserting all products and images into the gallery.
On the first import, everything is fine, the images go in and I see them on the frontend no problem. The problem comes with everytime I then re-import these products, it doesn't seem to delete all images, as when the products re-import, I see, for example the 4 images correct, then then loads of blank rows, like images should be there, but can't be found.
I don't want to see these blank lines, but I'm not sure why they are there.
Could it be because the images for the product are already in the catalogue?
I am really unsure what and why this is doing what it is.
Thanks
EDIT:
My code is:
require_once('app/Mage.php');
$app = Mage::app('default');
$product = Mage::getSingleton('catalog/product');
$txt_file = file_get_contents('test.txt');
$rows = explode("\n", $txt_file);
array_shift($rows);
foreach($rows as $row => $data)
{
//get row data
$row_data = explode('^', $data);
$info[$row]['uniqueid'] = $row_data[0];
$info[$row]['client'] = $row_data[1];
$info[$row]['make'] = $row_data[2];
$info[$row]['model'] = $row_data[3];
$info[$row]['adtext'] = $row_data[4];
//display images
$row_images = explode(',', $info[$row]['picturereference']);
foreach($row_images as $row_image)
{
$product->addImageToMediaGallery(Mage::getBaseDir('media') . DS . 'import/' . $row_image, array('image', 'small_image','thumbnail'), false, false);
}
$product->setStoreId(Mage::app()->getStore(true)->getWebsite()->getId());
$product->setWebsiteIds(array(Mage::app()->getStore(true)->getWebsite()->getId()));
$product->setId($info[$row]['id']);
$product->setSku(strtolower($info[$row]['make']).'-'.strtolower($info[$row]['model']));
$product->setName($info[$row]['make']);
$product->setDescription($info[$row]['adtext']);
try {
$product->save();
echo "Saved";
}
catch (Exception $ex) {
echo "<pre>".$ex."</pre>";
}
}
Is this because the addImageToMediaGallery is called on each iteration and adding all images to each product?
Thanks
Ok so I figured out my problem
Inside the foreach I moved the call to the getSingleton and I added the the following: $product = Mage::getModel('catalog/product');
I then, after each iteration, unset the following:
unset($product);
unset($info);
unset($stockData);
unset($row_images);
This seemed to fix my script and now imports each products images into the proper product rather than importing other and adding random blank entries
Thanks all
A few files you'll want to examine to break down addImageToMediaGallery and determine what exactly its doing.
app/code/core/Mage/Catalog/Model/Product.php - Contains the method your using, breaking it down more you'll find...
app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Media.php - Contains more of the "bits and pieces" of addImageToMediaGallery like addImage etc.
Some possibilities to try would be either
A) determine if the files already exist and are attached to the product and ignoring them upon a second round of import. Perhaps looking for different file stamp. getMediaGalleryImages within Product.php.
B) clear the media files associated with the products BEFORE importing again? clearMediaAttribute and removeImage within Media.php.
I would also try the $move option set to true within your addImageToMediaGallery call as well.
/**
* Add image to media gallery
*
* #param string $file file path of image in file system
* #param string|array $mediaAttribute code of attribute with type 'media_image',
* leave blank if image should be only in gallery
* #param boolean $move if true, it will move source file
* #param boolean $exclude mark image as disabled in product page view
*/
public function addImageToMediaGallery($file, $mediaAttribute=null, $move=false, $exclude=true)
{
Another option would be to try to specify null for the second param, if you note the PHPDoc comments, leaving it blank will only be for the gallery which sounds like what you are wanting...
foreach($row_images as $row_image)
{
$product->addImageToMediaGallery(Mage::getBaseDir('media') . DS . 'import/' . $row_image, null, false, false);
}
Let me know if any of these help.
Give something like this a try:
//Check if gallery attribute exists then remove all images if it exists
//Get products gallery attribute
$attributes = $product->getTypeInstance()->getSetAttributes();
if (isset($attributes['media_gallery'])) {
$gallery = $attributes['media_gallery'];
//Get the images
$galleryData = $product->getMediaGallery();
foreach($galleryData['images'] as $image){
//If image exists
if ($gallery->getBackend()->getImage($product, $image['file'])) {
$gallery->getBackend()->removeImage($product, $image['file']);
}
}
}
This blog posting may also help, as its where I got the code snippet form also:
http://www.sharpdotinc.com/mdost/2010/03/02/magento-import-multiple-images-or-remove-images-durring-batch-import/

Resources