/** * Saves a foreign relationship where a column on another object references the id for the supplied object. * * Used to save the "many" side of a one-to-many relationship. * * Missing objects will be deleted by default. * * @param DataObjectInterface[] $objects * @param string $className Class name of foreign objects to load * @param string $foreignObjectGetter Name of getter to retrieve foreign objects * @param string $foreignColumn Property on foreign object that relates to this object id * @param boolean $deleteMissing Set to false to leave objects alone if missing */ public function saveMany(array $objects, $className, $foreignObjectGetter = null, $foreignColumn = null, $deleteMissing = true) { if (empty($objects)) { return; } if (!$foreignObjectGetter) { $foreignObjectGetter = 'get' . $this->inflector->methodNameFromClass($className, true); } if (!$foreignColumn) { $foreignColumn = $this->inflector->idColumnFromClass(get_class(reset($objects))); } $objectIdSetter = 'set' . ucfirst($foreignColumn); if ($deleteMissing) { $existingForeignIdsByObjectId = $this->getExistingForeignIds($objects, $className, $foreignColumn); } $foreignObjectsToSave = []; $foreignIdsToDelete = []; foreach ($objects as $object) { if (!method_exists($object, $foreignObjectGetter)) { throw new MethodNotImplementedException("{$foreignObjectGetter} must be defined on {$object->getClassName()} to save relationship"); } $existingForeignIds = []; if ($deleteMissing) { if (isset($existingForeignIdsByObjectId[$object->getId()])) { $existingForeignIds = $existingForeignIdsByObjectId[$object->getId()]; } } /** @var DataObjectInterface[] $foreignObjects */ $foreignObjects = $object->{$foreignObjectGetter}(); if (!empty($foreignObjects)) { if (!is_array($foreignObjects)) { throw new MethodNotImplementedException("{$foreignObjectGetter} on {$object->getClassName()} must return an array to save relationship"); } foreach ($foreignObjects as $foreignObject) { if (!method_exists($foreignObject, $objectIdSetter)) { throw new MethodNotImplementedException("{$objectIdSetter} must be defined on {$foreignObject->getClassName()} to save relationship"); } $foreignObject->{$objectIdSetter}($object->getId()); $foreignObjectsToSave[] = $foreignObject; if ($deleteMissing && $foreignObject->getId()) { unset($existingForeignIds[$foreignObject->getId()]); } } } foreach ($existingForeignIds as $id => $true) { $foreignIdsToDelete[] = $id; } } $this->objectMapper->unitOfWork()->executeTransaction(function () use($foreignObjectsToSave, $deleteMissing, $className, $foreignIdsToDelete) { $this->objectMapper->saveAll($foreignObjectsToSave); if ($deleteMissing) { $foreignObjectsToDelete = $this->objectMapper->findByIds($className, $foreignIdsToDelete); $this->objectMapper->deleteAll($foreignObjectsToDelete); } }); }
/** * Loads objects of the foreign class onto the supplied objects linked by a link table containing the id's of both objects * * @param DataObjectInterface[] $objects * @param string $className Class name of foreign objects to load * @param string $linkTable Table that links two objects together * @param string $idColumn Column on link table = the id on this object * @param string $foreignIdColumn Column on link table = the id on the foreign object table * @return DataObjectInterface[] Loaded objects keyed by id */ public function loadManyToMany(array $objects, $className, $linkTable, $idColumn = null, $foreignIdColumn = null) { if (empty($objects)) { return []; } $ids = DataObject::getIds($objects); $queryHelper = $this->objectMapper->getQueryHelper(); $db = $queryHelper->getConnection(); $qb = $queryHelper->buildSelectQuery($linkTable, [$db->quoteIdentifier($idColumn) . ' AS id', $db->quoteIdentifier($foreignIdColumn) . ' AS ' . $db->quoteIdentifier('foreignId')], [$idColumn => $ids]); $foreignIdsById = []; $foreignIds = []; $linkRows = $qb->execute(); $linkRows->setFetchMode(\PDO::FETCH_OBJ); foreach ($linkRows as $linkRow) { $foreignIdsById[$linkRow->id][] = $linkRow->foreignId; $foreignIds[$linkRow->foreignId] = true; } $foreignObjects = $this->objectMapper->findByIds($className, array_keys($foreignIds)); unset($foreignIds); $foreignObjectsById = []; foreach ($foreignObjects as $foreignObject) { $foreignObjectsById[$foreignObject->getId()] = $foreignObject; } unset($foreignObjects); $setter = 'set' . $this->inflector->methodNameFromColumn($foreignIdColumn, true); foreach ($objects as $object) { if (method_exists($object, $setter)) { $foreignObjects = []; if (isset($foreignIdsById[$object->getId()])) { $foreignIds = $foreignIdsById[$object->getId()]; foreach ($foreignIds as $foreignId) { $foreignObjects[] = $foreignObjectsById[$foreignId]; } } $object->{$setter}($foreignObjects); } else { throw new MethodNotImplementedException("{$setter} must be defined on {$object->getClassName()} to load many-to-many relationship with {$className}"); } } return $foreignObjectsById; }