I have a problem with a multistore site created with Magento 2.4. Right now the products from the category page and the search page are arranged by name. The options for sorting are: Alphabetical A - Z and Z - A, and by price from low to high and high to low. When I try to sort the products alphabetical everything works fine, but when I try by price it's like a random sort. I also put a custom module but the same result. Instead, if I sort them by product id or by weight it is ok.
if ($currentOrder) {
if ($currentOrder == 'price_asc') {
$subject->getCollection()->setOrder('price', 'asc');
} elseif ($currentOrder == 'price_desc') {
$subject->getCollection()->setOrder('price', 'desc');
} elseif ($currentOrder == 'name_asc') {
$subject->getCollection()->setOrder('name', 'asc');
} elseif ($currentOrder == 'name_desc') {
$subject->getCollection()->setOrder('name', 'desc');
}
}
I also put ->getStoreId(1) after getCollection() but I have same the result. Edit: from what I saw instead of sort by price it's a sort by product id
Steps to Add Magento 2 Sort by Price for Low to High & High to Low Options:
Step 1: Create registration.php file in app\code\Vendor\Extension
Step 2: Create module.xml file in app\code\Vendor\Extension\etc
Step 3: Create di.xml file in app\code\Vendor\Extension\etc
Step 4: Create Toolbar.php file in app\code\Vendor\Extension\Plugin\Catalog\Block
Step 5: Create Config.php file in app\code\Vendor\Extension\Plugin\Catalog\Model
You can refer to the details here.
It Work's for Magento 2.4 versions
I am Assuming you have created a custom module Vendor/module_sort
create di.xml file in vendor/module_sort/etc/
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Catalog\Model\Config">
<plugin name="Vendor_module_sort::addCustomOptions" type="Vendor\module_sort\Plugin\Model\Config" />
</type>
<preference for="Magento\Catalog\Block\Product\ProductList\Toolbar" type="Vendor\module_sort\Plugin\Catalog\Block\Toolbar" />
</config>
Create Config.php in Vendor/module_sort/plugin/Model
<?php
namespace Vendor\module_sort\Plugin\Model;
class Config {
public function afterGetAttributeUsedForSortByArray(\Magento\Catalog\Model\Config $catalogConfig, $options)
{
unset($options['name']);
unset($options['price']);
//New sorting options
$customOption['high_to_low'] = __( 'Price High to Low' );
$customOption['low_to_high'] = __( 'Price Low to High' );
$customOption['name_Z_to_A'] = __( 'Product Title:Z-A' );
$customOption['name_A_to_Z'] = __( 'Product Title:A-Z' );
// $customOption['name'] = $default_options['name'];
//Merge default sorting options with custom options
$options = array_merge($customOption, $options);
return $options;
}
}
Create Toolbar.php in Vendor/module_sort/plugin/Catalog/Block/
<?php
namespace Vendor\module_sort\Plugin\Catalog\Block;
class Toolbar extends \Magento\Catalog\Block\Product\ProductList\Toolbar
{
/**
* Set collection to pager
*
* #param \Magento\Framework\Data\Collection $collection
* #return \Magento\Catalog\Block\Product\ProductList\Toolbar
*/
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()) {
if (($this->getCurrentOrder()) == 'position') {
$this->_collection->addAttributeToSort(
$this->getCurrentOrder(),
$this->getCurrentDirection()
);
} else {
if ($this->getCurrentOrder() == 'high_to_low') {
$this->_collection->setOrder('price', 'desc');
} elseif ($this->getCurrentOrder() == 'low_to_high') {
$this->_collection->setOrder('price', 'asc');
}elseif ($this->getCurrentOrder() == 'name_Z_to_A') {
$this->_collection->setOrder('name', 'desc');
}elseif ($this->getCurrentOrder() == 'name_A_to_Z') {
$this->_collection->setOrder('name', 'asc');
}
}
}
return $this;
}
}
Related
I want to add link category link in layered navigation of Magento so that when any visitor click on category in layered navigation it will open with their actual url instead of filtering the results on same page.
I found this : https://magento.stackexchange.com/questions/9513/layered-navigation-category-link
but this is little bit confusing. Anyone have any simple way to do this modification?
I know how remove category filter list from layered navigation of each category and now I want to know how can I add any static block above the filters in layered navigation so that I can add subcategory link of every category.
Please give some suggestion.
All you need to do is using layout handle <catalog_category_layered /> add a block in left. and on that phtml file using current category get all subcategories.
in your local.xml file
<layout>
<catalog_category_layered>
<block type="core/template" name="layeredcatnav" template="catalog/navigation/layeredcatnav.phtml"/>
</catalog_category_layered>
</layout>
And inside catalog/navigation/layeredcatnav.phtml file
<?php $current_cat = Mage::getSingleton('catalog/layer')->getCurrentCategory();
$cat_id = $current_cat->getId(); //current category id
//now get subcategories using this category id
$children = Mage::getModel('catalog/category')->getCategories($cat_id);
foreach ($children as $category) {
echo $category->getName();
echo $category->getURL();
}
?>
Note : not tested
This can be fixed quite easily by overriding the getUrl() method in the Mage_Catalog_Model_Layer_Filter_Item class
Go to /app/code/core/Mage/Catalog/Model/Layer/Filter/Item.php
public function getUrl()
{
$query = array(
$this->getFilter()->getRequestVar()=>$this->getValue(),
Mage::getBlockSingleton('page/html_pager')->getPageVarName() => null // exclude current page from urls);
return Mage::getUrl('*/*/*', array('_current'=>true, '_use_rewrite'=>true, '_query'=>$query));
}
Change to :
public function getUrl()
{
if($this->getFilter()->getRequestVar() == "cat"){
$category_url = Mage::getModel('catalog/category')->load($this->getValue())->getUrl();
$return = $category_url;
$request = Mage::getUrl('*/*/*', array('_current'=>true, '_use_rewrite'=>true));
if(strpos($request,'?') !== false ){
$query_string = substr($request,strpos($request,'?'));
}
else{
$query_string = '';
}
if(!empty($query_string)){
$return .= $query_string;
}
return $return;
}
else{
$query = array(
$this->getFilter()->getRequestVar()=>$this->getValue(),
Mage::getBlockSingleton('page/html_pager')->getPageVarName() => null // exclude current page from urls
);
return Mage::getUrl('*/*/*', array('_current'=>true, '_use_rewrite'=>true, '_query'=>$query));
}
}
If It's work for you then please copy Item.php file and put local file on this path /app/code/local/Mage/Catalog/Model/Layer/Filter/Item.php
It's possible you can you this code for magento 2.
For magento 2, we have the same function in vendor\magento\module-catalog\Model\Layer\Filter\Item.php we have getUrl() function.
the orignal code:
public function getUrl()
{
$query = [
$this->getFilter()->getRequestVar() => $this->getValue(),
// exclude current page from urls
$this->_htmlPagerBlock->getPageVarName() => null,
];
return $this->_url->getUrl('*/*/*', ['_current' => true, '_use_rewrite' => true, '_query' => $query]);
}
and then the custom code to solve the issue:
public function getUrl()
{
if($this->getFilter()->getRequestVar() == "cat"){
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$category_url = $objectManager->create('Magento\Catalog\Model\Category')->load($this->getValue())->getUrl();
$return = $category_url;
$request = $this->_url->getUrl('*/*/*', array('_current'=>true, '_use_rewrite'=>true));
if(strpos($request,'?') !== false ){
$query_string = substr($request,strpos($request,'?'));
}
else{
$query_string = '';
}
if(!empty($query_string)){
$return .= $query_string;
}
return $return;
}
else{
$query = array(
$this->getFilter()->getRequestVar()=>$this->getValue(),
$this->_htmlPagerBlock->getPageVarName() => null // exclude current page from urls
);
return $this->_url->getUrl('*/*/*', array('_current'=>true, '_use_rewrite'=>true, '_query'=>$query));
}
}
It's working only category section. It's working fine in my case.
i am not sure why the sorting of a product attribute on compare page is not working same as on product page. Like on product page the atribute sort is
on product page
1 name
2 new attribute
3 new attribute1
4 color
but on comapre page when i am comp[aring the 2 products has same attributes the attribute sort order becomes
on compare page
1 name
2 color
3 new attribute
4 new attribute1
I have googled a lot to find answer but unable to find. Please help me to fix this issue.
Below are the functions which i find
public function getComparableAttributes()
{
if (is_null($this->_comparableAttributes)) {
$this->_comparableAttributes = array();
$setIds = $this->_getAttributeSetIds();
if ($setIds) {
$attributeIds = $this->_getAttributeIdsBySetIds($setIds);
$select = $this->getConnection()->select()
->from(array('main_table' => $this->getTable('eav/attribute')))
->join(
array('additional_table' => $this->getTable('catalog/eav_attribute')),
'additional_table.attribute_id=main_table.attribute_id'
)
->joinLeft(
array('al' => $this->getTable('eav/attribute_label')),
'al.attribute_id = main_table.attribute_id AND al.store_id = ' . (int) $this->getStoreId(),
array('store_label' => $this->getConnection()->getCheckSql('al.value IS NULL', 'main_table.frontend_label', 'al.value'))
)
->where('additional_table.is_comparable=?', 1)
->where('main_table.attribute_id IN(?)', $attributeIds);
$attributesData = $this->getConnection()->fetchAll($select);
if ($attributesData) {
$entityType = Mage_Catalog_Model_Product::ENTITY;
Mage::getSingleton('eav/config')
->importAttributesData($entityType, $attributesData);
foreach ($attributesData as $data) {
$attribute = Mage::getSingleton('eav/config')
->getAttribute($entityType, $data['attribute_code']);
$this->_comparableAttributes[$attribute->getAttributeCode()] = $attribute;
}
unset($attributesData);
}
}
}
return $this->_comparableAttributes;
}
/**
* Load Comparable attributes
*
* #return Mage_Catalog_Model_Resource_Product_Compare_Item_Collection
*/
public function loadComparableAttributes()
{
$comparableAttributes = $this->getComparableAttributes();
$attributes = array();
foreach ($comparableAttributes as $attribute) {
$attributes[] = $attribute->getAttributeCode();
}
$this->addAttributeToSelect($attributes);
return $this;
}
bUt i cant understand how to filter it by sort order .Please suggest
check the modifications in function below:
the file used is vendor/magento/module-catalog/Model/ResourceModel/Product/Compare/Item/Collection.php
But this is not the best way to do it. you need to override it as per Magento Standards.
I am working on it, will update shortly.
public function getComparableAttributes()
{
if ($this->_comparableAttributes === null) {
$this->_comparableAttributes = [];
$setIds = $this->_getAttributeSetIds();
if ($setIds) {
$attributeIds = $this->_getAttributeIdsBySetIds($setIds);
$select = $this->getConnection()->select()->from(
['main_table' => $this->getTable('eav_attribute')]
)->join(
['additional_table' => $this->getTable('catalog_eav_attribute')],
'additional_table.attribute_id=main_table.attribute_id'
)->joinLeft(
['al' => $this->getTable('eav_attribute_label')],
'al.attribute_id = main_table.attribute_id AND al.store_id = ' . (int)$this->getStoreId(),
[
'store_label' => $this->getConnection()->getCheckSql(
'al.value IS NULL',
'main_table.frontend_label',
'al.value'
)
]
)
->joinLeft( //add sort order to sort the attributes as per backend sort. -- Abid
['as' => $this->getTable('eav_entity_attribute')],
'as.attribute_id = main_table.attribute_id'
)
->where(
'additional_table.is_comparable=?',
1
)->where(
'main_table.attribute_id IN(?)',
$attributeIds
)
->order('as.sort_order') //sort by sort_order -- Abid
;
$attributesData = $this->getConnection()->fetchAll($select);
if ($attributesData) {
$entityType = \Magento\Catalog\Model\Product::ENTITY;
$this->_eavConfig->importAttributesData($entityType, $attributesData);
foreach ($attributesData as $data) {
$attribute = $this->_eavConfig->getAttribute($entityType, $data['attribute_code']);
$this->_comparableAttributes[$attribute->getAttributeCode()] = $attribute;
}
unset($attributesData);
}
}
}
return $this->_comparableAttributes;
}
On product page attributes are sorted like in attribute set.
On compare page attributes are not sorted at all.
You can rewrite function getComparableAttributes in class Mage_Catalog_Model_Resource_Product_Compare_Item_Collection and implement your own sort logic.
But note, that this function differs in different Magento versions. You can try to use free extension ET Advanced Compare or take part of code from this extension (code is available on bitbucket.org. link i on extension page)
I've created a new attribute (type: dropdown) that is not a required field.
At this moment, every product shows in the frontend "my attribute: n/a".
After save anything in some product, magento write a null value inside catalog_product_entity_int table for this attribute.
But in the frontend the attribute now appear as "my attribute: No" instead of "N/A".
It looks like a bug, since I didn't touch in the attribute while editing the new product.
Is there a way to deal with it or to apply some rule in my phtml?
Actually this is not a bug. It's a feature.
N/A is displayed when there is no record in the table catalog_product_entity_int for that attribute.
When you add an attribute there are no values for that attribute for any product, but as soon as you save a product that has that attribute, a null value is inserted in the table (as you stated). So no value is different from null value.
All the magic happens here Mage_Catalog_Block_Product_View_Attributes::getAdditionalData().
These are the lines that interest you:
if (!$product->hasData($attribute->getAttributeCode())) { // no value in the database
$value = Mage::helper('catalog')->__('N/A');
} elseif ((string)$value == '') { // empty value in the database
$value = Mage::helper('catalog')->__('No');
}
If you want to change anything override this method.
If you change anything you might want to take a look at Mage_Catalog_Block_Product_Compare_List::getProductAttributeValue().
The same system is used for displaying attribute values in the compare products list.
I've ended up to create 2 observers... One that overrides getValue from Mage_Eav_Model_Entity_Attribute_Frontend_Default and other to override getAdditionalData in Mage_Catalog_Block_Product_View_Attributes as follows:
<?php
class Namespace_Module_Model_Entity_Attribute_Frontend_Default extends Mage_Eav_Model_Entity_Attribute_Frontend_Default{
public function getValue(Varien_Object $object)
{
$value = $object->getData($this->getAttribute()->getAttributeCode());
if (in_array($this->getConfigField('input'), array('select','boolean'))) {
$valueOption = $this->getOption($value);
if (!$valueOption) {
$opt = Mage::getModel('eav/entity_attribute_source_boolean');
$options = $opt->getAllOptions();
if ($options && !is_null($value)) { //added !is_null
foreach ($options as $option) {
if ($option['value'] == $value ) {
$valueOption = $option['label'];
}
}
}
}
$value = $valueOption;
} elseif ($this->getConfigField('input') == 'multiselect') {
$value = $this->getOption($value);
if (is_array($value)) {
$value = implode(', ', $value);
}
}
return $value;
}
}
and
<?php
class Namespace_Module_Block_Product_View_Attributes extends Mage_Catalog_Block_Product_View_Attributes
{
public function getAdditionalData(array $excludeAttr = array())
{
$data = array();
$product = $this->getProduct();
$attributes = $product->getAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getIsVisibleOnFront() && !in_array($attribute->getAttributeCode(), $excludeAttr)) {
$value = $attribute->getFrontend()->getValue($product);
if (!$product->hasData($attribute->getAttributeCode()) || (string)$value == '') { //modified
$value = Mage::helper('catalog')->__('N/A');
} elseif ($attribute->getFrontendInput() == 'price' && is_string($value)) {
$value = Mage::app()->getStore()->convertPrice($value, true);
}
if (is_string($value) && strlen($value)) {
$data[$attribute->getAttributeCode()] = array(
'label' => $attribute->getStoreLabel(),
'value' => $value,
'code' => $attribute->getAttributeCode()
);
}
}
}
return $data;
}
}
I'm using the Cart class in Codeigniter. What I want to do should (hopefully!) be simple... but i'm struggling.
On the product page, I have a button to 'add to cart'. What I want to happen is that when the item is already in the cart, the button changes to 'remove from cart'.
<? //if(**not in cart**) { ?>
Add to cart
<? } else { ?>
Remove from cart
<? } ?>
How can I query the cart to see if that item is in there or not and get the 'rowid' so I can use that for a remove function?
Many thanks!
I had a similar problem - I got round it by extending the CI_Cart library with 2 new functions - in_cart() and all_item_count().
<?php
class MY_Cart extends CI_Cart {
function __construct()
{
parent::__construct();
$this->product_name_rules = '\d\D';
}
/*
* Returns data for products in cart
*
* #param integer $product_id used to fetch only the quantity of a specific product
* #return array|integer $in_cart an array in the form (id => quantity, ....) OR quantity if $product_id is set
*/
public function in_cart($product_id = null) {
if ($this->total_items() > 0)
{
$in_cart = array();
// Fetch data for all products in cart
foreach ($this->contents() AS $item)
{
$in_cart[$item['id']] = $item['qty'];
}
if ($product_id)
{
if (array_key_exists($product_id, $in_cart))
{
return $in_cart[$product_id];
}
return null;
}
else
{
return $in_cart;
}
}
return null;
}
public function all_item_count()
{
$total = 0;
if ($this->total_items() > 0)
{
foreach ($this->contents() AS $item)
{
$total = $item['qty'] + $total;
}
}
return $total;
}
}
/* End of file: MY_Cart.php */
/* Location: ./application/libraries/MY_Cart.php */
You could check in your model if the job name or whatever you would like to check already exists. If it exists display delete button else show add.
I have an bundle product as array like this: (take from params when add product to cart)
Array
(
[product] => 165
[bundle_option] => Array
(
[17] => 47
[22] => 60
[16] => 46
[15] => 42
[14] => Array
(
[0] => 39
)
)
)
How could I get price for this bundle product?
Something like this should also work:
public function getDisplayPrice($product) {
if($product->getFinalPrice()) {
return $product->getFormatedPrice();
} else if ($product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_BUNDLE) {
$optionCol= $product->getTypeInstance(true)
->getOptionsCollection($product);
$selectionCol= $product->getTypeInstance(true)
->getSelectionsCollection(
$product->getTypeInstance(true)->getOptionsIds($product),
$product
);
$optionCol->appendSelections($selectionCol);
$price = $product->getPrice();
foreach ($optionCol as $option) {
if($option->required) {
$selections = $option->getSelections();
$minPrice = min(array_map(function ($s) {
return $s->price;
}, $selections));
if($product->getSpecialPrice() > 0) {
$minPrice *= $product->getSpecialPrice()/100;
}
$price += round($minPrice,2);
}
}
return Mage::app()->getStore()->formatPrice($price);
} else {
return "";
}
}
You can use the built-in static method calculatePrice of Mage_Bundle_Model_Product_Price like so:
if ($product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_BUNDLE){
$pricemodel = Mage::getModel('bundle/product_price');
$price = $pricemodel::calculatePrice(
$product->getData('price'),
$product->getSpecialPrice(),
$product->getSpecialPriceFrom(),
$product->getSpecialPriceTo(),
false,
Mage::app()->getStore()->getWebsite()->getId(),
Mage::getSingleton('customer/session')->getCustomer()->getGroupId(),
$product->getId()
);
$finalprice = $this->helper('core')->currencyByStore(Mage::helper('tax')->getPrice($product, $price));
}
You can use the getMinimalPrice() and getMaximalPrice() functions in the Mage_Bundle_Product_Price class, which are written specifically for bundle products.
// load product
$product = new Mage_Catalog_Model_Product();
$product->load(165);
$priceModel = $product->getPriceModel();
// get options
$block = Mage::getSingleton('core/layout')->createBlock('bundle/catalog_product_view_type_bundle');
$options = $block->setProduct($product)->getOptions();
$price = 0;
foreach ($options as $option) {
$selection = $option->getDefaultSelection();
if ($selection === null) {
continue;
}
$price += $priceModel->getSelectionPreFinalPrice($product, $selection, $selection->getSelectionQty());
}
or, you can use my magento module: https://github.com/head82/KH_ExtendedBundlePrice tested with magento 1.7
#Kevin, great work. I personally needed a slight modification. In Magento 1.7.0.2, the function getSelectionPreFinalPrice() actually calls upon getSelectionPrice() which calls upon getSelectionFinalTotalPrice() - but in that last part the price is calculated with the final price (so including tier-pricing and special prices) and not the original price.
I applied the following snippet to get that original price (without tier-pricing and without special prices):
$_normalPrice = 0;
$_options = $_priceModel->getOptions($_product);
foreach($_options as $_option) {
$_selection = $_option->getDefaultSelection();
if ($_selection === null) continue;
$_normalPrice = $_normalPrice + $_selection->getPrice();
}
Here is the magento way:
$_product = $this->getProduct();
$_priceModel = $_product->getPriceModel();
list($_minimalPriceTax, $_maximalPriceTax) = $_priceModel->getTotalPrices($_product, null, null, false);
list($_minimalPriceInclTax, $_maximalPriceInclTax) = $_priceModel->getTotalPrices($_product, null, true, false);
Assuming that $this->getProduct() returns Catalog/Product Model.
Works both with fixed and dynamic price types even compatible with Configurable Bundle extention. Taken from design/base/default/template/bundle/catalog/product/price.phtml
I solved it by own method, not sure its acceptable or not.send the post values and product id, the model will return the price.
$bundle_option = Mage::app ()->getRequest ()->getParam('bundle_option');
$bundle_option_array = call_user_func_array('array_merge', $bundle_option);
$price = Mage::Helper('airhotels/bundle')->getBundlePrice($productid,$bundle_option_array);
my helper file is
public function getBundlePrice($productId,$bundle_option_array) {
$product = new Mage_Catalog_Model_Product();
$product->load($productId);
$price=0;
$selectionCollection = $product->getTypeInstance(true)->getSelectionsCollection($product->getTypeInstance(true)->getOptionsIds($product), $product);
foreach($selectionCollection as $option)
{
if (in_array($option->getSelectionId(), $bundle_option_array)){
$price += $option->price;
}
}
return $price;
}
Concept: I built a 1-D array from the 2-D array(the question). from the function in helper we can get all the selection id of a bundle product . By matching (using in_array) we can calculate the price for our custom selected products.
Although I'm sure you have figured out what you needed several year ago I'm not quite sure where in the accepted answer you would put your params.
What I found was that you can get the price for a bundle product with getFinalPrice() just like any other product, but you can set the selected options using the catalog/product helper.
$_product = Mage::getModel('catalog/product')->load($this->getRequest()->getPost()['product'];
$productHelper = $this->helper('catalog/product');
//getpost() contains the array you mentioned when you click add to cart
$_configuredProducts = $_product->getTypeInstance(true)->processConfiguration(new Varien_Object($this->getRequest()->getPost()), $_product,Mage_Catalog_Model_Product_Type_Abstract::PROCESS_MODE_FULL );
echo $_product->getFinalPrice();