Magento : Get data from Varien_Data_Collection - php

Hiho everybody! I hope you'll give me a clue about this because I'm still noob with Magento.
I try to display a list of products I get in an array. In Mage/Catalog/Block/Product/List.php, I created a new Varien_Data_Collection() in which I pushed my products objects (with ->addItem($product)).
Then I return my custom collection and List.php class does his work with it to display the list of products.
When I call the page in my browser, I had the right number of displayed products and when I click on it to see the product page, I get the right page.
However, all the data (like the product name, the price, etc) are empty. I guess that the methods used by List class to catch these data fail with my Varien_Data_Collection object.
To illustrate, here is my code sample :
// getting a particular product
$mainProduct = Mage::getModel('catalog/category')->load($currentCat->getId());
$mainProduct = $mainProduct->getProductCollection();
$mainProduct = $mainProduct->addAttributeToFilter('product_id', $_GET['cat_id']);
// creating a custom collection
$myCollection = new Varien_Data_Collection();
foreach ($mainProduct as $product) {
// getting my particular product's related products in an array
$related = $product->getRelatedProductIds();
if ($this->array_contains($related, $_GET['cat_id'])) {
// if it suits me, add it in my custom collection
$myCollection->addItem($product);
}
}
return $myCollection;
And this is what I get in my list page :
When I var_dump($myCollection), I can see that ['name'], ['price'], etc fields are not referenced. Only ['product_id'] and many other fields I don't care about.
My very ultimate question is : how can I return a collection containing these products data to my List class ? I know that it is poorly explained but my English is very limited and I try to do my best :(

Calling ->getProductCollection() against a category only returns skeleton data for each product in the created collection. If you want full data for each of the products, you need to then load them, so in your foreach loop you would have:
$product = Mage::getModel('catalog/product')->load($product->getId());
However the way in which you are building the collection is not the best working practice - you should never have to create your own Varien_Data_Collection object, instead you should be creating a product collection as follows:
$collection = Mage::getModel('catalog/product')->getCollection();
Then before you load the collecion (which the foreach loop or calling ->load() against the collection will do as 2 examples), you can filter the collection according to your requirements. You can either do this using native Magento methods, one of which you are already using (addAttributeToFilter()) or I prefer to pull the select object from the collection and apply filtering this way:
$select = $collection->getSelect();
You can then run all of the Zend_Db_Select class methods against this select object to filter the collection.
http://framework.zend.com/manual/1.12/en/zend.db.select.html
When the collection has been loaded, the products inside it will then contain full product data.

first of all pelase do not use $_GET variable, use Mage::app()->getRequest()->getParams();
second why not try to build your collection correctly from the start?
here is what your code does:
$mainProduct = Mage::getModel('catalog/category')->load($currentCat->getId());
$mainProduct = $mainProduct->getProductCollection();
$mainProduct = $mainProduct->addAttributeToFilter('product_id', $_GET['cat_id']);
get one product, I mean you load a category then load a product collection, then filter by product id.. why not:
$mainProduct = Mage::getModel('catalog/product')->load($yourSearchedId);
I aslo do not see why you filter products by $_GET['cat_id'] which looks like a category id...
To conclude you can get more help if you explain exactly what you are trying to find. It looks like you are trying to find all products that have a given product as related. So why not set for that given product the related product correctly and get the related products collection.
$_product->getRelatedProductCollection();
UPDATE:
now that you cleared your request try this:
$relatedIds = $product->getRelatedProductIds();
$myCollection = Mage::getModel('catalog/category')
->load($currentCat->getId())
->getProductCollection();
$myCollection->addAttributeToFilter('product_id', array("in",$relatedIds));
//also addAttributeToSelect all attributes you may need like name etc
$myCollection->load(); //maybe you don't actualy need to load here
Please bear in mind I did not test this code it was written from teh top of my head, test it. But I hope you got the idea.

Related

Sonata admin dynamic admin list due to sortable

Please advise with the best solutions for such a task:
Entity hierarchy: Department -> Category -> Product
Currently in Category admin I have a list of ALL categories for ALL departments. BUT, I need to implement ordering feature for categories, which is done with Doctrine Sortable, so I'm having moving arrows in admin list view. BUT, because positions are grouped by Department of category, I have multiple "first" and "last" categories and moving is not that clear (since I cannot post images yet):
1. category [move down]
2. category [move down]
3. category [move down][move up]
4. category [move down][move up]
So, I added a default filter by department and now have shorter lists of categories by department. This has a lot of problems further on, so I'm looking for a better symfony/sonata/something else solution for:
Separate (dynamic) admin/list of categories by same department (same thing happens with products by category).
PS. Subclasses is not the solution, because I have only one category entity class, obviously.
I had the same issue, my solution was simple.
I wrote a Console Command Function, Where i did a repository call to retrieve all entries in that entity. Then I simply looped through them and assigned them an incremented position value. This will give structure to your admin listing. All the entries added post that command will automatically given the next position value and your sortable functionality will work perfectly.
Example :
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->em = $this->getContainer()->get('doctrine.orm.entity_manager');
$repo = $this->em->getRepository('AppBundle:Leadership');
$entries = $repo->findAll();
$index = 0;
foreach ($entries as $entry) {
$entry->setPosition($index);
$this->em->persist($entry);
$this->em->flush();
$index = $index + 1;
}
}
Hope this helps.

