modelFields, $this->invisibleFields ); if (in_array($field, $props)) { return self::getRawProperty($this, $field); } return null; } /** * Set prop * * @param string $field * @param mixed $value * @throws TypeError if the field has an incorrect value * @return mixed|null The input value or null is this prop does not exists */ public function __set(string $field, $value) { if (in_array($field, $this->modelFields)) { $type = $this->getPropType($field); if (!$this->checkFieldType($type, $value)) { throw new TypeError("Wrong type for $field"); } $this->validateValue($field, $value); $this->updatedValues[$field] = $value; return $this->setRawProperty($this, $field, $value); } return null; } protected static function setUpApiInterface() { self::$apiInterface = self::$apiInterface ?: new ApiInterface(); } /** * Fill values into model * * @param array $properties * @return void */ public function fill(array $properties) { if (count($properties)) { foreach ($properties as $field => $value) { self::setRawProperty($this, $field, $value); } } } /** * Set all properties to without any validation * * @param array $properties * @return void */ protected function fillRaw(array $properties) { if (count($properties)) { foreach ($properties as $field => $value) { self::setRawProperty($this, $field, $value); } } } /** * Validate all model properties * * @return array the validated values */ protected function validateAllValues(array $ignore = []): array { $out = []; foreach (static::$validation as $field => $validationRules) { if (\in_array($field, $ignore) or \is_null(self::getRawProperty($this, $field))) { continue; } $this->validateValue($field, self::getRawProperty($this, $field)); $out[$field] = self::getRawProperty($this, $field); } return $out; } /** * Validate a value against its rules * * @param string $field * @param mixed $value * @return boolean * @throws TypeError */ protected static function validateValue(string $field, $value): bool { if (!isset(static::$validation[$field])) { throw new Exception("$field has no validation rule"); } $validator = Utils::makeValidator( [$field => $value], [$field => static::$validation[$field]] ); if ($validator()) { throw new ValidationException(implode(' ', $validator->errors()->get($field))); } return true; } /** * Check a value against its field type * * @param string $type * @param mixed $value * @return boolean * @throws Exception if type is not handled handled types are bool, string, * int, \LDC\LDCCRM\Enums\Country, \LDC\LDCCRM\Enums\ArcadiaOrderType */ protected function checkFieldType(string $type, $value): bool { switch ($type) { case '\\' . Country::class: try { $value = \is_int($value) ? Country::make($value) : $value; } finally { return \is_object($value) and (get_class($value) === Country::class or \in_array(Country::class, \class_parents($value))); } case '\\' . ArcadiaOrderType::class: try { $value = \is_int($value) ? ArcadiaOrderType::make($value) : $value; } finally { return \is_object($value) and (get_class($value) === ArcadiaOrderType::class or \in_array(ArcadiaOrderType::class, \class_parents($value))); } case 'bool': $value = \is_int($value) ? (bool)$value : $value; return \is_bool($value); case 'string': return \is_string($value); case 'int': return \is_int($value); default: throw new Exception(\sprintf("Unhandled $type for %s", \json_encode($value))); } } /** * Get the type of any of this class property * from doc comment * * @param string $property * @return string|null the type or null if not defined */ protected function getPropType(string $property) { $matches = []; $propReflection = new ReflectionProperty(static::class, $property); $docComment = $propReflection->getDocComment(); preg_match_all('/@var ([\w_\x5c]+)/', $docComment, $matches); if (isset($matches[1][0]) and in_array($matches[1][0], [ 'string', 'int', 'bool', '\\' . Country::class, '\\' . ArcadiaOrderType::class ], true)) { return $matches[1][0]; } return null; } /** * Set a property withou any validation * * @param string $field * @param mixed $value * @return void */ protected static function setRawProperty(object $object, string $field, $value): void { if (is_object($value) and in_array(BaseEnum::class, class_parents($value))) { $value = $value->value; } $refClass = new ReflectionClass(\get_class($object)); $refProp = $refClass->getProperty($field); $refProp->setAccessible(true); $refProp->setValue($object, $value); } /** * Get a property * * @param string $field * @param mixed $value * @return mixed */ protected static function getRawProperty(object $object, string $field) { $refClass = new ReflectionClass(\get_class($object)); $refProp = $refClass->getProperty($field); $refProp->setAccessible(true); return $refProp->getValue($object); } /** * Assert primaryKey is valid * * @param int $primaryKey * @return void * @throws LDC\LDCCRM\Exceptions\ValidationException */ protected static function assertValidPrimaryKey(int $primaryKey): void { $validator = Utils::makeValidator( ['id' => $primaryKey], ['id' => 'required|int'] ); if ($validator()) { throw new ValidationException(implode(' ', $validator->errors()->get('id'))); } } /** * Assert hashcode is valid * * @param string|null $hashcode * @return void * @throws LDC\LDCCRM\Exceptions\ValidationException */ protected static function assertValidHascode(?string $hashcode): void { $validator = Utils::makeValidator( ['hashcode' => $hashcode], ['hashcode' => 'required|uuid'] ); if ($validator()) { throw new ValidationException(implode(' ', $validator->errors()->get('hashcode'))); } } }