Hide out of stock products only in search result pages - magento

We can easily hide out of stock products in categories and catalog search through : System > Configuration > Catalog > Inventory > Display Out of Stock Products
Yet, how can we hide products only in search results ?

You could override _getSearchableProducts function in class Mage_CatalogSearch_Model_Resource_Fulltext so that it always searches in-stock products.
File to modify: app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext.php
/**
* Retrieve searchable products per store
*
* #param int $storeId
* #param array $staticFields
* #param array|int $productIds
* #param int $lastProductId
* #param int $limit
* #return array
*/
protected function _getSearchableProducts($storeId, array $staticFields, $productIds = null, $lastProductId = 0, $limit = 100)
{
...
->join(
array('stock_status' => $this->getTable('cataloginventory/stock_status')),
$writeAdapter->quoteInto(
'stock_status.product_id=e.entity_id AND stock_status.stock_status = 1 AND stock_status.website_id=?', // add stock_status = 1 condition
$websiteId
),
array('in_stock' => 'stock_status')
);
....
}
Remember to reindex "Catalog Search Index" afterwards.

The above solution using observer also hides the in category. The question is to hide only in search results. so directly adding instock filter product collection results.phtml is appropriate.
i.e Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($productCollection);
correct me if i miss any thing here.

I know a way by which you can achieve this.
Inside Magento/app/design/frontend/rwd/default/template/catalogsearch/result.phtml
$this->getProductListHtml() ;
gives you the desired search result.
Inside list.phtml check this out.
$_productCollection=$this->getLoadedProductCollection();
Magento allows you a way by which you can observe the collection add filters before/after the collection is loaded that is called the Observer way.
Inside the event you observe, try using following particularly for search results:
Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($productCollection);
Hope it helps.

Related

How to filter zero-prices products in listing page

I have used the sorting by ->setOrder('price','desc');
but in this case its not working well when we sort the products by name
Is there any way to sort by price (from lower to bigger) in such way, that products with zero price will bein the end of the list???
public function setCollection($collection)
{
$this->_collection = $collection;
$this->_collection->setCurPage($this->getCurrentPage());
// we need to set pagination only if passed value integer and more that 0
$limit = (int)$this->getLimit();
if ($limit) {
$this->_collection->setPageSize($limit);
}
if ($this->getCurrentOrder()) {
$this->_collection->setOrder($this->getCurrentOrder(), $this->getCurrentDirection())
->setOrder('price','desc');
}
return $this;
}
The question not very clear, yet from what I understand:
(Price Sorting) This feature is provided by default in Magento in Product Listing Pages.
Maybe you have changed the price attribute # backend.
Check for price attribute.
Used in Product Listing = Yes
Used for Sorting in Product Listing = Yes
Clear Cache, and you should be ready to go.

Magento - Modify Shopping Cart Rules Function to apply discounts by Percentage of Original Price Instead of Product Price Discount

