I have 3 online stores running on a single Magento installation.
They share over 10.000+ SKU's (I've set them up as simple products), but the only products visible on the frontend are the Grouped Products for each store (which have the SKU's associated to them).
As such, my URL rewrite table is very heavy and while checking Varien Profiler I came across "mage::dispatch::routers_match" which is taking more than 5 seconds to complete. I reckon this is because it's such a large table. So that brings me to my question:
How do I specify which URL's I wan't Magento to rewrite. Is there anyway I can tell it to NOT rewrite simple products URLs? That alone would bring down the table to 1/3rd.
PS: Magento CE 1.7.0.2
Edit:
Thank you Tobias for pointing me in the right direction. I went to app/code/core/Mage/Catalog/Model/Url.php and edited the function refreshProductRewrites to this:
foreach ($products as $product) {
if($product->getTypeId() == "simple"){
continue;
}
else {
$this->_refreshProductRewrite($product, $this->_categories[$storeRootCategoryId]);
foreach ($product->getCategoryIds() as $categoryId) {
if ($categoryId != $storeRootCategoryId && isset($this->_categories[$categoryId])) {
if (strpos($this->_categories[$categoryId]['path'], $storeRootCategoryPath . '/') !== 0) {
continue;
}
$this->_refreshProductRewrite($product, $this->_categories[$categoryId]);
}
}
}
}
The collection for the products which are stored in core_url_rewrite is generated in Mage_Catalog_Model_Url refreshProductRewrites.
You could rewrite this class and implement your own implementation, which only stores the grouped products.
Another solution is just ignoring the products that have an empty url key.
protected function _refreshProductRewrite(Varien_Object $product, Varien_Object $category)
{
if ($category->getId() == $category->getPath()) {
return $this;
}
if ($product->getUrlKey() != '') {
$urlKey = $this->getProductModel()->formatUrlKey($product->getUrlKey());
You can clear the url keys for the simple products by (check the attribute id):
DELETE FROM catalog_product_entity_varchar WHERE entity_id IN (SELECT entity_id FROM catalog_product_entity WHERE type_id='simple' AND attribute_id='97');
Related
I'm developing a module for my Prestashop website and I'm stuck for hours now on a thing. I use Prestashop 1.7.5.1.
Here is the use case :
Some products are set in the default category "preorder" who have the ID 21. When a customer buy an article who is in this category, I would like to automatically change the order state ID to the preorder ID. The order state ID for the preorder is 18.
Here is the code :
public function hookDisplayOrderConfirmation($params)
{
$objOrder = $params['order'];
$products = $objOrder->getProducts();
foreach ($products as $product)
{
$cat = (int)$product->id_category_default;
if($cat == 21)
{
$history = new OrderHistory();
$history->id_order = $objOrder->id;
$history->changeIdOrderState(18, $objOrder->id);
break;
}
}
}
By the way, where I can find all the class and method of Prestashop ? for example, where I can find all the variable of the $objOrder above ?
Thanks a lot for your support :) and have a nice day !
You can find all the variable of Order object inside [prestashop]/classes/order/Order.php class. Here you can find all the variable and function/ methods related to the Order object.
I am trying to join some existing simple products programmatically to an existing configurable product.
I hardly found any hints / documentation on this. I examined the MAGMI Magento Mass Importer Plugin (in particular the magmi_productimportengine.php-file) with no success.
After that I found this snippet:
function attachProductToConfigurable($childProduct, $configurableProduct)
{
$loader = Mage::getResourceModel('catalog/product_type_configurable')
->load($configurableProduct, $configurableProduct->getId());
$ids = $configurableProduct
->getTypeInstance()
->getUsedProductIds();
$newids = array();
foreach ($ids as $id) {
$newids[$id] = 1;
}
$newids[$childProduct->getId()] = 1;
//$loader->saveProducts( $_configurableProduct->getid(), array_keys( $newids ) );
$loader->saveProducts($configurableProduct, array_keys($newids));
}
But when I am trying to call the function like this:
$sProduct = Mage::getModel('catalog/product')
->loadByAttribute('sku', $v);
$cProduct = Mage::getModel('catalog/product')
->loadByAttribute('sku', $sku);
attachProductToConfigurable($sProduct, $cProduct);
(each simple product SKU gets passed step by step to the configurable product)
Fatal error: Call to a member function getId() on a non-object in ... on line 1018
which is this line from the function itself
$loader = Mage::getResourceModel('catalog/product_type_configurable')
->load($configurableProduct, $configurableProduct
->getId());
Since I do not find anything similar to joining simple SKUs to an existing configurable product, I am stuck looking up what might be wrong upon initializing the function calls, resource models etc..
Any ideas on what to keep an eye on to get this going are highly appreciated.
Give this a try:
Mage::getResourceSingleton('catalog/product_type_configurable')
->saveProducts($mainConfigrableProduct, $simpleProductIds);
Where $mainConfigrableProduct must be an instance of the configurable product, and $simpleProductIds is an array with the ids of the simple products associated to the configurable products.
On a side note, be very careful when doing this. The simple products must be in the same attribute set as the configurable products. Here is what can happen if they are not.
On my product.tpl in Opencart, I am trying to display a "Warranty Included" icon.
My problem is, every product on my store doesnt have a Warranty, only specific Products do. On each of my products I have a whole list of Attributes (Specifications). I was wondering if there was some PHP code that I could wrap around the image that I would be able to use an IF statement to say something like this:
Single Statement:
<?php if (products attributes "Product type = Computer") {
//Do this
} ?>
Multiple Statement:
<?php if (products attributes "Product type = Computer" & "Screen Size = 56") {
//Do this
} ?>
I know thats not the code above, but I was wondering if there was away to use PHP if statements with Opencart Attributes.
Thanks in Advance!!
Method 1 : (Dirty method)
if ($attribute_groups) {
foreach ($attribute_groups as $attribute_group) {
foreach ($attribute_group['attribute'] as $attribute) {
if($attribute['name']=='warranty' && $attribute['text']=="yes")
{
// display image
// stop the loop if you don't need it further
}
}
}
}
Method 2 : create a function in model/catalog/product.php like checkProductWarranty() which may return a Boolean value
In this method basically you need to join tables product_attribute and attribute_description on attribute_id put name="warranty" in where clause and do your thing
for reference you can see public function getProductAttributes($product_id) in the same model file
Method 1 works but it will loop through all attributes which might not be efficient thing to do
I have made an online shop for clothes and probably I need to make some tagging system.
The whole application is build on CakePHP and I need an idea for managing all the products, something similar to ebay.
For example to tag each product with it's price , type, producer, size , status
And for example some of them should be multi-searchable, to be able to search for an item with: price between $10 and $20, with size S or M
Have an attributes table that will basically act as a key/value storage and assign these attributes to each product.
Attributes itself could have an attribute_options table from where you can read the different available sizes for an attribute.
You'll then just have to search the attributes table and product table.
From what you describe, you shouldn't need any additional tables. Just add them as fields in the product table, and query based on that. It will be faster, more logically laid out...etc.
Your example would be searchable like below. (It seems a bit overkill, but will make any future finds really simple, and follows the fat controller, skinny model mantra:
//ProductsController
public function whatever() {
$opts = array(
'price_high' => 10,
'price_low' => 20,
'sizes' => array('S', 'M')
);
$this->Product->getProducts($opts);
}
//Product Model
public function getProduts($opts = null) {
//initialize variables
$params = array('conditions'=>array());
//size(s)
if(!empty($opts['sizes']) {
array_push($params['conditions'], array('Product.size'=>$opts['sizes']));
}
//price(s)
if(!empty($opts['price_high']) {
array_push($params['conditions'], array('Product.price <='=>$opts['price_high']));
}
if(!empty($opts['price_low']) {
array_push($params['conditions'], array('Product.price >='=>$opts['price_low']));
}
return $this->find('all', $params);
}
I'm in the process of developing an extension for Magento 1.5.1.0, which allows me to add catalog price rules to products which quantity in stock is reduced to zero. I have added an attribute to my attribute-set called auto_discount_active. This attribute is my on/off switch which works as condition for my price rule.
I wrote an Observer that reacts on the events sales_order_place_after and catalog_product_save_before. It's task is to check wether to stock quantity of the current product has been changed and set my custom attribute to on or off.
The method which handles the catalog_product_save_before event works fine. After saving an article in the backend, the price rule becomes (in)active like it should. The code looks like following:
class Company_AutoDiscount_Model_Observer
{
public function updateAutoDiscount($observer)
{
/**
* #var Varien_Event
*/
$event = $observer->getEvent();
$product = $event->getProduct();
$data = $product->getStockData();
$discount = $data['qty'] < 1 ? true : false;
$attributes = $product->getAttributes();
$attribute = $attributes["auto_discount_active"];
if ($product->getAutoDiscountAllowed())
{
$product->setAutoDiscountActive($discount);
}
return $this;
}
}
Now I want to do the same thing, if someone places an order in my shop. That for I use the event sales_order_place_after which works so far. But after changing the custom attributes value, the price rules are not updated. My observer method looks like this:
public function updateAutoDiscountAfterOrder($observer)
{
/**
* #var Varien_Event
*/
$event = $observer->getEvent();
$order = $event->getOrder();
foreach ($order->getItemsCollection() as $item)
{
$productId = $item->getProductId();
$productIds[] = $productId;
$product = Mage::getModel('catalog/product')->setStoreId($order->getStoreId())->load($productId);
$data = $product->getStockData();
$discount = $data['qty'] < 1 ? true : false;
if ($product->getAutoDiscountAllowed())
{
$product->setAutoDiscountActive($discount);
$product->save();
}
Mage::getModel('catalogrule/rule')->applyAllRulesToProduct($productId);
}
return $this;
}
After placing an order and saving the bought article manually in the backend without changes, the price rule gets updated. But I have get the update working in my observer method.
What do I have to do to get the catalog price rule being assigned, after changing the custom attribute?
Thx in advance!
Okay, I want to advise you on some fairly major code optimisations.
You can reduce your collection size and remove the conditional logic inside your loop by using:
$order->getItemsCollection()->addFieldToFilter('is_in_stock', 0);
You could also update all the attributes with a much faster method than save(), by using:
Mage::getSingleton('catalog/product_action')
->updateAttributes($order->getItemsCollection()->addFieldToFilter('is_in_stock', 0)->getAllIds(), array('auto_discount_active' => 1), 0);
Also, bear in mind, you'll also need to apply your observer to any product stock level modification, ie. product save, import, credit memo (refund) - so its a fairly expansive area. You would probably be better served rewriting the stock class, as there isn't too many events dispatched that will give you enough scope to cover this.
Finally, to perform the assignation of rules, I would suggest extending the resource model for the rule (Mage/CatalogRule/Model/Mysql4/Rule.php) so that you can pass in your array of product ids (to save it iterating through the entire catalogue).
You could simply extend getRuleProductIds() to take a Mage::registry variable (if set) with your product ids from the collection above. Then after running the code above, you could just execute
Mage::getModel('catalogrule/rule')->load(myruleid)->save();
Which will re-index and apply rules to new products as necessary - for only the products that have changed.
I would imagine this method cutting overheads by an extremely significant amount.