Get a collection of products base on list of product id

Given a product id, I can query the product using
Mage::getModel('catalog/product')->load($id);
What I have is a list of ids (comma separated), I can explode it, loop through each id, and run load($id) like above. I am concern a bit about the performance. Is this a different way to handle it, something like where clause, with an IN(id1,id2,id3,id4) kind of syntax. I google around, and I see this
Mage::getModel('catalog/product')->getCollection()->addAtributeToSelect('*')
I think I can add a filter to this, right? Had anyone solve a similar problem? Thank you very much.
1) Filter your collection using Product Ids you have :
$productIds = explode(',', "1,2,3,4,5,6");
$collection = Mage::getModel('catalog/product')->getCollection()-
>addAttributeToFilter('entity_id', array('in' => $productIds));
2) If you want to retrive only specific information like name & sku etc, you can add attribute to select, this means collection will only fetch the name from database tables, rather than whole product information, you can select with below code
$collection->addAttributeToSelect(array('name','sku'));
3) Make Sure All this code is written in blocks or models and not in Phtmls, or else it can definitely affect the page speed.
As par r requirement you can use finset function of magento which accepts array as parameter
Try to use addAttributeToFilter with or condition
$collection->addAttributeToFilter($attribute,
array(
array('finset'=> array('237')),
array('finset'=> array('238')),
array('finset'=> array('239')),
)
);
Or
$collection->addAttributeToFilter(
array(
array('attribute'=> 'attributecode','finset' => array('237')),
array('attribute'=> 'attributecode','finset' => array('237')),
array('attribute'=> 'attributecode','finset' => array('237')),
)
);

Magento admin module using Grid and _prepareCollection: looping through the collection makes sorting fail

