1 2/** 3 * This file is part of the Phalcon Framework. 4 * 5 * (c) Phalcon Team <team@phalcon.io> 6 * 7 * For the full copyright and license information, please view the LICENSE.txt 8 * file that was distributed with this source code. 9 */ 10 11namespace Phalcon\Mvc\Model\MetaData\Strategy; 12 13use Phalcon\Di\DiInterface; 14use Phalcon\Db\Adapter\AdapterInterface; 15use Phalcon\Db\Column; 16use Phalcon\Mvc\ModelInterface; 17use Phalcon\Mvc\Model\Exception; 18use Phalcon\Mvc\Model\MetaData; 19 20/** 21 * Phalcon\Mvc\Model\MetaData\Strategy\Introspection 22 * 23 * Queries the table meta-data in order to introspect the model's metadata 24 */ 25class Introspection implements StrategyInterface 26{ 27 /** 28 * Read the model's column map, this can't be inferred 29 */ 30 final public function getColumnMaps(<ModelInterface> model, <DiInterface> container) -> array 31 { 32 var orderedColumnMap, userColumnMap, reversedColumnMap, name, userName; 33 34 let orderedColumnMap = null; 35 let reversedColumnMap = null; 36 37 /** 38 * Check for a columnMap() method on the model 39 */ 40 if method_exists(model, "columnMap") { 41 let userColumnMap = model->{"columnMap"}(); 42 43 if unlikely typeof userColumnMap != "array" { 44 throw new Exception("columnMap() not returned an array"); 45 } 46 47 let reversedColumnMap = [], 48 orderedColumnMap = userColumnMap; 49 50 for name, userName in userColumnMap { 51 let reversedColumnMap[userName] = name; 52 } 53 } 54 55 /** 56 * Store the column map 57 */ 58 return [orderedColumnMap, reversedColumnMap]; 59 } 60 61 /** 62 * The meta-data is obtained by reading the column descriptions from the database information schema 63 */ 64 final public function getMetaData(<ModelInterface> model, <DiInterface> container) -> array 65 { 66 var schema, table, readConnection, columns, attributes, primaryKeys, 67 nonPrimaryKeys, numericTyped, notNull, fieldTypes, automaticDefault, 68 identityField, fieldBindTypes, defaultValues, column, fieldName, 69 defaultValue, emptyStringValues; 70 string completeTable; 71 72 let schema = model->getSchema(), 73 table = model->getSource(); 74 75 /** 76 * Check if the mapped table exists on the database 77 */ 78 let readConnection = model->getReadConnection(); 79 80 if unlikely !readConnection->tableExists(table, schema) { 81 if schema { 82 let completeTable = schema . "'.'" . table; 83 } else { 84 let completeTable = table; 85 } 86 87 /** 88 * The table not exists 89 */ 90 throw new Exception( 91 "Table '" . completeTable . "' doesn't exist in database when dumping meta-data for " . get_class(model) 92 ); 93 } 94 95 /** 96 * Try to describe the table 97 */ 98 let columns = readConnection->describeColumns(table, schema); 99 100 if unlikely !count(columns) { 101 if schema { 102 let completeTable = schema . "'.'" . table; 103 } else { 104 let completeTable = table; 105 } 106 107 /** 108 * The table not exists 109 */ 110 throw new Exception( 111 "Cannot obtain table columns for the mapped source '" . completeTable . "' used in model " . get_class(model) 112 ); 113 } 114 115 /** 116 * Initialize meta-data 117 */ 118 let attributes = []; 119 let primaryKeys = []; 120 let nonPrimaryKeys = []; 121 let numericTyped = []; 122 let notNull = []; 123 let fieldTypes = []; 124 let fieldBindTypes = []; 125 let automaticDefault = []; 126 let identityField = false; 127 let defaultValues = []; 128 let emptyStringValues = []; 129 130 for column in columns { 131 let fieldName = column->getName(), 132 attributes[] = fieldName; 133 134 /** 135 * To mark fields as primary keys 136 */ 137 if column->isPrimary() { 138 let primaryKeys[] = fieldName; 139 } else { 140 let nonPrimaryKeys[] = fieldName; 141 } 142 143 /** 144 * To mark fields as numeric 145 */ 146 if column->isNumeric() { 147 let numericTyped[fieldName] = true; 148 } 149 150 /** 151 * To mark fields as not null 152 */ 153 if column->isNotNull() { 154 let notNull[] = fieldName; 155 } 156 157 /** 158 * To mark fields as identity columns 159 */ 160 if column->isAutoIncrement() { 161 let identityField = fieldName; 162 } 163 164 /** 165 * To get the internal types 166 */ 167 let fieldTypes[fieldName] = column->getType(); 168 169 /** 170 * To mark how the fields must be escaped 171 */ 172 let fieldBindTypes[fieldName] = column->getBindType(); 173 174 /** 175 * If column has default value or column is nullable and default value is null 176 */ 177 let defaultValue = column->getDefault(); 178 179 if defaultValue !== null || !column->isNotNull() { 180 if !column->isAutoIncrement() { 181 let defaultValues[fieldName] = defaultValue; 182 } 183 } 184 } 185 186 /** 187 * Create an array using the MODELS_* constants as indexes 188 */ 189 return [ 190 MetaData::MODELS_ATTRIBUTES : attributes, 191 MetaData::MODELS_PRIMARY_KEY : primaryKeys, 192 MetaData::MODELS_NON_PRIMARY_KEY : nonPrimaryKeys, 193 MetaData::MODELS_NOT_NULL : notNull, 194 MetaData::MODELS_DATA_TYPES : fieldTypes, 195 MetaData::MODELS_DATA_TYPES_NUMERIC : numericTyped, 196 MetaData::MODELS_IDENTITY_COLUMN : identityField, 197 MetaData::MODELS_DATA_TYPES_BIND : fieldBindTypes, 198 MetaData::MODELS_AUTOMATIC_DEFAULT_INSERT : automaticDefault, 199 MetaData::MODELS_AUTOMATIC_DEFAULT_UPDATE : automaticDefault, 200 MetaData::MODELS_DEFAULT_VALUES : defaultValues, 201 MetaData::MODELS_EMPTY_STRING_VALUES : emptyStringValues 202 ]; 203 } 204} 205