Beispiel #1
0
	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;
			}
		}
	}
Beispiel #2
0
	/**
	 * 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);
	}