I'm working on a project where the entire store products have a special price different (lowered) from the original price.
I want to be able to apply shopping cart discounts By Percentage of the Original Price just like Catalog Discount Price Rules does.
Right now, if I apply a shopping cart “Percentage of product price discount” rule, it will apply the discount to the special price instead of the original price.
Where is the function for Shopping Cart Rules at? Any details on modifying it to apply discounts on original price will be appreciated.
I recently had to solve the same problem. These two blog guides were very helpful in crafting my final solution.
My custom observer on the salesrule_validator_process event looks like this:
class My_SalesRule_Model_Observer
{
// New SalesRule type
const TO_ORIGINAL_PRICE = 'to_original_price';
/**
* Add the new SalesRule type to the admin form.
* #param Varien_Event_Observer $obs
*/
public function adminhtmlBlockSalesruleActionsPrepareform($obs)
{
$field = $obs->getForm()->getElement('simple_action');
$options = $field->getValues();
$options[] = array(
'value' => self::TO_ORIGINAL_PRICE,
'label' => Mage::helper('salesrule')->__('Percent of original product price discount')
);
$field->setValues($options);
}
/**
* Apply the new SalesRule type to eligible cart items.
* #param Varien_Event_Observer $obs
*/
public function salesruleValidatorProcess($obs)
{
/* #var Mage_SalesRule_Model_Rule $rule */
$rule = $obs->getRule();
if ($rule->getSimpleAction() == self::TO_ORIGINAL_PRICE) {
/* #var Mage_Sales_Model_Quote_Item $item */
$item = $obs->getItem();
// Apply rule qty cap if it exists.
$qty = $rule->getDiscountQty()? min($obs->getQty(), $rule->getDiscountQty()) : $obs->getQty();
// Apply rule stepping if specified.
$step = $rule->getDiscountStep();
if ($step) {
$qty = floor($qty / $step) * $step;
}
// Rule discount amount (assumes %).
$ruleDiscountPercent = $rule->getDiscountAmount();
/* #see My_Catalog_Model_Product::getDiscountPercent */
$productDiscountPercent = $item->getProduct()->getDiscountPercent();
// Ensure that the rule does not clobber a larger discount already present on the $cartItem.
// Do not apply the rule if the discount would be less than the price they were already quoted
// from the catalog (i.e. special_price or Catalog Price Rules).
if ($ruleDiscountPercent > $productDiscountPercent) {
// Reduce $ruleDiscountPercent to just the gap required to reach target pct of original price.
// In this way we add the coupon discount to the existing catalog discount (if any).
$ruleDiscountPercent -= $productDiscountPercent;
$pct = $ruleDiscountPercent / 100;
// Apply the discount to the product original price, not the current quote item price.
$discountAmount = ($item->getProduct()->getPrice() * $pct) * $qty;
$item->setDiscountPercent($ruleDiscountPercent);
$obs->getResult()
->setDiscountAmount($discountAmount)
->setBaseDiscountAmount($discountAmount);
}
}
}
}
If you have set up your extension properly, you should see the new rule type appear under:
Promotions -> Shopping Cart Price Rules -> Edit 'New Rule' -> Actions -> Update prices using the following information: Apply [Percent of original product price discount]
Caveats: you’ll notice I implemented this to work on percentages, including creating a method on the product model to calculate the catalog discounted price (i.e. special price and other discounts applicable at the catalog level). You will need to implement that if you wish to do the same, or update this logic to fit your scenario perhaps just referring to $item->getProduct()->getPrice() instead.

Add Child SKUs to Magento Fulltext Search Index

