<![CDATA[ Query/Join Multiple Tables with xPDO - MODX Community Forums]]> https://forums.modx.com/thread/?thread=99087 <![CDATA[Query/Join Multiple Tables with xPDO]]> https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=3#dis-post-535825
I have 3 separate tables (yes, the need to be separate tables) that all have different types of data for the same list of products.

I need to be able to create a page for a product and display all of these cells of data using simple placeholders.

My Package is all set up and ready to go and I know how to do this with a single table using getCollection. But I can't seem to figure out how I would join these so that ALL of the attributes will be available to me for use in TPLs.

Has anyone done something like this.]]>
ambaxter Dec 13, 2015, 07:29 PM https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=3#dis-post-535825
<![CDATA[Re: Query/Join Multiple Tables with xPDO (Best Answer)]]> https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=3#dis-post-560678 Quote from: BobRay at Dec 13, 2015, 11:52 PM
If (and it's a big if) you've properly created the tables as related objects of each other, you can use getCollectionGraph() to get them all in a single query. You can also use $query->leftJoin() with getCollection().

Here's getCollectionGraph() example for users that includes the user profile and custom data. In the example, Profile and Data are aliases of the two user-related objects. The result is an array of $userObjects, each with its own $user->Profile and $user->Data object containing the fields for that user from the other tables:

$c = $modx->newQuery('modUser');
$c->sortby('fullname', 'ASC');
$c->where(
   array('Data.last_name' => 'Johnson'),
);
$users = $modx->getCollectionGraph('modUser', '{"Profile":{},"Data":{}}', $c);


In the second argument to getCollectionGraph(), you can have as many related objects as you need in the JSON section.

Remove the $c->where() part of you want all of them.

To process the results you can do something like this (off the top of my head, so possibly not correct):

foreach ($users as $user) {
   $pFields = $user->Profile->toArray();
   $dFields = $user->Data->toArray();
   $ufields = $user->toArray();

   $allFields = array_merge($pFields, $dFields, $uFields);

   $output .= $modx->getChunk('MyTpl', $allFields);
}


Be sure to put the $user fields as the last argument in the array_merge() so that the user ID will be correct.

Thanks for updating this thread. It's helpful smiley]]>
imjamespeter Aug 11, 2018, 06:52 AM https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=3#dis-post-560678
<![CDATA[Re: Query/Join Multiple Tables with xPDO]]> https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-559931 Quote from: Bruno17 at Dec 14, 2015, 12:31 AM
Not sure, if it is good, to have the packageName starting with uppercase.
I think, its better, to have everthing lowercase.
In this case, you can try

[[migxLoopCollection?
&packageName=`Catalog`
&classname=`Data1`
&joins=`[
{"alias":"Data2","classname":"Data2","on":"Data2.model_number=Data1.model_number"},
{"alias":"Data3","classname":"Data3","on":"Data3.model_number=Data1.model_number"}
]`
&tpl=`yourTplChunk`
&where=`{"Data1.model_number":"themodelnumber"}`
]]


you will have all the Data1-fields without prefix and the other fields with prefix Data1_ and Data2_

I appreciated your comment. Thanks for sharing awesome info. :)]]>
imtomalex Jul 25, 2018, 06:29 AM https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-559931
<![CDATA[Re: Query/Join Multiple Tables with xPDO]]> https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-558389 Quote from: ambaxter at Dec 13, 2015, 07:29 PM

I have 3 separate tables (yes, the need to be separate tables) that all have different types of data for the same list of products.
I need to be able to create a page for a product and display all of these cells of data using simple placeholders.
Has anyone done something like this.

All the time.

Here are some examples:

The schema relation is:
Invoice->Items (Many)
Item->Invoice (One)
	public function getInvoiceGraph($key = 0, $useReferralCode = false) {
		$out = false;
		/* Block the database hit for a fake company user */
		if (is_numeric ( $key ) && ($key > 0)) {
			
			/* Use the remote key or the primary key */
			$criteria = ($useReferralCode) ? array (
					'referralCode' => $key 
			) : array (
					'id' => $key 
			);
			
			/*
			 * Retrieve objectGraph of an Invoice, its items, and the sku details
			 */
			$out = $this->modx->getObjectGraph ( 'xxInvoice', '{"Items":{}}', $criteria );
		}
		return $out;
	}


