/** * This will be called when getting value. * This should return end user value. * @param AnnotatedInterface $model Document model which will be decorated * @param mixed $dbValues * @param string $transformatorClass Transformator class used * @return bool Return true if value should be assigned to model */ public function read($model, &$dbValues, $transformatorClass = TransformatorInterface::class) { if (isset($dbValues[self::Key]) && isset($model->_id)) { $id = $dbValues[self::Key]; // Need to sanitize value, as $dbValues contains raw value $sanitizer = new Sanitizer($model, SearchArray::class, ManganelMeta::create($model)); $model->_id = $sanitizer->read('_id', $id); unset($dbValues[self::Key]); } }
public function testWillSanitizeArraysOfAttributes() { $model = new WithSanitizedArrayValues(); $model->goals = ['1', 'a2', '2a']; $model->shots = [1, 0.2, '0', 'yes']; $model->title = [1, true, 1.2]; $sanitizer = new Sanitizer($model); $this->assertSame([1, 0, 2], $sanitizer->read('goals', $model->goals)); $this->assertSame([true, true, false, true], $sanitizer->read('shots', $model->shots)); $this->assertSame(['1', '1', '1.2'], $sanitizer->read('title', $model->title)); $this->assertSame([1, 0, 2], $sanitizer->write('goals', $model->goals)); $this->assertSame([true, true, false, true], $sanitizer->write('shots', $model->shots)); $this->assertSame(['1', '1', '1.2'], $sanitizer->write('title', $model->title)); }
/** * Register event handlers for parent of parent-child relation. * * @param AnnotatedInterface $parent * @param string $childClass */ public function registerParent(AnnotatedInterface $parent, $childClass) { if (!ClassChecker::exists($childClass)) { throw new UnexpectedValueException(sprintf('Class `%s` not found', $childClass)); } // Delete all of this child items after removing from trash $beforeDelete = function (ModelEvent $event) use($parent, $childClass) { $model = $event->sender; $event->isValid = true; if ($model instanceof $parent) { $child = new $childClass(); $criteria = new Criteria(null, $child); $criteria->parentId = $model->_id; $event->isValid = $child->deleteAll($criteria); } }; Event::on($parent, EntityManagerInterface::EventBeforeDelete, $beforeDelete); // Trash all child items from parent item $afterTrash = function (ModelEvent $event) use($parent, $childClass) { $model = $event->sender; $event->isValid = true; if ($model instanceof $parent) { $child = new $childClass(); $criteria = new Criteria(null, $child); $criteria->parentId = $model->_id; $items = $child->findAll($criteria); // No items found, so skip if (empty($items)) { $event->isValid = true; return true; } // Trash in loop all items foreach ($items as $item) { if (!$item->trash()) { $event->isValid = false; return false; } } } }; Event::on($parent, TrashInterface::EventAfterTrash, $afterTrash); // Restore all child items from parent, but only those after it was trashed. // This will keep previously trashed items in trash $afterRestore = function (RestoreEvent $event) use($parent, $childClass) { $model = $event->sender; if ($model instanceof $parent) { $child = new $childClass(); $trash = $event->getTrash(); $criteria = new Criteria(null, $trash); // Conditions decorator do not work with dots so sanitize manually. $s = new Sanitizer($child); $id = $s->write('parentId', $model->_id); $criteria->addCond('data.parentId', '==', $id); // Restore only child items trashed when blog was trashed - skip earlier $criteria->addCond('createDate', 'gte', $trash->createDate); $trashedItems = $trash->findAll($criteria); if (empty($trashedItems)) { $event->isValid = true; return true; } // Restore all items foreach ($trashedItems as $trashedItem) { $trashedItem->restore(); } } $event->isValid = true; }; Event::on($parent, TrashInterface::EventAfterRestore, $afterRestore); }
/** * Create document from array * * @param mixed[] $data * @param string|object $className * @param AnnotatedInterface $instance * @return AnnotatedInterface * @throws TransformatorException */ public static function toModel($data, $className = null, AnnotatedInterface $instance = null) { $data = (array) $data; if (is_object($className)) { $className = get_class($className); } if (!$className) { if (array_key_exists('_class', $data)) { $className = $data['_class']; } else { if (null !== $instance) { $className = get_class($instance); } else { throw new TransformatorException('Could not determine document type'); } } } if ($instance) { $model = $instance; } else { $model = new $className(); } $meta = static::getMeta($model); $calledClass = get_called_class(); $decorator = new Decorator($model, $calledClass, $meta); $md = new ModelDecorator($model, $calledClass, $meta); $sanitizer = new Sanitizer($model, $calledClass, $meta); $filter = new Filter($model, $calledClass, $meta); foreach ($meta->fields() as $name => $fieldMeta) { /* @var $fieldMeta DocumentPropertyMeta */ if (array_key_exists($name, $data)) { // Value is available in passed data $value = $data[$name]; } elseif (!empty($instance)) { // Take value from existing instance // NOTE: We could `continue` here but value should be sanitized anyway $value = $model->{$name}; } else { // As a last resort set to default $value = $fieldMeta->default; } if (!$filter->toModel($model, $fieldMeta)) { continue; } $decorator->read($name, $value); $model->{$name} = $sanitizer->read($name, $model->{$name}); } $md->read($data); return FinalizingManager::toModel(static::class, $model); }