We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 9207 ☆ A M B ☆
    • 2,475 Posts
    Say I’ve got something like this:

    include_once( 'core/xpdo/xpdo.class.php');
    $search_term = 'Something';
    
    $my_xpdo= new xPDO('mysql:host=localhost;dbname=mydb','db_user','xxxxx','');
    $my_xpdo->addPackage('my','/Users/everett/Sites/EDF-modx/html/core/components/my/model/');
    
    $query = $my_xpdo->newQuery('Jobs');
    $query->where(array(  
        'title:LIKE' => "%$search_term%",  
    )); 
    
    $resources = $my_xpdo->getCollection('Jobs', $query );


    That works fine, but if my table ("jobs" in this example) has relations to other tables, how do I get access to those other attributes? Is xPDO performing the join on that other table automatically? Say for example I have a table for categories, and each job exists in only 1 category, so I’ve defined my XML schema, generated my classes, etc... and when I do a query like the above, I want to see more than just the category_id foreign key... I want to see the category name from the categories table.

    How is that done?

    UPDATE: I’ve tried using the select method, e.g.
    $query->select( 'Jobs.*, EducationLevels.*' );


    But at least that short form of it does not return any additional data from the query, even though it SAYS it’s generating a valid query. If I do this:

    $query->prepare();
    print $query->toSQL(); exit;


    Then execute the resulting query in MySQL directly, I get all the columns I want. What’s up with that?
      • 21257 MODX Staff
      • 730 Posts
      Hey Everett,

      There are a couple of ways to do this.

      One is to do the join yourself, and populate the Category name into the Job object you’re retrieving. This works fine when the relationship is one to one or many to one (ie the "cardinality" of the relationship from Jobs to Categories is 1). This is also a little more resource efficient than, but lacks some of the cool factor of the second way.

      include_once( 'core/xpdo/xpdo.class.php');
      $search_term = 'Something';
      
      $my_xpdo= new xPDO('mysql:host=localhost;dbname=mydb','db_user','xxxxx','');
      $my_xpdo->setOption(xPDO::OPT_HYDRATE_ADHOC_FIELDS, true); // I _think_ this has to be here for this example to work
      $my_xpdo->addPackage('my','/Users/everett/Sites/EDF-modx/html/core/components/my/model/');
      
      $query = $my_xpdo->newQuery('Jobs');
      $query->select('Jobs.*, Category.name as category_name');
      $query->where(array(  
          'title:LIKE' => "%$search_term%",  
      ));
      $query->leftJoin('Category', 'Category');
      $resources = $my_xpdo->getCollection('Jobs', $query );
      
      foreach($resources as $Job) {
          $job_name = $Job->get('name');
          $category_name = $Job->get('category_name');
          echo "Job {$job_name} is in Category {$category_name}<br />\n";
      }
      


      Second way is to use getObjectGraph or getCollectionGraph. xPDO does the joins for you, and give you back a graph of the objects.

      include_once( 'core/xpdo/xpdo.class.php');
      $search_term = 'Something';
      
      $my_xpdo= new xPDO('mysql:host=localhost;dbname=mydb','db_user','xxxxx','');
      $my_xpdo->setOption(xPDO::OPT_HYDRATE_RELATED_OBJECTS, true);
      $my_xpdo->addPackage('my','/Users/everett/Sites/EDF-modx/html/core/components/my/model/');
      
      $query = $my_xpdo->newQuery('Jobs');
      $query->where(array(  
          'title:LIKE' => "%$search_term%",  
      ));
      $resources = $my_xpdo->getCollectionGraph('Jobs', '{"Categories":{}}', $query );
      
      foreach($resources as $Job) {
          $job_name = $Job->get('name');
          $category_name = $Job->Category->get('name');
          echo "Job {$job_name} is in Category {$category_name}<br />\n";
      }
      

      In this example, the related Category object is created and given to you as a property of the $Job object.
      Under the hood the get*Graph methods, by default, add related objects into the $Job->_relatedObjects array. But since xPDO::OPT_HYDRATE_RELATED_OBJECTS is set to true, it also creates a reference to the related object (for convenience) as $Jobs->Category. The coolness here is that you have your full Category xPDOObject, and can do anything with it. If you were to change the name of the Category object, then save it, or better yet, even save the Job, xPDO would save the Category record. It saves any changes you’ve made to objects in the graph. It does this pretty efficiently in terms of SQL. If you have a huge graph though, ie. huge numbers of objects, it can take chug lots of resources/memory, and these conveniences wouldn’t be the way to go.
        Mike Schell
        Lead Developer, MODX Cloud
        Email: [email protected]
        GitHub: https://github.com/netProphET/
        Twitter: @mkschell
        • 21257 MODX Staff
        • 730 Posts
        I recommend checking out api.modxcms.com to look at xPDOQuery::leftJoin() and xPDO::getCollectionGraph from the examples above.. better than me playing broken telephone describing those parameters.
          Mike Schell
          Lead Developer, MODX Cloud
          Email: [email protected]
          GitHub: https://github.com/netProphET/
          Twitter: @mkschell
          • 9207 ☆ A M B ☆
          • 2,475 Posts
          THANK YOU. That got it working.

          So by "Hydrate", you mean to go ahead and fetch those fields from the joined table? Whereas without the hydrate option, the join is done, but nothing from the foreign table is selected.

          It seems that using that option REQUIRES that you specify columns with the select() method. Is that correct? That’s not database agnostic at that point... no?

          Man, are you looking at some other API docs? Those tell me everything and nothing at the same time. My brain shut down after I had come up with about 16 different ways to use that function, none of them correct.