magento Bug with ressource collection - magento

I noticed something strange in magento behavior.
it looks like either a bug, or i missed something...
I do a simple query to retreive products
$collection = Mage::getResourceModel('catalog/product_collection')
->addAttributeToSelect('price')
->addAttributeToSelect('sku')
->addAttributeToSelect('name')
->addAttributeToFilter(array(array(
'attribute' => 'my_attribute',
'eq' => 'my_value' )
));
//Then I update
foreach($collection as $_product){
$_product->setData("name","my_other_val");
$_product->save();
}
Magento will not only update "name", it will update ALL required fields and set the default value!!
So for instance, it changes the "visibility" attribute to "search, catalog" while the products were having another visibility!!
I have the mess now with my configurable products, and it changes also other attributes.
How do you explain this?
I did a reverse and the whole list of attributes is retrieved while saving the product, in this method:
walkAttributes
it does this:
case 'backend':
$instance = $attribute->getBackend();
Which retrieve ALL attributes. Since they have no value (they are not in the addAttributeToSelect section) then it uses the default value.
One soluton is to add
->addAttributeToSelect('visibility')
and all required attributes.
but too dangerous, I could miss one, or a new attribute could be added with the required attribute right?
For me it's a bug, because the default value should only apply to NON existing attribute value, but magento does not do a check, it does this query which either INSERT or UPDATE..
SQL: INSERT INTO catalog_product_entity_int (entity_type_id,attribute_id,store_id,entity_id,value) VALUES (?, ?, ?, ?, ?), (?, ?, ?, ?, ?), (?, ?, ?, ?, ?), (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)
...
Thanks,
Rod

This is not a bug. It's actually a feature. When loading a collection of products, not all attributes are loaded for the product, for performance reasons.
In order to be able to save it you need to call $product->load($id) and then $product->save().
Also you have to make sure that you are running your script under the store with id 0 (admin). The save works only for that. Add this at the top of your script
Mage::app()->setCurrentStore(Mage::getModel('core/store')->load(Mage_Core_Model_App::ADMIN_STORE_ID));
But here is an other idea. Don't use save. It's slow and you can do some damage if you are not careful. Use this instead.
Mage::getSingleton('catalog/product_action')->updateAttributes($productIds, $attributes, $storeId);
This works even if you are not on the admin store and it only changes the attributes you specify. This is what the parameters mean:
$productIds - an array with the product ids you need to change. array(12, 17, 219)
$attributes - an array with what you want to change array('name'=>'Your name here', 'description'=>'Your description here')
$storeId - the id of the store for which you do the change. Use 0 for default values.
Note: If you want to set different attribute values for different products you need to call this for each attribute value.
For example calling:
Mage::getSingleton('catalog/product_action')->updateAttributes(array(12, 17, 219), array('name'=>'Your name here'), 0);
will change the name for products with id 12,17 and 219

What you should do is update the product, from the collection result, after you load it explicitly.
Here's an example of what I mean:
$product_collection = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToFilter(array(array(
'attribute' => 'my_attribute',
'eq' => 'my_value'
)));
foreach ($product_collection as $p) {
$product = Mage::getModel('catalog/product')->load(
$p->getId()
);
$product->setName('NEW NAME HERE');
try {
$product->save();
echo 'Product '. $product->getSku() .' has been updated.<br />';
} catch (Exception $e) {
echo 'Failed to update product '. $product->getSku() .' - '. $e->getMessage() .'<br />';
}
}
I am not saying this is the ONLY way to do this; however this works as intended.

Related

Combine three unrelated tables and show results on single page

I have three tables in laravel5.6 vuejs2 application
Feed (id, message, created_at, updated_at, created_by, updated_by)
Classifieds (id, category_id, title, description, keywords, created_at, updated_at, created_by, updated_by)
News (id, title, subtitle, content, keywords, created_at, created_by, updated_at, updated_by).
I want to create a backend like facebook where i can list all the latest posts as stories. In this case i want to post feed, classifieds and news with load more button. How to achieve this?
$feed = Feed::latest()->paginate(10);
$classifieds = Classifieds::latest()->paginate(10);
$news = News::latest()->paginate(10);
$stories = array_merge(['feed', 'classifieds', 'news']);
this doesn't work and i know this is not the right way to do. I'm a learner in laravel and did not find much help on this.
Could anyone tell me how to do this so that i can list all the three tables which are not related on the dashboard page as latest stories.
Creating ModelCollection is cool way to do that.
$collection = new ModelCollection();
$feed = Feed::latest();
$classifieds = Classifieds::latest();
$news = News::latest();
$stories = $collection->merge($feed)->merge($classifieds)->merge($news);
EDIT:
You can do pagination things like that.
$page = $request->get('page');
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
$collection = new ModelCollection();
$feed = Feed::latest();
$classifieds = Classifieds::latest();
$news = News::latest();
$stories = $collection->merge($feed)->merge($classifieds)->merge($news);
$items = new LengthAwarePaginator($stories->forPage($page, 30), $result->count(), 30, $page,['path' => '/search']);
LengthAwarePaginator can make this, usage is
LengthAwarePaginator(currentPageItems, collection_count, perPage, currentPage)
I hope it was help. Edit section could not work, i just told it because lengthawarepaginator can gives you an idea.

