Adding attributes to collection requests in Magento - php

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.

Related

Laravel Duplicated queries in foreach

hey everyone I'm trying to make my laravel app faster and what I see in debug bar is so many duplicated queries
and why this happens? because I'm calculating discount in the product model and when I'm getting products in my foreach I use $product->discount() and let's see what's happening in the Product model and discount function, there it is
public function discount(){
$seller = ProductSeller::orderBy('price','asc')-where('product_id',$this->id)->first();
$sellerDiscount = Discount::where('seller_id',$seller->seller_id)-latest('created_at')->first();
$brandDiscount = Discount::where('brand_id',$this->brand_id)-latest('created_at')->first();
$categoryDiscount = Discount::whereIn('category_id',$cateName)-latest('created_at')->first();
}
these are the queries that are duplicating in my category pages in a foreach any idea to prevent the duplicating of each query?
Yes it happens, because you are calling function($product->discount()) instead of getting data from that. Try to use eager load to fix this.
And then get data like this,
$product->seller
You can do this from Your main query and put condition in that.

Getting a specific row other than first()

I'm having a little problem with laravel that may be easily solved. To be short, the situation is: I have two tables, one for the users and the other one for products that has a column 'user_id' so I can identify the associated user.
In Laravel, I can use
$user = Sentry::getUser(); //Or Auth::user() if you're not using Sentry
$products = DB::table('table2')->where('user_id',$user->id);
And that should give me every product that user has. Good.
Now I want to show the products individually on screen, but unfortunately that doesn't work. It seems I can't echo this information in a string because it's made of multiple rows. I get
Object of class Illuminate\Database\Query\Builder could not be converted to string
For the solution, since the maximum associated products I allowed in the system is 3, I came up with the idea of getting each row separately and echoing them. For the first one, it's simple: $products->first(); but I have no idea on how to get the other two.
And maybe I'm being a newbie here, but I don't think I can use the products' id info since $products->id returns an error.
Can anyone help me?
Thanks in advance!
You want to use take, limit the number of results to three and then print out every one with a foreach loop. Docs: Laravel Queries, see skip and take.
$products = DB::table('table2')->where('user_id',$user->id)->take(3)->get()
Then, inside your view, you can just iterate through this data:
#foreach($products as $p)
Alternatively, in your PHP you can iterate through this data using something like:
foreach ($products as $product) { var_dump($product); }
(You are getting that error because you are trying to output a result object as a whole, and not the data it contains. Using the loop actually fetches the data from the result object so you can then use the loop variable ($product) normally.)
To get data from database you can use one one those methods: all, get, or first.
Using all:
$products = DB::table('table2')->all();
you are getting all the products.
Using first you can use conditions but you will get only first record that fulfil conditions:
$products = DB::table('table2')->where('user_id',$user->id)->first();
Using get you can use conditions and you will get all the records that fulfil those conditions:
$products = DB::table('table2')->where('user_id',$user->id)->get();
So in your case you want to use get to get data from database.
When you are using
$products = DB::table('table2')->where('user_id',$user->id);
Then $products is an array and you do not have to echo an array.
To display the products you need to use a foreach loop like below
foreach ( $products as $key => $product ) {
var_dump( $product );
}
If you want to show only three products, then you can use for loop inside foreach.
You can learn more about foreach from below link
http://php.net/manual/en/control-structures.foreach.php

Magento : Get data from Varien_Data_Collection

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.

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.

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