/** * Save referenced model * @param AnnotatedInterface $referenced * @param DbRef $dbRef */ public static function save(AnnotatedInterface $referenced, DbRef $dbRef) { // Ensure ref is same as referenced model PkManager::applyToModel($referenced, $dbRef->pk); $em = new EntityManager($referenced); $em->save(); }
public function write($model, $name, &$dbValues, $transformatorClass = TransformatorInterface::class) { if (!empty($model->{$name})) { // Store empty field to trigger decorator read $dbValues[$name] = null; $fieldMeta = ManganMeta::create($model)->field($name); $relMeta = $fieldMeta->related; if ($relMeta->single) { $models = [$model->{$name}]; } else { $models = $model->{$name}; } $order = 0; foreach ($models as $relModel) { $fields = []; foreach ($relMeta->join as $source => $rel) { $fields[] = $rel; assert(isset($model->{$source})); $relModel->{$rel} = $model->{$source}; } if (!empty($relMeta->orderField)) { $fields[] = $relMeta->orderField; $fields = array_unique($fields); $relModel->order = $order; $order++; } $em = new EntityManager($relModel); if ($relMeta->updatable) { // Update whole model $em->upsert(); } else { // Update only relation info $criteria = PkManager::prepareFromModel($relModel); $em->updateOne($criteria, $fields); } } } }
public function testIfWillResolveNotFoundClass() { define('BogusClass', 'SomeClass'); $model = new WithPlainEmbedded(); $model->_id = new MongoId(); $model->stats = new SimplePlainEmbedded(); $em = new EntityManager($model); $em->save(); $pkCriteria = PkManager::prepareFromModel($model)->getConditions(); $set = ['$set' => ['stats._class' => BogusClass]]; $em->getCollection()->update($pkCriteria, $set); $finder = new Finder($model); try { $finder->findByPk($model->_id); $this->assertFalse(true); } catch (ManganException $ex) { $this->assertTrue(true); } // Attach class not found handlers new NotFoundResolver($model, [BogusClass => SimplePlainEmbedded::class]); $found = $finder->findByPk($model->_id); $this->assertInstanceOf(WithPlainEmbedded::class, $found); $this->assertInstanceOf(SimplePlainEmbedded::class, $found->stats); }
/** * Restore trashed item * @return boolean * @throws Exception * @Ignored */ public function restore() { if (!$this instanceof TrashInterface) { // When trying to restore normal document instead of trash item throw new Exception(sprintf('Restore can be performed only on `%s` instance', TrashInterface::class)); } $em = new EntityManager($this->data); // Set scenario to `restore` for model, which is just about to be restored ScenarioManager::setScenario($this->data, TrashInterface::ScenarioRestore); if (!Event::valid($this->data, TrashInterface::EventBeforeRestore)) { return false; } $saved = $em->save(); if (!$saved) { return false; } $finder = new Finder($this->data); $model = $finder->find(PkManager::prepareFromModel($this->data)); if (!$model) { return false; } $eventAfter = new RestoreEvent(); $eventAfter->setTrashed($this->data); $eventAfter->setTrash($this); if (!Event::valid($model, TrashInterface::EventAfterRestore, $eventAfter)) { return false; } $trashEm = new EntityManager($this); $this->data = null; // Use deleteOne, to avoid beforeDelete event, // which should be raised only when really removing document: // when emtying trash return $trashEm->deleteOne(PkManager::prepareFromModel($this)); }
/** * Validates the attribute of the object. * If there is any error, the error message is added to the object. * @param AnnotatedInterface $model the object being validated * @param string $attribute the attribute being validated */ public function isValid(AnnotatedInterface $model, $attribute) { $value = $model->{$attribute}; if ($this->allowEmpty && empty($value)) { return true; } $className = empty($this->className) ? get_class($model) : $this->className; $compareModel = new $className(); $criteria = (new Criteria())->decorateWith($compareModel); $criteria->addCond($attribute, '==', $value); if ($this->criteria !== []) { $criteria->mergeWith($this->criteria); } ScenarioManager::setScenario($compareModel, ValidatorInterface::ScenarioValidate); $finder = new Finder($compareModel); $found = $finder->find($criteria); // Not found entirely if (null === $found) { return true; } // Same pk if (PkManager::compare($found, $model)) { return true; } $label = ManganMeta::create($model)->field($attribute)->label; $this->addError('msgTaken', ['{attribute}' => $label, '{value}' => $value]); return false; }
/** * Deletes documents with the specified primary keys. * See {@link find()} for detailed explanation about $condition and $params. * @param mixed[] $pkValues Primary keys array * @param array|CriteriaInterface $criteria query criteria. * @since v1.0 */ public function deleteAllByPk($pkValues, $criteria = null) { if ($this->_beforeDelete()) { $criteria = $this->sm->apply($criteria); $criteria->mergeWith(PkManager::prepareAll($this->model, $pkValues, $criteria)); $result = $this->getCollection()->remove($criteria->getConditions(), $this->options->getSaveOptions(['justOne' => false])); return $this->_result($result); } return false; }
/** * Finds all documents with the specified primary keys. * In MongoDB world every document has '_id' unique field, so with this method that * field is in use as PK by default. * See {@link find()} for detailed explanation about $condition. * * @param mixed $pkValues primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). * @param array|CriteriaInterface $criteria query criteria. * @return AnnotatedInterface[]|Cursor - Array or cursor of Documents * @since v1.0 */ public function findAllByPk($pkValues, $criteria = null) { $pkCriteria = new Criteria($criteria); $pkCriteria->decorateWith($this->model); PkManager::prepareAll($this->model, $pkValues, $pkCriteria); return $this->findAll($pkCriteria); }
public function read($model, $name, &$dbValues, $transformatorClass = TransformatorInterface::class) { if (!$dbValues) { $fieldMeta = ManganMeta::create($model)->field($name); $model->{$name} = $fieldMeta->default; return; } /** * NOTE: Documents must be sorted as $dbRefs, * however mongo does not guarantiee sorting by list of id's. * This require sorting in php. * If document has composite key this must be taken care too * while comparision for sorting is made. */ $refs = []; $unsortedRefs = []; $pks = []; $sort = []; // Collect primary keys foreach ($dbValues as $key => $dbValue) { $dbValue['_class'] = DbRef::class; $dbRef = $transformatorClass::toModel($dbValue); // Collect keys separatelly for each type $pks[$dbRef->class][$key] = $dbRef->pk; $sort[$key] = $dbRef->pk; } // Fetch all types of db ref's en masse $i = 0; $unsortedPks = []; foreach ($pks as $referenced => $pkValues) { if (empty($referenced)) { continue; } // Find all referenced documents $refModel = new $referenced(); $found = (new RawFinder($refModel))->findAllByPk($pkValues); if (!$found) { continue; } foreach ($found as $document) { // Collect unsorted documents $unsortedRefs[$i] = $document; // Collect pk's $unsortedPks[$i] = PkManager::getFromArray($document, $refModel); $i++; } } // Find existing documents $existing = []; foreach ($model->{$name} as $key => $document) { foreach ($sort as $i => $pk) { if (PkManager::compare($pk, $document)) { // Set existing document with key same as in sort $existing[$i] = $document; } } } // Sort as stored ref foreach ($sort as $key => $pk) { foreach ($unsortedRefs as $i => $document) { if (PkManager::compare($pk, $unsortedPks[$i])) { if (array_key_exists($key, $existing)) { // Update existing instance $refs[$key] = $transformatorClass::toModel($document, $existing[$key], $existing[$key]); } else { // Create new instance $refs[$key] = $transformatorClass::toModel($document); } } } } $model->{$name} = $refs; }