Here is one you can base off of the MODX schema.
As you will find:
The [one] modUserGroup {note actual class name and not alias}
has GroupsMembers {note alias used}
who are User(s)

	
	/**
	 * Retrieves all the users found in the modUserGroup RR
	 *
	 * @return array Collection of modUserGroupMember
	 */
	private function getRRUsers() {
		$criteria = array (
				'name' => 'RR',
				'User.active' => true 
		);
		
		/*
		 * Retrieve objectGraph of a modUserGroup and any active modUserGroupMember attached to the group
		 */
		return $this->modx->getObjectGraph ( 'modUserGroup', '{"UserGroupMembers":{"User":{}}}', $criteria, false );
	}


getCollectionGraph works the same way.
Also, note this WILL NOT work if the tables are not hydrated. You have the MODX connection, so ignore that aspect, the concept is explained here: https://www.shawnwilkerson.com/profession/xpdo/establishing-database-connections.html
Hope this helps.]]>
wshawn May 05, 2018, 04:09 PM https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-558389
<![CDATA[Re: Query/Join Multiple Tables with xPDO]]> https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-549105
I can get the query itself to work but not with getCollection().

Here's is a simplified version of the schema.

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

    <object class="Location" table="location" extends="xPDOSimpleObject">
        <field key="location_name_en" dbtype="varchar" precision="20" phptype="string" null="true" />
        <composite alias="LocationSubject" class="LocationSubject" local="id" foreign="location_id" cardinality="many" owner="local" />
    </object>

    <object class="Subject" table="subject" extends="xPDOSimpleObject">
        <field key="subject_name_en" dbtype="varchar" precision="100" phptype="string" null="true" />
        <composite alias="LocationSubject" class="LocationSubject" local="id" foreign="subject_id" cardinality="many" owner="local" />
    </object>

    <!-- Many to Many. Assigns a subject to a location -->
    <object class="LocationSubject" table="location_subject" extends="xPDOSimpleObject">
        <field key="location_id" dbtype="int" precision="10" phptype="integer" null="false" />
        <field key="subject_id" dbtype="int" precision="10" phptype="integer" null="false" />
        <aggregate alias="Location" class="Location" local="location_id" foreign="id" cardinality="one" owner="foreign" />
        <aggregate alias="Subject" class="Subject" local="subject_id" foreign="id" cardinality="one" owner="foreign" />
    </object>
    
</model>
]]>
muzzstick Mar 02, 2017, 10:16 AM https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-549105
<![CDATA[Re: Query/Join Multiple Tables with xPDO]]> https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-549102
$on = 'Data3.model_number=Data2.model_number';


doesn't this work?

could you post your db-schema?]]>
Bruno17 Mar 02, 2017, 08:40 AM https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-549102
<![CDATA[Re: Query/Join Multiple Tables with xPDO]]> https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-549097
SELECT s.id AS subject_id, s.subject_name_en, ls.is_active, l.location_name_en, tg.group_name
FROM cw_subject s 
JOIN cw_location_subject ls ON (s.id = ls.subject_id)
JOIN cw_location l ON (ls.location_id = l.id)
JOIN cw_term_group tg ON (l.id = tg.locationsubject_id);



It works perfectly. I'm joining 4 tables together.
cw_subject ---- cw_location_subject ---- cw_location
                       |
                       |
                 cw_term_group


The class/alias in the xPDO schema are as follows:
cw_subject          = Subject/Subject
cw_location_subject = LocationSubject/LocationSubject
cw_location         = Location/Location
cw_term_group       = TermGroup/TermGroup


Would anyone know how to represent the same query in xPDO?


Update:

OK. Here's my xPDO code for the first three tables:
$query = $foreignDB->newQuery('Subject');
$query->select('Subject.id,subject_name_en');
$query->innerJoin('LocationSubject','LocationSubject');
$query->select($foreignDB->getSelectColumns('Location','Location','',array('Location.id','location_name_en')));
$query->leftJoin('Location','Location','LocationSubject.location_id = Location.id');

$query->prepare();
print $query->toSQL();


This prints out the SQL code and if I paste it into PHPMyAdmin, it works as expected. After all this hair-pulling it turns out it's the getCollection() function that doesn't like the above.
The rest of my code is:
$subjects = $foreignDB->getCollection('Subject',$query);
foreach($subjects as $subject) {
    $subjectArray = $subject->toArray('',false,true); // must set third arg to true due to toArray() lazy load.
    echo '<pre>';
    print_r($subjectArray);
    echo '</pre>';
}

This only returns columns from the Subject table and ignores the rest.


I found another post here with what looks to be the same problem
https://forums.modx.com/thread/32455/xpdo-retrieving-joined-rows-out-of-many-to-many-tables#dis-post-177416

It's not a lot to go on but it appears what @opengeek is saying is that I need to use PDO instead?

This works but obviously isn't xPDO:
$results = $foreignDB->query("
SELECT s.id AS subject_id, s.subject_name_en, ls.is_active, l.location_name_en 
FROM cw_subject s 
JOIN cw_location_subject ls ON (s.id = ls.subject_id) 
JOIN cw_location l ON (ls.location_id = l.id)");
echo '<pre>';
while ($r = $results->fetch(PDO::FETCH_ASSOC)) {
    print_r($r);
}
echo '</pre>';


]]>
muzzstick Mar 02, 2017, 06:16 AM https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-549097
<![CDATA[Re: Query/Join Multiple Tables with xPDO]]> https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-549096 Quote from: Bruno17 at Dec 14, 2015, 01:20 AM
if want to build it by yourself.
The code would look like that:

$classname = 'Data1';

$c = $modx->newQuery($classname);
$c->select($modx->getSelectColumns($classname, $classname));

$joinclass = 'Data2';
$jalias = 'Data2';
$on = 'Data2.model_number=Data1.model_number';

$c->leftjoin($joinclass, $jalias, $on);
$c->select($modx->getSelectColumns($joinclass, $jalias, $jalias . '_'));

$joinclass = 'Data3';
$jalias = 'Data3';
$on = 'Data3.model_number=Data1.model_number';

$c->leftjoin($joinclass, $jalias, $on);
$c->select($modx->getSelectColumns($joinclass, $jalias, $jalias . '_'));


if ($collection = $modx->getCollection($classname, $c)) {
    foreach ($collection as $object) {
        $row = $object->toArray();
        $output .= $modx->getChunk($tpl, $row);
    }
}



Is it possible to do the same thing as this but join the second table to the third one?
In the above example you're joining Data1 to Data2 and then joining Data1 to Data3.
In xPDO can you join Data1 to Data2 and then join Data2 to Data3?

Im trying to do a join across a many-to-many relationship and failing so far.

Here's a good example of what I'm trying to do: https://lornajane.net/posts/2011/inner-vs-outer-joins-on-a-many-to-many-relationship
Plain SQL is simple but can't quite work it out with xPDO.]]>
muzzstick Mar 02, 2017, 04:27 AM https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-549096
<![CDATA[Re: Query/Join Multiple Tables with xPDO]]> https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-535895 Quote from: BobRay at Dec 14, 2015, 10:54 PM
Yes, if you want to use getObjectGraph(), you'll need some aliases in your schema to relate the objects to each other. You can look at the MODX schema, especially the modUser object, for examples:

core\model\schema\modx.mysql.schema.xml

I see, thanks for the extra information. I'll get a grasp of these concepts soon enough.]]>
ambaxter Dec 14, 2015, 06:14 PM https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-535895
<![CDATA[Re: Query/Join Multiple Tables with xPDO]]> https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-535885
core\model\schema\modx.mysql.schema.xml
]]>
BobRay Dec 14, 2015, 04:54 PM https://forums.modx.com/thread/99087/query-join-multiple-tables-with-xpdo?page=2#dis-post-535885