Laravel dynamic where clauses

I would like to create a dynamic query with laravel ORM. In my example:
$query = Task::orderBy('customer_family_name');
if (array_key_exists('date', $request->filter)) {
if($request->filter['date'] != null) {
$query = $query->whereDate(....);
}
}
..... more filters
if (array_key_exists('search', $request->filter)) {
if(strlen($request->filter['search']) > 1) {
$searchText = $request->filter['search'];
$query = $query->where('customer_family_name', $searchText.'%');
}
}
}
$Tasks = $query->get();
But it is not working as expected. I thought that laravel will push all where elements, so that ALL where clauses must be met.
But the search for a family name is not working. Do i something wrong? How can i combine it correct? Is there a way to display the SQL Query with orm?
Edit: ok found out that is another problem, my query looks like:
"select * from [tasks] where [state_id] in (?, ?, ?, ?, ?, ?) and [customer_family_name] = ? and [regulator_user_id] in (?, ?, ?) or [regulator_user_id] is null order by [customer_family_name] asc"
And my filter:
if (array_key_exists('regulator', $request->filter)) {
$regulator_array = explode(",", $request->filter['regulator']);
if(is_array($regulator_array)) {
$query->whereIn('regulator_user_id', $regulator_array)->orWhereNull('regulator_user_id');
}
}
It looks like that the orWhereNull is the problem. I would like to return every row where
regulator_user_id is in that array
AND all where regulator_user_id IS NULL
But all other filters should work as expected.
How can i do that?
You should not be re-saving to your $query variable:
$query = $query->whereDate(....);
Change it to this (and for your other filters):
$query->whereDate(....);
Your final command :
$Tasks = $query->get();
Will then return the proper data according to the extra queries that were added to your $query object.
Ok ill found the correct solution:
Ill need my own query function to combine an Where with an orWhereNull:
if(is_array($regulator_array)) {
$query = $query->where(function($query) use($regulator_array) {
$query->whereIn('regulator_user_id', $regulator_array)
->orWhereNull('regulator_user_id');
});
}
Now the query works fine. Thanks for all the answers.
As mentioned from Jhonny Walker you shouldn't save every time the $query but use the $query->where each time.
One line before the $Tasks = $query->get();
try to debug the mysql command that is generated with the following command.
dd($query->toSql());
If you want paste the result in order to help you with the where clauses.
Or you can use this sweet eloquent method when(), so you don't have to use if statement and break the method chaining, instead you got fluent query building.
Eloquent when()

Get all existing values of attribute in the eav table

I have the attribute "country" which is varchar, it is set in every product that I have.
I need to have a list of all the values that are set for this attribute thought all the products. Basically what I want to do is the equivalent of this query:
SELECT `value` FROM `catalog_product_entity_varchar` WHERE attribute_id=147
How to do this the magento way?
The thing I am doing at the moment is to get the collection of all the products and loop through them, which is not an option.
Check below code:-
$attribute = Mage::getSingleton('eav/config')->getAttribute('catalog_product', 'color');
if ($attribute->usesSource()) {
$options = $attribute->getSource()->getAllOptions(false);
}
What about this?
$collection = Mage::getModel('catalog/product')->getCollection();
$collection->addAttributeToSelect('sku');
$collection->addAttributeToSelect('country');
foreach ($collection as $product) {
// ... Your stuff here
}

Issues with saving product on multiple stores in magento

