public static function create(DeclareSchema $schema) { $schemaProxyClass = $schema->getSchemaProxyClass(); $cTemplate = new TemplateClassFile($schemaProxyClass, array('template_dirs' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'Templates', 'template' => 'Schema.php.twig')); $schemaClass = get_class($schema); $schemaArray = $schema->export(); $cTemplate->addConst('schema_class', $schemaClass); $cTemplate->addConst('collection_class', $schemaArray['collection_class']); $cTemplate->addConst('model_class', $schemaArray['model_class']); $cTemplate->addConst('model_name', $schema->getModelName()); $cTemplate->addConst('model_namespace', $schema->getNamespace()); $cTemplate->addConst('primary_key', $schemaArray['primary_key']); $cTemplate->addConst('table', $schema->getTable()); $cTemplate->addConst('label', $schema->getLabel()); // export column names excluding virtual columns $cTemplate->addStaticVar('column_names', $schema->getColumnNames()); $cTemplate->addStaticVar('column_hash', array_fill_keys($schema->getColumnNames(), 1)); $cTemplate->addStaticVar('mixin_classes', array_reverse($schema->getMixinSchemaClasses())); // export column names including virutal columns $cTemplate->addStaticVar('column_names_include_virtual', $schema->getColumnNames(true)); $cTemplate->schema = $schema; $cTemplate->schema_data = $schemaArray; $cTemplate->now = new DateTime(); // Aggregate basic translations from labels $msgIds = $schema->getMsgIds(); $cTemplate->setMsgIds($msgIds); return $cTemplate; }
public function reverseTableSchema($table) { $tableDef = $this->parseTableSql($table); $schema = new DeclareSchema(); $schema->columnNames = $schema->columns = array(); $schema->table($table); foreach ($tableDef->columns as $columnDef) { $name = $columnDef->name; $column = $schema->column($name); if (!isset($columnDef->type)) { throw new LogicException("Missing column type definition on column {$table}.{$name}."); } $type = $columnDef->type; $typeInfo = TypeInfoParser::parseTypeInfo($type, $this->driver); $column->type($type); if (isset($columnDef->length)) { $column->length($columnDef->length); } if (isset($columnDef->decimals)) { $column->decimals($columnDef->decimals); } $isa = $this->typenameToIsa($type); $column->isa($isa); if (isset($columnDef->notNull) && $columnDef->notNull !== null) { if ($columnDef->notNull) { $column->notNull(); } else { $column->null(); } } if (isset($columnDef->primary)) { $column->primary(true); $schema->primaryKey = $name; if (isset($columnDef->autoIncrement)) { $column->autoIncrement(true); } } else { if (isset($columnDef->unique)) { $column->unique(true); } } if (isset($columnDef->default)) { $default = $columnDef->default; if (is_scalar($default)) { $column->default($default); } else { if ($default instanceof Token && $default->type == 'literal') { $column->default(new Raw($default->val)); } else { throw new Exception('Incorrect literal token'); } } } } return $schema; }
protected function expandSchemaDependency(DeclareSchema $schema) { $expands = array(); $refs = $schema->getReferenceSchemas(); foreach ($refs as $refClass => $v) { // $refSchema = new $refClass; // $expand = array_merge($expand, $this->expandSchemaDependency($refSchema), array($refClass)); $expands[] = $refClass; } $expands[] = get_class($schema); return array_unique($expands); }
/** * This method checks the exising schema file and the generated class file mtime. * If the schema file is newer or the forceUpdate flag is specified, then * the generated class files should be updated. * * @param ClassTemplate\ClassFile $cTemplate * @param DeclareSchema $schema */ protected function updateClassFile(ClassFile $cTemplate, DeclareSchema $schema, $canOverwrite = false) { // always update the proxy schema file $classFilePath = $schema->getRelatedClassPath($cTemplate->getShortClassName()); // classes not Model/Collection class are overwriteable if (file_exists($classFilePath)) { if ($canOverwrite && ($schema->isNewerThanFile($classFilePath) || $this->forceUpdate)) { $this->writeClassTemplateToPath($cTemplate, $classFilePath); return [$cTemplate->getClassName(), $classFilePath]; } } else { if ($this->writeClassTemplateToPath($cTemplate, $classFilePath)) { return [$cTemplate->getClassName(), $classFilePath]; } } }
public function testBasicComparison() { $before = new DeclareSchema(); $before->column('same'); $before->column('changed')->varchar(20); $before->column('removed')->boolean(); $after = new DeclareSchema(); $after->column('same'); $after->column('changed')->varchar(30); $after->column('added')->varchar(10); $comparator = new Comparator(); $diffs = $comparator->compare($before, $after); foreach ($diffs as $diff) { $this->assertInstanceOf('LazyRecord\\Schema\\Comparator\\ColumnDiff', $diff); } $firstDiff = $diffs[0]; $this->assertEquals('changed', $firstDiff->name); $this->assertEquals('M', $firstDiff->flag); $secondDiff = $diffs[1]; $this->assertEquals('removed', $secondDiff->name); $this->assertEquals('D', $secondDiff->flag); $thirdDiff = $diffs[2]; $this->assertEquals('added', $thirdDiff->name); $this->assertEquals('A', $thirdDiff->flag); /** * this can't work with posix (color output) */ # $this->expectOutputRegex('/^= same/sm'); # $this->expectOutputRegex('/^= changed/sm'); # $this->expectOutputRegex('/^- removed/sm'); /* ob_start(); $printer->output(); $content = ob_get_contents(); ob_clean(); like('#removed#',$content); like('#added#',$content); */ return $diffs; }
public static function create(DeclareSchema $schema, $baseCollectionClass) { $cTemplate = new ClassFile($schema->getBaseCollectionClass()); $cTemplate->addConsts(array('SCHEMA_PROXY_CLASS' => $schema->getSchemaProxyClass(), 'MODEL_CLASS' => $schema->getModelClass(), 'TABLE' => $schema->getTable(), 'READ_SOURCE_ID' => $schema->getReadSourceId(), 'WRITE_SOURCE_ID' => $schema->getWriteSourceId(), 'PRIMARY_KEY' => $schema->primaryKey)); if ($traitClasses = $schema->getCollectionTraitClasses()) { foreach ($traitClasses as $traitClass) { $cTemplate->useTrait($traitClass); } } $cTemplate->extendClass('\\' . $baseCollectionClass); // interfaces if ($ifs = $schema->getCollectionInterfaces()) { foreach ($ifs as $iface) { $cTemplate->implementClass($iface); } } return $cTemplate; }
public static function create(DeclareSchema $schema, $baseCollectionClass) { $cTemplate = new ClassFile($schema->getBaseCollectionClass()); $cTemplate->addConsts(array('schema_proxy_class' => $schema->getSchemaProxyClass(), 'model_class' => $schema->getModelClass(), 'table' => $schema->getTable(), 'read_source_id' => $schema->getReadSourceId(), 'write_source_id' => $schema->getWriteSourceId())); if ($traitClasses = $schema->getCollectionTraitClasses()) { foreach ($traitClasses as $traitClass) { $cTemplate->useTrait($traitClass); } } $cTemplate->extendClass('\\' . $baseCollectionClass); // interfaces if ($ifs = $schema->getCollectionInterfaces()) { foreach ($ifs as $iface) { $cTemplate->implementClass($iface); } } return $cTemplate; }
public function injectModelSchema(DeclareSchema $schema) { $model = $schema->getModel(); $injection = new ClassInjection($model); $injection->read(); $injection->removeContent(); $injection->appendContent("\t" . new ClassConst('schema_proxy_class', ltrim($schema->getSchemaProxyClass(), '\\'))); $injection->appendContent("\t" . new ClassConst('collection_class', ltrim($schema->getCollectionClass(), '\\'))); $injection->appendContent("\t" . new ClassConst('model_class', ltrim($schema->getModelClass(), '\\'))); $injection->appendContent("\t" . new ClassConst('table', ltrim($schema->getTable(), '\\'))); $injection->write(); $refl = new ReflectionObject($model); return array($schema->getModelClass() => $refl->getFilename()); }
public static function create(DeclareSchema $schema) { $cTemplate = new ClassFile($schema->getCollectionClass()); $cTemplate->extendClass('\\' . $schema->getBaseCollectionClass()); return $cTemplate; }
/** * This method checks the exising schema file and the generated class file mtime. * If the schema file is newer or the forceUpdate flag is specified, then * the generated class files should be updated. * * @param ClassTemplate\ClassFile $cTemplate * @param DeclareSchema $schema */ protected function updateClassFile(ClassFile $cTemplate, DeclareSchema $schema, $overwrite = false) { // always update the proxy schema file $classFilePath = $schema->getRelatedClassPath($cTemplate->getShortClassName()); // classes not Model/Collection class are overwriteable if (!file_exists($classFilePath)) { $this->writeClassTemplateToPath($cTemplate, $classFilePath, $overwrite); $this->logger->info2(" - Creating {$classFilePath}"); return array($cTemplate->getClassName(), $classFilePath); } else { if ($schema->isNewerThanFile($classFilePath) || $this->forceUpdate || $overwrite) { if ($this->writeClassTemplateToPath($cTemplate, $classFilePath, $overwrite)) { $this->logger->info2(" - Updating {$classFilePath}"); return array($cTemplate->getClassName(), $classFilePath); } else { $this->logger->info2(" - Skipping {$classFilePath}"); } } else { $this->logger->info2(" - Skipping {$classFilePath}"); } } }
public function reverseTableSchema($table, $referenceSchema = null) { $stm = $this->connection->query("SHOW COLUMNS FROM {$table}"); $schema = new DeclareSchema(); $schema->columnNames = $schema->columns = array(); $schema->table($table); $rows = $stm->fetchAll(); foreach ($rows as $row) { $type = $row['Type']; $typeInfo = TypeInfoParser::parseTypeInfo($type, $this->driver); $isa = $typeInfo->isa; $column = $schema->column($row['Field']); $column->type($typeInfo->type); if ($typeInfo->length) { $column->length($typeInfo->length); } if ($typeInfo->precision) { $column->decimals($typeInfo->precision); } if ($typeInfo->isa) { $column->isa($typeInfo->isa); } if ($typeInfo->unsigned) { $column->unsigned(); } if ($typeInfo->enum) { $column->enum($typeInfo->enum); } elseif ($typeInfo->set) { $column->set($typeInfo->set); } switch ($row['Null']) { case 'NO': // timestamp is set to Null=No by default. // However, it's possible that user didn't set notNull in the schema, // we should skip the check in comparator. if ($referenceSchema && isset($row['Field']) && $referenceSchema->getColumn($row['Field']) && !$referenceSchema->getColumn($row['Field'])->notNull && (strtolower($typeInfo->type) === 'timestamp' || isset($row['Key']) && $row['Key'] === 'PRI')) { } else { $column->notNull(true); } break; case 'YES': $column->null(); break; } switch ($row['Key']) { case 'PRI': $column->primary(true); $schema->primaryKey = $row['Field']; break; // If Key is MUL, multiple occurrences of a given value are // permitted within the column. The column is the first // column of a nonunique index or a unique-valued index // that can contain NULL values. // If Key is MUL, multiple occurrences of a given value are // permitted within the column. The column is the first // column of a nonunique index or a unique-valued index // that can contain NULL values. case 'MUL': break; case 'UNI': $column->unique(true); break; } // Parse information from the Extra field // @see https://dev.mysql.com/doc/refman/5.7/en/show-columns.html $extraAttributes = []; if (strtolower($row['Extra']) == 'auto_increment') { $column->autoIncrement(); } elseif (preg_match('/ON UPDATE (\\w+)/i', $row['Extra'], $matches)) { /* To specify automatic properties, use the DEFAULT CURRENT_TIMESTAMP and ON UPDATE CURRENT_TIMESTAMP clauses in column definitions. The order of the clauses does not matter. If both are present in a column definition, either can occur first. Any of the synonyms for CURRENT_TIMESTAMP have the same meaning as CURRENT_TIMESTAMP. These are CURRENT_TIMESTAMP(), NOW(), LOCALTIME, LOCALTIME(), LOCALTIMESTAMP, and LOCALTIMESTAMP(). */ $extraAttributes['OnUpdate' . Inflector::getInstance()->camelize(strtolower($matches[1]))] = true; } elseif (preg_match('/VIRTUAL GENERATED/i', $row['Extra'])) { $extraAttributes['VirtualGenerated'] = true; } elseif (preg_match('/VIRTUAL STORED/i', $row['Extra'])) { $extraAttributes['VirtualStored'] = true; } // The default value returned from MySQL is string, we need the // type information to cast them to PHP Scalar or other // corresponding type if (null !== $row['Default']) { $default = $row['Default']; if ($typeInfo->type == 'boolean') { if ($default == '1') { $column->default(true); } elseif ($default == '0') { $column->default(false); } } elseif ($typeInfo->isa == 'int') { $column->default(intval($default)); } elseif ($typeInfo->isa == 'double') { $column->default(doubleval($default)); } elseif ($typeInfo->isa == 'float') { $column->default(floatval($default)); } elseif ($typeInfo->isa == 'str') { $column->default($default); } elseif ($typeInfo->type == 'timestamp') { // for mysql, timestamp fields' default value is // 'current_timestamp' and 'on update current_timestamp' // when the two conditions are matched, we need to elimante // the default value just as what we've defined in schema. if (isset($extraAttributes['OnUpdateCurrentTimestamp']) && strtolower($default) == 'current_timestamp') { // Don't set default value } elseif (strtolower($default) == 'current_timestamp') { $column->default(new Raw($default)); } elseif (is_numeric($default)) { $column->default(intval($default)); } } elseif ($typeInfo->type == 'datetime') { // basically, CURRENT_TIMESTAMP, transaction_timestamp() // and now() do exactly the same. CURRENT_TIMESTAMP is a // syntactical oddity for a function, having no trailing // pair of parentheses. That's according to the SQL // standard. // // @see http://dba.stackexchange.com/questions/63548/difference-between-now-and-current-timestamp if (strtolower($default) == 'current_timestamp') { // XXX: NOW() will be converted into current_timestamp $column->default(new Raw($default)); } } } } return $schema; }
public static function create(DeclareSchema $schema, $baseClass) { $cTemplate = new ClassFile($schema->getBaseModelClass()); $cTemplate->addConsts(array('schema_proxy_class' => $schema->getSchemaProxyClass(), 'collection_class' => $schema->getCollectionClass(), 'model_class' => $schema->getModelClass(), 'table' => $schema->getTable(), 'read_source_id' => $schema->getReadSourceId(), 'write_source_id' => $schema->getWriteSourceId(), 'primary_key' => $schema->primaryKey)); $cTemplate->addMethod('public', 'getSchema', [], ['if ($this->_schema) {', ' return $this->_schema;', '}', 'return $this->_schema = \\LazyRecord\\Schema\\SchemaLoader::load(' . var_export($schema->getSchemaProxyClass(), true) . ');']); $cTemplate->addStaticVar('column_names', $schema->getColumnNames()); $cTemplate->addStaticVar('column_hash', array_fill_keys($schema->getColumnNames(), 1)); $cTemplate->addStaticVar('mixin_classes', array_reverse($schema->getMixinSchemaClasses())); if ($traitClasses = $schema->getModelTraitClasses()) { foreach ($traitClasses as $traitClass) { $cTemplate->useTrait($traitClass); } } $cTemplate->extendClass('\\' . $baseClass); // interfaces if ($ifs = $schema->getModelInterfaces()) { foreach ($ifs as $iface) { $cTemplate->implementClass($iface); } } // Create column accessor if ($schema->enableColumnAccessors) { foreach ($schema->getColumnNames() as $columnName) { $accessorMethodName = 'get' . ucfirst(Inflector::camelize($columnName)); $cTemplate->addMethod('public', $accessorMethodName, [], ['if (isset($this->_data[' . var_export($columnName, true) . '])) {', ' return $this->_data[' . var_export($columnName, true) . '];', '}']); } } return $cTemplate; }
public function reverseTableSchema($table) { $stm = $this->connection->query("SHOW COLUMNS FROM {$table}"); $schema = new DeclareSchema(); $schema->columnNames = $schema->columns = array(); $schema->table($table); $rows = $stm->fetchAll(); foreach ($rows as $row) { $type = $row['Type']; $typeInfo = TypeInfoParser::parseTypeInfo($type, $this->driver); $isa = $typeInfo->isa; $column = $schema->column($row['Field']); $column->type($typeInfo->type); if ($typeInfo->length) { $column->length($typeInfo->length); } if ($typeInfo->precision) { $column->decimals($typeInfo->precision); } if ($typeInfo->isa) { $column->isa($typeInfo->isa); } if ($typeInfo->unsigned) { $column->unsigned(); } if ($typeInfo->enum) { $column->enum($typeInfo->enum); } else { if ($typeInfo->set) { $column->set($typeInfo->set); } } if ($row['Null'] == 'NO') { $column->notNull(); } else { $column->null(); } switch ($row['Key']) { case 'PRI': $column->primary(true); $schema->primaryKey = $row['Field']; break; // If Key is MUL, multiple occurrences of a given value are // permitted within the column. The column is the first // column of a nonunique index or a unique-valued index // that can contain NULL values. // If Key is MUL, multiple occurrences of a given value are // permitted within the column. The column is the first // column of a nonunique index or a unique-valued index // that can contain NULL values. case 'MUL': break; case 'UNI': $column->unique(true); break; } // Parse information from the Extra field // @see https://dev.mysql.com/doc/refman/5.7/en/show-columns.html $extraAttributes = []; if (strtolower($row['Extra']) == 'auto_increment') { $column->autoIncrement(); } else { if (preg_match('/ON UPDATE CURRENT_TIMESTAMP/i', $row['Extra'])) { $extraAttributes['OnUpdateCurrentTimestamp'] = true; } else { if (preg_match('/VIRTUAL GENERATED/i', $row['Extra'])) { $extraAttributes['VirtualGenerated'] = true; } else { if (preg_match('/VIRTUAL STORED/i', $row['Extra'])) { $extraAttributes['VirtualStored'] = true; } } } } // The default value returned from MySQL is string, we need the // type information to cast them to PHP Scalar or other // corresponding type if (NULL !== $row['Default']) { $default = $row['Default']; if ($typeInfo->type == 'boolean') { if ($default == '1') { $column->default(true); } else { if ($default == '0') { $column->default(false); } } } else { if ($typeInfo->isa == 'int') { $column->default(intval($default)); } else { if ($typeInfo->isa == 'double') { $column->default(doubleval($default)); } else { if ($typeInfo->isa == 'float') { $column->default(floatval($default)); } else { if ($typeInfo->isa == 'str') { $column->default($default); } else { if ($typeInfo->type == 'timestamp') { // for mysql, timestamp fields' default value is // 'current_timestamp' and 'on update current_timestamp' // when the two conditions are matched, we need to elimante // the default value just as what we've defined in schema. if (isset($extraAttributes['OnUpdateCurrentTimestamp']) && strtolower($default) == 'current_timestamp') { // Do nothing } else { if (strtolower($default) == 'current_timestamp') { $column->default(new Raw($default)); } else { if (is_numeric($default)) { $column->default(intval($default)); } } } } } } } } } } } return $schema; }
public static function create(DeclareSchema $schema, $baseClass) { $cTemplate = new ClassFile($schema->getBaseModelClass()); $cTemplate->useClass('LazyRecord\\Schema\\SchemaLoader'); $cTemplate->useClass('LazyRecord\\Result'); $cTemplate->useClass('SQLBuilder\\Bind'); $cTemplate->useClass('SQLBuilder\\ArgumentArray'); $cTemplate->useClass('PDO'); $cTemplate->useClass('SQLBuilder\\Universal\\Query\\InsertQuery'); $cTemplate->addConsts(array('SCHEMA_PROXY_CLASS' => $schema->getSchemaProxyClass(), 'COLLECTION_CLASS' => $schema->getCollectionClass(), 'MODEL_CLASS' => $schema->getModelClass(), 'TABLE' => $schema->getTable(), 'READ_SOURCE_ID' => $schema->getReadSourceId(), 'WRITE_SOURCE_ID' => $schema->getWriteSourceId(), 'PRIMARY_KEY' => $schema->primaryKey)); $cTemplate->addProtectedProperty('table', $schema->getTable()); $cTemplate->addPublicProperty('readSourceId', $schema->getReadSourceId() ?: 'default'); $cTemplate->addPublicProperty('writeSourceId', $schema->getWriteSourceId() ?: 'default'); $cTemplate->addMethod('public', 'getSchema', [], ['if ($this->_schema) {', ' return $this->_schema;', '}', 'return $this->_schema = SchemaLoader::load(' . var_export($schema->getSchemaProxyClass(), true) . ');']); $cTemplate->addStaticVar('column_names', $schema->getColumnNames()); $cTemplate->addStaticVar('column_hash', array_fill_keys($schema->getColumnNames(), 1)); $cTemplate->addStaticVar('mixin_classes', array_reverse($schema->getMixinSchemaClasses())); if ($traitClasses = $schema->getModelTraitClasses()) { foreach ($traitClasses as $traitClass) { $cTemplate->useTrait($traitClass); } } $schemaReflection = new ReflectionClass($schema); $schemaDocComment = $schemaReflection->getDocComment(); // TODO: apply settings from schema... $codegenSettings = []; preg_match_all('/@codegen (\\w+)(?:\\s*=\\s*(\\S+))?$/m', $schemaDocComment, $allMatches); for ($i = 0; $i < count($allMatches[0]); $i++) { $key = $allMatches[1][$i]; $value = $allMatches[2][$i]; if ($value === "") { $value = true; } else { if (strcasecmp($value, "true") == 0 || strcasecmp($value, "false") == 0) { $value = filter_var($value, FILTER_VALIDATE_BOOLEAN); } else { if (preg_match('/^\\d+$/', $value)) { $value = intval($value); } } } $codegenSettings[$key] = $value; } /* if ($codegenSettings['validateColumn']) { $codegenSettings['handleValidationError'] = true; } */ if (!empty($codegenSettings)) { $reflectionModel = new ReflectionClass('LazyRecord\\BaseModel'); $createMethod = $reflectionModel->getMethod('create'); $methodFile = $createMethod->getFilename(); $startLine = $createMethod->getStartLine(); $endLine = $createMethod->getEndLine(); $lines = file($methodFile); $methodLines = array_slice($lines, $startLine + 1, $endLine - $startLine - 2); // exclude '{', '}' $blockRanges = array(); $blockLines = array(); // parse code blocks for ($i = 0; $i < count($methodLines); $i++) { $line = rtrim($methodLines[$i]); if (preg_match('/@codegenBlock (\\w+)/', $line, $matches)) { $blockId = $matches[1]; for ($j = $i; $j < count($methodLines); $j++) { $line = rtrim($methodLines[$j]); $blockLines[$blockId][] = $line; if (preg_match('/@codegenBlockEnd/', $line)) { $blockRanges[$blockId] = [$i, $j]; $i = $j; break; } } } } $overrideCreateMethod = $cTemplate->addMethod('public', 'create', ['array $args', 'array $options = array()']); $overrideBlock = $overrideCreateMethod->getBlock(); for ($i = 0; $i < count($methodLines); $i++) { $line = rtrim($methodLines[$i]); if (preg_match('/@codegenBlock (\\w+)/', $line, $matches)) { $blockId = $matches[1]; if (isset($codegenSettings[$matches[1]]) && isset($blockLines[$blockId])) { if ($codegenSettings[$matches[1]]) { $overrideBlock[] = $blockLines[$blockId]; list($startLine, $endLine) = $blockRanges[$blockId]; $i = $endLine; continue; } else { list($startLine, $endLine) = $blockRanges[$blockId]; $i = $endLine; continue; } } } $overrideBlock[] = $line; } } // TODO: refacory this into factory method // Generate findByPrimaryKey SQL query $arguments = new ArgumentArray(); $findByPrimaryKeyQuery = new SelectQuery(); $findByPrimaryKeyQuery->from($schema->getTable()); $primaryKey = $schema->primaryKey; $readFrom = $schema->getReadSourceId(); $readConnection = ConnectionManager::getInstance()->getConnection($readFrom); $readQueryDriver = $readConnection->createQueryDriver(); $primaryKeyColumn = $schema->getColumn($primaryKey); $findByPrimaryKeyQuery->select('*')->where()->equal($primaryKey, new Bind($primaryKey)); $findByPrimaryKeyQuery->limit(1); $findByPrimaryKeySql = $findByPrimaryKeyQuery->toSql($readQueryDriver, $arguments); $cTemplate->addConst('FIND_BY_PRIMARY_KEY_SQL', $findByPrimaryKeySql); foreach ($schema->getColumns() as $column) { if (!$column->findable) { continue; } $columnName = $column->name; $findMethodName = 'findBy' . ucfirst(Inflector::camelize($columnName)); $findMethod = $cTemplate->addMethod('public', $findMethodName, ['$value']); $block = $findMethod->block; $arguments = new ArgumentArray(); $findByColumnQuery = new SelectQuery(); $findByColumnQuery->from($schema->getTable()); $columnName = $column->name; $readFrom = $schema->getReadSourceId(); $findByColumnQuery->select('*')->where()->equal($columnName, new Bind($columnName)); $findByColumnQuery->limit(1); $findByColumnSql = $findByColumnQuery->toSql($readQueryDriver, $arguments); $block[] = '$conn = $this->getReadConnection();'; $block[] = 'if (!isset($this->_preparedFindStms[' . var_export($columnName, true) . '])) {'; $block[] = ' $this->_preparedFindStms[' . var_export($columnName, true) . '] = $conn->prepare(' . var_export($findByColumnSql, true) . ');'; $block[] = '}'; $block[] = '$this->_preparedFindStms[' . var_export($columnName, true) . ']->execute([' . var_export(":{$columnName}", true) . ' => $value ]);'; $block[] = 'if (false === ($this->_data = $this->_preparedFindStms[' . var_export($columnName, true) . ']->fetch(PDO::FETCH_ASSOC)) ) {'; $block[] = ' return $this->reportError("Record not found", ['; $block[] = ' "sql" => ' . var_export($findByColumnSql, true) . ','; $block[] = ' ]);'; $block[] = '}'; $block[] = '$this->_preparedFindStms[' . var_export($columnName, true) . ']->closeCursor();'; $block[] = 'return $this->reportSuccess( "Data loaded", array( '; $block[] = ' "sql" => ' . var_export($findByColumnSql, true) . ','; $block[] = ' "type" => Result::TYPE_LOAD,'; $block[] = '));'; } $cTemplate->extendClass('\\' . $baseClass); // interfaces if ($ifs = $schema->getModelInterfaces()) { foreach ($ifs as $iface) { $cTemplate->implementClass($iface); } } // Create column accessor if ($schema->enableColumnAccessors) { foreach ($schema->getColumnNames() as $columnName) { $accessorMethodName = 'get' . ucfirst(Inflector::camelize($columnName)); $cTemplate->addMethod('public', $accessorMethodName, [], [' return $this->get(' . var_export($columnName, true) . ');']); } } return $cTemplate; }
public function reverseTableSchema($table, $referenceSchema = null) { /* * postgresql information schema column descriptions * * @see http://www.postgresql.org/docs/8.1/static/infoschema-columns.html */ $sql = "SELECT * FROM information_schema.columns WHERE table_name = '{$table}';"; $stm = $this->connection->query($sql); $schema = new DeclareSchema(); $schema->columnNames = $schema->columns = array(); $rows = $stm->fetchAll(PDO::FETCH_OBJ); /* * more detailed attributes * * > select * from pg_attribute, pg_type where typname = 'addresses'; * > select * from pg_attribute, pg_type where typname = 'addresses' and attname not in ('cmin','cmax','ctid','oid','tableoid','xmin','xmax'); * * > SELECT * a.attname as "Column", * pg_catalog.format_type(a.atttypid, a.atttypmod) as "Datatype" * FROM * pg_catalog.pg_attribute a * WHERE * a.attnum > 0 * AND NOT a.attisdropped * AND a.attrelid = ( * SELECT c.oid * FROM pg_catalog.pg_class c * LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace * WHERE c.relname ~ '^(books)$' * AND pg_catalog.pg_table_is_visible(c.oid) * ) * ; * * @see http://notfaq.wordpress.com/2006/07/29/sql-postgresql-get-tables-and-columns/ */ foreach ($rows as $row) { $column = $schema->column($row->column_name); if ($row->is_nullable === 'YES') { $column->null(); } else { $column->notNull(); } $type = $row->data_type; $typeInfo = TypeInfoParser::parseTypeInfo($type); if ($typeInfo->type === 'varchar') { $type = 'varchar(' . $row->character_maximum_length . ')'; } $column->type($type); $isa = null; if (preg_match('/^(text|varchar|character)/i', $type)) { $isa = 'str'; } elseif (preg_match('/^(int|bigint|smallint|integer)/i', $type)) { $isa = 'int'; } elseif (preg_match('/^(timestamp|date)/i', $type)) { $isa = 'DateTime'; } elseif ($type === 'boolean') { $isa = 'bool'; } if ($isa) { $column->isa($isa); } if ($typeInfo->length) { $column->length($typeInfo->length); } if ($typeInfo->precision) { $column->decimals($typeInfo->precision); } // $row->ordinal_position // $row->data_type // $row->column_default // $row->character_maximum_length } return $schema; }
public function __construct($parentSchema, array $options = array()) { $this->parentSchema = $parentSchema; parent::__construct($options); }