Hey!
I'm using Magento ver. 2.4.2 and try join two tables using Custom Joins
According this example
I use it this way di.xml:
<virtualType name="Vendor\Module\Model\CollectionProcessor\JoinProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor\JoinProcessor"> <arguments> <argument name="customJoins" xsi:type="array"> <item name="rule_id" xsi:type="object">Vendor\Module\Model\CollectionProcessor\JoinProcessor\Product</item> </argument> </arguments> </virtualType> <virtualType name="Vendor\Module\Model\CollectionProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor"> <arguments> <argument name="processors" xsi:type="array"> <item name="join" xsi:type="object">Vendor\Module\Model\CollectionProcessor\JoinProcessor</item> </argument> </arguments> </virtualType> <type name="Vendor\Module\Model\ProductAttributesRepository"> <arguments> <argument name="collectionProcessor" xsi:type="object">Vendor\Module\Model\CollectionProcessor</argument> </arguments> </type>
My Custom Join:
namespace Vendor\Module\Model\CollectionProcessor\JoinProcessor; use ... class Product implements \Magento\Framework\Api\SearchCriteria\CollectionProcessor\JoinProcessor\CustomJoinInterface; { public function apply(\Magento\Framework\Data\Collection\AbstractDb $collection) { $collection->getSelect()->joinLeft( ['product_action' => 'action_product'], "main_table.rule_id = product_action.entity_id", [] ); return true; } }
Using in Collection Processor in Repository:
public function __construct( \Magento\Framework\Api\SearchCriteriaBuilder\SearchCriteriaBuilder $searchCriteriaBuilder, \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface $collectionProcessor, \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor ) { $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->collectionProcessor = $collectionProcessor; $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; } public function getByRuleId(int $ruleId): array { $searchCriteria = $this->searchCriteriaBuilder->addFilter( 'rule_id', $ruleId )->create(); return $this->getList($searchCriteria); } public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria) { $collection = $this->collectionFactory->create(); $this->extensionAttributesJoinProcessor->process( $collection, \Vendor\Module\Api\Data\ProductAttributesInterface::class ); $this->collectionProcessor->process($searchCriteria, $collection); // <-- Logger Place --> $writer = new \Zend\Log\Writer\Stream(BP . '/var/log/selector.log'); $logger = new \Zend\Log\Logger(); $logger->addWriter($writer); $logger->info('SQL: ' . $collection->getSelect()->__toString()); return $collection->getItems(); }
Test file:
require __DIR__ . '/../app/bootstrap.php'; $params = $_SERVER; $params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE] = 'base'; $params[\Magento\Store\Model\StoreManager::PARAM_RUN_TYPE] = 'website'; $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $params); $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $obj = $objectManager->get('\Vendor\Module\Api\ProductAttributesRepositoryInterface'); $obj->getByRuleId(1); $obj->getByRuleId(2);
Result:
Fatal error: Uncaught PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'rule_id' in 'where clause' in .../vendor/magento/framework/DB/Statement/Pdo/Mysql.php on line 126 ( ! ) Zend_Db_Statement_Exception: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'rule_id' in 'where clause', query was: SELECT `main_table`.* FROM `action_product_attributes` AS `main_table` WHERE ((`rule_id` = 2)) in ../vendor/magento/framework/DB/Statement/Pdo/Mysql.php on line 126
Result in Log file:
2021-07-01T14:56:57+00:00 INFO (6): SQL: SELECT `main_table`.* FROM `action_product_attributes` AS `main_table` LEFT JOIN `action_product` AS `product_action` ON main_table.rule_product_id = product_action.entity_id WHERE ((`rule_id` = 1)) 2021-07-01T14:56:57+00:00 INFO (6): SQL: SELECT `main_table`.* FROM `action_product_attributes` AS `main_table` WHERE ((`rule_id` = 2))
Part from core what joining tables:
file: \Magento\Framework\Api\SearchCriteria\CollectionProcessor\JoinProcessor :: process
public function process(SearchCriteriaInterface $searchCriteria, AbstractDb $collection) { if ($searchCriteria->getFilterGroups()) { //Process filters foreach ($searchCriteria->getFilterGroups() as $group) { foreach ($group->getFilters() as $filter) { if (!isset($this->appliedFields[$filter->getField()])) { $this->applyCustomJoin($filter->getField(), $collection); $this->appliedFields[$filter->getField()] = true; } } } } if ($searchCriteria->getSortOrders()) { // Process Sortings foreach ($searchCriteria->getSortOrders() as $order) { if (!isset($this->appliedFields[$order->getField()])) { $this->applyCustomJoin($order->getField(), $collection); $this->appliedFields[$order->getField()] = true; } } } }
So on first request `$this->appliedFields` not have my record about rule_id field, and then everything works. But in second request, core do not call a applyCustomJoin function because in `$this->appliedFields` already have this record, but collection is not have joined table yet...
Question:
How resolve the issue, when processing with second call of collection, not add Join part of SQL Query?
P.s.: To resolve that issue, i try add ' shared="false" ' on each DI element, but still not working...
BTW: I know how to join table in another way ...->getSelect()->join(array(... but I wonder what I’m doing wrong using this example.
Thanks.