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
    I’ve got an example brewing here:
    http://rtfm.modx.com/display/xPDO20/More+Examples+of+xPDO+XML+Schema+Files

    The example is that you’ve got two tables linked up together in a 1-to-1 relationship, in the same way as the built-in MODx users and user_attributes tables: for every row in the users table, there is one row in the user_attributes table. Therefore, the object is a single unit that coincidentally happens to be stored in 2 separate database tables. I’ve figured out a Snippet that can retrieve data from both tables, doing something like this:

    <?php
    	$base_path = MODX_CORE_PATH . 'components/mytest/';
    	$modx->addPackage('mytest',$base_path.'model/','');  
    	$user = $modx->getObject('Users', array('user_id' => 1 ) ); 
    	$userdata = $user->getOne('Userdata'); 
    	$output = '';
    	$output .= $user->get('username');
    	$output .= $userdata->get('age');
    	return $output;
    ?>


    But is there a way to fetch ALL attributes into a single object instead of splitting the attributes into 2 objects? It’s driving me mad that my objects are following my database too closely.

    Thanks!
      • 4172
      • 5,888 Posts
        -------------------------------

        you can buy me a beer, if you like MIGX

        http://webcmsolutions.de/migx.html

        Thanks!
        • 9207 ☆ A M B ☆
        • 2,475 Posts
        Thanks Bruno. Maybe a better question is:

        Is there a way to programmatically iterate through all the relationships that are defined for a given object? I know I can use the leftJoin method (or something similar) once I know a relationship exists, but is there any way to list all available relationships without having to look at the XML definition or the database tables?
          • 4172
          • 5,888 Posts
            -------------------------------

            you can buy me a beer, if you like MIGX

            http://webcmsolutions.de/migx.html

            Thanks!
            • 22303 MODX Staff
            • 10,725 Posts
            You can create classes that use two table classes in whatever way you want, but there is no multiple table to single object mapping features directly in xPDO. This is part of the design of xPDO that helps keep it flexible and lightweight. You can create and extend "domain" classes anyway you want to make use of xPDO’s simple table classes, or you can just override and add methods to your table classes to hide the table relationships and provide an array of aggregated data.
              • 9207 ☆ A M B ☆
              • 2,475 Posts
              It looks like the loadCollectionGraph function would get me there:

              <?php
              $base_path = MODX_CORE_PATH . 'components/mytest/';
              $modx->addPackage('mytest',$base_path.'model/','');  
              $output = '';
              $x = $modx->getObjectGraph('MyObject',array(),array('id'=>1)); 
              // $x = xPDOObject::loadCollectionGraph($modx,'MyObject',array(), array('id'=>1) ); // <-- optionally, you could use this format
              return print_r($x, true);
              


              There are lists of the aggregate and composite relationships for each object... so I could dynamically iterate through those relationships and then join on those tables like Bruno demonstrated.
                • 22303 MODX Staff
                • 10,725 Posts
                To clarify, and this needs to be worked into the docs, you would never call xPDOObject::loadCollectionGraph() directly like this, and I’ll explain why.

                To support a faux sort of late-static binding for calling custom static load* methods in versions of PHP before 5.3 (which now supports late-static binding natively), I introduced a way to define custom loader classes per class by simply extending an xPDOObject derivative. So the proper way to call any of these methods is the same way xPDO::getCollectionGraph() does:
                <?php
                        $loader = $this->getObjectLoader($className, 'loadCollectionGraph'); // get the defined loader class for $className
                        $objCollection= call_user_func_array($loader, array(& $this, $className, $graph, $criteria, $cacheFlag)); // call the static method on the loader class
                ?>

                This is necessary to allow custom loader classes in xPDO. For instance, any MODx table class that is securable via an ACL (modAccess* classes) extends modAccessibleObject. This class defines it’s own implementations of several of the load methods, including loadCollectionGraph. This allows us to automatically apply the policy check for the load permission on the results before they are returned. There are other uses for this that will allow us to easily extend the functionality for specific uses, e.g. i18n of fields, automatically handling intersection tables in many to many relations, implementing Nested Sets or Closure tables automatically, etc.
                  • 9207 ☆ A M B ☆
                  • 2,475 Posts
                  Interesting. Thanks for the clarification on this.

                  Is it worthwhile submitting a feature request to expose this type of action via a more user-friendly API method? Use of call_user_func_array() seems really out of place.

                  The overall problem I’m trying to solve here is loading an *ENTIRE* object. It makes good sense that xPDO doesn’t immediately load up all composite/aggregate fields, but there are times when that is exactly what should be done. E.g. this is exactly how a schema-less system such as MongoDB would store/retrieve an object: as a nested data structure, not divided into arbitrary tables.

                  I still don’t really understand what hydration means, but what leaps to mind is exactly what I’m trying to do here: get a full instance of an object, with all related fields available with their values. E.g. a blogpost with all its comments might be represented as an object such as:

                  $blogpost = $modx->getEntireObject('Blogpost', 123);  // <-- my fictitious function
                  print_r( $blogpost->toArray() );
                  
                  // Contains something like:
                  array(
                  	'title' => 'My Title',
                  	'content' => 'This is my post...',
                  	'Comments' => array(
                  		array('content' => 'My first comment...'),
                  		array('content' => 'Other comment...'),
                  		array('content' => 'Third comment.'),
                  	)
                  );
                  


                  I can imagine an option in the getObject method where you could specify LOAD_ALL or a "getEntireObject" method (as above) that specifies to fetch all related fields. I could also imagine that you could define limits to the depth of the retrieval. E.g. "fetch the first 10 comments unless I override it". Each of the composite/aggregate relationships represents additional queries, so it makes sense that you might be able to tap into the limits/offset or other criteria for each of those relationships, but the point is that it would be extremely useful to be able to tap into that stuff immediately when you request the object, and not manually "by-hand" when writing out how to load up all of an object’s relations.

                  Did that make sense? I can come up with my own function that iterates through an object instance and fetches all the related objects, but it seems plausible that this type of action could/should be available in the xPDO core. I’d bet this could even be done in a way that didn’t perform the joins until a field is requested...
                    • 22303 MODX Staff
                    • 10,725 Posts
                    Hydration is simply the process of populating the values retrieved from the result set into the object fields in memory.

                    I think I see what you are after now. I think you are asking for two separate things here; correct me if I’m missing the point:

                    1. A simple way to load all defined aggregate and composite relations when calling getObjectGraph() or getCollectionGraph(), maybe something like:
                    <?php
                    $collection = $xpdo->getCollectionGraph('myClass', $xpdo->getGraph('myClass'), $criteria);
                    ?>

                    where getGraph() simply builds the entire graph for you? Could add some options for depth or other needs as well...
                    2. A toArray()-like method that includes a multidimensional array representation of the related objects (and their fields) that you have loaded in memory? Something like toArrayGraph()
                    <?php
                    $collection = $xpdo->getCollectionGraph('myClass', $xpdo->getGraph('myClass'), $criteria);
                    foreach ($collection as $object) {
                        var_dump($object->toArrayGraph());
                    }
                    ?>

                    where again we could add options to limit the graph, or lazy load the graph (or parts of it) on demand, etc.

                    Am I close? If so, those would be a very simple methods to add, if someone say, added a feature request or two... wink
                      • 9207 ☆ A M B ☆
                      • 2,475 Posts
                      Genius. Yes, that’s exactly what I’m after. A feature request has been submitted: http://bugs.modx.com/issues/2854 wink

                      A case in point is here... check out the section on Many-to-many relationships:

                      http://rtfm.modx.com/display/xPDO20/More+Examples+of+xPDO+XML+Schema+Files

                      I’m not happy with how clunky it is to walk through each relation to get the related object attributes (bear in mind, my implementation there may have been completely whacko -- please let me know if that’s the case).

                      It’s similar to the problems with table- vs. CSS-layouts. Everyone says table layouts are horrible, and to be fair, table soup sucks... but div soup is no better. ORMs are great... but I’d rather write SQL joins than navigate through relations in an (unfamiliar) ORM layer. Having some methods that return an entire object with all related fields would spare developers the trouble of having to manually map out the relations. It’s also more useful to have an abstraction here: if I can return *entire* objects using one method, regardless of which objects are being operated on, then I can re-use a lot of code. More and more I end up passing around fully qualified objects as my "units". You know?