I am new in Magento , I created my code to remove the special offer
and to add new price for SKU.
I could remove the special offer but I could not add the new price for SKUs. How can this be done?
My code is as follows:
$productIds[][] = array('DF-12''200','DF-98''300','DF-87''400');
$products = Mage::getResourceModel('catalog/product_collection')
->addAttributeToSelect('*') // <- careful with this
->addAttributeToFilter(
'sku', array('in' => $productIds[0])
)
->load();
foreach ($products as $_product){
$product = Mage::getModel('catalog/product')->load($_product->getId());
$product->setprice($productIds[1])
$product->setOffertext('');
$product->setSpecialFromDate('');
$product->setSpecialToDate('');
$product->setSpecialPrice('');
$product->save();
}
I'm not quite sure how you're forming the array containing your products' SKU and prices, it seems off to me. But since I can only speculate on that part with the given information, an example of how I'd programmatically update smaller product collections might be of more help. This snippet works for me in a backend model:
// array('sku' => price )
$productPricesBySku = array('DF-1234' => 180.00, 'DF-1235' => 99.95);
$collection = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('price') // optional
->addAttributeToSelect('special_from_date') // optional
->addAttributeToSelect('special_to_date') // optional
->addAttributeToFilter('sku', array('in'=>array_keys($productPricesBySku)));
foreach($collection as $_product){
if(!($price=$productPricesBySku[$_product->getSku()])) continue;
// no need to load the product again
// just set the data directly on our collection-item and save it
$_product->setPrice((float)$price)
->setSpecialFromDate('')
->setSpecialToDate('')
->setSpecialPrice('')
->save();
}
(To prevent memory exhaustion on large collections you should use the Mage_Core_Model_Resource_Iterator rather than foreach-loops. You can read up on that here for instance.)
Related
I've written a method that I'm calling from my list.phtml file.
The logic should simply output the number of products in the currently viewed category and the highest and lowest prices. Pretty straight forward really.
For reasons beyond my understanding it's always returning that the current category contains no products, and I can't figure out why but I presume it's because I'm not fetching the product collection correctly.
Here's the code for my method.
public function writeCatInfo(){
if(Mage::registry('current_category')){
$_productCollection=$this->getLoadedProductCollection;
$catname = Mage::registry('current_category')->getName();
$priceArray = array();
foreach ($_productCollection as $_product){
$priceArray[] = $_product->getSpecialPrice();
}
$numItems = count($_productCollection);
$highPrice = number_format(max($priceArray),2);
$lowPrice = number_format(min($priceArray),2);
$infostring = ucwords($catname)." contains ".$numItems." products with prices ranging from £".$lowPrice." to £".$highPrice;
echo $infostring;
}
}
this will help you to get current category product collection.
$category_id = Mage::getModel('catalog/layer')->getCurrentCategory()->getId();
$category = Mage::getModel('catalog/category')->load($category_id);
$products = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*')
->addCategoryFilter($category)
->setOrder('price', 'ASC')
->load();
Turns out my helper was extending the wrong class - Mage_Core_Helper_Abtract instead of Mage_Catalog_Block_Product_List. Changing this fixed the problem.
Please see the below code to get product collection by Category id
$products = Mage::getModel('catalog/category')->load($category_id)
->getProductCollection()
->addAttributeToSelect('*') // add all attributes - optional
->addAttributeToFilter('status', 1) // enabled
->addAttributeToFilter('visibility', 4) //visibility in catalog,search
->setOrder('price', 'ASC'); //sets the order by price
I am a newbie in magento. Basically, I want to assign multiple products to multiple categories. I have followed this post and I have done the following code which is working fine:
$collection = Mage::getModel('catalog/product')->getCollection();//my coustom collection
$categorys_ids = array(1,2,3,4,5);//Array of ids etc
if ($categorys_ids != NULL && $collection->getData()!= NULL)
{
foreach ($collection as $product)
{
$categories_pd = $product->getCategoryIds();
$product->setCategoryIds(array_merge($product->getCategoryIds(),array($categorys_ids)));
$product->save();
}
}
Now, the main issue is that when I assign set category id for the products it takes a lot of time. I have 200 products and this takes up to two minutes or so, which is a lot of time.
I was wondering if there is a way that I can assign categories to a products array instead of assigning products to categories or something that can be optimized and take less time.
Here is how you can assign multiple products to a category and merge with the existing products.
The example is for one category but you can turn it into a loop to make it work for more.
$categoryId = 6;
$category = Mage::getModel('catalog/category')->setStoreId(Mage_Core_Model_App::ADMIN_STORE_ID)->load($categoryId);
//get the current products
$products = $category->getProductsPosition();
//now attach the other products.
$newProductIds = array(1,2,3,4,5);
foreach ($newProductIds as $id){
$products[$id] = 1;//you can put any other position number instead of 1.
}
//attach all the products to the category
$category->setPostedProducts($products);
//save the category.
$category->save();
If you want an even faster way of doing it you can do direct inserts in the table catalog_category_product.
Just make sure you reindex when you are done.
Here is how I did it, using some fast array management features from newer PHP versions:
<?php
require_once '../../app/Mage.php';
Mage::app();
Mage::app()->setCurrentStore(Mage::getModel('core/store')->load(Mage_Core_Model_App::ADMIN_STORE_ID));
$storeCode = Mage::app()->getStore()->getStoreId();
function addProductsToCategoryId($mergeProductIds, $categoryId, $storeCode) {
// load the $category by $categoryId
$category = Mage::getModel('catalog/category')->setStoreId($storeCode)->load($categoryId);
// build a flipped array of two merged arrays (1) array keys from flipped $mergeProductIds, (2) array keys from product_id keyed array in $category
$categoryProductIds = array_flip(array_merge(array_keys(array_flip($mergeProductIds)),array_keys($category->getProductsPosition())));
// combine array_keys from resulting merge with a matched index array filled with '0'
// THIS resets position of product within category, change this logic if desired
$categoryProductIds = array_combine(array_keys($categoryProductIds), array_fill(0, count($categoryProductIds), '0'));
$category->setPostedProducts($categoryProductIds);
$category->save();
// optional
// return $categoryProductIds;
}
// optional array of category IDs to test against for nin (not in) or in a find_in_set array test
// in the optional example line below, nin (not in) is used
$categoryIds = array(5,8,9,10,11,12,45,46);
$collectionIds = Mage::getModel('catalog/product')->getCollection()
->setStoreId($storeCode)
// optional inclusion of join for category_id
->joinField('category_id', 'catalog/category_product', 'category_id', 'product_id = entity_id', null, 'left')
// optional logic to only gather ids that are, or are not in a given categoryIds array, nin (not in) is shown in example
// ->addAttributeToFilter('category_id', array('nin' => array('finset' => $categoryIds)))
// optional line to test whether product is associated to ANY category
->addAttributeToFilter('category_id', array('null' => true))
// example qualifiers to affect gathered IDs
->addAttributeToFilter('sku', array('like' => 'M-H%'))
->addAttributeToFilter('sku', array('nlike' => '%E'))
->addAttributeToFilter('sku', array('nlike' => '%E#'))
->addAttributeToFilter('sku', array('nlike' => '%Euro'))
->addAttributeToFilter('sku', array('nlike' => '%Euro#'))
->getAllIds()
;
// if using a return value, you can set the results of this to a variable
// to perform further operations against the resulting data
addProductsToCategoryId($collectionIds, 8, $storeCode);
Please note, by default my method DOES NOT preserve any position for products within categories you had set. IT WILL set all positions back to a default of '0'.
Works beautifully. Requires a reindex afterward, fast mass adding. The code is a little complex, so explaining the code in direct context comments made more sense to me in this case.
I included a lot of optional extras in here, but they are all flagged as such and fully explained.
The following code worked for me:
include '../../app/Mage.php';
Mage::app('admin');
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
$product_id = Mage::getModel("catalog/product")->getIdBySku($sku);
$product = Mage::getModel('catalog/product')->load($product_id);
$product->setCategoryIds($category_id);
$product->save();
I am trying to get product price and description for different stores in magento, I can achieve this as follows:-
foreach ($productObj->getStoreIds() as $_storeId) {
$tempStoreObj = new Mage_Core_Model_Store();
$tempStoreObj->load($_storeId);
$tempProductObj = new Mage_Catalog_Model_Product();
$tempProductObj->setStoreId($_storeId);
$tempProductObj->load($productObj->getId());
$tempPriceArray[] = array(
'websiteId' => $tempStoreObj->getWebsiteId(),
'price' => $tempProductObj->getPrice(),
'baseCurrency' => $tempStoreObj->getBaseCurrencyCode(),
);
$tempDescArray[]=array(
'descprition' => $tempProductObj->getData('description'),
'shortDescription' => $tempProductObj->getData('short_description'),
);
}
Now In the above code there, I have first fetched stores for particular product, then loaded the stores then again created an object for product and loaded w.r.t product id and store id , in this way i have achieved the required task.
Now my problems start here when there are many products and many stores performance issue comes in and the loading process makes this slow.
Is there any other way achieve the same?
One thing you can do to speed things up when you want to get this from several products is to use the product collection, so there is only one DB query which fetches the information per store for all products.
This would then look something like this:
$storeId = 1; // Current Store you want to look at
$productIds = array(10,15,26); // Enter your Ids
$product = Mage::getModel('catalog/product');
$products = $product->getCollection()
->addStoreFilter($storeId)
->addAttributeToFilter('entity_id', array('in' => $productIds))
->addAttributeToSelect('price')
->addAttributeToSelect('description');
And then loop over the products:
$currPrices = array();
foreach ($products as $prod) {
$currPrices[$prod->getId()] = $prod->getPrice();
}
To make it clear for several stores:
$currPrices = array();
$currDescriptions = array();
foreach ($productObj->getStoreIds() as $_storeId) {
$productIds = array(10,15,26); // Enter your Ids
$product = Mage::getModel('catalog/product');
$products = $product->getCollection()
->addStoreFilter($_storeId)
->addAttributeToFilter('entity_id', array('in' => $productIds))
->addAttributeToSelect('price')
->addAttributeToSelect('description');
foreach ($products as $prod) {
$currPrices[$_storeId][$prod->getId()] = $prod->getPrice();
$currDescriptions[$_storeId][$prod->getId()] = $prod->getDescription();
}
}
The fastest way is to make direct queries (of course, wrapped to Zend_Db_Select) to store and product tables.
base_currency can be got from core_config_data
product prices can be found in catalog_category_product_index
description in catalog_product_entity_text
Look to
Mage_Reports_Model_Resource_Product_Index_Abstract
Mage_Reports_Model_Resource_Report_Product_Viewed_Collection
Mage_Reports_Model_Resource_Report_Product_Viewed
Mage_Reports_Model_Resource_Quote_Collection
Magento uses Zend_Db_Select, even fetch methods of the adapter, not collections, in case of increasing speed of queries.
How do i list products with a specific attribute? for example if Size is an attribute in my default attribute set. Then how do i list all the products which has Size = 22?
I am searching google for the answer, will post as answer if i find any.
Thanks,
Balan
I just did this not too long ago
To get the store id:
$storeId = Mage::app()->getStore()->getId();
To get all the products filtering out everything but Size
$_productCollection = Mage::getResourceModel('catalog/product_collection')
->addAttributeToSelect('*')
->addAttributeToFilter("Size", "1");
Then simply in a foreach loop add the following(this will loop through the array $_productCollection)
foreach($_productCollection as $_product)
{
$attr = Mage::getResourceModel('catalog/product')->getAttributeRawValue($_product->getId(), 'Size', $storeId);
if($attr == '22')
{
//Echo names of the products
echo $_product->getName();
}
}
This way you can still do a lot with the product list without having a strict listing of products. So you can still add names, prices, and more attributes if you wish. Hope this helped!
I don't think the catalog product is made for this. The best would be to move the attribute Size in the static table (catalog_product_entity). Then you will be able to search by size with keeping the catalog fast. After if you want to do this on different attribute, I guess you can give it as parameter in the collection. But I am not sure about it.
For example:
$storeId = Mage::app()->getStore()->getId();
$products = Mage::getResourceModel('catalog/product_collection');
$products->addAttributeToFilter(array(
array('attribute' => 'attribute_code', 'eq' => 22)))
->addAttributeToSelect('*')
->setStoreId($storeId)
->addStoreFilter($storeId)
->setOrder('name', 'desc');
Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($products);
Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($products);
$this->setProductCollection($products);
for me this works fine.
I am using Magento 1.4.0.1.
I have over 21000 simple products, each entered into a single category.
There are hundreds of categories in my site.
Some products belong in multiple categories.
Is there some way for me to programmatically add products into multiple categories?
In PHP code you can put them into the category while you are importing them.
Say you have a product called $product and a category ID called $category_id
You can set the categories which a product belongs to by doing the following
$categories = array($category_id);
$product->setCategoryIds($categories);
$product->save();
If the product already has categories and you'd like to add one more then you can use getCategoryIds() like this:
$categories = $product->getCategoryIds();
$categories[] = $categoryId;
$product->setCategoryIds($categories);
$product->save();
Or, as mentioned by Joshua Peck in the comments, you can use the category_api model to add or remove a product from a category without affecting it's current category assignments:
Mage::getSingleton('catalog/category_api')
->assignProduct($category->getId(),$product->getId());
Mage::getSingleton('catalog/category_api')
->removeProduct($category->getId(),$product->getId());
I just want to add that you can remove and add with getSingleton category API:
To Remove product from category:
Mage::getSingleton('catalog/category_api')->removeProduct($category->getId(),$product->getId());
To Add Product to Category:
Mage::getSingleton('catalog/category_api')->assignProduct($category->getId(),$product->getId());
This will not overwrite any categories the product is already in
You can write a module (which takes time but potentially quicker at importing your data) or you can put something together with the API (less involving of Magento programming but potentially slower at importing your data).
Your starting point of what-you-know-already, how valuable your time is and how often you will need to run the update should determine your choice.
Here is the Magento API documentation for adding products to categories (see example at foot of page):
http://www.magentocommerce.com/wiki/doc/webservices-api/api/catalog_category
Well I ended up doing this with the API for some reason of laziness. This adds all the visible products into the category with ID 146:
<?php
$client= new SoapClient('http://www.example.com/api/soap/?wsdl', array('trace' => 1, "connection_timeout" => 120));
// Can be added in Magento-Admin -> Web Services with role set to admin
$sess_id= $client->login('apiuser', 'apikey');
// Get the product list through SOAP
$filters = array('visibility' => '4', 'status' => '1');
$all_products=$client->call($sess_id, 'product.list', array($filters));
// Now chuck them into category 146
foreach($all_products as $product)
{ //var_dump($product);
echo $product['sku']."\n";
$doit=$client->call($sess_id, 'category.assignProduct', array('146', $product['sku']));
}
?>
After looking into the Magento API: Magento adds products to categories in the following way:
public function assignProduct($categoryId, $productId, $position = null, $identifierType = null)
{
$category = $this->_initCategory($categoryId);
$positions = $category->getProductsPosition();
$productId = $this->_getProductId($productId);
$positions[$productId] = $position;
$category->setPostedProducts($positions);
try {
$category->save();
} catch (Mage_Core_Exception $e) {
$this->_fault('data_invalid', $e->getMessage());
}
return true;
}
And getting all the asigned products:
public function assignedProducts($categoryId, $store = null)
{
$category = $this->_initCategory($categoryId);
$storeId = $this->_getStoreId($store);
$collection = $category->setStoreId($storeId)->getProductCollection();
($storeId == 0)? $collection->addOrder('position', 'asc') : $collection->setOrder('position', 'asc');;
$result = array();
foreach ($collection as $product) {
$result[] = array(
'product_id' => $product->getId(),
'type' => $product->getTypeId(),
'set' => $product->getAttributeSetId(),
'sku' => $product->getSku(),
'position' => $product->getPosition()
);
}
return $result;
}
The best above answer points to use Mage::getSingleton('catalog/category_api')
->assignProduct($category->getId(),$product->getId());
Anyway that function is pretty slow in case you have a lot of products/categories to update.
This is because the api function assignProduct():
only accept 1 product/category at time
for every call it loads the product and the category and then save the category
( very slow in case you need to update the same category multiple times )
For example suppose you want to assign 10 products to 1 category ... it gonna load and save the same category 10 times ...
( and load all product that is not actually required if you are sure you product ids are corrects)
A faster way
I came up with the below function that is the same as the api one but it loads and save the category only one time.
This function accept an array as parameter $data that needs to contains all collect changes in the form of $category_id => array(all the products you want to assign)
It is trivial to customize it as for your needs adding, for example, a parameter for store_id ( the function use 0 by default) and position information to the products ...
Add category
function assignCategories($data)
{
foreach ($data as $cat_id => $products_ids) {
/** #var $category Mage_Catalog_Model_Category */
$category = Mage::getModel('catalog/category')
->setStoreId(0)
->load($cat_id );
$positions = $category->getProductsPosition();
foreach ($products_ids as $pid) {
$positions[$pid] = null;
}
$category->setPostedProducts($positions);
$category->save();
}
}
Remove category
function removeProduct($data)
{
foreach ($data as $cat_id => $products_ids) {
/** #var $category Mage_Catalog_Model_Category */
$category = Mage::getModel('catalog/category')
->setStoreId(0)
->load($cat_id);
$positions = $category->getProductsPosition();
foreach ($products_ids as $pid) {
unset($positions[$pid]);
}
$category->setPostedProducts($positions);
$category->save();
}
}
note
Saving the category trigger the Category Flat Data and Catalog URL Rewrites reindex ( if they are set as update on save ).
This is not exactly fast ( the API call does the same ) ...
... so you may want to set these reindex to update manually before running your changes and then do a full reindex on them after
( depending on the number of categories/product you are updating this could be the best option )
We can assign multiple products to the category programmatically using magento scripts. Please create an array of the categories and the select the products based on the custom attribute or field.
$newproducts = $product->getCollection()->addAttributeToFilter(array(array('attribute'=>'attribute_label', 'eq'=> 'attribute_id')));
Loop through the products and assign to category as shown below.
$newCategory = array( $list[0] , $list[$key]);
foreach ($newproducts as $prod)
{
$prod->setCategoryIds(array_merge($prod->getCategoryIds(), $newCategory));
$prod->save();
}
Please refer my tutorial which gives a step by step explanation.
Best way to assign the categories to the products.
Reference from core code - app\code\core\Mage\Catalog\Model\Resource\Category.php
$write = Mage::getSingleton('core/resource')->getConnection('core_write');
$categoryProductTable = Mage::getSingleton('core/resource')->getTableName('catalog/category_product');
$productData = array();$position=0;
foreach ($_productCollection as $product) {
$productData[] = array(
'category_id' => (int)$catId1,
'product_id' => (int)$product->getId(),
'position' => (int)$position
);
$productData[] = array(
'category_id' => (int)$catId2,
'product_id' => (int)$product->getId(),
'position' => (int)$position
);
}
$write->insertMultiple($categoryProductTable, $productData);