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.