Magento Join Product Collection to Custom Table on Custom Attribute - magento

In Magento, how can a standard product collection be joined to a custom table on a custom attribute.
So for example, I have a custom product attribute warehouse_id and a custom table warehouse and now want a collection with product name, warehouse name.
Code example:
$collection = Mage::getModel('catalog/product')
->getCollection()
->joinAttribute('warehouse_id', 'warehouse/warehouse', 'warehouse_id', null, 'inner');
var_dump($collection);
However, this does not work because warehouse is a custom table/collection and is not in the
eav_entity_type table.
Turning the problem around thought this might work:
$collection = Mage::getModel('warehouse/warehouse')->getCollection()->joinAttribute( etc.
However, joinAttribute is not a method on my custom collection.
Have read every example, every post, wiki example etc. and cannot grasp how to do this. A specific code example would be perfect.

I think joinAttribute could possibly work if some of the parameters were changed. I'm a little more familiar with join and joinLeft which are more like working directly with mysql queries.
$collection = Mage::getModel('catalog/product')
->getCollection()->getSelect()
->joinLeft(array('warehouse_id'=>'warehouse'),'e.warehouse_id = warehouse_id.warehouse_id', array('name'));
var_dump($collection);
This assumes that the name of your custom table is warehouseand that the field in your custom table that corresponds to the custom warehouse_id attribute is also called warehouse_id. This also assumes that the field for the name of the warehouse in your custom table is name. You might have to do something like this instead of naming the table directly:
$collection = Mage::getModel('catalog/product')
->getCollection()->getSelect()
->joinLeft(array('warehouse_id'=>$this->getTable('warehouse/warehouse')),'e.warehouse_id = warehouse_id.warehouse_id', array('name'));
var_dump($collection);
If neither work, replace the var_dump line with the line:
echo $this->escapeHtml($collection->getSelect());
And post the resulting sql query here.

Related

Mass update of product attribute text

A previous developer at my work assigned all products an attribute called "delivery_text", which holds a string of html. For each product, the main body of this string is unique. However each one contains a link to a specific url (the link is the same for every product). Due to changes in our site layout, we now need to update this link for every product, without impacting the rest of the attribute text.
Obviously, I don't want to have to manually edit thousands of products just to change the url of the link in each product's delivery_text string.
Is there a way I can programmatically parse the contents of this attribute for every product, and replace instances of links to oldsite.com/old-url with links to newsite.com/new-url?
I presume I can do it with some kind of database trickery, and have access to phpmyadmin via cpan, but I have no idea how to actually go about doing so.
Note that I don't consider this a duplicate question, as while there are many "how do i mass update attribute values for all products" type questions, I have yet to find one that asks "how do i search and replace a specific character sequence within the attribute text for each product".
If you want to do this from the database, you could do something like this:
Find the attribute values
SELECT at.* FROM catalog_product_entity_varchar AS at
INNER JOIN eav_attribute AS ea
ON ea.attribute_id = at.attribute_id
WHERE ea.attribute_code = 'delivery_text';
This should show you a list of all of the attribute values in your database currently.
Update the attribute values
Once you've found all the values, you can use the same query to replace the URL within the values:
UPDATE catalog_product_entity_varchar AS at
INNER JOIN eav_attribute AS ea
ON ea.attribute_id = at.attribute_id
SET at.value = REPLACE(at.value, 'http://oldurl.com', 'http://newurl.com')
WHERE ea.attribute_code = 'delivery_text';
Doing it without SQL
You could also do this using the Magento model APIs. Start by gathering the data you need, the same way as above:
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
$collection = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('delivery_text');
foreach ($collection as $product) { /** #var Mage_Catalog_Model_Product $product */
var_dump($product->getDeliveryText());
}
Then you can update the attribute within your loop. Here you need to specify a store ID, so I've assumed you'll do this at the admin (global) scope:
$old = 'http://oldurl.com';
$new = 'http://newurl.com';
foreach ($collection as $product) { /** #var Mage_Catalog_Model_Product $product */
Mage::getModel('catalog/product_action')
->updateAttributes(
array($product->getId()),
array('delivery_text' => $new),
Mage_Core_Model_App::ADMIN_STORE_ID
);
}
Please note: Using Mage_Catalog_Model_Product_Action::updateAttributes will work quickly and efficiently to update product attributes at a given store level, but if you are not using the admin store level then you may not be able to retrieve the value in a collection because this method doesn't create a default value - it just does what it says it will; create/update a value at a given store level.

joining custom tables using magento commands

I have been trying to join two custom table using magento's commands. After searching i came across this block of generic code
$collection = Mage::getModel('module/model_name')->getCollection();
$collection->getSelect()->join( array('table_alias'=>$this->getTable('module/table_name')),
'main_table.foreign_id = table_alias.primary_key',
array('table_alias.*'),
'schema_name_if_different');
Following this as template I have tried to join my tables together but have only returned errors such as incorrect table name or table doesn't exist or some other error.
Just to clear things up can someone please correct me on my understanding
$collection = Mage::getModel('module/model_name')->getCollection();
Gets an instance of your model. Within that model is the table that holds the required data (for this example I shall call the table p)
$collection->getSelect()
Select data from table p
->join()
Requires three parameters to join two table together
PARAM1
array('table_alias'=>$this->getTable('module/table_name'))
'the alised name you give the table' => 'the table you want to add to the collection (this has been set up in the model folder)'
PARAM2
'main_table.foreign_id = table_alias.primary_key'
This bit i don't get (it seems straight forward though)
my main table (p) doesn't have a foreign id (it has it's primary key - is that also its foreign id)?
has to be equal to the alised name you gave in param1
PARAM3
'main_table.foreign_id = table_alias.primary_key'
get all from alised name
Where have I gone wrong on my understanding?
Please have a look in below sql join statement, I am using it in my project and it is working perfectly.
Syntax
$collection = Mage::getModel('module/model_name')->getCollection();
$collection->getSelect()->join(Mage::getConfig()->getTablePrefix().'table_name_for_join', 'main_table.your_table_field ='.Mage::getConfig()->getTablePrefix().'table_name_for_join.join_table_field', array('field_name_you_want_to_fetch_from_db'));
Working Query Example
$collection = Mage::getModel('module/model_name')->getCollection();
$collection->getSelect()->join(Mage::getConfig()->getTablePrefix().'catalog_product_entity_varchar', 'main_table.products_id ='.Mage::getConfig()->getTablePrefix().'catalog_product_entity_varchar.entity_id', array('value'));
Hope this will work for you !!