I'm not sure why this isn't part of the stock functionality of Magento, but I want customers to be able to search for a configurable product by a child SKU. For some reason, Magento doesn't index the child SKUs.
I found in app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext.php
$dynamicFields = array(
'int' => array_keys($this->_getSearchableAttributes('int')),
'varchar' => array_keys($this->_getSearchableAttributes('varchar')),
'text' => array_keys($this->_getSearchableAttributes('text')),
'decimal' => array_keys($this->_getSearchableAttributes('decimal')),
'datetime' => array_keys($this->_getSearchableAttributes('datetime')),
);
I've tried several variations, without success. SKU is a "static" attribute, accessible through $this->_getSearchableAttributes('static'). I'm fine with getting all static attributes, but it's not working. Depending on what a try I either get no change in results or an error that the static attribute table doesn't exist (which make sense, because static attributes are in the product entity table).
Does anybody have a suggestion to solve this problem?
Online research has found suggestions to add a hidden attribute with these values, but that shouldn't be needed. I would rather solve the problem properly.
So many ways to do this..
However, I will lead with something I believe would be much cleaner rather than screwing around with the actual Fulltext in app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext.php.
The problem is the lines AFTER that snippet you included:
// status and visibility filter
$visibility = $this->_getSearchableAttribute('visibility');
$status = $this->_getSearchableAttribute('status');
$statusVals = Mage::getSingleton('catalog/product_status')->getVisibleStatusIds();
$allowedVisibilityValues = $this->_engine->getAllowedVisibility();
which ultimately leads to:
core/Mage/CatalogSearch/Model/Resource/Fulltext/Engine.php
/**
* Retrieve allowed visibility values for current engine
*
* #return array
*/
public function getAllowedVisibility()
{
return Mage::getSingleton('catalog/product_visibility')->getVisibleInSearchIds();
}
then:
core/Mage/Catalog/Model/Product/Visibility.php
public function getVisibleInSearchIds()
{
return array(self::VISIBILITY_IN_SEARCH, self::VISIBILITY_BOTH);
}
So what you need to do is go to your simple product associated with your configurable and change the visibility to 'Search'.
However, here it is in action now:
However, yes, that doesn't look pretty at all. The next order of business is now modifying the search results so that when it lists a simple product item that's a.) associated to a configurable product b.) visibility ID is explicitly set to Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_SEARCH, you need to render the item in search results to the actual configurable product parent (and the rest of its details, such as images).
(I'll continue this later. my computer decided to crap out on me, and I lost over 50% of what I was writing. Even the part where we modify the fulltext class! Darn. see you later.)
I've used the method of setting simple product visibility to search. Then instead of displaying the simple product load/display its parent product.
Beside the frontend query in loop performance issues I never found this solution very satisfying. So after another whack at it I found another option.
I'm adding simple product SKUs to the fulltext index for grouped products but the same approach should work for configurable products.
Here is the idea:
Mage_CatalogSearch_Model_Resource_Fulltext->_prepareProductIndex
contains
$typeInstance = $this->_getProductTypeInstance($productData['type_id']);
if ($data = $typeInstance->getSearchableData($product)) {
$index['options'] = $data;
}
So by overriding the getSearchableData method in the product type class you can add data to the fulltext index. For grouped product my updated looked like:
... extends Mage_Catalog_Model_Product_Type_Grouped
{
/**
* all child SKUs to search data
*/
public function getSearchableData($product=null)
{
$searchData = parent::getSearchableData();
$adapter = Mage::getSingleton('core/resource')->getConnection('core_read');
$select = $adapter->select()
->from(
array('p' => Mage::getSingleton('core/resource')->getTableName('catalog/product')),
array('sku')
)
->joinLeft(
array('r' => Mage::getSingleton('core/resource')->getTableName('catalog/product_relation')),
'r.child_id = p.entity_id',
array()
)
->where('r.parent_id = ?', $this->getProduct($product)->getId());
$query = $adapter->query($select);
while ($row = $query->fetch()) {
$searchData[] = $row['sku'];
}
return $searchData;
}
Reindexed search and catalogsearch_fulltext.dataindex should contain the additional data.
There is a solution for this but it is a paid extension (we license it), it is not build in to Magento standard so you cannot search by child attributes and just return the parent configurable/grouped/bundle product.

Magento - Varien Grid - Totaling entire collection

I have created a custom product grid using the varien grid functionality and am using it as an order form. Basically, there is a Product Name, Product Price, and an input field for the user to enter the amount of a product they would like.
There are thousands of products, and I need to give a running subtotal of what the user has selected.
The closest I've gotten is as adding the following to Grid.php...
protected function _afterLoadCollection()
{
$collection = $this->getCollection()->load();
$total = 0;
if ($collection) {
foreach($collection as $item) {
$itemPrice = $item->getPrice();
$itemQty = $item->getQty();
$total = $total + ($itemPrice * $itemQty);
}
echo $total;
}
}
However, the results are affected by the current limit, and therefore only totals the current page.
Is there a way around this, or a better way to get the running total of the two columns?
Thanks in advance, I've been stuck on this for days!
You can try this to clear the collection complements, and set for the first page a big limit
$_productCollection = $this->getLoadedProductCollection();
$_productCollection->clear();
$_productCollection->setPage(1,99999);

How to access Magento Model data from a different scope

I am facing a requirement to access values for an attribute that are specified in another scope on a multi-website/multi-store site. In particular, we need to display the Admin (default) label for an attribute in the frontend when the label for the Store has been set.
So the code should render the Hex value from the Admin column in one part of the page, and the textual Description from the English (US) on another part of the page. How do I do that?
Conversely, I have seen instances where values have been set on a Store View but are null for Default, and the code returns null even when the Store has been set. Can someone please explain how that works?
Here is how to do it using Magento classes:
// Get the model of the attribute in question
/* #var $attribute Mage_Catalog_Model_Resource_Eav_Attribute */
$attribute = Mage::getSingleton('eav/config')->getAttribute('catalog_product', 'color');
// Load the option collection for that attribute adding the storeFilter()
/* #var $collection Mage_Eav_Model_Resource_Entity_Attribute_Option_Collection */
$collection = Mage::getResourceModel('eav/entity_attribute_option_collection')
->setPositionOrder('asc')
->setAttributeFilter($attribute->getId())
->setStoreFilter();
// Load the product so we can get the correct option from the collection
$product = Mage::getModel('catalog/product')->load(39);
$productOption = $collection->getItemById($product->getColor());
printf("Default: %s, Store: %s\n", $productOption->getDefaultValue(), $productOption->getValue());
Adjust as needed.

Resources