public function readModel($model){ // Construct a general object so I can use the getKeySchemas method inside. $ref = new ReflectionClass($model); /** @var Model $obj */ $obj = $ref->newInstanceWithoutConstructor(); $schema = $obj->getKeySchemas(); $indexes = $ref->getMethod('GetIndexes')->invoke(null); // RESET! $this->indexes = []; $this->definitions = []; $this->order = []; foreach($schema as $name => $def){ if($def['type'] == Model::ATT_TYPE_ALIAS){ $this->aliases[$name] = $def['alias']; } else{ $def['name'] = $name; $column = \Core\Datamodel\Columns\SchemaColumn::FactoryFromSchema($def); $this->definitions[$name] = $column; $this->order[] = $name; } } foreach($indexes as $key => $dat){ if(!is_array($dat)){ // Models can be defined with a single element for its index. // This is an acceptable shorthand standard, but the lower level utilities // are expecting an array. $this->indexes[$key] = array($dat); } else{ $this->indexes[$key] = $dat; } } }
/** * Create a new instance of the requested model. * * @param null $key * * @throws Exception */ public function __construct($key = null) { // If an extending model set links in that constructor, reformat them to ensure they're set correctly. if(sizeof($this->_linked)){ $clone = $this->_linked; $this->_linked = []; foreach($clone as $model => $dat){ if(strrpos($model, 'Model') !== strlen($model) - 5){ // All models need to end with Model, (allow shorthand definitions to ignore that though) $model .= 'Model'; } $dat['model'] = $model; $this->_linked[] = $dat; } } // Update the _data array based on the schema. $s = self::GetSchema(); $this->_columns = []; $this->_aliases = []; foreach ($s as $k => $sdat) { if($sdat['type'] == Model::ATT_TYPE_ALIAS){ $this->_aliases[$k] = $sdat['alias']; } else{ $this->_columns[$k] = \Core\Datamodel\Columns\SchemaColumn::FactoryFromSchema($sdat); $this->_columns[$k]->field = $k; $this->_columns[$k]->parent = $this; } if(isset($sdat['link'])){ // If the link is requested on this property, populate the linked array for the corresponding model! if(is_array($sdat['link'])){ if(!isset($sdat['link']['model'])){ throw new Exception('Required attribute [model] not provided on link [' . $k . '] of model [' . get_class($this) . ']'); } if(!isset($sdat['link']['type'])){ throw new Exception('Required attribute [type] not provided on link [' . $k . '] of model [' . get_class($this) . ']'); } $linkmodel = $sdat['link']['model']; $linktype = isset($sdat['link']['type']) ? $sdat['link']['type'] : Model::LINK_HASONE; $linkon = isset($sdat['link']['on']) ? $sdat['link']['on'] : 'id'; } else{ // Allow the short-hand version to be used too. // This will setup a 1-to-1 relationship. $linkmodel = $sdat['link']; $linktype = Model::LINK_HASONE; $linkon = 'id'; // ... erm yeah... hopefully this is it! } if(strrpos($linkmodel, 'Model') !== strlen($linkmodel) - 5){ // All models need to end with Model, (allow shorthand definitions to ignore that though) $linkmodel .= 'Model'; } // And populate the linked array with this link data. $this->_linked[] = [ 'key' => $k, 'model' => $linkmodel, 'on' => is_array($linkon) ? $linkon : [$linkon => $k], 'link' => $linktype, ]; } } // Check the index (primary), and the incoming data. If it matches, load it up! $i = self::GetIndexes(); $pri = (isset($i['primary'])) ? $i['primary'] : false; if($pri && !is_array($pri)) $pri = [$pri]; if ($pri && func_num_args() == sizeof($i['primary'])) { foreach ($pri as $idx => $k) { /** @var \Core\Datamodel\Columns\SchemaColumn $c */ $c = $this->_columns[$k]; $c->setValueFromApp(func_get_arg($idx)); } } if($key !== null){ $this->load(); } }
/** * Get the resolved column schema from a row returned by the mysql command SHOW FULL COLUMNS. * * @param array $def * * @return \Core\Datamodel\Columns\SchemaColumn */ private function _getColumnDefinition($def){ // First thing will be to generate a valid Schema array based on the mysqli data. $schema = [ 'type' => false, 'name' => false, 'required' => false, 'default' => false, 'null' => false, 'comment' => false, 'encoding' => false, 'maxlength' => false, 'precision' => false, 'options' => false, 'autoinc' => false, ]; // The simple ones, these are 1-to-1 translations. $schema['name'] = $def['Field']; $schema['comment'] = $def['Comment']; $schema['autoinc'] = ($def['Extra'] == 'auto_increment'); $schema['null'] = ($def['Null'] == 'YES'); $schema['default'] = $def['Default']; $schema['required'] = (($def['Key'] == 'PRI') || ($def['Null'] == 'NO' && $def['Key'] == 'UNI')); if(!$schema['null'] && $schema['default'] === null){ // TEXT types will sometimes incorrectly report that their default value is NULL // when NULL is disabled. // This is because those fields cannot properly set a 'None' default value without returning NULL. $schema['default'] = false; } switch($def['Type']){ case "enum('0','1')": case "enum('1','0')": $schema['type'] = \Model::ATT_TYPE_BOOL; break; case 'text': case 'longtext': $schema['type'] = \Model::ATT_TYPE_TEXT; break; case 'datetime': $schema['type'] = \Model::ATT_TYPE_ISO_8601_DATETIME; break; case 'timestamp': $schema['type'] = \Model::ATT_TYPE_MYSQL_TIMESTAMP; break; case 'date': $schema['type'] = \Model::ATT_TYPE_ISO_8601_DATE; break; case 'blob': case 'mediumblob': case 'longblob': $schema['type'] = \Model::ATT_TYPE_DATA; break; case 'float': $schema['type'] = \Model::ATT_TYPE_FLOAT; break; } // None of the above cases matched? Maybe it's a more complex if statement. if($schema['type'] === false){ if(strpos($def['Type'], 'varchar(') !== false){ // This field is a variable character field, "VARCHAR(123)" $schema['type'] = \Model::ATT_TYPE_STRING; $schema['maxlength'] = (int)substr($def['Type'], 8, -1); } elseif(strpos($def['Type'], 'enum(') !== false){ $schema['type'] = \Model::ATT_TYPE_ENUM; $schema['options'] = eval('return array(' . substr($def['Type'], 5, -1) . ');'); } elseif(strpos($def['Type'], 'int(') !== false && $def['Field'] == 'updated'){ $schema['type'] = \Model::ATT_TYPE_UPDATED; $schema['maxlength'] = (int)substr($def['Type'], 4, -1); } elseif(strpos($def['Type'], 'int(') !== false && $def['Field'] == 'created'){ $schema['type'] = \Model::ATT_TYPE_CREATED; $schema['maxlength'] = (int)substr($def['Type'], 4, -1); } elseif(strpos($def['Type'], 'int(') !== false && $def['Field'] == 'deleted'){ $schema['type'] = \Model::ATT_TYPE_DELETED; $schema['maxlength'] = (int)substr($def['Type'], 4, -1); } elseif(strpos($def['Type'], 'int(') !== false && $def['Field'] == 'site'){ $schema['type'] = \Model::ATT_TYPE_SITE; $schema['maxlength'] = (int)substr($def['Type'], 4, -1); } elseif( strpos($def['Type'], 'int(') !== false && isset($this->indexes['primary']) && in_array($def['Field'], $this->indexes['primary']) && sizeof($this->indexes['primary']) == 1 ){ $schema['type'] = \Model::ATT_TYPE_ID; $schema['maxlength'] = (int)substr($def['Type'], 4, -1); } elseif(strpos($def['Type'], 'int(') !== false){ $schema['type'] = \Model::ATT_TYPE_INT; $schema['maxlength'] = (int)substr($def['Type'], 4, -1); } elseif(strpos($def['Type'], 'decimal(') !== false){ $schema['type'] = \Model::ATT_TYPE_FLOAT; $schema['precision'] = substr($def['Type'], 8, -1); } // Version < 5.0.1 of UUIDs elseif( strpos($def['Type'], 'char(') !== false && strpos($def['Type'], '32') !== false && isset($this->indexes['primary']) && in_array($def['Field'], $this->indexes['primary']) ){ $schema['type'] = \Model::ATT_TYPE_UUID; $schema['maxlength'] = 32; } elseif( strpos($def['Type'], 'char(') !== false && strpos($def['Type'], '32') !== false ){ $schema['type'] = \Model::ATT_TYPE_UUID_FK; $schema['maxlength'] = 32; } // Version < 5.0.1 of UUIDs elseif( strpos($def['Type'], 'char(') !== false && strpos($def['Type'], '21') !== false && isset($this->indexes['primary']) && in_array($def['Field'], $this->indexes['primary']) ){ $schema['type'] = \Model::ATT_TYPE_UUID; $schema['maxlength'] = 21; } elseif( strpos($def['Type'], 'char(') !== false && strpos($def['Type'], '21') !== false ){ $schema['type'] = \Model::ATT_TYPE_UUID_FK; $schema['maxlength'] = 21; } else{ // Well hmm... $schema['type'] = 'text'; } } // Handle the encoding type if($def['Collation'] !== null){ $schema['encoding'] = substr($def['Collation'], 0, strpos($def['Collation'], '_')); } // Now that everything has been translated to a standard array... return \Core\Datamodel\Columns\SchemaColumn::FactoryFromSchema($schema); }