{"data":{"id":"magento2-37550","source":"github-pr","sourceRef":"magento/magento2#37550","title":"JoinProcessor caching issue","pr":{"number":37550,"url":"https://github.com/magento/magento2/pull/37550","author":"ouzned","mergedAt":"2025-02-17T11:20:05Z","mergeCommit":"c93b59c723156b9f62390dca555027b957e2a85b","headCommit":"24de63e9f50e35c2ec65bc1cb8ffd271313acaeb","baseRef":"2.4-develop","diffSha256":"5136b351a2effbbeb7ff26a25f5d5974467e452e46a6d7e73288e4e623e432e3"},"issues":[{"number":27504,"url":"https://github.com/magento/magento2/issues/27504","title":"JoinProcessor caching issue","labels":["Area: Order","Component: Cache","Issue: Confirmed","Priority: P1","Priority: P2","Progress: PR in progress","Progress: done","Reported on 2.4.0","Reported on 2.4.x","Reproduced on 2.4.x","Triage: Dev.Experience"],"kind":"human"}],"fixedIn":"2.4.8","containingTags":["2.4.8","2.4.8-p1","2.4.8-p2","2.4.8-p3","2.4.8-p4","2.4.8-p5","2.4.9"],"reportedOn":"2.4.0","codeMatch":{"2.4.6":"clean","2.4.6-p1":"clean","2.4.6-p2":"clean","2.4.6-p3":"clean","2.4.6-p4":"clean","2.4.6-p5":"clean","2.4.6-p6":"clean","2.4.6-p7":"clean","2.4.6-p8":"clean","2.4.6-p9":"clean","2.4.6-p10":"clean","2.4.6-p11":"clean","2.4.6-p12":"clean","2.4.6-p13":"clean","2.4.6-p14":"clean","2.4.6-p15":"clean","2.4.7":"clean","2.4.7-p1":"clean","2.4.7-p2":"clean","2.4.7-p3":"clean","2.4.7-p4":"clean","2.4.7-p5":"clean","2.4.7-p6":"clean","2.4.7-p7":"clean","2.4.7-p8":"clean","2.4.7-p9":"clean","2.4.7-p10":"clean","2.4.8":"conflict","2.4.8-p1":"conflict","2.4.8-p2":"conflict","2.4.8-p3":"conflict","2.4.8-p4":"conflict","2.4.8-p5":"conflict","2.4.9":"conflict"},"affectedVersions":["2.4.6","2.4.6-p1","2.4.6-p2","2.4.6-p3","2.4.6-p4","2.4.6-p5","2.4.6-p6","2.4.6-p7","2.4.6-p8","2.4.6-p9","2.4.6-p10","2.4.6-p11","2.4.6-p12","2.4.6-p13","2.4.6-p14","2.4.6-p15","2.4.7","2.4.7-p1","2.4.7-p2","2.4.7-p3","2.4.7-p4","2.4.7-p5","2.4.7-p6","2.4.7-p7","2.4.7-p8","2.4.7-p9","2.4.7-p10"],"components":["magento/framework"],"files":[{"path":"lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/JoinProcessor.php","change":"modified","package":"magento/framework"}],"stripped":{"tests":["dev/tests/integration/testsuite/Magento/Framework/Api/SearchCriteria/CollectionProcessor/JoinProcessorTest.php"],"docs":[],"outsideCode":[]},"linesChanged":6,"mergeBatched":false,"excluded":null,"sections":{"description":"```\nSearching for order #001 ...  Not Found\nSearching for order #002 ... \nIn Mysql.php line 110:\n                                                                                                                                                                            \n  SQLSTATE[42S22]: Column not found: 1054 Unknown column 'store.group_id' in 'where clause', query was: SELECT `main_table`.* FROM `sales_order` AS `main_table` WHERE ((`  \n  increment_id` = '002')) AND ((`store`.`group_id` = 1))                                                                                                                    \n                                                                                                                                                                            \n\nIn Mysql.php line 91:\n                                                                                             \n  SQLSTATE[42S22]: Column not found: 1054 Unknown column 'store.group_id' in 'where clause'  \n                                                                                             \n\norder:update\n```","stepsToReproduce":"1. Add a module with [JoinProcessor](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/searching-with-repositories.html#join-processor) for the OrderRepository using the files below:\n    <details><summary>app/code/SomeVendor/SomeModule/registration.php</summary>\n\n    ```php\n    <?php\n\n    use Magento\\Framework\\Component\\ComponentRegistrar;\n\n    ComponentRegistrar::register(ComponentRegistrar::MODULE, 'SomeVendor_SomeModule', __DIR__);\n    ```\n    </details>\n    <details><summary>app/code/SomeVendor/SomeModule/etc/module.xml</summary>\n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"urn:magento:framework:Module/etc/module.xsd\">\n        <module name=\"SomeVendor_SomeModule\" />\n    </config>\n    ```\n    </details>\n    <details><summary>app/code/SomeVendor/SomeModule/etc/di.xml</summary>\n\n    ```xml\n    <?xml version=\"1.0\"?>\n    <config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"urn:magento:framework:ObjectManager/etc/config.xsd\">\n        <type name=\"Magento\\Framework\\Console\\CommandListInterface\">\n            <arguments>\n                <argument name=\"commands\" xsi:type=\"array\">\n                    <item name=\"order:update\" xsi:type=\"object\">SomeVendor\\SomeModule\\Console\\Command\\OrderUpdate</item>\n                </argument>\n            </arguments>\n        </type>\n        <type name=\"SomeVendor\\SomeModule\\Console\\Command\\OrderUpdate\">\n            <arguments>\n                <argument name=\"orderRepository\" xsi:type=\"object\">SomeVendor\\SomeModule\\Model\\OrderRepository</argument>\n            </arguments>\n        </type>\n        <virtualType name=\"SomeVendor\\SomeModule\\Model\\OrderRepository\" type=\"Magento\\Sales\\Model\\OrderRepository\">\n            <arguments>\n                <argument name=\"collectionProcessor\" xsi:type=\"object\">SomeVendor\\SomeModule\\Model\\OrderRepository\\CollectionProcessor</argument>\n            </arguments>\n        </virtualType>\n        <virtualType name=\"SomeVendor\\SomeModule\\Model\\OrderRepository\\CollectionProcessor\" type=\"Magento\\Framework\\Api\\SearchCriteria\\CollectionProcessor\">\n            <arguments>\n                <argument name=\"processors\" xsi:type=\"array\">\n                    <item name=\"joins\" xsi:type=\"object\">SomeVendor\\SomeModule\\Model\\Api\\SearchCriteria\\CollectionProcessor\\StoreViewJoinProcessor</item>\n                </argument>\n            </arguments>\n        </virtualType>\n        <virtualType name=\"SomeVendor\\SomeModule\\Model\\Api\\SearchCriteria\\CollectionProcessor\\StoreViewJoinProcessor\" type=\"Magento\\Framework\\Api\\SearchCriteria\\CollectionProcessor\\JoinProcessor\">\n            <arguments>\n                <argument name=\"customJoins\" xsi:type=\"array\">\n                    <item name=\"store.group_id\" xsi:type=\"object\">SomeVendor\\SomeModule\\Model\\Api\\SearchCriteria\\JoinProcessor\\Store</item>\n                </argument>\n            </arguments>\n        </virtualType>\n    </config>\n    ```\n    </details>\n    <details><summary>app/code/SomeVendor/SomeModule/Model/Api/SearchCriteria/JoinProcessor/Store.php</summary>\n\n    ```php\n    <?php\n    /**\n     * Store Join Processor\n     */\n    declare(strict_types = 1);\n\n    namespace SomeVendor\\SomeModule\\Model\\Api\\SearchCriteria\\JoinProcessor;\n\n    use Magento\\Framework\\Api\\SearchCriteria\\CollectionProcessor\\JoinProcessor\\CustomJoinInterface;\n    use Magento\\Framework\\Data\\Collection\\AbstractDb;\n\n    /**\n     * Store Join Processor\n     */\n    class Store implements CustomJoinInterface\n    {\n        /**\n         * @inheritDoc\n         */\n        public function apply(AbstractDb $collection)\n        {\n            $collection->join(\n                ['store' => $collection->getResource()->getTable('store')],\n                'store.store_id = main_table.store_id',\n                []\n            );\n\n            return true;\n        }\n    }\n    ```\n    </details>\n    <details><summary>app/code/SomeVendor/SomeModule/Console/Command/OrderUpdate.php</summary>\n\n    ```php\n    <?php\n    /**\n     * Order Update CLI\n     */\n\n    declare(strict_types = 1);\n\n    namespace SomeVendor\\SomeModule\\Console\\Command;\n\n    use Magento\\Framework\\Api\\SearchCriteriaBuilder;\n    use Magento\\Sales\\Api\\Data\\OrderInterface;\n    use Magento\\Sales\\Api\\OrderRepositoryInterface;\n    use Symfony\\Component\\Console\\Command\\Command;\n    use Symfony\\Component\\Console\\Input\\InputInterface;\n    use Symfony\\Component\\Console\\Output\\OutputInterface;\n\n    /**\n     * Order Update CLI\n     */\n    class OrderUpdate extends Command\n    {\n        /** @var OrderRepositoryInterface */\n        protected $orderRepository;\n\n        /** @var SearchCriteriaBuilder */\n        protected $searchCriteriaBuilder;\n\n        /**\n         * OrderUpdate constructor.\n         *\n         * @param OrderRepositoryInterface $orderRepository\n         * @param SearchCriteriaBuilder $searchCriteriaBuilder\n         */\n        public function __construct(\n            OrderRepositoryInterface $orderRepository,\n            SearchCriteriaBuilder $searchCriteriaBuilder\n        ) {\n            $this->orderRepository = $orderRepository;\n            $this->searchCriteriaBuilder = $searchCriteriaBuilder;\n            parent::__construct();\n        }\n\n        /**\n         * @inheritDoc\n         */\n        protected function configure()\n        {\n            $this->setName('order:update');\n            $this->setDescription('Order Update');\n\n            parent::configure();\n        }\n\n        /**\n         * @inheritDoc\n         */\n        protected function execute(InputInterface $input, OutputInterface $output)\n        {\n            foreach (['001', '002'] as $orderId) {\n                echo 'Searching for order #' . $orderId . ' ... ';\n                $order = $this->searchOrder($orderId, 1);\n                echo $order ? ' Found' : ' Not Found';\n                echo PHP_EOL;\n            }\n        }\n\n        /**\n         * Search Order\n         *\n         * @param string $incrementId\n         * @param int $storeId\n         *\n         * @return OrderInterface|null\n         */\n        protected function searchOrder(string $incrementId, int $storeId): ?OrderInterface\n        {\n            $searchCriteria = $this->searchCriteriaBuilder\n                ->addFilter('increment_id', $incrementId)\n                ->addFilter('store.group_id', $storeId)\n                ->create();\n            $orders = $this->orderRepository->getList($searchCriteria)->getItems();\n\n            return current($orders) ?: null;\n        }\n    }\n    ```\n    </details>\n2. Run the following CLI commands:\n    ```bash\n    php bin/magento module:enable SomeVendor_SomeModule\n    php bin/magento setup:upgrade\n    php bin/magento cache:clean\n    php bin/magento order:update\n    ```","expectedResult":"```\nSearching for order #001 ... Not Found\nSearching for order #002 ... Not Found\n```","actualResult":"```\nSearching for order #001 ...  Not Found\nSearching for order #002 ... \nIn Mysql.php line 110:\n                                                                                                                                                                            \n  SQLSTATE[42S22]: Column not found: 1054 Unknown column 'store.group_id' in 'where clause', query was: SELECT `main_table`.* FROM `sales_order` AS `main_table` WHERE ((`  \n  increment_id` = '002')) AND ((`store`.`group_id` = 1))                                                                                                                    \n                                                                                                                                                                            \n\nIn Mysql.php line 91:\n                                                                                             \n  SQLSTATE[42S22]: Column not found: 1054 Unknown column 'store.group_id' in 'where clause'  \n                                                                                             \n\norder:update\n```\n\n### More Details\n\nIn the second `foreach` iteration the JoinProcessor it not applied because it is marked as already applied in the `$this->appliedFields` property of the [`Magento\\Framework\\Api\\SearchCriteria\\CollectionProcessor\\JoinProcessor`](https://github.com/magento/magento2/blob/2.4-develop/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/JoinProcessor.php#L58) class. It looks right, but in the second iteration we have a fresh new instance of the order collection where is no JOIN which was added in the first iteration. You may check it with the \n```php\necho 'Collection ID: ' . spl_object_id($collection) . PHP_EOL;\n``` \n---\nPlease provide [Severity](https://devdocs.magento.com/guides/v2.4/contributor-guide/contributing.html#backlog) assessment for the Issue as Reporter. This information will help during Confirmation and Issue triage processes.\n\n- [ ] Severity: **S0** _- Affects critical data or functionality and leaves users without workaround._\n- [ ] Severity: **S1** _- Affects critical data or functionality and forces users to employ a workaround._\n- [x] Severity: **S2** _- Affects non-critical data or functionality and forces users to employ a workaround._\n- [ ] Severity: **S3** _- Affects non-critical data or functionality and does not force users to employ a workaround._\n- [ ] Severity: **S4** _- Affects aesthetics, professional look and feel, “quality” or “usability”._","source":"issue"},"signatures":[],"labels":{"area":["Order"],"component":["Cache"],"priority":"P1","severity":null,"reportedOn":["2.4.0","2.4.x"]},"categories":[],"triage":{"model":"@cf/cloudflare/clef","requestHash":"4a68d52c6140163c32f2a8483bf1921845016562000efdfdfc419010741bb947","isBugfix":0.9748,"changeKind":{"choice":"bugfix","probabilities":{"bugfix":0.9686,"feature":0.0059,"refactor":0.0116,"tests_only":0.0079,"docs_only":0.0028,"dependency":0.0032},"confidence":0.9261},"scope":{"score":0.5406,"probabilities":{"0":0.5575,"1":0.3444,"2":0.0981},"confidence":0.1586},"risk":{"score":0.2916,"probabilities":{"0":0.7464,"1":0.2155,"2":0.0381},"confidence":0.4076},"area":{"choice":"framework","probabilities":{"catalog":0.0082,"checkout":0.0164,"customer":0.003,"admin":0.0165,"graphql_api":0.0234,"framework":0.916,"frontend":0.0048,"other":0.0117},"confidence":0.8176},"securityRelevant":0.0054,"reportedVersion":{"choice":"unspecified","probabilities":{"2.4.0":0.0348,"2.4.0-p1":0.0084,"2.4.1":0.0045,"2.4.1-p1":0.0047,"2.4.2":0.0051,"2.4.2-p1":0.0059,"2.4.2-p2":0.0081,"2.4.3":0.0075,"2.4.3-p1":0.008,"2.4.3-p2":0.0092,"2.4.3-p3":0.0066,"2.4.4":0.008,"2.4.4-p1":0.0066,"2.4.4-p10":0.0047,"2.4.4-p11":0.0063,"2.4.4-p12":0.0059,"2.4.4-p13":0.0055,"2.4.4-p14":0.006,"2.4.4-p15":0.0056,"2.4.4-p16":0.006,"2.4.4-p17":0.0047,"2.4.4-p18":0.0055,"2.4.4-p2":0.0038,"2.4.4-p3":0.0038,"2.4.4-p4":0.0045,"2.4.4-p5":0.0045,"2.4.4-p6":0.0043,"2.4.4-p7":0.0038,"2.4.4-p8":0.0038,"2.4.4-p9":0.0038,"2.4.5":0.0056,"2.4.5-p1":0.0074,"2.4.5-p10":0.0047,"2.4.5-p11":0.0065,"2.4.5-p12":0.0065,"2.4.5-p13":0.0064,"2.4.5-p14":0.0067,"2.4.5-p15":0.0048,"2.4.5-p16":0.0043,"2.4.5-p17":0.0066,"2.4.5-p2":0.0035,"2.4.5-p3":0.0048,"2.4.5-p4":0.0047,"2.4.5-p5":0.0043,"2.4.5-p6":0.0044,"2.4.5-p7":0.0047,"2.4.5-p8":0.0047,"2.4.5-p9":0.0033,"2.4.6":0.0075,"2.4.6-p1":0.0079,"2.4.6-p10":0.0055,"2.4.6-p11":0.0079,"2.4.6-p12":0.0067,"2.4.6-p13":0.0074,"2.4.6-p14":0.0076,"2.4.6-p15":0.0056,"2.4.6-p2":0.004,"2.4.6-p3":0.0058,"2.4.6-p4":0.0067,"2.4.6-p5":0.006,"2.4.6-p6":0.0056,"2.4.6-p7":0.0066,"2.4.6-p8":0.0052,"2.4.6-p9":0.0035,"2.4.7":0.006,"2.4.7-p1":0.0083,"2.4.7-p10":0.0059,"2.4.7-p2":0.005,"2.4.7-p3":0.0082,"2.4.7-p4":0.0115,"2.4.7-p5":0.0089,"2.4.7-p6":0.0098,"2.4.7-p7":0.0079,"2.4.7-p8":0.0058,"2.4.7-p9":0.0044,"2.4.8":0.0073,"2.4.8-p1":0.0085,"2.4.8-p2":0.011,"2.4.8-p3":0.0137,"2.4.8-p4":0.0123,"2.4.8-p5":0.0101,"2.4.9":0.0117,"unspecified":0.4504},"confidence":0.1981},"backportWorthy":{"score":1.4533,"probabilities":{"0":0.1255,"1":0.2957,"2":0.5788},"confidence":0.1573}},"curated":{"title":"JoinProcessor join definitions are only applied the first time per request","description":"When a JoinProcessor is called multiple times during a request, the join definitions are only applied the first time, so the fresh order collection in the second iteration has no JOIN and the query fails with Column not found: 1054 Unknown column 'store.group_id' in 'where clause'. The change sets appliedFields to [] at the end of JoinProcessor::process.","categories":[],"author":"claude-code/opus-5.5","date":"2026-10-06","reviewedBy":null},"tests":{"2.4.7-p10":{"before":"fail","after":"pass","adapted":false,"runAt":"2026-10-06T09:39:26.490Z","releaseCommit":"72561bf80652f57cc642a03e2c9a51d74a285b14","suites":{"integration":{"before":"fail","after":"pass","runAt":"2026-10-06T09:39:26.490Z"}}}}},"_documentation":"https://magento.watch/api","_description":"Upstream fix magento2-37550 details"}