I am having an issue while saving a product in Magento.
I have a product and have multiple stores (multiple groups and views).
Example Website
Store Group 1
English
French
German
Store Group 2
Bangla
Hindi
Say I am editing the product for store French and clicked on save. But the thing is, the newly saved/entered value for store French is copied to all stores (English, German, etc.).
I need to save the values (even for price values) only for specified stores. Not to all of the stores.
If the solution is a programmatic way, I agree to go with that.
Thanks
Newly Added:
I also want to know about saving price, cost, special price, grouped price, tier price
before saving set the storeId of the store you want to save the product for, same is working with loading a product
$product->setStoreId('2')->setName('Name for Store 2')->save();
$product->setStoreId('2')->load(17)->getName()
good luck!
I have solved the price, cost, special price, grouped price, tier price saving issues myself.
I searched on Google and got an extension called Store View Pricing for free. That solved my 80% of the problem. It just gives the opportunity to save prices on a per store basis.
But, to make the other fields (cost, special price, grouped price, tier price) also behave like the price field/attribute, I needed to change the Scope (is_global: column name at DB) to Store View (0: set to DB). I did run the following query on DB:
SELECT ea.attribute_id, ea.entity_type_id, ea.attribute_code, ea.frontend_label, cea.is_global
FROM `eav_attribute` AS ea
INNER JOIN `catalog_eav_attribute` AS cea ON ea.attribute_id = cea.attribute_id
WHERE `attribute_code` LIKE '%price%'
ORDER BY ea.`attribute_id` ASC
I found the attributes what I needed. And changed the is_global value of them to 0.
After doing so, all is working fine as I need. I know this kinda manual process. I am trying to get a programmatic solution.
I had this issue in 1.5
i overide app\code\core\Mage\Catalog\Model\Resource\Eav\Mysql4\Abstract.php
protected function _insertAttribute($object, $attribute, $value) {
/**
* save required attributes in global scope every time if store id different from default
*/
$storeId = Mage::app()->getStore($object->getStoreId())->getId();
if ($attribute->getIsRequired() && $this->getDefaultStoreId() != $storeId) {
$bind = array(
'entity_type_id' => $attribute->getEntityTypeId(),
'attribute_id' => $attribute->getAttributeId(),
'store_id' => $this->getDefaultStoreId(),
'entity_id' => $object->getEntityId(),
'value' => $this->_prepareValueForSave($value, $attribute)
);
$this->_getWriteAdapter()->insertOnDuplicate($attribute->getBackend()->getTable(), $bind, array('value'));
}
to
protected function _insertAttribute($object, $attribute, $value) {
/**
* save required attributes in global scope every time if store id different from default
*/
$storeId = Mage::app()->getStore($object->getStoreId())->getId();
if ($attribute->getIsRequired() && $this->getDefaultStoreId() != $storeId) {
$table = $attribute->getBackend()->getTable();
$select = $this->_getReadAdapter()->select()
->from($table)
->where('entity_type_id = ?', $attribute->getEntityTypeId())
->where('attribute_id = ?', $attribute->getAttributeId())
->where('store_id = ?', $this->getDefaultStoreId())
->where('entity_id = ?', $object->getEntityId());
$row = $this->_getReadAdapter()->fetchOne($select);
if (!$row) {
$bind = array(
'entity_type_id' => $attribute->getEntityTypeId(),
'attribute_id' => $attribute->getAttributeId(),
'store_id' => $this->getDefaultStoreId(),
'entity_id' => $object->getEntityId(),
'value' => $this->_prepareValueForSave($value, $attribute)
);
$this->_getWriteAdapter()->insertOnDuplicate($attribute->getBackend()->getTable(), $bind, array('value'));
}
}

Magento Products Sort by Category

I need to sort the product list in productCollection by Category.
I need this facility because in my site i have to display New Products and Special Products in separate page(tab).
I could even use addAttributetoSort() for sku, news_to_date, news_from_date, name, price etc. But could not use category_id. It has no effect.
I know I can set it from admin panel in Manage Attribute. In Manage Attribute price, name is available to sort. Even I can create sku, news_to_date etc as attribute in from Manage Attribute. But when I create attribute code as category or category_id, it comes with an error like
System reserved category/category_id
Means I could not use category or category_id as attribute from Manage Attribute (As they are reserved by the System).
Can anyone suggest how to do this?
I'm sure this will sort by category:
$collection = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSort('category_ids', 'ASC')
->addAttributeToSelect(array('name', 'sku', 'status', 'visibility', 'is_saleable'))
;
That works for me:
$category ='1503'; // Is in my case Categoryid of New Products Category
$_products = Mage::getResourceModel('catalog/product_collection')
->joinField('category_id','catalog/category_product','category_id','product_id=entity_id',null,'left')
->addAttributeToFilter('category_id', array('in' => $category))
->addAttributeToSelect('*');
$_products->load();
Regards

Resources