We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
  • In my usual style I’ve jumped into the deep end without fully knowing what I’m doing!

    I’m working away trying to represent some classes in a manner explained here:
    http://www.agiledata.org/essays/mappingObjects.html#Figure9GenericDataSchema
    This is a different style of persisting object data than I’ve seen in the xPDO examples and in discussion here. After reading through that essay I’ve come to realize that there are several valid strategies for persisting objects relationally, and I’m curious to see just how flexible xPDO really is. So far I have a good deal of faith in it.

    What I’m hoping to accomplish using this approach is to be able to dynamically create objects, assign arbitrary attributes to them, persist them and to eventually describe the relationships between them.. all without requiring any change to the database schema. In terms of the php classes, I guess it’ll be like hand-coding them with a huge leg-up provided by xPDO-generated scaffolding.

    To describe my problem in an abstract way, I:
    - created an Object A1
    - created an Object B1 and B2, and related them to Object A1 (many B’s can be related to an A)
    - created Object C1 and related it to Object B1(many C’s can be related to a B)
    - created Object C2 and C3 and related them to Object B2
    - save()’d Object A1 and automagically persisted all objects described above (whoo hoo)

    Then I wanted to load up the whole mess with as little code as I could, so I came across the hydrateGraphNode() function.
    What I found was that I got Object A1 back, with related Objects B1 and B2, but no Object C’s.
    I traced through the code and eventually found a place where the Object C’s were indeed being assigned to Object B’s.. but still weren’t making it back out into my Object A.
    After looking at what was happening, I found that I had to make a couple of changes to the hydrateGraphNode() function.
    This is my modified version of the function, commented where altered:
        function hydrateGraphNode(& $row, & $instance, $alias, $relations) {
            $relObj= null;
            if ($relationMeta= $instance->getFKDefinition($alias)) {
                if ($relObj= $this->xpdo->newObject($relationMeta['class'])) {
                    $prefix= $alias . '_';
                    $relObj->fromArray($row, $prefix, true);
                    $relObj->_new= false;
                    $relObj->_dirty= array ();
                    if (strtolower($relationMeta['cardinality']) == 'many') {
                        $instance->addMany($relObj, $alias);
                    }
                    else {
                        $instance->addOne($relObj, $alias);
                    }
                }
            }
            if (!empty ($relations) && is_object($relObj)) {
            	// nP: when recursively calling hydrateGraphNode, we need $instance
            	// to be a reference to items just added via addMany or addOne, or else
            	// we end up adding the subrelations to copies that don't go anywhere
            	if(isset($relObj->_alias) && is_array($instance->_relatedObjects[$relObj->_alias])) {
                    $relKeys= array_keys($instance->_relatedObjects[$relObj->_alias]);
                    foreach($relKeys as $relKey) {
                        $refObj= & $instance->_relatedObjects[$relObj->_alias][$relKey];
                        foreach ($relations as $relationAlias => $subRelations) {
                            // nP: in some cases, the $subRelation array will be empty, but we'll still want to run hydrateGraphNode with the relation represented by relationAlias
                            if(is_array($subRelations) && count($subRelations) > 0) {
                                foreach ($subRelations as $subRelation) {
                                    $this->hydrateGraphNode($row, $refObj, $relationAlias, $subRelation);
                                }
                            } else {
                                $this->hydrateGraphNode($row, $refObj, $relationAlias, null);
                            }
                        }
                    }
            	}
            }
        }
    


    Now when I call
     $myCategory = $xpdo->getObjectGraph('aliasA', array('aliasB'=>array('aliasC'=>array())));
    

    I get the whole happy heap of objects back all nicely related inside $myCategory.
    I am not familiar enough with xPDO yet to know whether this is a sane approach or if I even approached the problem-solving properly.
      Mike Schell
      Lead Developer, MODX Cloud
      Email: [email protected]
      GitHub: https://github.com/netProphET/
      Twitter: @mkschell
    • Looks to me like you know exactly what you are doing, as you caught a bug in the refactoring from alpha to beta. I’m going to add a bug report, author some unit tests to monitor this behavior, and incorporate the fixes.

      Have any interest in joining the xPDO development team? wink
      • Here is the bug report... http://www.xpdo.org/bugs/?do=details&task_id=11

        I’ll work on the unit tests and get the solution committed this afternoon.
        • netProphET:

          To help fully solve this issue properly, can you confirm what environment you were testing this in? Specifically, I need to know PHP version and if native PDO extensions are enabled and being used.
          • I’m using PHP/FI .. er, no, wait.. I did upgrade to PHP 4.4.6 after all!
            ;-)
              Mike Schell
              Lead Developer, MODX Cloud
              Email: [email protected]
              GitHub: https://github.com/netProphET/
              Twitter: @mkschell
            • @OpenGeek
              Thanks for the recent commits.. either they solved this problem, or I learned what the ’hydrate_rel_obj_vars’ config variable is for, or both.

              I’d like to confirm that what I’m seeing is what you expect in my little php 4 example:

              Object A aggregates many Object Bs (which become composite in A).
              Object B aggregates many Object Cs (which become composite in B).

              If I simply do this,
              $myA = $xpdo->getObjectGraph('A', array('b'=>array('c'=>array())));
              

              , then $myA is loaded with it’s attributes, and I get my Object Bs fully loaded in A’s _relatedObjects array.
              My Object Bs contain an empty array ’c’ (Object C’s alias) in their _relatedObject array.
              My Object Bs’ _relatedObject arrays also contain a null variable named after A’s alias (?!)
              None of the objects are loaded up as root properties of their container (not sure what terminology is right here - parent?), so there is no
              $myA->b->c stuff going on.

              Now. If I do this:
              $xpdo->config['hydrate_rel_obj_vars'] = 1;
              $myA = $xpdo->getObjectGraph('A', array('b'=>array('c'=>array())));
              

              , then everything is loaded up fully. My Object Bs contain references to full Object Cs, and the objects are referenced such that I can
              $myA->b->c->vaporize($cheese);

              Now I’m just scratching my head because my Object Cs contain a null attribute named after the "parent" Object B’s alias.
                Mike Schell
                Lead Developer, MODX Cloud
                Email: [email protected]
                GitHub: https://github.com/netProphET/
                Twitter: @mkschell
              • Sorry for the slow response; that is correct though.

                As for the null attribute back to the parent: currently xPDO does not build bi-directional references to related objects since it is assumed that you already have the object b in memory in order to get the relations down the line. The null attribute is there until you execute $c->getOne(’b’). In other words, the relation back to B would only be used if you had an instance of your C class initially, or were building an object graph query from the C instance. I do not see a benefit in creating another reference back to the same object I just came from by default, but feel free to convince me otherwise.
                • Quote from: netProphET at Sep 10, 2007, 10:11 PM

                  I’m working away trying to represent some classes in a manner explained here:
                  http://www.agiledata.org/essays/mappingObjects.html#Figure9GenericDataSchema
                  This is a different style of persisting object data than I’ve seen in the xPDO examples and in discussion here. After reading through that essay I’ve come to realize that there are several valid strategies for persisting objects relationally, and I’m curious to see just how flexible xPDO really is. So far I have a good deal of faith in it.

                  What I’m hoping to accomplish using this approach is to be able to dynamically create objects, assign arbitrary attributes to them, persist them and to eventually describe the relationships between them.. all without requiring any change to the database schema. In terms of the php classes, I guess it’ll be like hand-coding them with a huge leg-up provided by xPDO-generated scaffolding.
                  At the moment, I’ve only fully implemented the single-table inheritance model, and partially implemented simple multiple-table inheritance in xPDO (no searching built-in across tables for superclasses). However, it is possible to develop these generic inheritence data models in xPDO without implementing base class (xPDO, xPDOObject, etc.) support for it.

                  I’m in the process of developing some documentation and tutorials for xPDO that I am basing on many of the descriptions in that article, and some related articles on that site. This should help reveal more of how xPDO implements (or why it doesn’t) many of the ideas there. Thanks for the link, BTW.
                  • Quote from: OpenGeek at Sep 18, 2007, 09:25 PM

                    I do not see a benefit in creating another reference back to the same object I just came from by default, but feel free to convince me otherwise.
                    Naw, I agree completely with you.

                    Quote from: OpenGeek at Sep 18, 2007, 09:37 PM

                    However, it is possible to develop these generic inheritence data models in xPDO without implementing base class (xPDO, xPDOObject, etc.) support for it.
                    Aha, I think I’m starting to get it.

                    Quote from: OpenGeek at Sep 18, 2007, 09:37 PM

                    I’m in the process of developing some documentation and tutorials for xPDO that I am basing on many of the descriptions in that article, and some related articles on that site. This should help reveal more of how xPDO implements (or why it doesn’t) many of the ideas there. Thanks for the link, BTW.
                    Wow, that’s more than I had hoped for! I think that’s going to help push a lot of coders over the cusp towards xPDO. Beautiful!
                      Mike Schell
                      Lead Developer, MODX Cloud
                      Email: [email protected]
                      GitHub: https://github.com/netProphET/
                      Twitter: @mkschell