Magento - filtering customers based on an EAV (custom) attribute in RMA Grid

I am using Magento 1.4.1.1 and the AW RMA extension over it.
With this extension installed I see an RMA requests grid and Pending RMA requests grid in admin.
I want to be able to filter requests in this grid based on an EAV (custom) attribute e.g., customer_referrer_id which is stored in customer_entity_varchar
The extension has a table aw_rma_entity in which it also keeps customer_id. The RMA Grid fetches data like this:
$collection = Mage::getModel('awrma/entity')
->getCollection();
ATTEMPT 1
I have attempted to join the customer entity with this table as follows:
$collection->getSelect()->join('customer_entity', 'customer_id = customer_entity.entity_id', array('entity_id' => 'customer_entity.entity_id'));
RESULT 1
This just shows a page with no grid. If it had worked I would have then attempted to join customer_entity_varchar to apply the filter on the customer_referrer_id field.
ATTEMPT 2
The other attempt I have made is to load the customer collection first and then join RMA entity data to it as in the following code:
$collection = Mage::getResourceModel('customer/customer_collection');
$collection->joinRight('awrma/entity', 'customer_id=entity_id', array('*'));
RESULT 2
The second attempt generates the following error:
Error Message--------------------------------> Item
(Mage_Customer_Model_Customer) with the same id "XXX" already exist";
This is despite of the fact that it had worked for me earlier in this way after a similar question for filtering results in the Sales Orders Grid
This is probably not the best solution, but it will most likely work:
$collection = Mage::getModel('awrma/entity')->getCollection();
$collection->getSelect()->joinInner('customer_entity_varchar', 'customer_id=entity_id', array('attribute_id' =>'attribute_id','value'=>'value') );
$logged_in_admin = Mage::getSingleton('admin/session')->getUser()->getEmail();
$customer_referrer_attribute_id = //Set this equal to attribute_id FROM table eav_attribute WHERE attribute_code = "customer_referrer_id"
$collection->addFieldToFilter('attribute_id', $customer_referrer_attribute_id );
$collection->addFieldToFilter('value', $customer_referrer_id);