I have made a small admin module that mirrors CMS->Page. The block for my admin page uses the code from app\code\core\Mage\Adminhtml\Block\Catalog\Product\Grid.php which gives me a grid that I can sort and filter just as I can on the original CMS->Page section.
This is the code in my block for my admin page:
protected function _prepareCollection()
{
$collection = Mage::getModel('cms/page')->getCollection();
$collection->setFirstStoreFlag(true);
$this->setCollection($collection);
return parent::_prepareCollection();
}
Which as I mentioned displays and allows sorting correctly.
However, when I try to modify the data in the rows by amending the above with:
protected function _prepareCollection()
{
$collection = Mage::getModel('cms/page')->getCollection();
$collection->setFirstStoreFlag(true);
foreach ($collection as $order) {
$order->setData( 'title', 'Hello world' );
}
$this->setCollection($collection);
return parent::_prepareCollection();
}
I can no longer sort or filter. Can anyone shed any light on whats happening and if I have the right way of changing row data? thanks
What's happening: your collection is being loaded. The foreach language construction triggers the load method of your collection, and it pulls the data from the db and fill the items.
Why is your sorting not working? Because the sorting is applied to collection after you've loaded it already. That means that the items are already present in your collection with default sorting order.
How should you do your thing? Right now I don't know what you want to accomplish with that setData method for each of the collection items. If you're going to use it while creating columns, just add string value in the column.
...->addColumn('title', array('default' => 'Hello world'))
If you would want to pull some more data that is present in the collection, you don't need to load it either, just use collection methods like
addAttributeToSelect (if it's EAV entities) or addFieldToFilter.

Adding attributes to collection requests in Magento

I hope someone can help me puzzle this one out. I'm trying to load some data out of a Magento catalog model using a collection. The code looks like this:
$model = Mage::getModel('catalog/product');
$collection = $model->getCollection();
$collection->addAttributeToSelect('short_description');
$collection->addFieldToFilter('SKU',array('like' => array('%EBOOK%')));
$collection->load();
var_dump($collection->getData());
This produces a dump of objects with all the fields in the flat catalog product table, but not the field that I have requested with the $collection->addAttributeToSelect() method. No matter what field I specify with this method (even '*'), I cannot get the collection to return anything other than its standard set of fields. I also can't unset any fields using $collection->removeFieldFromSelect(NULL) which is supposed to work.
Am I doing something stupid/wrong/both?
Thanks in advance.
This is because you call getData() on the collection, but not on a product of this collection.
I never really analyzed why this happens, but if you use
foreach ($collection as $product) {
var_dump($product->getData());
}
instead of
$collection->load();
var_dump($collection->getData());
you'll get the data you're expecting.

Magento get a product collection in an arbitrary order

I have developed a custom search engine for our Magento store and I am trying to load the product collection in a very specific order (I have ranked the results according to an algorithm I designed).
I can load the product collection correctly, however it is not in the order that I would like it to be in. Here is basically how it is working now:
My database query basically comes back with a PHP array of product IDs. For this example lets say it looks like this:
$entity_ids = array(140452, 38601 );
Now I can transpose the 140452 and the 38601 and the product collection comes back in the same order each time. I would like the product collection to be in the same order as the ID of the entity ids.
The code I am using to create my collection is as follows:
$products = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*')
->addAttributeToFilter('entity_id', array('in' => $entity_ids))
->setPageSize($results_per_page)
->setCurPage($current_page)
->load();
Is there a way to set the sort order to be the order of the $entity_ids array?
Collections inherit from the class
Varien_Data_Collection_Db
There's a method named addOrder on that class.
public function addOrder($field, $direction = self::SORT_ORDER_DESC)
{
return $this->_setOrder($field, $direction);
}
So, you'd think something like this should work for basic ordering
Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*')
->addOrder('entity_id');
However, it doesn't. Because of the complex joining involved in EAV Collections, there's a special method used to add an attribute to the order clause
Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection::addAttributeToSort
However again, this can only be used to add simple attributes. To create an arbitrary sort, you'll need to manipulate the Zend_Select object directly. I'm not a big fan of this, and I'm not a big fan of using custom mysql functions to achieve things, but it appears it's the only way to do this
I tested the following code on a stock install and got the desired results. You should be able to use it to get what you want.
$ids = array(16,18,17,19);
$products = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('*')
->addAttributeToFilter('entity_id',$ids);
//shakes fist at PDO's array parameter
$ids = array_map('intval', $ids);
$products->getSelect()->order("find_in_set(e.entity_id,'".implode(',',$ids)."')");
foreach($products as $product)
{
var_dump($product->getEntityId());
var_dump($product->getSku());
}
There is no way to sort arbitrarily in SQL so you would have to sort the results in PHP afterwards. Then the bigger problem is you are using page sizing to limit the number of results being returned, some of the records you want might not be returned because of this.
The better solution is to add an attribute to products which you can then use to sort by. Products in categories already have a 'position' value which is used in this way. Then you only need to use the addOrder()addAttributeToSort() method that Alan suggested but with your custom attribute.
(Explanation is hurried, let me know if not clear enough)

Categories