joining custom tables using magento commands - magento

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 !!

Related

Partial select of left joined table with doctrine query builder

When querying one table using the doctrine query builder a partial select can be written like this:
$queryBuilder = $this->createQueryBuilder('person');
$queryBuilder->addSelect('partial person.{id, name}');
How can one write a partial select be written for a left joined table? I tried something like this, but can't figure out the correct syntax:
$queryBuilder = $this->createQueryBuilder('person');
$queryBuilder->join('person.address');
$queryBuilder->addSelect('partial person.{id, name} person.address.city'); // ???
My goal would be to select only parts of the Person and the Address object when executing the query to be more memory efficient.
Your syntax is off for your join operation. You have to give an alias when using join. From there, you can just use the same syntax to query your partial Address object:
// In a method of PersonRepository
$qb = $this->createQueryBuilder('person')
->select(['partial person.{id, name}', 'partial address.{id, city}'])
->join('person.address', 'address');
Notice that I added id to the fields retrieved for Address. If you don't, Doctrine will give you the following error:
Error: The partial field selection of class Path\To\Entity\Address must contain the identifier
As a side note, you said you wanted to write this select for a left joined table. If you want to perform a LEFT JOIN, you need to use leftJoin instead of join (the signature of both methods is the same).

Drupal 7 | Query through multiple node references

Let me start with structure first:
[main_node]->field_reference_to_sub_node->[sub_node]->field_ref_to_sub_sub_node->[sub_sub_node]
[sub_sub_node]->field_type = ['wrong_type', 'right_type']
How to efficiently query all [sub_sub_node] ids with right_type, referenced by main_node (which is current opened node)?
Doing node_load on foreach seems a bit of overkill for this. Anybody has some better solutions? Greatly appreciated!
If you want to directly query the table of the fields:
$query = db_select('node', 'n')->fields('n_sub_subnode', array('nid'));
$query->innerJoin('table_for_field_reference_to_sub_node', 'subnode', "n.nid = subnode.entity_id AND subnode.entity_type='node'");
$query->innerJoin('node', 'n_subnode', 'subnode.subnode_target_id = n_subnode.nid');
$query->innerJoin('table_for_field_ref_to_sub_sub_node', 'sub_subnode', "n_subnode.nid = sub_subnode.entity_id AND sub_subnode.entity_type='node'");
$query->innerJoin('node', 'n_sub_subnode', 'sub_subnode.sub_subnode_target_id = n_sub_subnode.nid');
$query->innerJoin('table_for_field_type', 'field_type', "n_sub_subnode.nid = field_type.entity_id AND field_type.entity_type='node'");
$query->condition('n.nid', 'your_main_node_nid');
$query->condition('field_type.field_type_value', 'right_type');
Here is the explanation of each line:
$query = db_select('node', 'n')->fields('n_sub_subnode', array('nid'));
We start by querying the base node table, with the alias 'n'. This is the table used for the 'main_node'. The node ids which will be returned will be however from another alias (n_sub_subnode), you will see it below.
$query->innerJoin('table_for_field_reference_to_sub_node', 'subnode', "n.nid = subnode.entity_id AND subnode.entity_type='node'");
The first join is with the table of the field_reference_to_sub_node field, so you have to replace this with the actual name of the table. This is how we will get the references to the subnodes.
$query->innerJoin('node', 'n_subnode', 'subnode.subnode_target_id = n_subnode.nid');
A join back to the node table for the subnodes. You have to replace the 'subnode_target_id' with the actual field for the target id from the field_reference_to_sub_node table. The main purpose of this join is to make sure there are valid nodes in the subnode field.
$query->innerJoin('table_for_field_ref_to_sub_sub_node', 'sub_subnode', "n_subnode.nid = sub_subnode.entity_id AND sub_subnode.entity_type='node'");
The join to the table that contains references to the sub_sub_node, so you have to replace the 'table_for_field_ref_to_sub_sub_node' with the actual name of the table. This is how we get the references to the sub_sub_nodes.
$query->innerJoin('node', 'n_sub_subnode', 'sub_subnode.sub_subnode_target_id = n_sub_subnode.nid');
The join back to the node table for the sub_sub_nodes, to make sure we have valid references. You have to replace the 'sub_subnode_target_id' with the actual field for the target id from the 'field_ref_to_sub_sub_node' table.
$query->innerJoin('table_for_field_type', 'field_type', "n_sub_subnode.nid = field_type.entity_id AND field_type.entity_type='node'");
And we can now finally join the table with the field_type information. You have to replace the 'table_for_field_type' with the actual name of the table.
$query->condition('n.nid', 'your_main_node_nid');
You can put now a condition for the main node id if you want.
$query->condition('field_type.field_type_value', 'right_type');
And the condition for the field type. You have to replace the 'field_type_value' with the actual name of the table field for the value.
Of course, if you are really sure that you always have valid references, you can skip the joins to the node table and directly join the field tables using the target id and the entity_id fields (basically the target_id from on field table has to be the entity_id for the next one).
I really hope I do not have typos, so please check the queries carefully.

Magento Join Product Collection to Custom Table on Custom Attribute

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.

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);

Doctrine toarray does not convert relations

I followed doctrine documnetation to get started. Here is the documentation.
My code is
$User = Doctrine_Core::getTable("User")->find(1);
when I access relations by $User->Phonenumbers, it works. When I convert User object to array by using toArray() method, it does not convert relations to array. It simply display $User data.
Am I missing something?
By using the find method you've only retrieved the User data which is why the return of toArray is limited to that data. You need to specify the additional data to load, and the best place to do this is usually in the original query. From the example you linked to, add the select portion:
$q = Doctrine_Query::create()
->select('u.*, e.*, p.*') // Example only, select what you need, not *
->from('User u')
->leftJoin('u.Email e')
->leftJoin('u.Phonenumbers p')
->where('u.id = ?', 1);
Then when toArray'ing the results from that, you should see the associated email and phonenumber data as well.
I also noticed an anomaly with this where if you call the relationship first then call the ToArray, the relationship somehow gets included. what i mean is that, taking your own eg,
$User = Doctrine_Core::getTable("User")->find(1);
$num= $User->Phonenumbers->office; // assumed a field 'office' in your phone num table
$userArray = $user->toArray(true);
In the above case, $userArray somehow contains the whole relationship. if we remove the $num assignment it doesn't.
am guessing this is due to doctrine only fetching the one record first, and it's only when you try to access foreign key values that it fetches the other related tables

Resources