inner join between Simple and Grouped products filter by Attributes of simple product (Magento)

I am trying to show Grouped Products list based on attributes of its associated simple products. Right now i am doing like below
- Create a collection of simple products and add attribute filters like color,brand etc., like below
$productCollection = Mage::getModel('catalog/product')->getCollection()
->addStoreFilter(Mage::app()->getStore())
->addAttributeToFilter($aname,$avalue)
->addAttributeToFilter('type_id', array('eq' => 'simple'))
->addAttributeToFilter(ATTRIBUTE_CODE,ATTRIBUTE_VALUE_ID);
(->addAttributeToFilter('color',5))
- Obtain all resultant ids and get its parent ids using the below
Mage::getModel('catalog/product_type_grouped')->getParentIdsByChild($productCollection->getAllIds());
- Read the parent ids from above object and show the result in a custom grid that i created
- Created a paging logic for parent ids and do paging in view file
This logic really consumes more time, is there any way that i can do all these in a single collection? may be inner join sort of method between simple and grouped products!
Please suggest.
Thanks,
Balan
It depends of attributes you want to filter with.
Magento already have prepared data for filtering grouped products by simple product attributes. It is stored in catalog_product_index_eav.
But these attributes should be marked as 'Filterable' in magento admin.
/** #var $productCollection Mage_Catalog_Model_Resource_Product_Collection */
$productCollection = Mage::getModel('catalog/product')->getCollection();
$productCollection->addStoreFilter(Mage::app()->getStore())
->addAttributeToFilter('type_id', 'grouped');
$resource = Mage::getModel('core/resource');
$productCollection->getSelect()
->join(array('filter_eav' => $resource->getTableName('catalog/product_index_eav')),
"e.entity_id = filter_eav.entity_id AND filter_eav.attribute_id = {$attributeId} AND value = {$valueId}",
array(''));
Originally this table is used in Mage_Catalog_Model_Resource_Layer_Filter_Attribute function applyFilterToCollection. But it requires filter object as param.
This might be what you are looking for. It works well.
http://www.commerceextensions.com/filterable-grouped-products.html

How to select specific fields with aliases using joinTable or joinField in Magento

