I'm playing arround with magento product attributes.
What i'm trying to do is getting an "Unique Product Number" attached to the same " Unique Video Number" That works perfectly.
The same goes for an Dropdown Attribute using the following code.
<script src="js/jquery-1.9.1.min.js"></script>
<script src="js/avembed.js"></script>
<div class="av_videoplayer" data-av-search-ean="<?php echo $_product->getAttributeText('DropDown_Atribute') ?>"></div>
But as soon as i try it with an TextBox or TextField it fails to return an output.
I have been using getAttributeName to make it happen but i can't seem to get it working or find the anwser to do this.
Any tips on what i might be doing wrong here?
Thanks in advance!
Magento provides magic getters and setters, so for most product attributes, you can retrieve them using the attribute name, preceded by get. For example:
$freeShipping = $_product->getFreeShipping();
The alternative to this is the method you are using, getAttributeText.
$freeShipping = $_product->getAttributeText('free_shipping');
HOWEVER, the attribute must be in the product collection, which most custom attributes are not. When you setup your attribute, you need to set the option Visible on Product View Page on Front-end to Yes.
Failing that, you can recreate the product collection (in your own helper, for example) and add your attribute(s) manually as follows:
public function getMyProductCollection( $skuArray ) {
$products = Mage::getResourceSingleton('catalog/product_collection')
->addAttributeToSelect(
array(
'free_shipping',
'list_price',
'manufacturer',
'name',
'price',
'special_price',
'special_from_date',
'special_to_date',
'thumbnail'
)
)
->addAttributeToFilter(
'sku', array( 'in' => $skuArray )
)
->load();
return $products;
}
You can then use the public function as follows:
$_productObj = $this->helper('my_helper')->getMyProductCollection(
$_productSkus
);
Understand that I'm describing the principle, this code is not copy + pastable. Hope that helps.
Related
My problem:
I need to retrieve the main product image from a cut down product object which is supplied by the class: Mage_Bundle_Model_Resource_Price_Index
My code demonstrating the issue:
$_product = $this->getProduct();
$_options = Mage::helper('core')->decorateArray($this->getOptions());
foreach($_options as $_option):
$_selections = $_option->getSelections();
foreach ($_option->getSelections() as $tmpsel) {
var_dump($tmpsel->getImageUrl());
}
Which returns my placeholder image:
http://dev.dev/media/catalog/product/cache/7/image/265x/0dc2d03fe217f8c83829496872af24a0/placeholder/default/logo_3.jpg
My horrible and hacky work around:
In order to get correct image Url, I have resorted to loading a completely new product object, which is terribly inefficient.
foreach ($_option->getSelections() as $tmpsel) {
$product = Mage::getModel("catalog/product")->load($tmpsel->getId());
$image = Mage::helper('catalog/image')->init($product, 'image');
var_dump($image."");
}
This returns correctly:
http://dev.dev/media/catalog/product/cache/7/image/0dc2d03fe217f8c83829496872af24a0/M/P/MP1428219-107-Main.jpg
What I want to do:
I want to be able to use the catalog/image helper with the selection ($tmpsel), but when I try I end up getting the placeholder image again.
foreach ($_option->getSelections() as $tmpsel) {
$image = Mage::helper('catalog/image')->init($tmpsel, 'image');
var_dump($image."");
}
Extra Info:
Anything I think of that could help I will add here
Cut down product object includes some reference to image
'small_image' => string '/M/P/MP1428219-107-Main.jpg'
'thumbnail' => string '/M/P/MP1428219-107-Main.jpg'
Description of getSelection()
In: ./app/code/core/Mage/Bundle/Model/Resource/Price/Index.php
* Retrieve bundle options with selections and prices by product
The function uses low level SQL to generate the collection, so I can always extend it to add options if needed, not sure which options though.
Thank you for reading, hope someone can give me a good way of doing this, will keep updated.
Init() method require a product object
init(Mage_Catalog_Model_Product $product, $attributeName, $imageFile=null)
Try
<?php echo $this->helper('catalog/image')->init($tmpsel, 'small_image')->resize(40,40); ?>
You do not have 'image' but you do have 'small_image' attribute
See Magento - how to retrieve bundled option images
To get the full web address you can use this code:
Mage::getSingleton('catalog/product_media_config')->getMediaUrl($product->getImage());
The limitation is that bring only the main image.
Oh wow, I guess I can thank you stack overflow for making me write my thoughts down.
After comparing the full product object and the semi product object I can see that I was calling the incorrect attribute name for catalog/image
$image = Mage::helper('catalog/image')->init($tmpsel, 'image');
Should have been
$image = Mage::helper('catalog/image')->init($tmpsel, 'small_image');
I am trying to display a grouped product's price on the product view page in Magento 1.7.0.2, just as it is being displayed in the category product listing ("Starting at: xx.xx"). I thought I could just use
$this->getPriceHtml($_product, true);
to do so because it works the same way in the category product listing, but as everything I tried to do in Magento, it is not that easy because that method doesnt return anything on the product view page.
I looked a little deeper into Magento's code and figured out that the cause of this problem is the product's minimal price data not being set on the product view page ($_product->getMinimalPrice() returns null).
Therefore I did some research on how to load a product's minimal price which brought up ideas like
$_product->getPriceModel()->getMinimalPrice($_product);
which doesnt work because apparently that method was deprecated and removed in one of the last updates or
$priceModel = Mage::getResourceModel('catalogindex/price');
$priceModel->setStoreId(Mage::app()->getStore()->getId());
$priceModel->setCustomerGroupId(Mage::getSingleton('customer/session')->getCustomerGroupId());
$minimalPrices = $priceModel->getMinimalPrices(array($_product->getId()));
$minimalPrice = $minimalPrices[0];
$_product->setData('minimal_price', $minimalPrice['value']);
$_product->setData('minimal_tax_class_id', $minimalPrice['tax_class_id']);
which doesnt work either because the table 'catalogindex_minimal_price' is empty.
So my question is, how does Magento load the minimal price in the category product listing?
I looked a little deeper into Magento's code and tracked down how Magento loads the products of the product list.
When viewing the product list, the product collection is loaded by the model Mage_Catalog_Model_Layer, which adds the minimal price and other stuff to the products in the collection by adding joins to the collection’s SQL, but I have not yet found a model that does the same thing for single products instead of collections.
Because I don’t want to use a direct SQL query, I checked out the CatalogIndex module, but that module seems not to be working at all because all its indexing tables are empty or even corrupted.
$data_grouped = Mage::getModel("catalogindex/data_grouped");
$minimal = $data_grouped->getMinimalPrice(array($_product->getId()), Mage::app()->getStore());
looked good at first but it gives me the following error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'catalog_product_index_price.value' in 'field list'
The CatalogIndex module is either deprecated or I’m just too stupid to enable it.
However, I have now created a workaround in my price.phtml template, which uses the same method to retrieve the minimal price and tax percents as the product list does:
At the beginning of the template I added the following:
$this->setProduct(
Mage::getModel("catalog/product")->getCollection()
->addAttributeToSelect(Mage::getSingleton("catalog/config")->getProductAttributes())
->addAttributeToFilter("entity_id", $this->getProduct()->getId())
->setPage(1, 1)
->addMinimalPrice()
->addFinalPrice()
->addTaxPercents()
->load()
->getFirstItem()
);
which may not be the perfect solution, but it does the job and it does it a little cleaner than iterating over all child products and finding the minimal price that way.
This isn't an answer as much as a big HTML comment on Subsurf's answer. If you're reading this, you might be implementing your own custom module to display a product list. In my case, it's a single page just for wholesalers. My goal was to get a list of my own company's products and give them a list like it was another category. I copied my template's list.phtml to my new content.phtml. But getMinimalPrice() was returning NULL, meaning that when this code calls the getPriceHtml(), price.phtml wasn't showing wholesale pricing.
I did a simple module with index controller and a content.phtml. Using the boilerplate I see all over the net, in the indexController.php file, change:
$block = $this->getLayout()->createBlock(
'Mage_Core_Block_Template',
'b2b',
array('template' => 'b2b/content.phtml')
);
to:
$block = $this->getLayout()->createBlock(
'Mage_Catalog_Block_Product_List',
'b2b',
array('template' => 'b2b/content.phtml')
);
This does a lot of the heavy lifting for you to show the products list correctly.
Now, on to the template file, in my case content.phtml, you get your product collection and then use Subsurf's code in the top of the foreach() which repeats.
$_productCollection = Mage::getModel('catalog/product')
->getCollection()
// ->addAttributeToSelect('*')
// doesn't work ->addAttributeToSelect('special_price')
->addAttributeToSelect('sku')
->addAttributeToFilter('status', 1)
->addAttributeToFilter('visibility', 4)
// http://stackoverflow.com/questions/1332742/magento-retrieve-products-with-a-specific-attribute-value
->addFieldToFilter( array(
array('attribute'=>'manufacturer','eq'=>'143')
, array('attribute'=>'manufacturer','eq'=>'139')
))
;
echo "<p>There are ".count($_productCollection)." products: </p>";
echo '<div class="category-products">';
// List mode
if($this->getMode()!='grid') {
$_iterator = 0;
echo'<ol class="products-list" id="products-list">';
foreach ($_productCollection as $_product) {
?> <li class="item<?php if( ++$_iterator == sizeof($_productCollection) ): ?> last<?php endif; ?>">
<?php
$_product=Mage::getModel("catalog/product")->getCollection()
->addAttributeToSelect(Mage::getSingleton("catalog/config")->getProductAttributes())
->addAttributeToFilter("entity_id", $_product->getId())
->setPage(1, 1)
->addMinimalPrice()
->addFinalPrice()
->addTaxPercents()
->load()
->getFirstItem()
);
echo "Minimal price: ".$_product->getMinimalPrice()."<br>\n";
Since this isn't a single product's page and I'm using other template code, $this->setProduct() didn't do anything for me. I guessed if there was a set, there might be a get, so $_product=$this->getProduct() was the magic my template's price.phtml needed to work properly. Then I noticed I could just assign the Mage:: in setProduct() directly to $_product.
Thanks, Subsurf!!!
There is another way that will still allow you to use $this->getPriceHtml($_product, true); in your template, which then handles all the complex price display rules and puts "Starting at" before grouped product prices.
I'm using this for a custom featured product block. In a block class I have this function:
class MyCompany_Catalog_Block_Product_Featured extends Mage_Catalog_Block_Product_Abstract {
public function setGroupedProductPrice($group_product) {
$prices = array();
foreach ($group_product->getTypeInstance()->getChildrenIds($group_product->getId()) as $ids) {
foreach ($ids as $id) {
$product = Mage::getModel('catalog/product')->load($id);
$prices[] = $product->getPriceModel()->getPrice($product);
}
}
ksort($prices);
$group_product->setPrice(array_pop($prices));
}
}
Then in the template:
<?php
if ($_product->getTypeId() == 'grouped')
$this->prepareGroupedProduct($_product);
echo $this->getPriceHtml($_product, true);
?>
The following does the trick without using a product collection:
$priceResource = Mage::getResourceSingleton('catalogindex/price');
$select = $priceResource->getReadConnection()->select();
$select->from(
array('price_table' => $priceResource->getMainTable())
)
->where('price_table.entity_id = ?', $_product->getId())
->where('price_table.website_id = ?', Mage::app()->getStore()->getWebsiteId())
->where('price_table.customer_group_id = ?', Mage::getSingleton('customer/session')->getCustomerGroupId());
$result = $priceResource->getReadConnection()->fetchRow($select);
This code is adapted from \Mage_CatalogIndex_Model_Resource_Price::getMinimalPrices and made suitable for one single product.
I used this tutorial (especially Lesson 6 and 7) to create my own backend grid for Magento: http://www.pierrefay.com/magento-developper-guide-howto-tutorial-5
Everything works fine. I can create new data entries for my grid. If I click on an entry the VarienForm is displayed again but all the text fields are empty. This seems as if Magento thinks I want to edit all the text fields. But actually I want it to display the entry data first. But it only shows empty fields.
Can anyone help me out here? Thanks a lot!
There are a lot of things that could be wrong with your implementation, but it's impossible to say without seeing your code. Nevertheless, I'm going to try. That tutorial looks fine to me, but I haven't run the code so I can't be sure. I'm inclined to think you might have just missed something. Working in grids & tabs can be particularly delicate at the best of times.
It does sound to me like it's one of two things. It sounds like either
A) Your model data is not being stored in the registry. That means the problem is in this part of the code:
<?php
class Pfay_Test_Adminhtml_IndexController extends Mage_Adminhtml_Controller_Action
{
...
public function editAction()
{
$testId = $this->getRequest()->getParam('id');
$testModel = Mage::getModel('test/test')->load($testId);
if ($testModel->getId() || $testId == 0)
{
Mage::register('test_data', $testModel);
}
What this section of code does is 'registers' the selected model in Magento's registry. Later in the code, you'll see that it calls:
$form->setValues(Mage::registry('test_data')->getData());
to populate your form fields.
Try putting commands like these in the code above:
var_dump($testId);
die();
or
print_r($testModel);
die();
and running it again. Is $testId being set? Is $testModel being loaded? Is the if statement for the registry loading? If not, trace the problem back.
or it might also be
B) Your form is not prepopulating data because the column names are wrong.
Look where the code says:
<?php
class Pfay_Test_Block_Adminhtml_Test_Edit_Tab_Form extends Mage_Adminhtml_Block_Widget_Form
{
protected function _prepareForm()
{
$form = new Varien_Data_Form();
$this->setForm($form);
$fieldset = $form->addFieldset('test_form', array('legend'=>'ref information'));
$fieldset->addField('nom', 'text',
array(
'label' => 'Nom',
'class' => 'required-entry',
'required' => true,
'name' => 'nom',
)
);
You need to ensure that "nom" is in fact one of your model's attribute names. Did you change the attribute names when you created your test model and forgot to change it here? Change these values accordingly.
I hope that this helps you to solve your problem. Good luck!
I've been trying to edit the getAddressesHtmlSelect() function (found in code/core/Mage/Checkout/Block/Onepage/abstract.php) in order to enable the "new address" to display first in the dropdpown created.
I've located the place it needs to be changed in, but I can't figure out how to do that. Can anyone help? The code in question is:
$select = $this->getLayout()->createBlock('core/html_select')
->setName($type.'_address_id')
->setId($type.'-address-select')
->setClass('address-select')
->setExtraParams('onchange="'.$type.'.newAddress(!this.value)"')
->setValue($addressId)
->setOptions($options);
$select->addOption('', Mage::helper('checkout')->__('New Address'));
return $select->getHtml();
Look for magento block rewrite.
You need to rewrite Mage_Checkout_Block_Onepage_Billing and Mage_Checkout_Block_Onepage_Shipping
Just rewrite this blocks in your custom module and define new logic for getAddressesHtmlSelect
function
To set "New address" as default one:
Assembled working sample for you.
array_unshift($options, array('value' => '', 'label'=> Mage::helper('checkout')->__('New Address')));
$select = $this->getLayout()->createBlock('core/html_select')
->setName($type.'_address_id')
->setId($type.'-address-select')
->setClass('address-select')
->setExtraParams('onchange="'.$type.'.newAddress(!this.value)"')
->setOptions($options);
return $select->getHtml();
I created a new attribute (multiple select) with some values, everything works fine but when I want to delete all the selected values for a product, I get the message "The product attribute has been saved." but the values are still selected.
Notes:
I press Ctrl + Click to unselect the last value before I save.
I set the parameter Value Required of my attribute to No
If I save a product without any value selected yet, then no values get selected
My Indexes are properly refreshed
See below two screens, on the left the parameters of my attribute and on the right my multiple select.
I'm running out of ideas so thanks for your help.
This is a known (annoying) behaviour of the Magento Adminhtml forms.
The problem is that if no value is selected for the multiselect, no value for that attribute is posted when the form is submitted.
On the server side Magento then loads the model, sets all the posted attribute values on the model and saves it.
Because no value was posted the original value that was loaded on the model wasn't updated.
As a solution for attributes with a custom source model I tend to provide an empty option with a special option value (e.g. -1). That value must not be 0 or an empty string.
Then I specify a backend model for that attribute that checks for that special value in the _beforeSave() method. If it is found the backend model unsets the attribute on the model instance.
Here is an example:
Source Model:
class Your_Module_Model_Entity_Attribute_Source_Example
extends Mage_Eav_Model_Entity_Attribute_Source_Abstract
{
const EMPTY = '-1';
public function getAllOptions()
$options = array(
array('value' => 1, 'label' => 'One'),
array('value' => 2, 'label' => 'Two'),
array('value' => 3, 'label' => 'Three')
);
if ($this->getAttribute()->getFrontendInput() === 'multiselect')
{
array_unshift($options, array('value' => self::EMPTY, 'label' => ''));
}
return $options;
}
}
Backend Model:
class Your_Module_Model_Entity_Attribute_Backend_Example
extends Mage_Eav_Model_Entity_Attribute_Backend_Abstract
{
public function beforeSave($object)
{
$code = $this->getAttribute()->getAttributeCode();
$value = $object->getData($code);
if ($value == Your_Module_Model_Entity_Attribute_Source_Example::EMPTY)
{
$object->unsetData($code);
}
return parent::beforeSave($object);
}
}
If you find a better workaround please let me know.
There is a feature called <can_be_empty> you need to go to your system.xml and add this configuration into your file:
<can_be_empty>1</can_be_empty>
then inspect the element and remove the selected="selected" and hit save, now you can save the multi-select without any values.
Yes I found this a big pain in the bum too BUT it is an improvement on the previous bug which caused drop down attribute selections to be wiped if you tried to update attributes for several products at once.
Anyway, here is my what I do if I want to remove an option from products using a drop down attribute:
Go to Manage attributes
Click Manage Label Options
Add a temporary option to the list
Assign this new attribute option to all the products you want to
change
Delete the temporary attribute option
All solved.
Add a non existent option to html via chrome/firefox developer tool, select that option and save.
eg.
<option value="99999999">Click this to unselect option</option>
Just ran into this problem in Magento 1.7.0.2, my solution :
Use Firefox with Firebug
right-click the multiselect list, choose Inspect with Element and you'll see something like this at the bottom in Firebug :
XLarge
Double-click on selected, right-click, cut, no more selected attribute and just save the page.