public function isValid(AnnotatedInterface $model, $attribute) { if ($this->allowEmpty && empty($model->{$attribute})) { return true; } $label = ManganMeta::create($model)->field($attribute)->label; if (!is_scalar($model->{$attribute})) { $this->addError('msgValid', ['{attribute}' => $label]); return false; } if (!preg_match($this->pattern, $model->{$attribute})) { $this->addError('msgValid', ['{attribute}' => $label]); return false; } $domain = rtrim(substr($model->{$attribute}, strpos($model->{$attribute}, '@') + 1), '>'); if ($this->checkMX) { if (function_exists('checkdnsrr')) { if (!checkdnsrr($domain, 'MX')) { $this->addError('msgDomain', ['{domain}' => $domain]); return false; } } } if ($this->checkPort) { if ($this->checkMxPorts($domain)) { $this->addError('msgPort', ['{domain}' => $domain]); return false; } } return true; }
/** * Validate model, optionally only selected fields * @param string[] $fields * @return boolean */ public function validate($fields = []) { $valid = []; if (empty($fields)) { $fields = array_keys($this->meta->fields()); } foreach ($fields as $name) { $fieldMeta = $this->meta->field($name); // Reset errors $this->errors[$name] = []; // Check if meta for field exists if (empty($fieldMeta)) { throw new InvalidArgumentException(sprintf("Unknown field `%s` in model `%s`", $name, get_class($this->model))); } // Validate sub documents // Skip fields that are not updatable if ($fieldMeta->owned && $fieldMeta->updatable) { if (is_array($this->model->{$name})) { // Handle arrays of documents foreach ($this->model->{$name} as $model) { $validator = new Validator($model); $isValid = $validator->validate(); $valid[] = (int) $isValid; if (!$isValid) { $errors = $validator->getErrors(); $this->setErrors($errors); } } } elseif (!empty($this->model->{$name})) { // Handle single documents $validator = new Validator($this->model->{$name}); $isValid = $validator->validate(); $valid[] = (int) $isValid; if (!$isValid) { $errors = $validator->getErrors(); $this->setErrors($errors); } } } // Skip field without validators if (empty($fieldMeta->validators)) { continue; } $valid[] = (int) $this->validateEntity($name, $fieldMeta->validators); } // Model validators $typeValidators = $this->meta->type()->validators; if (!empty($typeValidators)) { $typeName = $this->meta->type()->name; // Reset errors $this->errors[$typeName] = []; $valid[] = (int) $this->validateEntity($typeName, $typeValidators); } return count($valid) === array_sum($valid); }
public function isValid(AnnotatedInterface $model, $attribute) { $value = $model->{$attribute}; if ($this->allowEmpty && empty($value)) { return true; } if ($this->pattern === null) { $msg = sprintf('The `pattern` property must be specified with a valid regular expression on attribute `%s` of model `%s`', $attribute, get_class($model)); throw new InvalidArgumentException($msg); } $label = ManganMeta::create($model)->field($attribute)->label; if (!is_scalar($value)) { $this->addError('msgInvalid', ['{attribute}' => $label]); return false; } $match = preg_match($this->pattern, $value); if ($this->not) { if ($match) { $this->addError('msgInvalid', ['{attribute}' => $label]); return false; } } else { if (!$match) { $this->addError('msgInvalid', ['{attribute}' => $label]); return false; } } return true; }
/** * Move to trash. Validation will be performed * before trashing with `trash` (TrashInterface::ScenarioTrash) scenario. * * * @see TrashInterface::ScenarioTrash * @param boolean $runValidation whether to perform validation before saving the record. * If the validation fails, the record will not be saved to database. * * @return boolean * @Ignored */ public function trash($runValidation = true) { ScenarioManager::setScenario($this, TrashInterface::ScenarioTrash); $validator = new Validator($this); if (!$runValidation || $validator->validate()) { $eventBefore = new TrashEvent($this); if (Event::hasHandler($this, TrashInterface::EventBeforeTrash)) { if (!Event::valid($this, TrashInterface::EventBeforeTrash, $eventBefore)) { return false; } } $meta = ManganMeta::create($this); $trash = $eventBefore->getTrash(); if (empty($trash)) { $trash = new Trash(); } $trash->name = (string) $this; $trash->data = $this; $trash->type = isset($meta->type()->label) ? $meta->type()->label : get_class($this); if (!$trash->save()) { return false; } $eventAfter = new TrashEvent($this); $eventAfter->setTrash($trash); if (!Event::valid($this, TrashInterface::EventAfterTrash, $eventAfter)) { return false; } // Use deleteOne, to avoid beforeDelete event, // which should be raised only when really removing document: // when emtying trash $em = new EntityManager($this); return $em->deleteOne(PkManager::prepareFromModel($this)); } return false; }
public function isValid(AnnotatedInterface $model, $attribute) { $value = $model->{$attribute}; if ($this->allowEmpty && empty($value)) { return true; } if (!is_array($this->range)) { $msg = sprintf('The "range" property must be specified with a list of values on attribute `%s` of model `%s`', $attribute, get_class($model)); throw new InvalidArgumentException($msg); } $result = false; if ($this->strict) { $result = in_array($value, $this->range, true); } else { foreach ($this->range as $r) { $result = $r === '' || $value === '' ? $r === $value : $r == $value; if ($result) { break; } } } $label = ManganMeta::create($model)->field($attribute)->label; if (!$this->not && !$result) { $this->addError('msgIsNot', ['{attribute}' => $label]); return false; } elseif ($this->not && $result) { $this->addError('msgIs', ['{attribute}' => $label]); return false; } return true; }
/** * This will be called when getting value. * This should return end user value. * @param AnnotatedInterface $model Document model which will be decorated * @param string $name Field name * @param mixed $dbValue * @return bool Return true if value should be assigned to model */ public function read($model, $name, &$dbValue, $transformatorClass = TransformatorInterface::class) { if (!$model instanceof InternationalInterface) { throw new ManganException(sprintf('Model class %s must implement interface %s to support I18N fields. You can use trait I18NAbleTrait as default implementation.', get_class($model), InternationalInterface::class)); } $lang = $model->getLang(); if (!is_array($dbValue)) { $value = $dbValue; $dbValue = []; $dbValue[$lang] = $value; } $model->setRawI18N(array_merge($model->getRawI18N(), [$name => $dbValue])); if (array_key_exists($lang, $dbValue)) { $model->{$name} = $dbValue[$lang]; } else { $defaultLang = $model->getDefaultLanguage(); $i18nMeta = ManganMeta::create($model)->field($name)->i18n; if ($i18nMeta->allowDefault && array_key_exists($defaultLang, $dbValue)) { $model->{$name} = $dbValue[$defaultLang]; return true; } if ($i18nMeta->allowAny) { foreach ($dbValue as $value) { if ($value) { $model->{$name} = $value; } } } } return true; }
public function testIfLabelIsSetInPlainModel() { $model = new PlainModelWithLabel(); $meta = ManganMeta::create($model); $this->assertSame(ModelWithLabel::TitleLabel, $meta->title->label); $this->assertSame(ModelWithLabel::TitleLabel, $meta->title->label); $this->assertSame(ModelWithLabel::StateLabel, $meta->state->label); }
/** * This method must return collection name for use with this model. * By default it uses full class name, with slashes replaced by dots. * * If `CollectionName` annotation is defined, it will collection name defined * by this annotation. * * @return string collection name * @Ignored */ public function getCollectionName() { $collectionName = ManganMeta::create($this)->type()->collectionName; if (!empty($collectionName)) { return $collectionName; } $name = get_class($this); return ltrim(str_replace('\\', '.', $name), '\\'); }
public function __construct(AnnotatedInterface $model = null) { if (!$model || !$model instanceof AnnotatedInterface) { return; } // Clone is to prevent possible required constructor params issues $this->model = clone $model; $this->meta = ManganMeta::create($this->model); }
/** * This will be called when setting value. * This should return db acceptable value * @param AnnotatedInterface $model Model which is about to be decorated * @param mixed[] $dbValues Whole model values from database. This is associative array with keys same as model properties (use $name param to access value). This is passed by reference. * @param string $transformatorClass Transformator class used * @return bool Return true to store value to database */ public function write($model, &$dbValues, $transformatorClass = TransformatorInterface::class) { $meta = ManganMeta::create($model); $typeMeta = $meta->type(); /* @var $typeMeta DocumentTypeMeta */ foreach ($typeMeta->aliases as $from => $to) { $this->_writeValue($meta, $from, $to, $dbValues, $model); $this->_writeValue($meta, $to, $from, $dbValues, $model); } }
public function testCanReadFromModelAnnotation() { // From annotation: // w = 3, fsync = true $model = new ModelWithClientFlags(); $o = new EntityOptions($model); $meta = ManganMeta::create($model)->type(); $this->assertSame($o->fsync, true); $this->assertSame($o->w, 3); }
public function isValid(AnnotatedInterface $model, $attribute) { $valid = filter_var($model->{$attribute}, FILTER_VALIDATE_URL); if (!$valid) { $label = ManganMeta::create($model)->field($attribute)->label; $this->addError('msgNotUrl', ['{attribute}' => $label]); return false; } return true; }
public function __construct($scenario = 'insert', $lang = '') { parent::__construct($scenario, $lang); $this->setLang($lang); foreach (ManganMeta::create($this)->fields() as $name => $fieldMeta) { if ($fieldMeta->callGet || $fieldMeta->callSet || !$fieldMeta->direct) { unset($this->{$name}); } } unset($this->rawI18N); }
public function __construct($model) { // This is to use get/set foreach ($this->_getOptionNames() as $name) { PropertyMaker::defineProperty($this, $name, $this->_defaults); } foreach (ManganMeta::create($model)->type()->clientFlags as $name => $value) { $this->_values[$name] = $value; } $this->_mangan = Mangan::fromModel($model); }
public function testIfWillThrowExceptionOnClassNotImportedSanitizer() { $model = new ModelWithNonExistentSanitizer2(); try { ManganMeta::create($model); $this->fail('Exception was not thrown'); } catch (UnexpectedValueException $exc) { codecept_debug($exc->getMessage()); $this->assertTrue(true, 'That exception was thrown'); } }
public static function nameCollection($model) { if ($model instanceof CollectionNameInterface) { return $model->getCollectionName(); } $meta = ManganMeta::create($model)->type(); $name = $meta->collectionName; if ($name) { return $name; } $name = get_class($model); return ltrim(str_replace('\\', '.', $name), '\\'); }
public function write($model, $name, &$dbValue, $transformatorClass = TransformatorInterface::class) { if (!$model->{$name}) { return; } $dbRef = DbRefManager::extractRef($model, $name); $referenced = $model->{$name}; $fieldMeta = ManganMeta::create($model)->field($name); if ($fieldMeta->dbRef->updatable) { DbRefManager::save($referenced, $dbRef); } $dbValue[$name] = $transformatorClass::fromModel($dbRef, false); }
/** * * @param string $name * @return mixed */ public function getFor($name) { if (!array_key_exists($name, $this->transformators)) { if (!$this->meta->{$name}) { throw new TransformatorException(sprintf('There is not metadata for field `%s` of model `%s`, have you declared this field?', $name, get_class($this->getModel()))); } $this->transformators[$name] = $this->_getTransformer($this->transformatorClass, $this->meta->type(), $this->meta->{$name}); } // Support for setting name in sanitizers etc. if ($this->transformators[$name] instanceof NameAwareInterface) { $this->transformators[$name]->setName($name); } return $this->transformators[$name]; }
public function isValid(AnnotatedInterface $model, $attribute) { $value = $model->{$attribute}; $label = ManganMeta::create($model)->field($attribute)->label; if (!empty($this->requiredValue)) { if (!$this->strict && $value != $this->requiredValue || $this->strict && $value !== $this->requiredValue) { $this->addError('msgExact', ['{attribute}' => $label, '{value}' => $this->requiredValue]); return false; } } elseif ($this->isEmpty($value, $this->trim)) { $this->addError('msgBlank', ['{attribute}' => $label]); return false; } return true; }
/** * This will be called when setting value. * This should return db acceptable value * @param AnnotatedInterface $model Document model which will be decorated * @param string $name Field name * @param mixed $dbValue * @return bool Return true to store value to database */ public function write($model, $name, &$dbValue, $transformatorClass = TransformatorInterface::class) { $secretMeta = ManganMeta::create($model)->field($name)->secret; if (false === $secretMeta) { return true; } if (empty($secretMeta->callback) || !$secretMeta->callback) { return true; } if (!empty($model->{$name})) { $converted = call_user_func($secretMeta->callback, $model->{$name}); $dbValue[$name] = $converted; } return true; }
public function testIfWillSkipNonIndexableFields() { $model = new ExplicitlyNonIndexableField(); $model->title = 'blah'; $model->password = '******'; $model->details = 'secret details'; $model->garbage = 'some non persistend garbadge'; $meta = ManganMeta::create($model); $filter = new Filter($model, SearchArray::class, $meta); $passwordFilter = $filter->getFor('password'); $data = SearchArray::fromModel($model); codecept_debug($data); $this->assertTrue(array_key_exists('title', $data), 'That title *is* stored'); $this->assertSame('blah', $data['title']); $this->assertFalse(array_key_exists('password', $data), 'That password is *not* stored'); $this->assertFalse(array_key_exists('details', $data), 'That details is *not* stored'); $this->assertFalse(array_key_exists('garbage', $data), 'That garbage is *not* stored'); }
public function testIfWillValidateModelUsingCustomValidator() { $model = new ModelWithCustomValidatorProxy(); $validator = new Validator($model); $result = $validator->validate(); // Should not pass validation $this->assertFalse($result); // Should pass validation $model->login = '******'; $result2 = $validator->validate(); $this->assertTrue($result2); // Get validators meta $validatorsMeta = ManganMeta::create($model)->login->validators; // should have one $this->assertSame(1, count($validatorsMeta)); $validatorInstance = Factory::create($model, $validatorsMeta[0]); // Check type of validator $this->assertInstanceof(RequiredValidator::class, $validatorInstance); }
/** * 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) { foreach (ManganMeta::create($model)->properties('owned') as $name => $metaProperty) { /* @var $metaProperty DocumentPropertyMeta */ if (!isset($model->{$name})) { continue; } if ($model->{$name} instanceof OwneredInterface) { $model->{$name}->setOwner($model); } if (is_array($model->{$name})) { foreach ($model->{$name} as $document) { if ($document instanceof OwneredInterface) { $document->setOwner($model); } } } } }
public function isValid(AnnotatedInterface $model, $attribute) { $value = $model->{$attribute}; if ($this->allowEmpty && empty($value)) { return true; } $label = ManganMeta::create($model)->field($attribute)->label; if (!is_scalar($value)) { $this->addError('msgNumber', ['{attribute}' => $label]); return false; } if (!is_numeric($value)) { $this->addError('msgNumber', ['{attribute}' => $label]); return false; } if ($this->integerOnly) { if (!filter_var($value, FILTER_VALIDATE_INT)) { $this->addError('msgInteger', ['{attribute}' => $label]); return false; } } else { if (!filter_var($value, FILTER_VALIDATE_FLOAT)) { $this->addError('msgNumber', ['{attribute}' => $label]); return false; } } if ($this->min !== null && $value < $this->min) { if (!empty($this->tooSmall)) { $this->addError($this->tooSmall, ['{min}' => $this->min, '{attribute}' => $label]); return false; } $this->addError('msgTooSmall', ['{min}' => $this->min, '{attribute}' => $label]); return false; } if ($this->max !== null && $value > $this->max) { if (!empty($this->tooBig)) { $this->addError($this->tooBig, ['{max}' => $this->max, '{attribute}' => $label]); } $this->addError('msgTooBig', ['{max}' => $this->max, '{attribute}' => $label]); return false; } return true; }
/** * Add error. Let it be either error message, or validator field with message. * * This is meant to be used inside validators. * * Will display value of `$msgError` if set or it's Label annotation value: * ```php * $this->addError('msgError'); * ``` * * Will display error message as is: * ```php * $this->addError('This field is required'); * ``` * * **NOTE**: If field `message` is set, it will use this custom message. * * @param string $message * @param string[] $params */ public function addError($message, $params = []) { // Use custom message if (!empty($this->message)) { $message = $this->message; } elseif (preg_match('~^[a-zA-Z0-9_]+$~', $message) && isset($this->{$message})) { // Use message from @Label if it's validator field and is empty if ($this->{$message} === '') { $fieldMeta = ManganMeta::create($this)->field($message); if (false === $fieldMeta) { throw new InvalidArgumentException(sprintf("Unknown validator message field: `%s`", $message)); } $message = $fieldMeta->label; } else { $message = $this->{$message}; } } if (!empty($params)) { $message = str_replace(array_keys($params), $params, $message); } $this->messages[] = $message; }
public function isValid(AnnotatedInterface $model, $attribute) { $value = $model->{$attribute}; if ($this->allowEmpty && empty($value)) { return true; } $label = ManganMeta::create($model)->field($attribute)->label; if (!is_scalar($value)) { $this->addError('msgInvalid', ['{attribute}' => $label]); return false; } if (!is_string($value)) { $this->addError('msgInvalid', ['{attribute}' => $label]); return false; } $length = mb_strlen($value); if ($this->min !== null && $length < $this->min) { if ($this->tooShort) { $this->addError($this->tooShort, ['{min}' => $this->min, '{attribute}' => $label]); return false; } $this->addError('msgTooShort', array('{min}' => $this->min, '{attribute}' => $label)); return false; } if ($this->max !== null && $length > $this->max) { if ($this->tooLong) { $this->addError($this->tooLong, array('{max}' => $this->max, '{attribute}' => $label)); return false; } $this->addError('msgTooLong', array('{max}' => $this->max, '{attribute}' => $label)); return false; } if ($this->is !== null && $length !== $this->is) { $this->addError('msgLength', array('{length}' => $this->is, '{attribute}' => $label)); return false; } return true; }
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); } } } }
/** * Change i18n attributes values to apropriate language * @param string $fromCode * @param string $toCode */ private function _changeAttributesLang($fromCode, $toCode) { $meta = ManganMeta::create($this); $fields = $meta->properties('i18n'); foreach ($fields as $name => $i18n) { $current = $this->{$name}; if (isset($this->_rawI18N[$name]) && array_key_exists($toCode, $this->_rawI18N[$name])) { $new = $this->_rawI18N[$name][$toCode]; } else { $new = $meta->{$name}->default; } $this->_rawI18N[$name][$fromCode] = $current; $this->{$name} = $new; } }
protected static function getClassName($model, $name) { $fieldMeta = ManganMeta::create($model)->{$name}; /* @var $fieldMeta DocumentPropertyMeta */ return $fieldMeta->dbRef->class; }
/** * Get properties which should be propagated. * NOTE: This is cached, as it might be called numerous times * @param object $model * @return bool[] */ private static function getPropagatedProperties($model) { $key = get_class($model); if (empty(self::$propagated[$key])) { $propageted = []; foreach (ManganMeta::create($model)->properties('propagateEvents') as $name => $isPropagated) { if (!$isPropagated) { continue; } $propageted[$name] = true; } self::$propagated[$key] = $propageted; } return self::$propagated[$key]; }