I want to pre-filter* data in the invoice grid visible in Magento's admin panel.
Here is a question that I asked earlier, and this one is related to the solution presented for that, hence it might act as a good explanation.
So, I am modifying the Mage_Adminhtml_Block_Sales_Invoice_Grid::_prepareCollection method so that it first fetches customer referred by the logged in admin. Then it will fetch orders from these customer(s) - ideally only the order id's - Then join this collection to sales/order_invoice_grid, to get invoices to be listed for this admin.
Based on the last answer and using these docs, following are 3 ways I have tried joining this information: (Code Sample 1)
$collection = Mage::getResourceModel('customer/customer_collection');
$collection->joinTable('sales/order_grid', 'customer_id=entity_id', array('*'));
$collection->joinTable('sales/invoice_grid', 'order_id=main_table.entity_id', array('*'));
When I do the above, I see the following error:
A joint field with this alias (0) is already declared.
#0 /var/www/magento/app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php(706): Mage::exception('Mage_Eav', 'A joint field w...')
#1 /var/www/magento/app/code/local/Myproject/Adminhtml/Block/Sales/Invoice/Grid.php(41): Mage_Eav_Model_Entity_Collection_Abstract->joinTable('sales/invoice_g...', 'order_id=main_t...', Array)
#2 /var/www/magento/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php(576): Myproject_Adminhtml_Block_Sales_Invoice_Grid->_prepareCollection()
#3 /var/www/magento/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php(582): Mage_Adminhtml_Block_Widget_Grid->_prepareGrid()
If I remove the second call to joinTable, the above code works, but it is not what I want.
The other method I tried is with this code:
$collection = Mage::getResourceModel('customer/customer_collection');
$collection->joinTable('sales/order_grid', 'customer_id=entity_id', array('entity_id as order_entity_id'));
$collection->joinTable('sales/invoice_grid', 'order_id=main_table.entity_id', array('*'));
Here the error appears in the second line, where I am actually trying to alias the field order.entity_id so that it does not conflict with invoice tables entity_id. However that produces an error like:
Item (Mage_Customer_Model_Customer)
with the same id "1" already exist
I only need order id's so that I can get related invoices, which suggests that I can also use joinField function, which I tried as follows:
$collection = Mage::getResourceModel('customer/customer_collection');
$collection->joinField('order_entity_id', 'sales/order_grid', 'entity_id', 'customer_id=entity_id' , null, 'left');
But it gives me the following error:
Item (Mage_Customer_Model_Customer) with the same id "1" already exist
I am looking for a solution that joins customer->invoices.
By pre-filter I mean that data listed in the grid is filtered even before anything is presented in the grid.
Ok, now my code looks like:
$collection =
Mage::getResourceModel('customer/customer_collection');
$collection->joinTable('sales/order_grid', 'customer_id=entity_id', array('entity_id' => 'order_entity_id'));
And the error that I get is:
SELECT `e`.*, `sales_flat_order_grid`.`order_entity_id` AS `entity_id` FROM `customer_entity` AS `e`
INNER JOIN `sales_flat_order_grid` ON (sales_flat_order_grid.customer_id=e.entity_id) WHERE (e.entity_type_id = '1') ORDER BY `e`.`created_at` desc, `e`.`created_at` desc LIMIT 20
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'sales_flat_order_grid.order_entity_id' in 'field list'
Here is the total of my test script. To use put it in a file at the Magento root and type it's URL directly in your browser, it is not handled by Magento's controllers. This is a good way of experimenting as it is not as influenced by other modules, page layouts, etc.
<pre><?php
require 'app/Mage.php';
Mage::app();
$collection = Mage::getResourceModel('customer/customer_collection');
$collection->getSelect()->reset('columns');
$collection->joinTable('sales/order_grid', 'customer_id=entity_id', array('order_entity_id' => 'entity_id'));
$collection->joinTable('sales/invoice_grid', 'order_id=order_entity_id', array('*'));
foreach ($collection as $invoice)
print_r($invoice->debug());
?></pre>
As with your previous question I choose to reset the initial columns because I don't believe in giving the database more work than necessary. However it's not essential, the test still succeeds without it.
If this doesn't work in your installation then we need to consider what that outside influence could be.
The error "A joint field with this alias (0) is already declared." occurs because it uses array keys as aliases. Since you have two joinTable() calls, each with an array, it is trying to use the zero-based index of both and obviously having a conflict.
So instead of
array('entity_id as order_entity_id')
try
array('entity_id' => 'order_entity_id')
to avoid the conflict.
I finally did achieve this by going from invoice->order->customer as 'Anda B' suggested. I am just pasting my solution here as a reference, but will be using this solution from clockworkgeek, since it seems much cleaner. And my solution still needs to be made cleaner by getting the 'id' of eav_attribute (agent_id) from the database at runtime, instead of hard coding it, as pasted here:
class Myproject_Adminhtml_Block_Sales_Invoice_Grid extends Mage_Adminhtml_Block_Sales_Invoice_Grid
{
const AGENT_ID_ATTRIBUTE_ID = 118;
protected function _prepareCollection()
{
$collection = Mage::getResourceModel($this->_getCollectionClass());
$collection->join('order_grid', 'order_id = order_grid.entity_id', array ('order_entity_id' => 'order_grid.entity_id'));
$collection->getSelect()->join( 'customer_entity', 'customer_id = customer_entity.entity_id', array('customer_entity_id' => 'entity_id', 'email'));
$collection->getSelect()->joinLeft( 'customer_entity_int', 'customer_entity_int.entity_id = customer_entity.entity_id AND attribute_id = ' . Myproject_Adminhtml_Block_Sales_Invoice_Grid::AGENT_ID_ATTRIBUTE_ID,
array('attribute_entity_id' => 'customer_entity_int.entity_id', 'attribute_id' , 'value'));
//Apply Desired Data Filters here
$this->setCollection($collection);
return $collection;
It gives you this error "Item (Mage_Customer_Model_Customer) with the same id "1" already exist" because a customer can have multiple orders and so could have two or more entries with the same customer id - you are creating a collection of customers and you must have unique entries in the collection.
You have to start from invoices and join them with the customers.
The simplest way I found out from the magento forum ,
In the
protected function _prepareCollection()
{
$collection = Mage::getResourceModel('customer/customer_collection')
We can use custom queries as
$collection->getSelect()->columns(array('filename' => new Zend_Db_Expr ("(SELECT filename FROM cat WHERE customer_id =e.entity_id)")));
and it works
$this->setCollection($collection);
var_dump($collection);

Resources