예제 #1
1
 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;
 }
예제 #2
0
 /**
  * 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);
 }
예제 #3
0
 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;
 }
예제 #4
0
 /**
  * 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;
 }
예제 #5
0
 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;
 }
예제 #6
0
 /**
  * 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;
 }
예제 #7
0
 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);
 }
예제 #8
0
 /**
  * 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), '\\');
 }
예제 #9
0
 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);
 }
예제 #10
0
 /**
  * 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);
     }
 }
예제 #11
0
 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);
 }
예제 #12
0
 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;
 }
예제 #13
0
 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);
 }
예제 #14
0
 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);
 }
예제 #15
0
 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');
     }
 }
예제 #16
0
 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), '\\');
 }
예제 #17
0
 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);
 }
예제 #18
0
 /**
  *
  * @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];
 }
예제 #19
0
 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;
 }
예제 #20
0
 /**
  * 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;
 }
예제 #21
0
 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');
 }
예제 #22
0
 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);
 }
예제 #23
0
 /**
  * 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);
                 }
             }
         }
     }
 }
예제 #24
0
 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;
 }
예제 #25
0
 /**
  * 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;
 }
예제 #26
0
 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;
 }
예제 #27
0
 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);
             }
         }
     }
 }
예제 #28
0
 /**
  * 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;
     }
 }
예제 #29
0
 protected static function getClassName($model, $name)
 {
     $fieldMeta = ManganMeta::create($model)->{$name};
     /* @var $fieldMeta DocumentPropertyMeta */
     return $fieldMeta->dbRef->class;
 }
예제 #30
0
파일: Event.php 프로젝트: maslosoft/mangan
 /**
  * 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];
 }