I have a public function in my Observer folder. (I use this to browse through images) but the thing is, I don't want to use 'Mage::app()->setCurrentStore()'
What would be the alternative to browse through the given store without using setCurrentStore()?
function getImages($store, $v){
Mage::app()->setCurrentStore($store);
$products = Mage::getModel('catalog/product')->getCollection();
$products->addAttributeToSelect('name');
foreach($products as $product) {
$images = Mage::getModel('catalog/product')->load($product->getId())->getMediaGalleryImages();
if($images){
$i2=0; foreach($images as $image){ $i2++;
$curr = Mage::helper('catalog/image')->init($product, 'image', $image->getFile())->resize(265).'<br>';
}
}
}
}
foreach (Mage::app()->getWebsites() as $website) {
foreach ($website->getGroups() as $group) {
$stores = $group->getStores();
foreach ($stores as $store) {
getImages($store, $i);
$i++;
}
}
}
PS:
If I use setCurrentStore() my admin screws up :-S
I think this happens because you do not change store back to default when you exit from your function.
But a better solution is to use emulation environment:
function getImages($store, $v){
$appEmulation = Mage::getSingleton('core/app_emulation');
$initialEnvironmentInfo = $appEmulation->startEnvironmentEmulation($storeId);
//if $store is a model you can use $store->getId() to replace $storeId
try {
//your function code here
catch(Exception $e){
// handle exception code here
}
$appEmulation->stopEnvironmentEmulation($initialEnvironmentInfo);
}
All the code between environment emulation will act as like magento has set the desired store and outside everything should work normally.
Also please note that your code may throw exceptions so, you should use a try catch statement in order to make sure that last line from function that stop environment emulation will be executed every time.
You can use emulation for this. From http://inchoo.net/ecommerce/magento/emulate-store-in-magento/ :
$appEmulation = Mage::getSingleton('core/app_emulation');
//Start environment emulation of the specified store
$initialEnvironmentInfo = $appEmulation->startEnvironmentEmulation($storeId);
/*
* Any code thrown here will be executed as we are currently running that store
* with applied locale, design and similar
*/
//Stop environment emulation and restore original store
$appEmulation->stopEnvironmentEmulation($initialEnvironmentInfo);
I would like to point out that you are making a resource-expensive decision when you call load() in the collection iteration. It may be ok given your catalog size and running context, but you can accomplish the same without thrashing your database by calling $products->addAttributeToSelect('*'). This will grab all attributes and values though; given current case, you could get what you need as follows:
$products = Mage::getModel('catalog/product')->getCollection();
$products->addAttributeToSelect('name')
->addAttributeToSelect('media_gallery');
foreach($products as $product) {
$images = $product->getMediaGalleryImages();
if($images){
// your logic/needs
}
}
Related
I already sort the products in every category in the Admin Panel. It doesn't work accordingly in the Frontend but in the Homepage everything is fine. I checked on this link and I want to know how to make it enforce sorting by position STACK QUE
I'm also referring to the same list.phtml in Grid view. Can anyone help me? Thank you!
Just in case someone bump into the same error. Here is the solution I managed to make it work. First you need to copy the Toolbar.php from the mage core and paste it in the local mage pool and find the the function setCollection and replace it with the following code:
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->setOrder('position','asc');
}
else {
$this->_collection->setOrder($this->getCurrentOrder(),$this->getCurrentDirection());
}
}
return $this;
}
To create sort by position write below code after getting product collection in app/design/frontend//default/template/catalog/product/list.phtml
$_productCollection = new Varien_Data_Collection();
$sortedCollection = array();
foreach ($_defaultProductCollection as $key => $_product) {
if(!isset($sortedCollection[$positions[$_product->getId()]])){
$sortedCollection[$positions[$_product->getId()]] = array();
}
$sortedCollection[$positions[$_product->getId()]][] = $_product;
}
ksort($sortedCollection);
foreach ($sortedCollection as $_products) {
foreach ($_products as $_product) {
$_productCollection->addItem($_product);
}
}
Hope it will work for you.
I been trying for hours now to successfully rewrite Magento's build-in Autosuggest Function so it displays productnames instead of query history entries. I want nothing fancy, no product pictures and whatnot, just plain product name suggestions.
So to get the productnames, I created under app/code/local/Aw the folder CatalogSearch/Model and there created a file named Query.php. Inside that file I have the following class and rewritten method:
class Aw_CatalogSearch_Model_Query
extends Mage_CatalogSearch_Model_Query {
public function getSuggestCollection() {
$collection = $this->getData('suggest_collection');
if (is_null($collection)) {
$collection = Mage::getModel('catalog/product');
Mage::getSingleton('catalog/product_status')
->addVisibleFilterToCollection($collection);
$collection->getCollection()
->addAttributeToSelect('name')
->addAttributeToFilter('name', array('like' =>
'%'.$this->getQueryText().'%'))
->addExpressionAttributeToSelect('query_text', '{{name}}', 'name')
->addAttributeToSort('name', 'ASC')
->setPageSize(10)
->addStoreFilter($this->getStoreId());
$this->setData('suggest_collection', $collection);
}
return $collection;
}
};
I created the module xml file in app/etc/modules/ and the module configuration in app/code/local/Aw/CatalogSearch/etc/config.xml
All good so far, the overwritten method getSuggestCollection() is executed.
The problem comes in app/code/core/Mage/CatalogSearch/Block/Autocomplete.php, in the getSuggestData() method.
public function getSuggestData()
{
if (!$this->_suggestData) {
$collection = $this->helper('catalogsearch')->getSuggestCollection();
$query = $this->helper('catalogsearch')->getQueryText();
$counter = 0;
$data = array();
foreach ($collection as $item) {
$_data = array(
'title' => $item->getQueryText(),
'row_class' => (++$counter)%2?'odd':'even',
'num_of_results' => $item->getNumResults()
);
if ($item->getQueryText() == $query) {
array_unshift($data, $_data);
}
else {
$data[] = $_data;
}
}
$this->_suggestData = $data;
}
return $this->_suggestData;
}
When it iterates over the collection, I get a
Call to a member function getQueryText() on a non-object ...
The point I do not understand is that I have defined an alias field named 'query_text' in the collection query inside the getSuggestCollection() method. Even when I used something like getData('query_text') or $item->getQuery_text() to get the data of this field is not working.
I have the strong feeling, that the collection object is not valid as it supposed be within the getSuggestData() method of Mage_CatalogSearch_Block_Autocomplete class.
Can anybody point me out how to solve this issue? Is it not possible as above way to gather suggestions from the products collection and pass these to Autocomplete.php?
This is my first magento project, so please bear with me! I am really lost on this one!
Any hint is greatly apprecitated.
Using Magento 1.7.0.2 for this project.
Well, I found a solution. For anyone who might be interested in this, the problem stated in my question is located in the following lines
$collection = Mage::getModel('catalog/product');
Mage::getSingleton('catalog/product_status')
->addVisibleFilterToCollection($collection);
$collection->getCollection() ... // continue method chaining ...
I changed the code, so that the constructor and methods are chained all together, like this:
$collection = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('name') ... // continue method chaining
...
I added the filters for product_status, cataloginventory/stock and catalog/product_visibility with singleton calls right after the collection is available
In that way, everything works as expected.
For anyone else wanting to do something similar, I just rewrote app/code/core/Mage/CatalogSearch/Block/Autocomplete.php to my own module and made the search results query the sku and return product names. Your mileage may vary, however, my sku codes are sensible names rather than random digits so this worked for me.
public function getSuggestData()
{
if (!$this->_suggestData) {
$collection = $this->helper('catalogsearch')->getSuggestCollection();
$query = $this->helper('catalogsearch')->getQueryText();
$counter = 0;
$data = array();
foreach ($collection as $item) {
$_data = array(
'title' => $item->getQueryText(),
'row_class' => (++$counter)%2?'odd':'even',
'num_of_results' => $item->getNumResults()
);
if ($item->getQueryText() == $query) {
array_unshift($data, $_data);
}
else {
$data[] = $_data;
}
}
// Get products where the url matches the query in some meaningful way
$products = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('name')
->addAttributeToFilter('type_id', 'configurable')
->addAttributeToFilter('sku',array('like'=>'%'.$query.'%'))
->load();
foreach($products as $product) {
$_data = array(
'title' => $product->getName(),
'row_class' => (++$counter)%2?'odd':'even',
'num_of_results' => 1
);
// if ($item->Name() == $query) {
// array_unshift($data, $_data);
// }
// else {
$data[] = $_data;
// }
}
$this->_suggestData = $data;
}
return $this->_suggestData;
}
I did not need to rewrite Mage_CatalogSearch_Model_Query, just the code for the suggestions.
Is it possible to use zend pagination without calling the query every time i request a page from the pager?
When I hit a page number from the pager a request to the zzAction below is done and the query is fetched again. My query is huge and I don't want to fetch the query all over again. Am I missing something in the code.
Code:
Controller:
public function getOnePageOfEntries($array, $page=1) {
$paginator = Zend_Paginator::factory($array);
$paginator->setItemCountPerPage(6);
$paginator->setCurrentPageNumber($page);
return $paginator;
}
public function zzAction() {
...
$tt= $this->yyObject->xx(....);
$paginator = $this -> getOnePageOfEntries($tt, $page);
$this->view->paginator = $paginator;
}
Model:
public function xx(...){
try{
...
$stmt = $this->prepare("CALL sp_yy(...)");
....
$stmt->execute();
$result = $stmt->fetchAll();
if (is_null($result)) {
return null;
}
return $result;
}catch (ErrorsException $obj){
echo $obj;exit;
}//end try
}
View:
<?php
$config = Zend_Registry::get('appsConfig');
?>
<?php if (count($this->paginator)){ ?>
<?php foreach($this->paginator as $cc){ ?>
<?php echo $cc['id'] . '/';?>
<?php } ?>
<?php } ?>
<?php echo $this->paginationControl($this->paginator,
'Sliding','ff/my_pagination_control.phtml'); ?>
using Zend_Paginator_Adapater_DbSelect();
example:
$adapter = new Zend_Paginator_Adapter_DbSelect($data); //$data is database query
$pagination = new Zend_Paginator($adapter);
See more here:
Zend_Paginator_Adapter_DbSelect
Your current code artificially limits the utility of the array adapter and forces you to execute the whole query for every page. What you need to accomplish in your controller actions that consume this paginator is to only execute the query if the data doesn't already exist. Maybe something similar to:
//consider this to psuedocode as it has not been tested a represents an idea
public function zzAction()
{
//get page number
$page = $this->getRequest()->getParam('page');
//set session namespace, probably better to do this in init() method or bootstrap
$session = new Zend_Session_Namespace('paged')
//test for presence of persisted array
if (!isset($session->paginator)) {
//perform query
$arrayToPage = $this->yyObject->xx(....);
//persist result array
$session->paginator = $arrayToPage;
} else {
//retrieve persisted array
$arrayToPage = $session->paginator;
}
$paginator = $this -> getOnePageOfEntries($arrayToPage, $page);
$this->view->paginator = $paginator;
}
Using the DbTableSelect or DbSelect paginator adapter is often far more effiecient as it only queries for the data need to populate a specific page. This is very useful when your user wants to go form page 1 to page 7 ...
Another consideration when using a paginator is custom entity models. This is fairly easy to deal with in ZF:
<?php
class Record_Model_Paginator_Record extends Zend_Paginator_Adapter_DbTableSelect
{
//override getItems to customize the adapter to use a specific mapper to create entities
public function getItems($offset, $itemCountPerPage)
{
$rows = parent::getItems($offset, $itemCountPerPage);
$record = array();
foreach ($rows as $row) {
//initiate mapper
$recordEntity = new Application_Model_Mapper_Record();
//create entity models
$record[] = $recordEntity->createEntity($row);
}
//returns an array of objects, similar to a Zend_Db_Rowset object
return $record;
}
}
I hope this helps.
When I empty the 'image cache'
I run this script:
<?php
apc_clear_cache();
require '../app/Mage.php';
function getImages($store){
Mage::app()->setCurrentStore($store);
$products = Mage::getModel('catalog/product')->getCollection();
$products->addAttributeToSelect('name');
foreach($products as $product) {
echo 'product: '.$product->name.' IMAGE: '.$product->getImageUrl().'<br>';
}
}
foreach (Mage::app()->getWebsites() as $website) {
echo $website;
foreach ($website->getGroups() as $group) {
$stores = $group->getStores();
foreach ($stores as $store) {
getImages($store);
}
}
}
echo 'done';
What I want is that all the images in the cache get refreshed, but they don't (only when I load the urls in the browser)
How can I do this?
To create the images, you need to use the Mage::helper('catalog/image').
The problem is, that only the template file knows in which dimensions the image is needed, therefore you have to copy the code from the template (and all the other places where you have product images) and create the different sizes of the images via the helper.
One tipp: The create is in the __toString() method.
Can anybody give me a hint about how to load product's media gallery along with the collection?
I'm getting the collection like this:
$collection = Mage::getModel('catalog/product')->getCollection()
->addStoreFilter($storeId)
->addAttributeToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED);
foreach ($collection as $product) {
var_dump($product->getMediaGalleryImages());
}
But getMediaGalleryImages() returns null. I know that I can load each product separately with $product = Mage::getModel('catalog/product')->load($product->getId()) but I want to avoid this, because it causing unnecessary workload.
In case anyone’s looking for another approach on this, I found this to work (in just one case so no guarantees!):
Be sure to do $collection->addAttributeToSelect(’image’); first, then when looping through the collection products, do:
$attributes = $product->getTypeInstance(true)->getSetAttributes($product);
$media_gallery = $attributes[’media_gallery’];
$backend = $media_gallery->getBackend();
$backend->afterLoad($product); //this loads the media gallery to the product object
Not sure if all of this is necessary but I’m in a hurry. In my particular case I was trying to get the image url using $product->getImageUrl(); and this approach worked for me.
Hope it helps someone else.
I had to do the same recently, fastest method:
class My_Module_Block_Name extends Mage_Catalog_Block_Product_View_Abstract
{
/** #var null|Mage_Catalog_Model_Resource_Eav_Attribute */
protected static $_mediaGalleryBackend = null;
public function getGalleryImages()
{
$product = $this->getProduct();
$this->_getBackend()->afterLoad($product);
$collection = $product->getMediaGalleryImages();
return $collection;
}
/**
* #return Mage_Catalog_Model_Resource_Eav_Attribute
*/
protected function _getBackend() {
if (self::$_mediaGalleryBackend === null) {
$mediaGallery = Mage::getSingleton('eav/config')
->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'media_gallery');
self::$_mediaGalleryBackend = $mediaGallery->getBackend();
}
return self::$_mediaGalleryBackend;
}
}
try this
$collection = Mage::getModel('catalog/product')->getCollection()
->addStoreFilter($storeId)
->addAttributeToSelect(array('image', 'media_gallery'))
->addAttributeToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED);
foreach ($collection as $product) {
var_dump($product->getMediaGallery());
}
It can be used directly in loop:
foreach ($collection as $product) {
$product->getResource()->getAttribute('media_gallery')->getBackend()->afterLoad($product);
foreach ($product->getMediaGalleryImages() as $image) {
var_dump($image->debug());
}
}
You are going to have to use:
// Returns the Media Gallery Images
Mage::getModel(’catalog/product’)->load(productid)->getMediaGalleryImages();
Reference: http://www.magentocommerce.com/boards/viewthread/29639/
The secret sauce when working with custom collections involving products is the third parameter of init method... at least it was for me. This way I don't need to load the whole product and run expensive queries.
So, having my custom $product which represents an instance of Mage_Catalog_Model_Product but from my custom collection, I can do:
(string)Mage::helper('catalog/image')->init($product, 'small_image', $product->getImage())
I also needed to add the image attribute to my custom collection, and I did that by adding ->addAttributeToSelect(['image']) to it.
You can also resize your image accordingly:
(string)Mage::helper('catalog/image')->init($product, 'small_image', $product->getImage())->resize($width, $height=null)
Here is a function to add the media gallery to a collection:
// Source: http://www.magentocommerce.com/boards/viewthread/17414/#t141830
public function addMediaGalleryAttributeToCollection(Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection $_productCollection) {
$_mediaGalleryAttributeId = Mage::getSingleton('eav/config')->getAttribute('catalog_product', 'media_gallery')->getAttributeId();
$_read = Mage::getSingleton('core/resource')->getConnection('catalog_read');
$_mediaGalleryData = $_read->fetchAll('
SELECT
main.entity_id, `main`.`value_id`, `main`.`value` AS `file`,
`value`.`label`, `value`.`position`, `value`.`disabled`, `default_value`.`label` AS `label_default`,
`default_value`.`position` AS `position_default`,
`default_value`.`disabled` AS `disabled_default`
FROM `catalog_product_entity_media_gallery` AS `main`
LEFT JOIN `catalog_product_entity_media_gallery_value` AS `value`
ON main.value_id=value.value_id AND value.store_id=' . Mage::app()->getStore()->getId() . '
LEFT JOIN `catalog_product_entity_media_gallery_value` AS `default_value`
ON main.value_id=default_value.value_id AND default_value.store_id=0
WHERE (
main.attribute_id = ' . $_read->quote($_mediaGalleryAttributeId) . ')
AND (main.entity_id IN (' . $_read->quote($_productCollection->getAllIds()) . '))
ORDER BY IF(value.position IS NULL, default_value.position, value.position) ASC
');
$_mediaGalleryByProductId = array();
foreach ($_mediaGalleryData as $_galleryImage) {
$k = $_galleryImage['entity_id'];
unset($_galleryImage['entity_id']);
if (!isset($_mediaGalleryByProductId[$k])) {
$_mediaGalleryByProductId[$k] = array();
}
$_mediaGalleryByProductId[$k][] = $_galleryImage;
}
unset($_mediaGalleryData);
foreach ($_productCollection as &$_product) {
$_productId = $_product->getData('entity_id');
if (isset($_mediaGalleryByProductId[$_productId])) {
$_product->setData('media_gallery', array('images' => $_mediaGalleryByProductId[$_productId]));
}
}
unset($_mediaGalleryByProductId);
return $_productCollection;
}
An example of it's usage below:
$products = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect('*');
$this->addMediaGalleryToArray($products);