On March 26, 2019 we launched new MODX Forums. Please join us at the new MODX Community Forums.
Subscribe: RSS
  • Thanks for the reply and sorry about messing up the usual convention but I am getting so confused with this error msg that it drives me nuts.

    Your example is working fine and doing exactly what I wanted to do.

    Interestingly using an alias with addMany is not causing any error msgs in the MODx error log, but the database joins are also not coming through...soo strange.

    $blogpost->addMany($tags, 'aliasBlogpostsTags');


    Additionally I figured out that the "No foreign key definition for parentClass" error is printed as many times as there are objects in addMany function.

    Any idea how I can investigate further why my relationships are not working? I did almost anything to debug this, but no success so far and using your provided workaround seems to be very complicated to deploy for a way more complicated schema that I have in mind for my production system. Therefore it would be much easier if the default xpdo relationship model would work...any more ideas? [ed. note: mojorisin last edited this post 4 years, 10 months ago.]
    • I don't have a lot of ideas. You can try using the class name of aliasBlogpostsTags instead of the alias, but the argument is called $alias, so I doubt if it will work.

      A couple of things to try:

      First, check the return value of loadClass(). It returns false on failure.

      Second, it shouldn't be necessary, but you could try calling
      loadClass() on the classes involved. They are supposed to be loaded automatically when needed after loadClass(), but addMany() may not trigger that for all of them. loadClass() also returns false on failure, so be sure to check that as well.
        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
      • I did some comprehensive investigation last night and here are my results...

        First of all loadClass() on all three classes is returning true

        $ofcModelPath = MODX_CORE_PATH . 'components/ofc/model/';
        
        $success = $modx->loadClass('ofc.classTags', $ofcModelPath, true, false);
        if (!$success) {return 'Could not load class classTags';}
        
        $success = $modx->loadClass('ofc.classBlogposts', $ofcModelPath, true, false);
        if (!$success) {return 'Could not load class classBlogposts';}
        
        $success = $modx->loadClass('ofc.classBlogpostsTags', $ofcModelPath, true, false);
        if (!$success) {return 'Could not load class classBlogpostsTags';}


        Secondly calling loadClass() on all classes involved is returning the foreign key error again

        $modx->addPackage('ofc', MODX_CORE_PATH . 'components/ofc/model/');
        
        $ofcModelPath = MODX_CORE_PATH . 'components/ofc/model/';
        
        $success = $modx->loadClass('ofc.classTags', $ofcModelPath, true, false);
        if (!$success) {return 'Could not load class classTags';}
        
        $success = $modx->loadClass('ofc.classBlogposts', $ofcModelPath, true, false);
        if (!$success) {return 'Could not load class classBlogposts';}
        
        $success = $modx->loadClass('ofc.classBlogpostsTags', $ofcModelPath, true, false);
        if (!$success) {return 'Could not load class classBlogpostsTags';}
         
        $blogpost = $modx->getObject('classBlogposts', 1);
        $tags = $modx->getCollection('classTags');
         
        $blogpost->addMany($tags);
        $blogpost->save();


        [2015-09-18 09:02:33] (ERROR @ /index.php) No foreign key definition for parentClass: classBlogposts using relation alias: classTags
        [2015-09-18 09:02:33] (ERROR @ /index.php) No foreign key definition for parentClass: classBlogposts using relation alias: classTags

        This is pretty interesting. When leaving out the second parameter of the addMany() the error msg prints the classname of the parentClass ('classTags') where it should use the alias of the composite relationship ('aliasBlogpostsTags'). This doesn't make any sense at all. Is my code wrong?

        $blogpost = $modx->getObject('classBlogposts', 1);
        $tags = $modx->getCollection('classTags');
         
        $blogpost->addMany($tags, 'aliasBlogpostsTags'); // the correct alias of the composite relationship
        $blogpost->save();


        No error msgs but also no database transactions.

        $blogpost = $modx->getObject('classBlogposts', 1);
        $tags = $modx->getCollection('classTags');
         
        $blogpost->addMany($tags, 'classBlogpostsTags'); // class name of the composite relationship
        $blogpost->save();


        [2015-09-18 09:12:58] (ERROR @ /index.php) No foreign key definition for parentClass: classBlogposts using relation alias: classBlogpostsTags
        [2015-09-18 09:12:58] (ERROR @ /index.php) No foreign key definition for parentClass: classBlogposts using relation alias: classBlogpostsTags

        So _getAliases() is not processing or not picking up the correct relationship alias. The question is why...

        I also did some further investigation in the forums and exactly the same error msg is mentioned in a couple of threads related to xpdo with many-to-many relationships and without any solution. E.g. http://forums.modx.com/thread/90890/migxdb-cmp---creating-many-to-many-relationships?page=2#dis-post-505965

        Then I came across this post by opengeek http://forums.modx.com/thread/31879/starting-with-xpdo-addmany-not-working?page=2#dis-post-172412 which states that xpdo doesn't include any automatic functionality for managing intersection tables. Does this mean that addMany() and addOne() is not working for custom DB tables and I need to code this manually? What is the whole point of defining relationships in the schema when xpdo doesn't take care of it? I am confused...



        [ed. note: mojorisin last edited this post 4 years, 10 months ago.]
        • I'm confused too. The MODX core has lots of instances of addMany(). The resource/create processor uses it (without the second argument) to add TVs to a resource and that's a many-to-many relationship.

          That post from OpenGeek is almost 5 years old.

          You might take a look at your class and map files and compare them to the ones for MODX objects like modResource, modTemplateVar, and modTemplateVarResource, to see if yours are missing something. The MODX ones are in in core/model/modx/mysql.
            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
          • While xPDO can at times be magical, it's not as magical as you seem to think it is. Going back to your first schema and post, this is how your models are related according to the schema:

            - sfStore
            -- StoreOwners relation to sfStoreOwner (id=store)
            - sfOwner
            -- StoreOwners relation to sfStoreOwner (id=owner)
            - sfStoreOwner
            -- Store relation to sfStore (store=id)
            -- Owner relation to sfOwner (owner=id)

            These are the only relations xPDO knows off, as those are in your schema and model.

            What you're trying to do in this code:
            $modx->addPackage('storefinder', MODX_CORE_PATH . 'components/storefinder/model/');
             
            $store = $modx->getObject('sfStore', 1);
            $owners = $modx->getCollection('sfOwner');
             
            $store->addMany($owners);
            $store->save();


            is add a bunch of sfOwner records directly to sfStore records. However, there is no direct relation between sfStore and sfOwner - there is only sfStore -> StoreOwners relation -> sfStoreOwner -> Owner relation -> sfOwner. That's what the error message is telling you, it doesn't know what to do with the sfOwner records you are trying to add. It only knows what to do with sfStoreOwner records.

            Bobs example code for your other schema is the right way to go about this; managing the sfStoreOwner records directly.

            $modx->addPackage('storefinder', MODX_CORE_PATH . 'components/storefinder/model/');
             
            $store = $modx->getObject('sfStore', 1);
            $owners = $modx->getCollection('sfOwner');
            $storeOwners = array();
            foreach ($owners as $owner) {
              // before adding a new record, check if it's already there
              $so = $modx->getObject('sfStoreOwner', array('owner' => $owner->get('id'), 'store' => $store->get('id')));
              if (!$so) {
                $so = $modx->newObject('sfStoreOwner');
                $so->fromArray(array('owner' => $owner->get('id')));
                // as xPDO can manage the relation for you from this point on, supplying store is optional:
                // $so->fromArray(array('owner' => $owner->get('id'), 'store' => $store->get('id')));
                $storeOwners[] = $so;
              }
            }
             
            $store->addMany($storeOwners);
            $store->save();


            Does this mean that addMany() and addOne() is not working for custom DB tables and I need to code this manually? What is the whole point of defining relationships in the schema when xpdo doesn't take care of it? I am confused...

            addMany and addOne work great for custom db tables; it just doesn't automatically deal with many-to-many intersect tables like sfStoreOwner because the relation is to the intersect table, and not the other actual data table.

            If you had a one-to-many relation like this where each store has a single owner:
            <?xml version="1.0" encoding="UTF-8"?>
            <model package="storefinder" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" phpdoc-package="storefinder" phpdoc-subpackage="model" version="1.1">
              <object class="sfStore" table="sfinder_stores" extends="xPDOSimpleObject">
                <!-- .. insert fields and stuff .. -->
                <field key="owner" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0" />
                <index alias="owner" name="owner" primary="false" unique="false" type="BTREE">
                    <column key="owner" length="" collation="A" null="false" />
                </index>
                <aggregate alias="Owner" class="sfOwner" local="owner" foreign="id" cardinality="one" owner="foreign" />
              </object>
              <object class="sfOwner" table="sfinder_owners" extends="xPDOSimpleObject">
                <field key="name" dbtype="varchar" precision="100" phptype="string" null="false" default="" index="index" />
                <field key="email" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
                <index alias="name" name="name" primary="false" unique="false" type="BTREE">
                    <column key="name" length="" collation="A" null="false" />
                </index>
                <composite alias="Stores" class="sfStore" local="id" foreign="owner" cardinality="many" owner="local" />
              </object>
            </model>


            then your first example code would have worked perfectly.
              Mark Hamstra • Developer spending his days working on Premium Extras and a MODX Site Dashboard with the ability to remotely upgrade MODX and extras to make the MODX world a little better.

              Tweet me @mark_hamstra, check my infrequent blog at markhamstra.com, my slightly more frequent ramblings at MODX.today or see code at Github.