[{"data":1,"prerenderedAt":761},["ShallowReactive",2],{"blog-\u002Fblog\u002Fmagento2-join-two-tables-for-admin-grid-module-creation-from-scratch":3,"content-query-xydgESI0jI":434},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"image":11,"body":12,"_type":428,"_id":429,"_source":430,"_file":431,"_stem":432,"_extension":433},"\u002Fblog\u002Fmagento2-join-two-tables-for-admin-grid-module-creation-from-scratch","blog",false,"","Magento2 Join two tables for admin grid module creation from scratch","A comprehensive guide to creating a custom Magento 2 module with an admin grid that joins data from two different database tables.","2017-02-10","\u002Fimages\u002Fblog\u002Fdatabase-tech.png",{"type":13,"children":14,"toc":421},"root",[15,23,28,35,40,70,76,98,107,182,188,216,271,277,290,298,379,385,405,410,415],{"type":16,"tag":17,"props":18,"children":19},"element","p",{},[20],{"type":21,"value":22},"text","Hi, everybody! I had a chance to get my hands on the latest Magento 2 and work on a learning project. I decided to create a custom module that features an admin listing pulling data from two separate tables.",{"type":16,"tag":17,"props":24,"children":25},{},[26],{"type":21,"value":27},"In many cases, we need to display listings from two tables by using MySQL Joins. This tutorial provides a step-by-step explanation of how to achieve this from scratch.",{"type":16,"tag":29,"props":30,"children":32},"h3",{"id":31},"what-you-will-learn",[33],{"type":21,"value":34},"What You Will Learn",{"type":16,"tag":17,"props":36,"children":37},{},[38],{"type":21,"value":39},"By the end of this tutorial, you will be able to:",{"type":16,"tag":41,"props":42,"children":43},"ul",{},[44,50,55,60,65],{"type":16,"tag":45,"props":46,"children":47},"li",{},[48],{"type":21,"value":49},"Create a module installable via Composer.",{"type":16,"tag":45,"props":51,"children":52},{},[53],{"type":21,"value":54},"Understand the controller and rewrite system.",{"type":16,"tag":45,"props":56,"children":57},{},[58],{"type":21,"value":59},"Work with blocks, layouts, and templates.",{"type":16,"tag":45,"props":61,"children":62},{},[63],{"type":21,"value":64},"Interact with the database using Models and Resource Models.",{"type":16,"tag":45,"props":66,"children":67},{},[68],{"type":21,"value":69},"Setup an admin listing with joins from two tables using UI Components.",{"type":16,"tag":29,"props":71,"children":73},{"id":72},"step-1-module-registration",[74],{"type":21,"value":75},"Step 1: Module Registration",{"type":16,"tag":17,"props":77,"children":78},{},[79,81,88,90,96],{"type":21,"value":80},"First, create your ",{"type":16,"tag":82,"props":83,"children":85},"code",{"className":84},[],[86],{"type":21,"value":87},"composer.json",{"type":21,"value":89}," and ",{"type":16,"tag":82,"props":91,"children":93},{"className":92},[],[94],{"type":21,"value":95},"registration.php",{"type":21,"value":97}," to tell Magento about your module.",{"type":16,"tag":17,"props":99,"children":100},{},[101],{"type":16,"tag":102,"props":103,"children":104},"strong",{},[105],{"type":21,"value":106},"Ekvi\u002Fadminjoins\u002Fregistration.php",{"type":16,"tag":108,"props":109,"children":113},"pre",{"className":110,"code":111,"language":112,"meta":7,"style":7},"language-php shiki shiki-themes github-dark","\u003C?php \n\\Magento\\Framework\\Component\\ComponentRegistrar::register( \n    \\Magento\\Framework\\Component\\ComponentRegistrar::MODULE, \n    'Ekvi_Adminjoins', \n    __DIR__ \n); \n?>\n","php",[114],{"type":16,"tag":82,"props":115,"children":116},{"__ignoreMap":7},[117,128,137,146,155,164,173],{"type":16,"tag":118,"props":119,"children":122},"span",{"class":120,"line":121},"line",1,[123],{"type":16,"tag":118,"props":124,"children":125},{},[126],{"type":21,"value":127},"\u003C?php \n",{"type":16,"tag":118,"props":129,"children":131},{"class":120,"line":130},2,[132],{"type":16,"tag":118,"props":133,"children":134},{},[135],{"type":21,"value":136},"\\Magento\\Framework\\Component\\ComponentRegistrar::register( \n",{"type":16,"tag":118,"props":138,"children":140},{"class":120,"line":139},3,[141],{"type":16,"tag":118,"props":142,"children":143},{},[144],{"type":21,"value":145},"    \\Magento\\Framework\\Component\\ComponentRegistrar::MODULE, \n",{"type":16,"tag":118,"props":147,"children":149},{"class":120,"line":148},4,[150],{"type":16,"tag":118,"props":151,"children":152},{},[153],{"type":21,"value":154},"    'Ekvi_Adminjoins', \n",{"type":16,"tag":118,"props":156,"children":158},{"class":120,"line":157},5,[159],{"type":16,"tag":118,"props":160,"children":161},{},[162],{"type":21,"value":163},"    __DIR__ \n",{"type":16,"tag":118,"props":165,"children":167},{"class":120,"line":166},6,[168],{"type":16,"tag":118,"props":169,"children":170},{},[171],{"type":21,"value":172},"); \n",{"type":16,"tag":118,"props":174,"children":176},{"class":120,"line":175},7,[177],{"type":16,"tag":118,"props":178,"children":179},{},[180],{"type":21,"value":181},"?>\n",{"type":16,"tag":29,"props":183,"children":185},{"id":184},"step-2-database-setup-installschema",[186],{"type":21,"value":187},"Step 2: Database Setup (InstallSchema)",{"type":16,"tag":17,"props":189,"children":190},{},[191,193,199,200,206,208,214],{"type":21,"value":192},"We need to create two tables: ",{"type":16,"tag":82,"props":194,"children":196},{"className":195},[],[197],{"type":21,"value":198},"ekvi_employee",{"type":21,"value":89},{"type":16,"tag":82,"props":201,"children":203},{"className":202},[],[204],{"type":21,"value":205},"ekvi_employee_salary",{"type":21,"value":207},". We will join these on ",{"type":16,"tag":82,"props":209,"children":211},{"className":210},[],[212],{"type":21,"value":213},"salary_id",{"type":21,"value":215},".",{"type":16,"tag":108,"props":217,"children":219},{"className":110,"code":218,"language":112,"meta":7,"style":7},"\u002F\u002F Ekvi\u002Fadminjoins\u002FSetup\u002FInstallSchema.php snippet\n$table = $setup->getConnection()->newTable($setup->getTable('ekvi_employee'))\n    ->addColumn('id', Table::TYPE_INTEGER, null, ['identity' => true, 'primary' => true], 'Employee Id')\n    ->addColumn('name', Table::TYPE_TEXT, '200', [], 'Employee Name')\n    ->addColumn('salary_id', Table::TYPE_SMALLINT, '2', [], 'Current Salary');\n$setup->getConnection()->createTable($table);\n",[220],{"type":16,"tag":82,"props":221,"children":222},{"__ignoreMap":7},[223,231,239,247,255,263],{"type":16,"tag":118,"props":224,"children":225},{"class":120,"line":121},[226],{"type":16,"tag":118,"props":227,"children":228},{},[229],{"type":21,"value":230},"\u002F\u002F Ekvi\u002Fadminjoins\u002FSetup\u002FInstallSchema.php snippet\n",{"type":16,"tag":118,"props":232,"children":233},{"class":120,"line":130},[234],{"type":16,"tag":118,"props":235,"children":236},{},[237],{"type":21,"value":238},"$table = $setup->getConnection()->newTable($setup->getTable('ekvi_employee'))\n",{"type":16,"tag":118,"props":240,"children":241},{"class":120,"line":139},[242],{"type":16,"tag":118,"props":243,"children":244},{},[245],{"type":21,"value":246},"    ->addColumn('id', Table::TYPE_INTEGER, null, ['identity' => true, 'primary' => true], 'Employee Id')\n",{"type":16,"tag":118,"props":248,"children":249},{"class":120,"line":148},[250],{"type":16,"tag":118,"props":251,"children":252},{},[253],{"type":21,"value":254},"    ->addColumn('name', Table::TYPE_TEXT, '200', [], 'Employee Name')\n",{"type":16,"tag":118,"props":256,"children":257},{"class":120,"line":157},[258],{"type":16,"tag":118,"props":259,"children":260},{},[261],{"type":21,"value":262},"    ->addColumn('salary_id', Table::TYPE_SMALLINT, '2', [], 'Current Salary');\n",{"type":16,"tag":118,"props":264,"children":265},{"class":120,"line":166},[266],{"type":16,"tag":118,"props":267,"children":268},{},[269],{"type":21,"value":270},"$setup->getConnection()->createTable($table);\n",{"type":16,"tag":29,"props":272,"children":274},{"id":273},"step-3-the-collection-join-logic",[275],{"type":21,"value":276},"Step 3: The Collection Join Logic",{"type":16,"tag":17,"props":278,"children":279},{},[280,282,288],{"type":21,"value":281},"The \"magic\" happens in your Resource Model Collection. We override ",{"type":16,"tag":82,"props":283,"children":285},{"className":284},[],[286],{"type":21,"value":287},"_initSelect()",{"type":21,"value":289}," to perform the left join.",{"type":16,"tag":17,"props":291,"children":292},{},[293],{"type":16,"tag":102,"props":294,"children":295},{},[296],{"type":21,"value":297},"Ekvi\u002Fadminjoins\u002FModel\u002FResourceModel\u002FEmployee\u002FCollection.php",{"type":16,"tag":108,"props":299,"children":301},{"className":110,"code":300,"language":112,"meta":7,"style":7},"protected function _initSelect()\n{\n    parent::_initSelect();\n    $this->getSelect()->joinLeft(\n        ['secondTable' => $this->getTable('ekvi_employee_salary')],\n        'main_table.salary_id = secondTable.salary_id',\n        ['secondTable.salary_id as salaryId', 'salary']\n    );\n}\n",[302],{"type":16,"tag":82,"props":303,"children":304},{"__ignoreMap":7},[305,313,321,329,337,345,353,361,370],{"type":16,"tag":118,"props":306,"children":307},{"class":120,"line":121},[308],{"type":16,"tag":118,"props":309,"children":310},{},[311],{"type":21,"value":312},"protected function _initSelect()\n",{"type":16,"tag":118,"props":314,"children":315},{"class":120,"line":130},[316],{"type":16,"tag":118,"props":317,"children":318},{},[319],{"type":21,"value":320},"{\n",{"type":16,"tag":118,"props":322,"children":323},{"class":120,"line":139},[324],{"type":16,"tag":118,"props":325,"children":326},{},[327],{"type":21,"value":328},"    parent::_initSelect();\n",{"type":16,"tag":118,"props":330,"children":331},{"class":120,"line":148},[332],{"type":16,"tag":118,"props":333,"children":334},{},[335],{"type":21,"value":336},"    $this->getSelect()->joinLeft(\n",{"type":16,"tag":118,"props":338,"children":339},{"class":120,"line":157},[340],{"type":16,"tag":118,"props":341,"children":342},{},[343],{"type":21,"value":344},"        ['secondTable' => $this->getTable('ekvi_employee_salary')],\n",{"type":16,"tag":118,"props":346,"children":347},{"class":120,"line":166},[348],{"type":16,"tag":118,"props":349,"children":350},{},[351],{"type":21,"value":352},"        'main_table.salary_id = secondTable.salary_id',\n",{"type":16,"tag":118,"props":354,"children":355},{"class":120,"line":175},[356],{"type":16,"tag":118,"props":357,"children":358},{},[359],{"type":21,"value":360},"        ['secondTable.salary_id as salaryId', 'salary']\n",{"type":16,"tag":118,"props":362,"children":364},{"class":120,"line":363},8,[365],{"type":16,"tag":118,"props":366,"children":367},{},[368],{"type":21,"value":369},"    );\n",{"type":16,"tag":118,"props":371,"children":373},{"class":120,"line":372},9,[374],{"type":16,"tag":118,"props":375,"children":376},{},[377],{"type":21,"value":378},"}\n",{"type":16,"tag":29,"props":380,"children":382},{"id":381},"step-4-ui-component-configuration",[383],{"type":21,"value":384},"Step 4: UI Component Configuration",{"type":16,"tag":17,"props":386,"children":387},{},[388,390,396,398,404],{"type":21,"value":389},"Magento 2 uses UI Components for admin grids. You need to define a data source in ",{"type":16,"tag":82,"props":391,"children":393},{"className":392},[],[394],{"type":21,"value":395},"di.xml",{"type":21,"value":397}," and configure the grid columns in ",{"type":16,"tag":82,"props":399,"children":401},{"className":400},[],[402],{"type":21,"value":403},"adminjoins_employee_listing.xml",{"type":21,"value":215},{"type":16,"tag":17,"props":406,"children":407},{},[408],{"type":21,"value":409},"By defining your data source to point to your custom collection, the admin grid will automatically handle the joined data, allowing you to display and filter by fields from both tables seamlessly.",{"type":16,"tag":17,"props":411,"children":412},{},[413],{"type":21,"value":414},"This approach is powerful because it leverages Magento's native UI Component framework while giving you full control over the underlying SQL query.",{"type":16,"tag":416,"props":417,"children":418},"style",{},[419],{"type":21,"value":420},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":130,"depth":130,"links":422},[423,424,425,426,427],{"id":31,"depth":139,"text":34},{"id":72,"depth":139,"text":75},{"id":184,"depth":139,"text":187},{"id":273,"depth":139,"text":276},{"id":381,"depth":139,"text":384},"markdown","content:blog:magento2-join-two-tables-for-admin-grid-module-creation-from-scratch.md","content","blog\u002Fmagento2-join-two-tables-for-admin-grid-module-creation-from-scratch.md","blog\u002Fmagento2-join-two-tables-for-admin-grid-module-creation-from-scratch","md",{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"image":11,"body":435,"_type":428,"_id":429,"_source":430,"_file":431,"_stem":432,"_extension":433},{"type":13,"children":436,"toc":754},[437,441,445,449,453,476,480,496,503,558,562,584,632,636,646,653,722,726,742,746,750],{"type":16,"tag":17,"props":438,"children":439},{},[440],{"type":21,"value":22},{"type":16,"tag":17,"props":442,"children":443},{},[444],{"type":21,"value":27},{"type":16,"tag":29,"props":446,"children":447},{"id":31},[448],{"type":21,"value":34},{"type":16,"tag":17,"props":450,"children":451},{},[452],{"type":21,"value":39},{"type":16,"tag":41,"props":454,"children":455},{},[456,460,464,468,472],{"type":16,"tag":45,"props":457,"children":458},{},[459],{"type":21,"value":49},{"type":16,"tag":45,"props":461,"children":462},{},[463],{"type":21,"value":54},{"type":16,"tag":45,"props":465,"children":466},{},[467],{"type":21,"value":59},{"type":16,"tag":45,"props":469,"children":470},{},[471],{"type":21,"value":64},{"type":16,"tag":45,"props":473,"children":474},{},[475],{"type":21,"value":69},{"type":16,"tag":29,"props":477,"children":478},{"id":72},[479],{"type":21,"value":75},{"type":16,"tag":17,"props":481,"children":482},{},[483,484,489,490,495],{"type":21,"value":80},{"type":16,"tag":82,"props":485,"children":487},{"className":486},[],[488],{"type":21,"value":87},{"type":21,"value":89},{"type":16,"tag":82,"props":491,"children":493},{"className":492},[],[494],{"type":21,"value":95},{"type":21,"value":97},{"type":16,"tag":17,"props":497,"children":498},{},[499],{"type":16,"tag":102,"props":500,"children":501},{},[502],{"type":21,"value":106},{"type":16,"tag":108,"props":504,"children":505},{"className":110,"code":111,"language":112,"meta":7,"style":7},[506],{"type":16,"tag":82,"props":507,"children":508},{"__ignoreMap":7},[509,516,523,530,537,544,551],{"type":16,"tag":118,"props":510,"children":511},{"class":120,"line":121},[512],{"type":16,"tag":118,"props":513,"children":514},{},[515],{"type":21,"value":127},{"type":16,"tag":118,"props":517,"children":518},{"class":120,"line":130},[519],{"type":16,"tag":118,"props":520,"children":521},{},[522],{"type":21,"value":136},{"type":16,"tag":118,"props":524,"children":525},{"class":120,"line":139},[526],{"type":16,"tag":118,"props":527,"children":528},{},[529],{"type":21,"value":145},{"type":16,"tag":118,"props":531,"children":532},{"class":120,"line":148},[533],{"type":16,"tag":118,"props":534,"children":535},{},[536],{"type":21,"value":154},{"type":16,"tag":118,"props":538,"children":539},{"class":120,"line":157},[540],{"type":16,"tag":118,"props":541,"children":542},{},[543],{"type":21,"value":163},{"type":16,"tag":118,"props":545,"children":546},{"class":120,"line":166},[547],{"type":16,"tag":118,"props":548,"children":549},{},[550],{"type":21,"value":172},{"type":16,"tag":118,"props":552,"children":553},{"class":120,"line":175},[554],{"type":16,"tag":118,"props":555,"children":556},{},[557],{"type":21,"value":181},{"type":16,"tag":29,"props":559,"children":560},{"id":184},[561],{"type":21,"value":187},{"type":16,"tag":17,"props":563,"children":564},{},[565,566,571,572,577,578,583],{"type":21,"value":192},{"type":16,"tag":82,"props":567,"children":569},{"className":568},[],[570],{"type":21,"value":198},{"type":21,"value":89},{"type":16,"tag":82,"props":573,"children":575},{"className":574},[],[576],{"type":21,"value":205},{"type":21,"value":207},{"type":16,"tag":82,"props":579,"children":581},{"className":580},[],[582],{"type":21,"value":213},{"type":21,"value":215},{"type":16,"tag":108,"props":585,"children":586},{"className":110,"code":218,"language":112,"meta":7,"style":7},[587],{"type":16,"tag":82,"props":588,"children":589},{"__ignoreMap":7},[590,597,604,611,618,625],{"type":16,"tag":118,"props":591,"children":592},{"class":120,"line":121},[593],{"type":16,"tag":118,"props":594,"children":595},{},[596],{"type":21,"value":230},{"type":16,"tag":118,"props":598,"children":599},{"class":120,"line":130},[600],{"type":16,"tag":118,"props":601,"children":602},{},[603],{"type":21,"value":238},{"type":16,"tag":118,"props":605,"children":606},{"class":120,"line":139},[607],{"type":16,"tag":118,"props":608,"children":609},{},[610],{"type":21,"value":246},{"type":16,"tag":118,"props":612,"children":613},{"class":120,"line":148},[614],{"type":16,"tag":118,"props":615,"children":616},{},[617],{"type":21,"value":254},{"type":16,"tag":118,"props":619,"children":620},{"class":120,"line":157},[621],{"type":16,"tag":118,"props":622,"children":623},{},[624],{"type":21,"value":262},{"type":16,"tag":118,"props":626,"children":627},{"class":120,"line":166},[628],{"type":16,"tag":118,"props":629,"children":630},{},[631],{"type":21,"value":270},{"type":16,"tag":29,"props":633,"children":634},{"id":273},[635],{"type":21,"value":276},{"type":16,"tag":17,"props":637,"children":638},{},[639,640,645],{"type":21,"value":281},{"type":16,"tag":82,"props":641,"children":643},{"className":642},[],[644],{"type":21,"value":287},{"type":21,"value":289},{"type":16,"tag":17,"props":647,"children":648},{},[649],{"type":16,"tag":102,"props":650,"children":651},{},[652],{"type":21,"value":297},{"type":16,"tag":108,"props":654,"children":655},{"className":110,"code":300,"language":112,"meta":7,"style":7},[656],{"type":16,"tag":82,"props":657,"children":658},{"__ignoreMap":7},[659,666,673,680,687,694,701,708,715],{"type":16,"tag":118,"props":660,"children":661},{"class":120,"line":121},[662],{"type":16,"tag":118,"props":663,"children":664},{},[665],{"type":21,"value":312},{"type":16,"tag":118,"props":667,"children":668},{"class":120,"line":130},[669],{"type":16,"tag":118,"props":670,"children":671},{},[672],{"type":21,"value":320},{"type":16,"tag":118,"props":674,"children":675},{"class":120,"line":139},[676],{"type":16,"tag":118,"props":677,"children":678},{},[679],{"type":21,"value":328},{"type":16,"tag":118,"props":681,"children":682},{"class":120,"line":148},[683],{"type":16,"tag":118,"props":684,"children":685},{},[686],{"type":21,"value":336},{"type":16,"tag":118,"props":688,"children":689},{"class":120,"line":157},[690],{"type":16,"tag":118,"props":691,"children":692},{},[693],{"type":21,"value":344},{"type":16,"tag":118,"props":695,"children":696},{"class":120,"line":166},[697],{"type":16,"tag":118,"props":698,"children":699},{},[700],{"type":21,"value":352},{"type":16,"tag":118,"props":702,"children":703},{"class":120,"line":175},[704],{"type":16,"tag":118,"props":705,"children":706},{},[707],{"type":21,"value":360},{"type":16,"tag":118,"props":709,"children":710},{"class":120,"line":363},[711],{"type":16,"tag":118,"props":712,"children":713},{},[714],{"type":21,"value":369},{"type":16,"tag":118,"props":716,"children":717},{"class":120,"line":372},[718],{"type":16,"tag":118,"props":719,"children":720},{},[721],{"type":21,"value":378},{"type":16,"tag":29,"props":723,"children":724},{"id":381},[725],{"type":21,"value":384},{"type":16,"tag":17,"props":727,"children":728},{},[729,730,735,736,741],{"type":21,"value":389},{"type":16,"tag":82,"props":731,"children":733},{"className":732},[],[734],{"type":21,"value":395},{"type":21,"value":397},{"type":16,"tag":82,"props":737,"children":739},{"className":738},[],[740],{"type":21,"value":403},{"type":21,"value":215},{"type":16,"tag":17,"props":743,"children":744},{},[745],{"type":21,"value":409},{"type":16,"tag":17,"props":747,"children":748},{},[749],{"type":21,"value":414},{"type":16,"tag":416,"props":751,"children":752},{},[753],{"type":21,"value":420},{"title":7,"searchDepth":130,"depth":130,"links":755},[756,757,758,759,760],{"id":31,"depth":139,"text":34},{"id":72,"depth":139,"text":75},{"id":184,"depth":139,"text":187},{"id":273,"depth":139,"text":276},{"id":381,"depth":139,"text":384},1778042223424]