We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 14951
    • 151 Posts
    Hello,

    I can’t quite figure out how to accomplish an innerJoin in xPDO. I have spent hours reading and re-reading the docs to no avail. My snippet code looks as follows:
    <?php
    	$path = MODX_CORE_PATH . 'components/ocLatestProducts/';
    	$result = $modx->addPackage('ocLatestProducts',$path . 'model/','opcart_');
    	if (! $result) {
    	  return 'failed to add package';
    	}
    	
    	$query = $modx->newQuery('Product');
    	$query->innerJoin('ProductDescription', 'PD');
    	$query->select(array (
    	    'Product.*',
    	    'PD.name'
    	));
    	$query->where(array (
    	    'Product.product_id' => 'PD.product_id',  
    	));
    	$query->sortby('date_added', 'DESC');
    	$query->limit(6);
    	
    	$products = $modx->getCollection('Product',$query); 
    
    	$output = '<p>Total: '. count($products) . '</p>';
    
    	foreach($products as $product) {
    
    	    $output .= '<p>Name: ' . $product->get('name');
    	    $output .= '<p>Price: ' . $product->get('price');
    	    $output .= '<br />Image: ' . $product->get('image') . '<br /></p>';
    	}
    
    	return $output;

    The query that is being generated from this shown via the toSQL() function:
    SELECT Product.*, PD.name FROM `opcart_product` AS `Product` JOIN `opcart_product_description` `PD` ON WHERE `Product`.`product_id` = 0 ORDER BY date_added DESC LIMIT 6

    Everything functions great when I don’t use innerJoin and just grab the two fields from a single table. It seams that I need some xPDO expert advice on this one as I’ve read every possible example I can find.
    How do I get data from two different tables (or xPDO objects) wherein each table is associated by "product_id"?

    Thanks for any assistance!
    -Jared
      • 28042 ☆ A M B ☆
      • 24,524 Posts
      innerJoin is only for joins within the same table. You need left join, or maybe just join to connect two tables.
        Studying MODX in the desert - http://sottwell.com
        Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
        Join the Slack Community - http://modx.org
        • 22303 MODX Staff
        • 10,725 Posts
        Actually an inner join is just a regular join in SQL terms, and it just means that there must be records on both sides of the join for a row to be returned, unlike an outer join which only requires a record on one side of the join to be present in order for a row to be returned. See http://en.wikipedia.org/wiki/Join_(SQL)#Inner_join and http://en.wikipedia.org/wiki/Join_(SQL)#Outer_joins for more information.

        You need to define the join condition( s ) explicitly in the innerJoin() call, not in the where() call.
          • 28042 ☆ A M B ☆
          • 24,524 Posts
          Ok, I’ll shut up now and go back to commenting on things I know something about.
            Studying MODX in the desert - http://sottwell.com
            Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
            Join the Slack Community - http://modx.org
            • 14951
            • 151 Posts
            Thank you Susan, and thank you OpenGeek! Based on this, my innerJoin would look like this:
            $query->innerJoin('ProductDescription', 'PD',array('Product.product_id' =>'PD.product_id'));

            Is the above correct syntax?

            I’ve removed the where() call completely and given this a try with the updated innerJoin() as shown above. Here is what toSQL() returns:
            SELECT Product.*, PD.name FROM `opcart_product` AS `Product` JOIN `opcart_product_description` `PD` ON `Product`.`product_id` = 0 ORDER BY date_added DESC LIMIT 6


            How come "PD.product_id" is evaluating to "0"? I must be missing something? It seems like I’m just about to a successful query though!

            Thanks again for any assistance!
            -Jared


              • 22303 MODX Staff
              • 10,725 Posts
              You can’t bind a column reference as a prepared statement parameter like that—it is assuming the PD.product_id is a value of the Product.product_id column. Just make it a string:
              $query->innerJoin('ProductDescription', 'PD', array("Product.product_id = PD.product_id"));

                • 14951
                • 151 Posts
                Thank you very much!

                Your suggestion works beautifully provided I remove my select() call. If I keep my select() call, I end up with a 500 Internal Server error. Thankfully, I don’t really need the select() call...It was just there for optimization purposes. For what it’s worth, the select() call works fine if I remove the innerJoin() call.

                Anyhow, it seems as though things are pretty much working with one exception. Sorry to seem so elementary about this, but I’m not sure how to reference the items from my secondary table now that I (presumably) have the data.
                <?php
                	$products = $modx->getCollection('Product',$query); 
                
                	$output = '<p>Total: '. count($products) . '</p>';
                
                	foreach($products as $product) {
                
                	    $output .= '<p>Name: ' . $product->get('name');
                	    $output .= '<p>Price: ' . $product->get('price');
                	    $output .= '<br />Image: ' . $product->get('image') . '<br /></p>';
                	}
                
                	return $output;

                I tried doing print_r($products) to make sure the data was actually there, but that resulted in a memory error.

                I’m getting the data fine for both the price and image, but getting nothing for name. I assume I’m just referencing it wrong..?

                Thank you so very much for all the assistance you have already provided! This has been an amazing learning experience!

                -Jared
                  • 3749
                  • 24,544 Posts
                  Try this (minus the <?php of course):

                  <?php
                  $output .= '<pre>' . print_r($product->toArray(),true);
                    Did I help you? Buy me a beer
                    Get my Book: MODX:The Official Guide
                    MODX info for everyone: http://bobsguides.com/modx.html
                    My MODX Extras
                    Bob's Guides is now hosted at A2 MODX Hosting
                    • 14951
                    • 151 Posts
                    Uhg, I think that means my innerJoin() isn’t working. That code you supplied produced the following:
                    Array
                    (
                        [product_id] => 43
                        [model] => Product 16
                        [sku] => 
                        [location] => 
                        [quantity] => 9
                        [stock_status_id] => 5
                        [image] => data/macbook_1.jpg
                        [manufacturer_id] => 8
                        [shipping] => 0
                        [price] => 500
                        [tax_class_id] => 9
                        [date_available] => 2009-02-03
                        [weight] => 0
                        [weight_class_id] => 1
                        [length] => 0
                        [width] => 0
                        [height] => 0
                        [length_class_id] => 2
                        [status] => 1
                        [date_added] => 2009-02-03 21:07:49
                        [date_modified] => 2010-03-25 14:37:30
                        [viewed] => 0
                        [sort_order] => 0
                        [subtract] => 1
                        [minimum] => 1
                        [cost] => 0
                    )

                    Unfortunately, that is only the data from the table "products". (I didn’t put it in the loop, because I assumed I’d only need the info for one product to see if "name" was included)

                    The query on the other hand looks good aside from the selection: (It isn’t selecting any of the fields from ProductDescription, although the JOIN syntax is there..?)
                    Array
                    SELECT `Product`.`product_id` AS `Product_product_id`, `Product`.`model` AS `Product_model`, `Product`.`sku` AS `Product_sku`, `Product`.`location` AS `Product_location`, `Product`.`quantity` AS `Product_quantity`, `Product`.`stock_status_id` AS `Product_stock_status_id`, `Product`.`image` AS `Product_image`, `Product`.`manufacturer_id` AS `Product_manufacturer_id`, `Product`.`shipping` AS `Product_shipping`, `Product`.`price` AS `Product_price`, `Product`.`tax_class_id` AS `Product_tax_class_id`, `Product`.`date_available` AS `Product_date_available`, `Product`.`weight` AS `Product_weight`, `Product`.`weight_class_id` AS `Product_weight_class_id`, `Product`.`length` AS `Product_length`, `Product`.`width` AS `Product_width`, `Product`.`height` AS `Product_height`, `Product`.`length_class_id` AS `Product_length_class_id`, `Product`.`status` AS `Product_status`, `Product`.`date_added` AS `Product_date_added`, `Product`.`date_modified` AS `Product_date_modified`, `Product`.`viewed` AS `Product_viewed`, `Product`.`sort_order` AS `Product_sort_order`, `Product`.`subtract` AS `Product_subtract`, `Product`.`minimum` AS `Product_minimum`, `Product`.`cost` AS `Product_cost` FROM `opcart_product` AS `Product` JOIN `opcart_product_description` `PD` ON Product.product_id = PD.product_id ORDER BY Product.date_added 

                    Adding the select() call from my original snippet results in an Internal Server Error (nothing in logs). Am I correct in assuming that selection should be automatically generated based upon the join? All of the examples in the docs are without a select() call (even with innerJoin()’s), so I assume this to be the case.

                    Thanks for sending that extra line of code to further debug!
                    -Jared
                      • 14372
                      • 49 Posts
                      Hi, I’m trying to achieve the following SQL statement;

                      SELECT * 
                      FROM ah_user_attributes 
                      INNER JOIN ah_member_groups 
                      ON ah_user_attributes.internalkey=ah_member_groups.member


                      ah_ is my database index.

                      This is my xPDO code ...

                      $query = $modx->newQuery('modUserProfile');
                      $query->innerJoin('modUserGroupMember','Group', array("modUserProfile.internalKey = modUserGroupMember.member"));
                      $user_table_info = $modx->getCollection('modUserProfile',$query);
                      
                      foreach ($user_table_info as $ah_user) {
                      
                          $ah_user_ext = $ah_user->get('extended');
                          $output .= '<tr>
                                         <td>'.$ah_user->get('fullname').'</td>    
                                         <td>'.$ah_user_ext['institution'].'</td>  
                                         <td>'.$ah_user_ext['degree'].'</td>
                                         <td>'.$ah_user_ext['field_of_study'].'</td>
                                         <td>'.$ah_user_ext['major'].'</td>
                                         <td>'.$ah_user_ext['year_of_study'].'</td>
                                         <td>'.$ah_user->get('gender').'</td>
                                  </tr>';
                        }
                        return $output;


                      Where I’m I going wrong? Really getting worn out reading about this stuff ... been trying for quite a while on the docs and the forums *sigh*