We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 34004
    • 125 Posts
    I am trying to get checkpoint times for a particular runner in a marathon. I want to use getMany to get all related checkpoint times to the runner. I've followed the example as closely as I can here: http://rtfm.modx.com/display/xPDO20/More+Examples+of+xPDO+XML+Schema+Files#MoreExamplesofxPDOXMLSchemaFiles-OnetoMany but I'm still having issues.

    Can anyone see where I am going wrong?

    Error log:

    [2012-02-12 18:53:37] (ERROR @ /index.php) No foreign key definition for parentClass: RunnerEntrant using relation alias: TimeLive
    


    My schema file:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <model package="athletes" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.1">
    
    <object class="RunnerEntrant" table="runnerEntrant" extends="xPDOObject">
    		<field key="AutoId" dbtype="int" precision="10" phptype="integer" null="false" index="pk"  generated="native" />
    		<field key="RegId" dbtype="int" precision="10" phptype="integer" null="true" />
    		<field key="EventId" dbtype="int" precision="10" phptype="integer" null="true" />
    		<field key="EventTitle" dbtype="varchar" precision="100" phptype="string" null="true" />
    		<field key="ClientEventId" dbtype="int" precision="10" phptype="integer" null="true" />
    		<field key="GroupId" dbtype="int" precision="10" phptype="integer" null="true" />
    		<field key="StatusDescription" dbtype="varchar" precision="30" phptype="string" null="true" />
    		<field key="RegTypeDescription" dbtype="varchar" precision="60" phptype="string" null="true" />
    		<field key="first_Name" dbtype="varchar" precision="30" phptype="string" null="true" />
    		<field key="last_Name" dbtype="varchar" precision="30" phptype="string" null="true" />
    		<field key="FirstLastName" dbtype="varchar" precision="60" phptype="string" null="true" />
    		<field key="City" dbtype="varchar" precision="30" phptype="string" null="true" />
    		<field key="Country" dbtype="varchar" precision="30" phptype="string" null="true" />
    		<field key="HomePhone" dbtype="varchar" precision="20" phptype="string" null="true" />
    		<field key="CellPhone" dbtype="varchar" precision="20" phptype="string" null="true" />
    		<field key="Email_Address" dbtype="varchar" precision="100" phptype="string" null="true" />
    		<field key="DateOfBirth" dbtype="varchar" precision="9" phptype="string" null="true" />
    		<field key="Gender" dbtype="varchar" precision="1" phptype="string" null="true" />
    		<field key="Nationality" dbtype="varchar" precision="30" phptype="string" null="true" />
    		<field key="BibNumber" dbtype="int" precision="4" phptype="integer" null="true" />
    		<field key="EmergencyContactName" dbtype="varchar" precision="40" phptype="string" null="true" />
    		<field key="EmergencyContactPhone" dbtype="varchar" precision="20" phptype="string" null="true" />
    		<field key="Shuttle" dbtype="varchar" precision="8" phptype="string" null="true" />
    		<field key="Shuttle50" dbtype="varchar" precision="8" phptype="string" null="true" />
    		<field key="Shuttle100" dbtype="varchar" precision="8" phptype="string" null="true" />
    		<field key="alphaCountryCode" dbtype="varchar" precision="2" phptype="string" null="true" />
    		<field key="timeAdded" dbtype="timestamp" phptype="timestamp" null="false" default="CURRENT_TIMESTAMP" />
    
    		<index alias="PRIMARY" name="PRIMARY" primary="true" unique="true" type="BTREE" >
    			<column key="AutoId" length="" collation="A" null="false" />
    		</index>
    		
    		<composite alias="TimeLive" class="TimeLive" local="RegId" foreign="runnerId" cardinality="many" owner="local" />
    		
    	</object>
    
    	<object class="TimeLive" table="timeLive" extends="xPDOObject">
    		<field key="autoId" dbtype="int" precision="10" phptype="integer" null="false" index="pk"  generated="native" />
    		<field key="runnerId" dbtype="int" precision="20" phptype="integer" null="true" />
    		<field key="stationId" dbtype="int" precision="6" phptype="integer" null="true" />
    		<field key="raceId" dbtype="int" precision="4" phptype="integer" null="true" />
    		<field key="timeUnix" dbtype="timestamp" phptype="timestamp" null="true" />
    		<field key="createdon" dbtype="timestamp" phptype="timestamp" null="false" default="CURRENT_TIMESTAMP" />
    
    		<index alias="PRIMARY" name="PRIMARY" primary="true" unique="true" type="BTREE" >
    			<column key="autoId" length="" collation="A" null="false" />
    		</index>
    		<aggregate alias="RunnerEntrant" class="RunnerEntrant" local="runnerId" foreign="RegId" cardinality="one" owner="foreign" />
    		
    		
    	</object>
    </model>
    
    


    My php xpdo call:

    foreach ($athletes as $athlete){
      echo ($athlete->get('FirstLastName'). '<br />');
      
      // get all related times
      $runnerTimes = $athlete->getMany('TimeLive');
      print_r($runnerTimes);
      
      // Loop over times and do stuff
      if (!empty ($runnerTimes)) {
      	foreach ($runnerTimes as $runnerTime){
        		echo ('Unixtime: '. $runnerTime->get('timeUnix') . '<br/>'); 
      	}
      }
      echo ('<hr />');
      
    }
    
    


    Any ideas?

      • 1778
      • 659 Posts
      Hi
      not sure of that but:
      in your 'RunnerEntrant object ' your primary key is 'AutoId' and in the relation aggregate in your 'TimeLive' object you refer the foreign key as 'RegId' which is not the primary key of your RunnerEntrant object

      Hope this helps
      cheers
        • 34004
        • 125 Posts
        Thanks for your reply. That's correct - do they have to both be primary keys? I'll give it a go.

        Cheers
        [ed. note: ficklelife last edited this post 14 years, 8 months ago.]
          • 33968
          • 863 Posts
          They don't have to be primary keys - 'foreign' is the field in the parent/child table that matches up with the 'local' field in the current table. What you've done is fine.

          At first glance I don't see anything wrong with your schema or code. One thing that's caught me out before though is making an amendment to the schema but forgetting to regenerate the class files. Perhaps check that out and run your build script again?

          Note: it would definitely help to add an index on 'regId' and 'RunnerId' which will speed up your queries [ed. note: okyanet last edited this post 14 years, 8 months ago.]
            • 34004
            • 125 Posts
            I'm using the createXpdoClasses build script on Bobs site here: http://bobsguides.com/custom-db-tables.html - I have just read further down and it mentions that I will have to run parseSchema after I have modified the schema to generate the correct class files. Thanks to you pointing it out Lucas, that's something I'm not doing!

            He says "add &createSchema=`0` when you re-run the snippet so it won't overwrite your manually edited schema file." The call is now:

            [[!CreateXpdoClasses? &myPackage=`athletes` &myPrefix=`cr_` &createSchema=`0`]]


            When I do that, the xml schema is overwritten with this, basically making it blank:

            <?xml version="1.0" encoding="UTF-8"?>
            <model package="athletes" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.1">
            </model>


            Any clues where I am going wrong?

              • 3749
              • 24,544 Posts
              It shouldn't be doing that. I would do this:

              Change this line in the snippet:

              $createSchema = $modx->getOption('createSchema',$scriptProperties,true);


              to this:

              $createSchema = false;


              Just to be on the safe side, back up your schema file after modifying it.


              ---------------------------------------------------------------------------------------------------------------
              PLEASE, PLEASE specify the version of MODX you are using . . . PLEASE!
              MODx info for everyone: http://bobsguides.com/modx.html
                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
                • 34004
                • 125 Posts
                That seems to have worked Bob, I can see in the map files the aggregate and composite arrays exist for each map. However I now have a memory error

                Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 11482154 bytes) in /home/centurio/public_html/core/model/modx/modparser.class.php on line 120


                So I'll increase the memory and see how I go. Thanks very much for you help Bob and Lucas.
                  • 34004
                  • 125 Posts
                  I've increased my memory limit and now I'm getting

                  Fatal error: Maximum execution time of 60 seconds exceeded in /home/centurio/public_html/core/xpdo/om/xpdoobject.class.php on line 628


                  my foreach is

                  foreach ($athletes as $athlete){
                    echo ($athlete->get('FirstLastName'). '<br />');
                    
                    // get all related times
                    $runnerTimes = $athlete->getMany('TimeLive');
                    
                  }
                  


                  I am only fetching one record from the DB so $athletes only contains one object. As soon as I remove this line

                   $runnerTimes = $athlete->getMany('TimeLive');


                  The time out error disappears and the page renders - without the data that the getMany was supposed to retrieve of course. Any ideas why it would be taking so long to execute getMany?

                  I've added indexes to RedId and runnerId, I've checked the schema and all is as above, I've had a look in the TimeLive map file and it says:

                  <?php
                  $xpdo_meta_map['TimeLive']= array (
                    'package' => 'athletes',
                    'version' => '1.1',
                    'table' => 'timeLive',
                    'fields' => 
                    array (
                      'autoId' => NULL,
                      'runnerId' => NULL,
                      'stationId' => NULL,
                      'raceId' => NULL,
                      'timeUnix' => NULL,
                      'createdon' => 'CURRENT_TIMESTAMP',
                    ),
                    'fieldMeta' => 
                    array (
                      'autoId' => 
                      array (
                        'dbtype' => 'int',
                        'precision' => '10',
                        'phptype' => 'integer',
                        'null' => false,
                        'index' => 'pk',
                        'generated' => 'native',
                      ),
                      'runnerId' => 
                      array (
                        'dbtype' => 'int',
                        'precision' => '20',
                        'phptype' => 'integer',
                        'null' => true,
                      ),
                      'stationId' => 
                      array (
                        'dbtype' => 'int',
                        'precision' => '6',
                        'phptype' => 'integer',
                        'null' => true,
                      ),
                      'raceId' => 
                      array (
                        'dbtype' => 'int',
                        'precision' => '4',
                        'phptype' => 'integer',
                        'null' => true,
                      ),
                      'timeUnix' => 
                      array (
                        'dbtype' => 'timestamp',
                        'phptype' => 'timestamp',
                        'null' => true,
                      ),
                      'createdon' => 
                      array (
                        'dbtype' => 'timestamp',
                        'phptype' => 'timestamp',
                        'null' => false,
                        'default' => 'CURRENT_TIMESTAMP',
                      ),
                    ),
                    'indexes' => 
                    array (
                      'PRIMARY' => 
                      array (
                        'alias' => 'PRIMARY',
                        'primary' => true,
                        'unique' => true,
                        'type' => 'BTREE',
                        'columns' => 
                        array (
                          'autoId' => 
                          array (
                            'length' => '',
                            'collation' => 'A',
                            'null' => false,
                          ),
                        ),
                      ),
                    ),
                    'aggregates' => 
                    array (
                      'RunnerEntrant' => 
                      array (
                        'class' => 'RunnerEntrant',
                        'local' => 'runnerId',
                        'foreign' => 'RegId',
                        'cardinality' => 'one',
                        'owner' => 'foreign',
                      ),
                    ),
                  );
                  
                  


                  so not sure what I am missing?
                    • 3749
                    • 24,544 Posts
                    If you are getting the athletes with getObject(), each athlete requires a separate query of the DB. Switch to getCollection() to get them all in one query and then display them. If you are getting the athletes with getCollection() already, try getting them with getIterator(). Use the same arguments. It will use less memory and might solve the timeout problem.

                    You can also try adding this at the top of your code:

                    set_time_limit(0);


                    Whether it works or not will depend on the server configuration. You might also ask the host to increase the PHP time limit.


                    ---------------------------------------------------------------------------------------------------------------
                    PLEASE, PLEASE specify the version of MODX you are using . . . PLEASE!
                    MODx info for everyone: http://bobsguides.com/modx.html
                      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
                      • 34004
                      • 125 Posts
                      Thanks Bob. below is my entire snippet

                      
                      <?php
                      set_time_limit(0);
                      $path = MODX_CORE_PATH . 'components/athletes/';
                      $result = $modx->addPackage('athletes', $path . 'model/','cr_');
                      
                      if (! $result) {
                        echo 'failed to add package';
                      } else {
                        echo '<p>Connected to live DB!</p>';
                      }
                      
                      
                      //Get all the Entrants for TP100 only
                      $query = $modx->newQuery('RunnerEntrant');
                      $query->select(array(
                          'AutoId',
                          'RegId',
                          'FirstLastName',
                          'BibNumber'
                      ));
                      
                      // just get one runner for now
                      $query->where(array(
                         'RegTypeDescription' => $race,
                         'StatusDescription' => 'Confirmed',
                         'RegId' => '31519946'
                      ));
                      $query->sortby('BibNumber','ASC');
                      $athletes = $modx->getCollection('RunnerEntrant', $query);
                      
                      
                      //Get all the Aid stations for TP100 only
                      $query2 = $modx->newQuery('Station');
                      $query2->where(array(
                         'stationRaceId' => $raceId
                      ));
                      $aidStations = $modx->getCollection('Station', $query2);
                      
                      
                      // Get all the times for TP100 only
                      $query3 = $modx->newQuery('TimeLive');
                      $query3->where(array(
                         'raceId' => $raceId
                      ));
                      $times = $modx->getCollection('TimeLive', $query3);
                      
                      
                      
                      //count how many entrants
                      $athletesCount = count($athletes);
                      echo ("<h2>no of athletes competing</h2>" . $athletesCount);
                      
                      //count how many times have been recorded
                      $timesCount = count($times);
                      echo ("<h2>no of times recorded in the db</h2>" . $timesCount);
                      
                      //count how many aidstaions there are
                      $aidStationsCount = count($aidStations);
                      echo ("<h2>no of aidstations</h2>" . $aidStationsCount);
                      
                      
                      echo("<h1>runnerEntrants </h1>");
                      foreach ($athletes as $athlete){
                        echo ($athlete->get('FirstLastName'). '<br />');
                        
                        // get all related times
                        $runnerTimes = $athlete->getMany('TimeLive');
                        print_r($runnerTimes);
                        
                        // Loop over times and do stuff
                        /*
                        if (!empty ($runnerTimes)) {
                        	foreach ($runnerTimes as $runnerTime){
                          		echo ('Unixtime: '. $runnerTime->get('timeUnix') . '<br/>'); 
                        	}
                        }
                        echo ('<hr />');
                      */
                      }
                      
                      echo("<h1>StationName</h1>");
                      foreach ($aidStations as $aidStation){
                        echo ("<br />");
                        echo ($aidStation->get('stationName'));
                      }
                      
                      

                      The line 'RegId' => '31519946' ensures I only get one runner. When I change to getIterator I still get the same timeout.

                      I added set_time_limit(0); and a page rendered! The problem is however it took a very long time and I can see why as it it rendered EVERYTHING, pages and pages of unrelated stuff, arrays etc - hard to explain so have a look here and wait for a bit for it to load: http://www.centurionrunning.com/live/standard-homepage/

                      I modified the same snippet, commenting the getMany call:

                      
                      
                      
                      echo("<h1>runnerEntrants </h1>");
                      foreach ($athletes as $athlete){
                        echo ($athlete->get('FirstLastName'). '<br />');
                        
                        // get all related times
                        // $runnerTimes = $athlete->getMany('TimeLive');
                        // print_r($runnerTimes);
                        
                        // Loop over times and do stuff
                        /*
                        if (!empty ($runnerTimes)) {
                        	foreach ($runnerTimes as $runnerTime){
                          		echo ('Unixtime: '. $runnerTime->get('timeUnix') . '<br/>'); 
                        	}
                        }
                        echo ('<hr />');
                      */
                      }
                      
                      
                      


                      And this is the resulting page that renders instantly: http://www.centurionrunning.com/live/standard-homepage-without-getmany/

                      I'm not sure what this indicates... but something is going very wrong somewhere. Could it be my class/map files?