We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 14951
    • 151 Posts
    Bob,

    Thanks for yet another great resource! With this, I’m thinking my issues must have something to do with my schema not properly establishing relationships.

    $query->toSQL(); returns:
    SELECT Product.*, PD.name FROM `opcart_product` AS `Product` JOIN `opcart_product_description` `PD` ON WHERE `PD`.`product_id` == 0 ORDER BY date_added DESC LIMIT 6

    There is no output after "ON". I’m not entirely sure how that is determined, but my next best guess is via relationships, because everything after "ON" is pretty messy.

    Any further thoughts? I’m off to the documentation yet again smiley

    Thanks!
    -Jared
      • 28215
      • 4,149 Posts
      Quote from: jtech at Jan 19, 2011, 08:40 AM

      $query->toSQL(); returns:
      SELECT Product.*, PD.name FROM `opcart_product` AS `Product` JOIN `opcart_product_description` `PD` ON WHERE `PD`.`product_id` == 0 ORDER BY date_added DESC LIMIT 6

      There is no output after "ON". I’m not entirely sure how that is determined, but my next best guess is via relationships, because everything after "ON" is pretty messy.

      You need to define an aggregate or composite relationship in your XML schema (and then regenerate the maps) to tell xPDO how to join the tables.
        shaun mccormick | bigcommerce mgr of software engineering, former modx co-architect | github | splittingred.com
        • 3749
        • 24,544 Posts
        Duh. I should have thought of that. I’ll try to update the article to talk about the limitations of generating the schema from the tables.
          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
          • 14951
          • 151 Posts
          Thank you SplittingRed!

          I’m glad I was at least looking in the correct area! I’m not entirely sure how to "regenerate the maps" as you suggested, nor am I confident that my relationships are correct. Does this look good:
          	<object class="Product" table="product" extends="xPDOObject">
          		<field key="product_id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
          		<field key="model" dbtype="varchar" precision="64" phptype="string" null="false" />
          		<field key="sku" dbtype="varchar" precision="64" phptype="string" null="false" />
          		<field key="location" dbtype="varchar" precision="128" phptype="string" null="false" />
          		<field key="quantity" dbtype="int" precision="4" phptype="integer" null="false" default="0" />
          		<field key="stock_status_id" dbtype="int" precision="11" phptype="integer" null="false" />
          		<field key="image" dbtype="varchar" precision="255" phptype="string" null="true" />
          		<field key="manufacturer_id" dbtype="int" precision="11" phptype="integer" null="false" />
          		<field key="shipping" dbtype="int" precision="1" phptype="integer" null="false" default="1" />
          		<field key="price" dbtype="decimal" precision="15,4" phptype="float" null="false" default="0.0000" />
          		<field key="tax_class_id" dbtype="int" precision="11" phptype="integer" null="false" />
          		<field key="date_available" dbtype="date" phptype="date" null="false" />
          		<field key="weight" dbtype="decimal" precision="5,2" phptype="float" null="false" default="0.00" />
          		<field key="weight_class_id" dbtype="int" precision="11" phptype="integer" null="false" default="0" />
          		<field key="length" dbtype="decimal" precision="5,2" phptype="float" null="false" default="0.00" />
          		<field key="width" dbtype="decimal" precision="5,2" phptype="float" null="false" default="0.00" />
          		<field key="height" dbtype="decimal" precision="5,2" phptype="float" null="false" default="0.00" />
          		<field key="length_class_id" dbtype="int" precision="11" phptype="integer" null="false" default="0" />
          		<field key="status" dbtype="int" precision="1" phptype="integer" null="false" default="0" />
          		<field key="date_added" dbtype="datetime" phptype="datetime" null="false" default="0000-00-00 00:00:00" />
          		<field key="date_modified" dbtype="datetime" phptype="datetime" null="false" default="0000-00-00 00:00:00" />
          		<field key="viewed" dbtype="int" precision="5" phptype="integer" null="false" default="0" />
          		<field key="sort_order" dbtype="int" precision="11" phptype="integer" null="false" default="0" />
          		<field key="subtract" dbtype="int" precision="1" phptype="integer" null="false" default="1" />
          		<field key="minimum" dbtype="int" precision="11" phptype="integer" null="false" default="1" />
          		<field key="cost" dbtype="decimal" precision="15,4" phptype="float" null="false" default="0.0000" />
          
          		<index alias="PRIMARY" name="PRIMARY" primary="true" unique="true" type="BTREE" >
          			<column key="product_id" length="" collation="A" null="false" />
          		</index>
          		
          		<composite alias="ProductDescription" class="ProductDescription" local="product_id" foreign="product_id" cardinality="many" owner="local" />
          	</object>
          	<object class="ProductDescription" table="product_description" extends="xPDOObject">
          		<field key="product_id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
          		<field key="language_id" dbtype="int" precision="11" phptype="integer" null="false" index="pk" />
          		<field key="name" dbtype="varchar" precision="255" phptype="string" null="false" index="index" />
          		<field key="meta_keywords" dbtype="varchar" precision="255" phptype="string" null="false" />
          		<field key="meta_description" dbtype="varchar" precision="255" phptype="string" null="false" />
          		<field key="description" dbtype="text" phptype="string" null="false" />
          
          		<index alias="PRIMARY" name="PRIMARY" primary="true" unique="true" type="BTREE" >
          			<column key="product_id" length="" collation="A" null="false" />
          			<column key="language_id" length="" collation="A" null="false" />
          		</index>
          		<index alias="name" name="name" primary="false" unique="false" type="BTREE" >
          			<column key="name" length="" collation="A" null="false" />
          		</index>
          		
          		<aggregate alias="Product" class="Product" local="product_id" foreign="product_id" cardinality="one" owner="foreign" />
          	</object>

          Assuming I’m understanding the relationships okay, how do I regenerate the maps? Originally I used the script from Bob’s tutorial, but I have a feeling that this will just overwrite the changes I’ve made. Sorry for the trouble...I’m pretty sure I’m just on the edge of fully grasping the basics of this.

          Thank you!
          -Jared
            • 18913
            • 654 Posts
            For what it’s worth, I finally figured out how to access my external database using xPDO. It involved generating the schema, classes, etc. so here goes.

            Digging through the forum postings, I found a thread on reverse engineering a database. Combining that code with the snippet on Bob’s Guides, I came up with this snippet (naming it CreateXpdoSchema) :

            <?php
            $mtime= microtime();
            $mtime= explode(" ", $mtime);
            $mtime= $mtime[1] + $mtime[0];
            $tstart= $mtime;
            
            //Customize this line based on the location of your script
            include_once (strtr(realpath(dirname(__FILE__)) . '/../../xpdo/xpdo.class.php', '\\', '/'));
            
            //note : this database does not have table prefixes
            $xpdo= new xPDO('mysql:host=localhost;dbname=THEDATABASENAME','THEDATABASEUSER','THEDATABASEPWD','');
            
            $myPackage = 'exttest';  //A name I picked for testing
            
            // Set the package name and root path of that package
            $xpdo->setPackage($myPackage, XPDO_CORE_PATH . '../components/');
            
            $xpdo->setDebug(true);
            
            $manager= $xpdo->getManager();
            $generator= $manager->getGenerator();
            
            $sources = array(
                'config' => MODX_CORE_PATH . 'config/config.inc.php',
                'package' => MODX_CORE_PATH . 'components/' . $myPackage . '/',
                'model' => MODX_CORE_PATH. 'components/' . $myPackage . '/model/',
                'schema' => MODX_CORE_PATH . 'components/' . $myPackage . '/schema/',
                );
            
            if (! file_exists($sources['package'])) {
                mkdir($sources['package'],0777);
            }
            
            if (! file_exists($sources['model'])) {
                mkdir($sources['model'],0777);
            }
            if (! file_exists($sources['schema'])) {
                mkdir($sources['schema'],0777);
            }
            
            //Use this to create a schema from an existing database
            //Note again - no database table prefix
            $xml= $generator->writeSchema($sources['schema'] . $myPackage . '.mysql.schema.xml', $myPackage, 'xPDOObject', '');
            
            //Use this to generate classes and maps from your schema
            // NOTE: by default, only maps are overwritten; delete class files if you want to regenerate classes
            $generator->parseSchema($sources['schema'] . $myPackage . '.mysql.schema.xml', $sources['model']);
            
            $mtime= microtime();
            $mtime= explode(" ", $mtime);
            $mtime= $mtime[1] + $mtime[0];
            $tend= $mtime;
            $totalTime= ($tend - $tstart);
            $totalTime= sprintf("%2.4f s", $totalTime);
            
            echo "\nExecution time: {$totalTime}\n";
            
            exit ();
            


            So now I have my classes, maps, schema in a folder (XAMPP on Vista) consistent with that described in Bob’s tutorial

            My snippet to test access to the database and performing a query :
            <?php
            $dsn = 'mysql:host=localhost;dbname=THEDATABASENAME;';
                    $xpdo = new xPDO($dsn,'THEDATABASEUSER','THEDATABASEPWD');
                    //$xpdo->setDebug(true);
                    $myPackage = 'exttest';
            	$path = MODX_CORE_PATH . 'components/' . $myPackage . '/';
            
                    //note : database does not have table prefixes
            	$result = $xpdo->addPackage($myPackage,$path. 'model/','');
            	if (! $result) {
            	  return 'failed to add package';
            	}
            	
                    //$query = $xpdo->newQuery('Tracks');  //Tracks is associated with one of the tables.  See schema file
                    //$query->where(array('cd:=' => 3));  //want songs on cd 3
                    $query = $xpdo->query('SELECT *');  //select all
            
            	$songs = $xpdo->getCollection('Tracks',$query); //Tracks is associated with one of the tables.  See schema file
            
            	$output = '<p>Total: '. count($songs) . '</p>';
            
            	foreach($songs as $song) {
            
            	    $output .= '<p>Title: ' . $song->get('title');
            	    $output .= '<p>Price: ' . $song->get('price');
            	    $output .= '<br />CD: ' . $song->get('cd') . '<br /></p>';
            	}
            
            	return $output;
            


            This returns the correct results. I’d be curious if there’s any "clean up" I should do given that it’s an external database. If nothing else though, you can see where your mappings are.

            Matt
              • 3749
              • 24,544 Posts
              It’s been a while, but I think the script in the tutorial will let you skip generating the schema if you put &createSchema=`0` in the snippet tag. Then it will just do the maps and class files. You might want to back up your schema file first.

              A added a section at the end of the tutorial explaining aggregate and composite relationships. I also fixed some bugs with the embedded PhpDoc tags in the code.

              The code is correct, but I’m not sure about the explanations (Shaun?), but it you may find it helpful.

              http://www.bobsguides.com/custom-db-tables.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
                • 18913
                • 654 Posts
                @Bob Perhaps I did something wrong in trying to use that script. But when I ran it, I got a blizzard of files created that were all related to the MODX database, in addition to a schema for the external database in question. So that’s how I ended up with the approach I took. Almost certainly, I overlooked something as xPDO is - obviously - something I’m very new to.

                Matt
                  • 14951
                  • 151 Posts
                  Hey Bob,

                  Thanks for your continued insight! I never cease to be amazed by the brilliance that surrounds me in this community. However, what by far surpasses the brilliance is everyones willingness and dedication to help and teach those who are still learning!

                  That being said, I unfortunately didn’t have much luck regenerating the maps with &createSchema set to ’0’. It simply overwrote my file anyhow. I tried again with both &createSchema, and &createClasses set to ’0’, but it again simply overwrote the relationships I manually added.

                  Is there an alternative way to regenerate the maps? I tried the following code:
                  <?php
                  $xpdo= new xPDO('mysql:host=localhost;dbname=myDBName','myDBUser','myDBPass','myDBPrefix_');
                  $manager = $xpdo->getManager();  
                  $generator = $manager->getGenerator();  
                  $generator->parseSchema('ocLatestProducts.mysql.schema.xml','/core/components/ocLatestProducts/model/');

                  I ran this snippet in a newly created resource with no template. I’m not sure if it did anything or not, but it didn’t seem to fix my problem in any case.

                  I just pulled this code from the docs, so I don’t even know if I’m applying it correctly. Any thoughts?

                  Thanks Again!
                  -Jared
                    • 18913
                    • 654 Posts
                    Stupid question, perhaps, but did you try running the "writeSchema" code first? Also, I had to make create the directories for the files, as seen in the first snippet I posted.
                    Matt
                      • 14951
                      • 151 Posts
                      Hey Matt,

                      My schema was already created through Bob’s snippet, so I guess "yes" I did run writeSchema() first. It seems as though if I yet again ran writeSchema() prior to parseSchema(), it would just overwrite the custom relationships I’ve added and then parse it?

                      I don’t want to recreate the schema, I just want to parse the relationship additions I added.

                      Thanks for the idea!
                      -Jared