<?php
 namespace TCMS;
 use DateTime;
 use FIELD_DATA_TYPE;
 abstract class Model {
 public $tableName = null;
 protected $timestamp = false;
 private $__mdb = null;
 private $__fields = null;
 public function __set($name, $value) {
 if($this->__fields == null) $this->callRegisterField();
 if(isset($this->__fields[$name])) {
 $_f = $this->__fields[$name];
 $_f->value = $value;
 $_f->hasSet = true;
 }
 }
 public function __get($name) {
 if($this->__fields == null) $this->callRegisterField();
 if(array_key_exists($name, $this->__fields)) {
 return $this->__fields[$name]->value;
 }
 return false;
 }
 abstract protected function registerField();
 protected function addField(string $name, int $type, Callable $setFieldFunction = null) {
 $fieldModel = new FieldModel($name, $type);
 if($setFieldFunction != null && is_callable($setFieldFunction)) call_user_func($setFieldFunction, $fieldModel);
 if($fieldModel->displayName === null) $fieldModel->displayName = $fieldModel->name;
 if($fieldModel->defaultValue !== null) $fieldModel->value = $fieldModel->defaultValue;
 $this->__fields[$fieldModel->name] = $fieldModel;
 }
 protected function removeField(string $name) {
 if(isset($this->__fields[$name])) {
 unset($this->__fields[$name]);
 }
 }
 public function getFields() {
 if($this->__fields == null) $this->callRegisterField();
 return $this->__fields;
 }
 private function callRegisterField() {
 $this->__fields = [];
 $this->registerField();
 if($this->timestamp) {
 if(!isset($this->__fields['created_at'])) {
 $this->addField('created_at', FIELD_DATA_TYPE::DATETIME, function(\TCMS\FieldModel $f) {
 $f->showInCreatePage = false;
 $f->showInUpdatePage = false;
 $f->displayName = lang('time.created_at');
 }
);
 }
 if(!isset($this->__fields['updated_at'])) {
 $this->addField('updated_at', FIELD_DATA_TYPE::DATETIME, function(\TCMS\FieldModel $f) {
 $f->showInCreatePage = false;
 $f->showInUpdatePage = false;
 $f->displayName = lang('time.updated_at');
 }
);
 }
 }
 }
 public function isValid() {
 if($this->__fields == null) $this->callRegisterField();
 if($this->id || $this->_id) {
 $pageType = 2;
 }
 else {
 $pageType = 1;
 }
 $isValid = true;
 foreach($this->__fields as &$field) {
 if($pageType && ($pageType == 1 && !$field->showInCreatePage || $pageType == 2 && (!$field->showInUpdatePage || !$field->hasSet))) {
 continue;
 }
 if(isset($field->modelError)) {
 $field->error = $field->modelError;
 unset($field->modelError);
 $isValid = false;
 continue;
 }
 $field->error = false;
 if($field->required && ($field->value === null || trim($field->value . '') === '')) {
 if($field->type == FIELD_DATA_TYPE::FILE || $field->type == FIELD_DATA_TYPE::IMAGE || $field->type == FIELD_DATA_TYPE::FILE_LIST || $field->type == FIELD_DATA_TYPE::IMAGE_LIST) {
 if(isset($_FILES[$field->name])) {
 if($field->type == FIELD_DATA_TYPE::FILE || $field->type == FIELD_DATA_TYPE::IMAGE) {
 if($_FILES[$field->name]['error'] == UPLOAD_ERR_OK) continue;
 }
 else {
 if($_FILES[$field->name]['name'][0]) continue;
 }
 }
 $field->error = lang('validate.err_file');
 }
 else {
 $field->error = lang('validate.err_empty', [$field->displayName]);
 }
 $isValid = false;
 }
 if($field->value || $field->value === 0 || $field->value === false) {
 if ($field->validate) {
 if(!is_array($field->validate)) {
 throw new \TCMS\TException("Validate phải là array", 500);
 }
 foreach ($field->validate as $kkk => $vvv) {
 if ($kkk == "unique") {
 if(!$vvv) {
 throw new \TCMS\TException("Vui lòng truyền message cho validate $kkk", 500);
 }
 $v = $field->value;
 if (is_string($v)) {
 $v = trim($v);
 }
 if(!$v) {
 $field->error = lang('validate.err_empty', [$field->displayName]);
 $isValid = false;
 break;
 }
 if($pageType == 2) {
 if(\DB::table($this->tableName)->where('id', $this->id, '<>')->where($field->name, $v)->count($field->name)) {
 $field->error = $vvv;
 $isValid = false;
 break;
 }
 }
 else {
 if(\DB::table($this->tableName)->where($field->name, $v)->count($field->name)) {
 $field->error = $vvv;
 $isValid = false;
 break;
 }
 }
 continue;
 }
 else if($kkk == "max") {
 $maxV = $vvv;
 continue;
 }
 else if($kkk == "min") {
 $minV = $vvv;
 continue;
 }
 if(!$vvv) {
 throw new \TCMS\TException("Vui lòng truyền message cho validate $kkk", 500);
 }
 if (!preg_match($kkk, $field->value)) {
 $field->error = $vvv;
 $isValid = false;
 break;
 }
 }
 if(!$isValid) {
 continue;
 }
 }
 switch($field->type) {
 case FIELD_DATA_TYPE::INTEGER: case FIELD_DATA_TYPE::BIGINT: if(is_numeric($field->value) === false) {
 $field->error = lang('validate.err_integer', [$field->displayName]);
 $isValid = false;
 }
 break;
 case FIELD_DATA_TYPE::DOUBLE: if(is_double($field->value) === false) {
 $field->error = lang('validate.err_double', [$field->displayName]);
 $isValid = false;
 }
 break;
 case FIELD_DATA_TYPE::PRICE: $price =preg_replace("/(\.| |\,)*/", "", trim($field->value));
 if(is_numeric($price) === false || $price < 0) {
 $field->error = lang('validate.err_price_vn');
 $isValid = false;
 }
 break;
 case FIELD_DATA_TYPE::DATETIME: if(is_string($field->value)) {
 if(isset($field->autoFormat)) {
 try {
 $field->value = new \DateTime($field->value);
 }
 catch (\Exception $e) {
 try {
 if(strpos($field->value, '(') !== false) {
 $startIndex = strpos($field->value, ' (');
 $field->value = trim(substr($field->value, 0, $startIndex));
 $field->value = date('Y-m-d H:i:s', strtotime($field->value));
 }
 $field->value = new \DateTime($field->value);
 }
 catch (\Exception $e2) {
 $vDt = \DateTime::createFromFormat(empty($field->format) ? 'd/m/Y H:i:s' : $field->format, $field->value);
 if($vDt) {
 $field->value = $vDt;
 }
 else {
 $field->error = lang('validate.err_datetime');
 $isValid = false;
 }
 }
 }
 }
 else {
 $vDt = \DateTime::createFromFormat(empty($field->format) ? 'd/m/Y H:i:s' : $field->format, $field->value);
 if($vDt) {
 $field->value = $vDt;
 }
 else {
 $field->error = lang('validate.err_datetime');
 $isValid = false;
 }
 }
 }
 break;
 case FIELD_DATA_TYPE::DATE: if(is_string($field->value)) {
 if(isset($field->autoFormat)) {
 try {
 $field->value = new \DateTime($field->value);
 }
 catch (\Exception $e) {
 try {
 if(strpos($field->value, '(') !== false) {
 $startIndex = strpos($field->value, ' (');
 $field->value = trim(substr($field->value, 0, $startIndex));
 $field->value = date('Y-m-d', strtotime($field->value));
 }
 $field->value = new \DateTime($field->value);
 }
 catch (\Exception $e2) {
 $vDt = \DateTime::createFromFormat(empty($field->format) ? 'd/m/Y' : $field->format, $field->value);
 if($vDt) {
 $field->value = $vDt;
 }
 else {
 $field->error = lang('validate.err_datetime');
 $isValid = false;
 }
 }
 }
 }
 else {
 $vDt = \DateTime::createFromFormat(empty($field->format) ? 'd/m/Y' : $field->format, $field->value);
 if($vDt) {
 $field->value = $vDt;
 }
 else {
 $field->error = lang('validate.err_datetime');
 $isValid = false;
 }
 }
 }
 break;
 case FIELD_DATA_TYPE::TIME: if(is_string($field->value)) {
 if(\DateTime::createFromFormat("H:i:s", $field->value) === false && \DateTime::createFromFormat("H:i", $field->value) === false) {
 $field->error = lang('validate.err_datetime');
 $isValid = false;
 }
 }
 break;
 case FIELD_DATA_TYPE::EMAIL: if (!filter_var($field->value, \FILTER_VALIDATE_EMAIL)) {
 $field->error = lang('validate.err_mail', [$field->displayName]);
 $isValid = false;
 }
 break;
 case FIELD_DATA_TYPE::PHONE: if (!preg_match('/^[0-9\\+\\. ]/', $field->value)) {
 $field->error = lang('validate.err_phone', [$field->displayName]);
 $isValid = false;
 }
 break;
 }
 if($isValid) {
 switch($field->type) {
 case FIELD_DATA_TYPE::INTEGER: case FIELD_DATA_TYPE::BIGINT: case FIELD_DATA_TYPE::DOUBLE: case FIELD_DATA_TYPE::PRICE: if(isset($maxV) || isset($minV)) {
 if(isset($maxV)) {
 if(is_array($maxV) && isset($maxV['value'])) {
 if($field->value > $maxV['value']) {
 if(isset($maxV['message'])) {
 $field->error = $maxV['message'];
 $isValid = false;
 }
 else {
 $field->error = lang('validate.err_min_value', [$field->displayName, $maxV['value']]);
 $isValid = false;
 }
 }
 }
 else {
 if($field->value > $maxV) {
 $field->error = lang('validate.err_min_value', [$field->displayName, $maxV]);
 $isValid = false;
 }
 }
 }
 if($isValid && isset($minV)) {
 if(is_array($minV) && isset($minV['value'])) {
 if($field->value < $minV['value']) {
 if(isset($minV['message'])) {
 $field->error = $minV['message'];
 $isValid = false;
 }
 else {
 $field->error = lang('validate.err_max_value', [$field->displayName, $minV['value']]);
 $isValid = false;
 }
 }
 }
 else {
 if($field->value > $minV) {
 $field->error = lang('validate.err_max_value', [$field->displayName, $minV]);
 $isValid = false;
 }
 }
 }
 }
 break;
 case FIELD_DATA_TYPE::JSON: case FIELD_DATA_TYPE::STRING: case FIELD_DATA_TYPE::PASSWORD: case FIELD_DATA_TYPE::TEXT: case FIELD_DATA_TYPE::FILE: case FIELD_DATA_TYPE::IMAGE: case FIELD_DATA_TYPE::IMAGE_LIST: case FIELD_DATA_TYPE::FILE_LIST: case FIELD_DATA_TYPE::EMAIL: case FIELD_DATA_TYPE::PHONE: if(is_string($field->value) && (isset($maxV) || isset($minV))) {
 if(isset($maxV)) {
 if(is_array($maxV) && isset($maxV['value'])) {
 if(strlen($field->value) > $maxV['value']) {
 if(isset($maxV['message'])) {
 $field->error = $maxV['message'];
 $isValid = false;
 }
 else {
 $field->error = lang('validate.err_min_length', [$field->displayName, $maxV['value']]);
 $isValid = false;
 }
 }
 }
 else {
 if(strlen($field->value) > $maxV) {
 $field->error = lang('validate.err_min_length', [$field->displayName, $maxV]);
 $isValid = false;
 }
 }
 }
 if($isValid && isset($minV)) {
 if(is_array($minV) && isset($minV['value'])) {
 if(strlen($field->value) < $minV['value']) {
 if(isset($minV['message'])) {
 $field->error = $minV['message'];
 $isValid = false;
 }
 else {
 $field->error = lang('validate.err_max_length', [$field->displayName, $minV['value']]);
 $isValid = false;
 }
 }
 }
 else {
 if(strlen($field->value) > $minV) {
 $field->error = lang('validate.err_max_length', [$field->displayName, $minV]);
 $isValid = false;
 }
 }
 }
 }
 break;
 }
 unset($minV);
 unset($maxV);
 }
 }
 }
 return $isValid;
 }
 public function getErrors() {
 if($this->__fields == null) return [];
 $erros = [];
 foreach($this->__fields as $k => $f) {
 if($f->error) {
 $erros[$k] = $f->error;
 }
 }
 return $erros;
 }
 public function addModelError($fieldName, $message) {
 if($this->__fields == null) $this->callRegisterField();
 if(array_key_exists($fieldName, $this->__fields)) {
 $this->__fields[$fieldName]->modelError = $message;
 $this->__fields[$fieldName]->error = $message;
 }
 else {
 $this->addField($fieldName, FIELD_DATA_TYPE::INTEGER, function(FieldModel $field) {
 $field->isCustomField = true;
 }
);
 $this->__fields[$fieldName]->modelError = $message;
 $this->__fields[$fieldName]->error = $message;
 }
 }
 public function callExtensionModelChanged() {
 return false;
 }
 public function enableDebug() {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->enableDebug();
 return $this;
 }
 public function query($query, array $prepareData = null) {
 if(empty($query)) return $this;
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->query($query, $prepareData);
 return $this;
 }
 public function getCollection() {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 return $this->__mdb->getCollection();
 }
 public function toArray() {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 return $this->__mdb->toArray();
 }
 public function toList() {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 return $this->__mdb->toList();
 }
 public function first() {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $data = $this->__mdb->first();
 if($data) {
 return $data;
 }
 return false;
 }
 public function find($id, string $column = 'id') {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $data = $this->__mdb->find($id, $column);
 if($data) {
 return $data;
 }
 return false;
 }
 public function findArray($id, string $column = 'id') {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 return $this->__mdb->findArray($id, $column);
 }
 public function count($column = '*', bool $isRelease = true) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 return $this->__mdb->count($column, $isRelease);
 }
 public function sum(string $column = 'id', bool $isRelease = true) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 return $this->__mdb->sum($column, $isRelease);
 }
 public function avg($column = 'id', bool $isRelease = true) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 return $this->__mdb->avg($column, $isRelease);
 }
 public function limit($limit, $offset = 0) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->limit($limit, $offset);
 return $this;
 }
 public function orderBy(string $column) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->orderBy($column);
 return $this;
 }
 public function orderByDesc($column) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->orderByDesc($column);
 return $this;
 }
 public function distinct() {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->distinct();
 return $this;
 }
 public function select() {
 $args = func_get_args();
 if(count($args) == 0) return $this;
 $cols = [];
 foreach($args as $arg) {
 if(is_array($arg)) {
 foreach($arg as $k => $v) {
 $cols[] = $v;
 }
 }
 else {
 $cols[] = $arg;
 }
 }
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->selectCol($cols);
 return $this;
 }
 public function where($column, $value = null, $operator = '=') {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->where($column, $value, $operator);
 return $this;
 }
 public function orWhere($column, $value = null, $operator = '=') {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->orWhere($column, $value, $operator);
 return $this;
 }
 public function whereNotNull(string $column, string $type = 'AND') {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->whereNotNull($column, $type);
 return $this;
 }
 public function whereNull(string $column, string $type = 'AND') {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->whereNull($column, $type);
 return $this;
 }
 public function whereIn(string $column, array $value) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->whereIn($column, $value);
 return $this;
 }
 public function orWhereIn(string $column, array $value) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->orWhereIn($column, $value);
 return $this;
 }
 public function like($column, $value = null) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->like($column, $value);
 return $this;
 }
 public function orLike($column, $value = null) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->orLike($column, $value);
 return $this;
 }
 public function beginGroup() {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->beginGroup();
 return $this;
 }
 public function endGroup() {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->endGroup();
 return $this;
 }
 public function join(string $table, string $localTableColumn, string $joinTableColumn) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->join($table, $localTableColumn, $joinTableColumn);
 return $this;
 }
 public function outerJoin(string $table, string $curTableColumn, string $joinTableColumn) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->outerJoin($table, $curTableColumn, $joinTableColumn);
 return $this;
 }
 public function leftJoin(string $table, string $curTableColumn, string $joinTableColumn) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->leftJoin($table, $curTableColumn, $joinTableColumn);
 return $this;
 }
 public function rightJoin(string $table, string $curTableColumn, string $joinTableColumn) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->rightJoin($table, $curTableColumn, $joinTableColumn);
 return $this;
 }
 public function insert($data = null) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 if($data == null) {
 if($this->__fields == null) $this->callRegisterField();
 $data = [];
 foreach($this->__fields as $k => $v) {
 if($k != 'id' && $v->value !== null && !$v->isCustomField) {
 $data[$k] = $this->convertFiledValueByType($v);
 }
 }
 }
 if($this->timestamp) {
 if(is_array($data)) {
 $data['created_at'] = new \DateTime();
 $data['updated_at'] = $data['created_at'];
 }
 else {
 $data->created_at = new \DateTime();
 $data->updated_at = $data->created_at;
 }
 }
 $id = $this->__mdb->insert($data);
 if($id) {
 $this->id = $id;
 $this->callModelExtensionOnChanged(true);
 }
 return $id;
 }
 public function inserts(array $data) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 return $this->__mdb->inserts($data);
 }
 public function update($data = null) {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 if($data == null) {
 if($this->__fields == null) throw new TException('Vui lòng dữ liệu cần update');
 $data = [];
 foreach($this->__fields as $k => $v) {
 if($v->hasSet && $v->value !== null && !$v->isCustomField) {
 $data[$k] = $this->convertFiledValueByType($v);
 }
 }
 }
 else if(is_object($data) && $data instanceof \TCMS\Model) {
 $data = [];
 foreach($this->__fields as $k => $v) {
 if($v->hasSet && $v->value !== null && !$v->isCustomField) {
 $data[$k] = $this->convertFiledValueByType($v);
 }
 }
 $data['id'] = $this->id ? $this->id : $this->_id;
 }
 if($this->timestamp) {
 if(is_array($data)) {
 $data['updated_at'] = new \DateTime();
 if(isset($data['created_at'])) unset($data['created_at']);
 }
 else {
 $data->updated_at = new \DateTime();
 if(isset($data->created_at)) unset($data->created_at);
 }
 }
 if($this->__mdb->update($data)) {
 $this->callModelExtensionOnChanged(false);
 return true;
 }
 return false;
 }
 public function save() {
 if($this->__fields) {
 if(isset($this->__fields['id']) && $this->__fields['id']->value) {
 return $this->update();
 }
 if(isset($this->__fields['_id']) && $this->__fields['_id']->value) {
 return $this->update();
 }
 return $this->insert();
 }
 else {
 throw new \TCMS\TException("Vui lòng truyền dữ liệu cho model trước khi gọi hàm save", 500);
 }
 }
 public function delete($id = null, $column = 'id') {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 if($this->__mdb->delete($id, $column)) {
 $this->callModelExtensionOnChanged(null, $id);
 return true;
 }
 return false;
 }
 public function deletes(array $ids, $column = 'id') {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 return $this->__mdb->deletes($ids, $column);
 }
 public function beginTransaction() {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->beginTransaction();
 return $this;
 }
 public function commitTransaction() {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->commitTransaction();
 return $this;
 }
 public function rollBackTransaction() {
 if($this->__mdb == null) {
 $this->__mdb = \Db::table($this->tableName);
 }
 $this->__mdb->rollBackTransaction();
 return $this;
 }
 public function callModelExtensionOnChanged($isInsert, $id = null) {
 $extensions = $this->callExtensionModelChanged();
 if($extensions) {
 foreach($extensions as $extKey) {
 $extension = load_extension($extKey);
 if($extension && $extension instanceof ModelExtension) {
 if($id) {
 $extension->onModelDeleted($this, $id);
 }
 else {
 $extension->onModelChanged($this, $isInsert);
 }
 }
 }
 }
 }
 private function convertFiledValueByType($field) {
 switch($field->type) {
 case \FIELD_DATA_TYPE::INTEGER: case \FIELD_DATA_TYPE::BIGINT: if(is_numeric($field->value)) return (int)$field->value;
 throw new TException('Dữ liệu ' . $field->name . ' phải là số', 501);
 case \FIELD_DATA_TYPE::PRICE: $price =preg_replace("/(\.| |\,)*/", "", trim($field->value));
 if(is_numeric($price) === false || $price < 0) {
 throw new TException('Dữ liệu ' . $field->name . ' phải là số tiền', 501);
 }
 return (int)$price;
 break;
 case \FIELD_DATA_TYPE::FILE: case \FIELD_DATA_TYPE::IMAGE: return $field->value . '';
 case \FIELD_DATA_TYPE::BOOLEAN: return (int)$field->value;
 case \FIELD_DATA_TYPE::STRING: case \FIELD_DATA_TYPE::TEXT: return htmlspecialchars($field->value . '');
 case \FIELD_DATA_TYPE::DATETIME: if($field->value instanceof \DateTime) {
 return $field->value;
 }
 return $field->value . '';
 case \FIELD_DATA_TYPE::DATE: if($field->value instanceof \DateTime) {
 return $field->value->format('Y-m-d') . ' 00:00:00';
 }
 return $field->value . '';
 case \FIELD_DATA_TYPE::TIME: if($field->value instanceof \DateTime) {
 return $field->value->format('H:i:s');
 }
 return $field->value . '';
 case \FIELD_DATA_TYPE::DOUBLE: return doubleval($field->value);
 default: return $field->value;
 }
 }
 public function exportJson() {
 if(!$this->__fields) $this->callRegisterField();
 return [ 'className' => get_class($this), 'tableName' => $this->tableName, 'fields' => $this->__fields ];
 }
 }
 class FieldModel {
 public $name = null;
 public $type = 0;
 public $displayName = null;
 public $extension = null;
 public $extensionOption = null;
 public $value = null;
 public $hasSet = false;
 public $defaultValue = null;
 public $required = false;
 public $showInListPage = true;
 public $showInCreatePage = true;
 public $showInUpdatePage = true;
 public $error = false;
 public $dataSource = null;
 public $displayMember = null;
 public $valueMember = null;
 public $defaultText = null;
 public $description = null;
 public $format = null;
 public $isCustomField = false;
 public $validate = null;
 public function __construct(string $name, int $type) {
 $this->name = $name;
 $this->type = $type;
 $this->displayName = $name;
 }
 public function setDataSource($data, string $displayMember, string $valueMember = 'id', string $defaultText = null) {
 if(is_string($data) || is_array($data)) {
 $this->dataSource = $data;
 $this->displayMember = $displayMember;
 $this->valueMember = $valueMember;
 $this->defaultText = $defaultText;
 }
 }
 }
 ?>