includes/codegen/QDatabaseCodeGen.class.php
<?php
require(__QCUBED_CORE__ . '/codegen/QSqlColumn.class.php');
require(__QCUBED_CORE__ . '/codegen/QIndex.class.php');
require(__QCUBED_CORE__ . '/codegen/QManyToManyReference.class.php');
require(__QCUBED_CORE__ . '/codegen/QReference.class.php');
require(__QCUBED_CORE__ . '/codegen/QReverseReference.class.php');
require(__QCUBED_CORE__ . '/codegen/QSqlTable.class.php');
require(__QCUBED_CORE__ . '/codegen/QTypeTable.class.php');
require(__QCUBED_CORE__ . '/codegen/QModelConnectorOptions.class.php');
/**
* @package Codegen
*/
class QDatabaseCodeGen extends QCodeGen {
public $objSettingsXml; // Make public so templates can use it directly.
// Objects
/** @var array|QSqlTable[] Array of tables in the database */
protected $objTableArray;
protected $strExcludedTableArray;
protected $objTypeTableArray;
protected $strAssociationTableNameArray;
/** @var QDatabaseBase The database we are dealing with */
protected $objDb;
protected $intDatabaseIndex;
/** @var string The delimiter to be used for parsing comments on the DB tables for being used as the name of ModelConnector's Label */
protected $strCommentConnectorLabelDelimiter;
// Table Suffixes
protected $strTypeTableSuffixArray;
protected $intTypeTableSuffixLengthArray;
protected $strAssociationTableSuffix;
protected $intAssociationTableSuffixLength;
// Table Prefix
protected $strStripTablePrefix;
protected $intStripTablePrefixLength;
// Exclude Patterns & Lists
protected $strExcludePattern;
protected $strExcludeListArray;
// Include Patterns & Lists
protected $strIncludePattern;
protected $strIncludeListArray;
// Uniquely Associated Objects
protected $strAssociatedObjectPrefix;
protected $strAssociatedObjectSuffix;
// Manual Query (e.g. "Beta 2 Query") Suppor
protected $blnManualQuerySupport = false;
// Relationship Scripts
protected $strRelationships;
protected $blnRelationshipsIgnoreCase;
protected $strRelationshipsScriptPath;
protected $strRelationshipsScriptFormat;
protected $blnRelationshipsScriptIgnoreCase;
protected $strRelationshipLinesQcubed = array();
protected $strRelationshipLinesSql = array();
// Type Table Items, Table Name and Column Name RegExp Patterns
protected $strPatternTableName = '[[:alpha:]_][[:alnum:]_]*';
protected $strPatternColumnName = '[[:alpha:]_][[:alnum:]_]*';
protected $strPatternKeyName = '[[:alpha:]_][[:alnum:]_]*';
protected $blnGenerateControlId;
protected $objModelConnectorOptions;
protected $blnAutoInitialize;
protected $blnPrivateColumnVars;
/**
* @param $strTableName
* @return QSqlTable|QTypeTable
* @throws QCallerException
*/
public function GetTable($strTableName) {
$strTableName = strtolower($strTableName);
if (array_key_exists($strTableName, $this->objTableArray))
return $this->objTableArray[$strTableName];
if (array_key_exists($strTableName, $this->objTypeTableArray))
return $this->objTypeTableArray[$strTableName];; // deal with table special
throw new QCallerException(sprintf('Table does not exist or could not be processed: %s. %s', $strTableName, $this->strErrors));
}
public function GetColumn($strTableName, $strColumnName) {
try {
$objTable = $this->GetTable($strTableName);
} catch (QCallerException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
$strColumnName = strtolower($strColumnName);
if (array_key_exists($strColumnName, $objTable->ColumnArray))
return $objTable->ColumnArray[$strColumnName];
throw new QCallerException(sprintf('Column does not exist in %s: %s', $strTableName, $strColumnName));
}
/**
* Given a CASE INSENSITIVE table and column name, it will return TRUE if the Table/Column
* exists ANYWHERE in the already analyzed database
*
* @param string $strTableName
* @param string $strColumnName
* @return boolean true if it is found/validated
*/
public function ValidateTableColumn($strTableName, $strColumnName) {
$strTableName = trim(strtolower($strTableName));
$strColumnName = trim(strtolower($strColumnName));
if (array_key_exists($strTableName, $this->objTableArray))
$strTableName = $this->objTableArray[$strTableName]->Name;
else if (array_key_exists($strTableName, $this->objTypeTableArray))
$strTableName = $this->objTypeTableArray[$strTableName]->Name;
else if (array_key_exists($strTableName, $this->strAssociationTableNameArray))
$strTableName = $this->strAssociationTableNameArray[$strTableName];
else
return false;
$objFieldArray = $this->objDb->GetFieldsForTable($strTableName);
foreach ($objFieldArray as $objField) {
if (trim(strtolower($objField->Name)) == $strColumnName)
return true;
}
return false;
}
public function GetTitle() {
if (!QApplication::$Database) {
return '';
}
if (array_key_exists($this->intDatabaseIndex, QApplication::$Database)) {
$objDatabase = QApplication::$Database[$this->intDatabaseIndex];
return sprintf('Database Index #%s (%s / %s / %s)', $this->intDatabaseIndex, $objDatabase->Adapter, $objDatabase->Server, $objDatabase->Database);
} else
return sprintf('Database Index #%s (N/A)', $this->intDatabaseIndex);
}
public function GetConfigXml() {
$strCrLf = "\r\n";
$strToReturn = sprintf(' <database index="%s">%s', $this->intDatabaseIndex, $strCrLf);
$strToReturn .= sprintf(' <className prefix="%s" suffix="%s"/>%s', $this->strClassPrefix, $this->strClassSuffix, $strCrLf);
$strToReturn .= sprintf(' <associatedObjectName prefix="%s" suffix="%s"/>%s', $this->strAssociatedObjectPrefix, $this->strAssociatedObjectSuffix, $strCrLf);
$strToReturn .= sprintf(' <typeTableIdentifier suffix="%s"/>%s', implode(',', $this->strTypeTableSuffixArray), $strCrLf);
$strToReturn .= sprintf(' <associationTableIdentifier suffix="%s"/>%s', $this->strAssociationTableSuffix, $strCrLf);
$strToReturn .= sprintf(' <stripFromTableName prefix="%s"/>%s', $this->strStripTablePrefix, $strCrLf);
$strToReturn .= sprintf(' <excludeTables pattern="%s" list="%s"/>%s', $this->strExcludePattern, implode(',', $this->strExcludeListArray), $strCrLf);
$strToReturn .= sprintf(' <includeTables pattern="%s" list="%s"/>%s', $this->strIncludePattern, implode(',', $this->strIncludeListArray), $strCrLf);
$strToReturn .= sprintf(' <manualQuery support="%s"/>%s', ($this->blnManualQuerySupport) ? 'true' : 'false', $strCrLf);
$strToReturn .= sprintf(' <relationships>%s', $strCrLf);
if ($this->strRelationships)
$strToReturn .= sprintf(' %s%s', $this->strRelationships, $strCrLf);
$strToReturn .= sprintf(' </relationships>%s', $strCrLf);
$strToReturn .= sprintf(' <relationshipsScript filepath="%s" format="%s"/>%s', $this->strRelationshipsScriptPath, $this->strRelationshipsScriptFormat, $strCrLf);
$strToReturn .= sprintf(' </database>%s', $strCrLf);
return $strToReturn;
}
public function GetReportLabel() {
// Setup Report Label
$intTotalTableCount = count($this->objTableArray) + count($this->objTypeTableArray);
if ($intTotalTableCount == 0)
$strReportLabel = 'There were no tables available to attempt code generation.';
else if ($intTotalTableCount == 1)
$strReportLabel = 'There was 1 table available to attempt code generation:';
else
$strReportLabel = 'There were ' . $intTotalTableCount . ' tables available to attempt code generation:';
return $strReportLabel;
}
public function GenerateAll() {
$strReport = '';
// Iterate through all the tables, generating one class at a time
if ($this->objTableArray) foreach ($this->objTableArray as $objTable) {
if ($this->GenerateTable($objTable)) {
$intCount = $objTable->ReferenceCount;
if ($intCount == 0)
$strCount = '(with no relationships)';
else if ($intCount == 1)
$strCount = '(with 1 relationship)';
else
$strCount = sprintf('(with %s relationships)', $intCount);
$strReport .= sprintf("Successfully generated DB ORM Class: %s %s\r\n", $objTable->ClassName, $strCount);
} else
$strReport .= sprintf("FAILED to generate DB ORM Class: %s\r\n", $objTable->ClassName);
}
// Iterate through all the TYPE tables, generating one TYPE class at a time
if ($this->objTypeTableArray) foreach ($this->objTypeTableArray as $objTypeTable) {
if ($this->GenerateTypeTable($objTypeTable))
$strReport .= sprintf("Successfully generated DB Type Class: %s\n", $objTypeTable->ClassName);
else
$strReport .= sprintf("FAILED to generate DB Type class: %s\n", $objTypeTable->ClassName);
}
return $strReport;
}
public static function GenerateAggregateHelper($objCodeGenArray) {
$strToReturn = array();
if (count($objCodeGenArray)) {
// Standard ORM Tables
$objTableArray = array();
foreach ($objCodeGenArray as $objCodeGen) {
$objCurrentTableArray = $objCodeGen->TableArray;
foreach ($objCurrentTableArray as $objTable)
$objTableArray[$objTable->ClassName] = $objTable;
}
$mixArgumentArray = array('objTableArray' => $objTableArray);
if ($objCodeGenArray[0]->GenerateFiles('aggregate_db_orm', $mixArgumentArray))
$strToReturn[] = 'Successfully generated Aggregate DB ORM file(s)';
else
$strToReturn[] = 'FAILED to generate Aggregate DB ORM file(s)';
// Type Tables
$objTableArray = array();
foreach ($objCodeGenArray as $objCodeGen) {
$objCurrentTableArray = $objCodeGen->TypeTableArray;
foreach ($objCurrentTableArray as $objTable)
$objTableArray[$objTable->ClassName] = $objTable;
}
$mixArgumentArray = array('objTableArray' => $objTableArray);
if ($objCodeGenArray[0]->GenerateFiles('aggregate_db_type', $mixArgumentArray))
$strToReturn[] = 'Successfully generated Aggregate DB Type file(s)';
else
$strToReturn[] = 'FAILED to generate Aggregate DB Type file(s)';
}
return $strToReturn;
}
public function __construct($objSettingsXml) {
parent::__construct($objSettingsXml);
// Make settings file accessible to templates
//$this->objSettingsXml = $objSettingsXml;
// Setup Local Arrays
$this->strAssociationTableNameArray = array();
$this->objTableArray = array();
$this->objTypeTableArray = array();
$this->strExcludedTableArray = array();
// Set the DatabaseIndex
$this->intDatabaseIndex = QCodeGen::LookupSetting($objSettingsXml, null, 'index', QType::Integer);
// Append Suffix/Prefixes
$this->strClassPrefix = QCodeGen::LookupSetting($objSettingsXml, 'className', 'prefix');
$this->strClassSuffix = QCodeGen::LookupSetting($objSettingsXml, 'className', 'suffix');
$this->strAssociatedObjectPrefix = QCodeGen::LookupSetting($objSettingsXml, 'associatedObjectName', 'prefix');
$this->strAssociatedObjectSuffix = QCodeGen::LookupSetting($objSettingsXml, 'associatedObjectName', 'suffix');
// Table Type Identifiers
$strTypeTableSuffixList = QCodeGen::LookupSetting($objSettingsXml, 'typeTableIdentifier', 'suffix');
$strTypeTableSuffixArray = explode(',', $strTypeTableSuffixList);
foreach ($strTypeTableSuffixArray as $strTypeTableSuffix) {
$this->strTypeTableSuffixArray[] = trim($strTypeTableSuffix);
$this->intTypeTableSuffixLengthArray[] = strlen(trim($strTypeTableSuffix));
}
$this->strAssociationTableSuffix = QCodeGen::LookupSetting($objSettingsXml, 'associationTableIdentifier', 'suffix');
$this->intAssociationTableSuffixLength = strlen($this->strAssociationTableSuffix);
// Stripping TablePrefixes
$this->strStripTablePrefix = QCodeGen::LookupSetting($objSettingsXml, 'stripFromTableName', 'prefix');
$this->intStripTablePrefixLength = strlen($this->strStripTablePrefix);
// Exclude/Include Tables
$this->strExcludePattern = QCodeGen::LookupSetting($objSettingsXml, 'excludeTables', 'pattern');
$strExcludeList = QCodeGen::LookupSetting($objSettingsXml, 'excludeTables', 'list');
$this->strExcludeListArray = explode(',',$strExcludeList);
array_walk($this->strExcludeListArray, 'array_trim');
// Include Patterns
$this->strIncludePattern = QCodeGen::LookupSetting($objSettingsXml, 'includeTables', 'pattern');
$strIncludeList = QCodeGen::LookupSetting($objSettingsXml, 'includeTables', 'list');
$this->strIncludeListArray = explode(',',$strIncludeList);
array_walk($this->strIncludeListArray, 'array_trim');
// ManualQuery Support
$this->blnManualQuerySupport = QCodeGen::LookupSetting($objSettingsXml, 'manualQuery', 'support', QType::Boolean);
// Relationship Scripts
$this->strRelationships = QCodeGen::LookupSetting($objSettingsXml, 'relationships');
$this->strRelationshipsScriptPath = QCodeGen::LookupSetting($objSettingsXml, 'relationshipsScript', 'filepath');
$this->strRelationshipsScriptFormat = QCodeGen::LookupSetting($objSettingsXml, 'relationshipsScript', 'format');
// Column Comment for ModelConnectorLabel setting.
$this->strCommentConnectorLabelDelimiter = QCodeGen::LookupSetting($objSettingsXml, 'columnCommentForModelConnector', 'delimiter');
// Check to make sure things that are required are there
if (!$this->intDatabaseIndex)
$this->strErrors .= "CodeGen Settings XML Fatal Error: databaseIndex was invalid or not set\r\n";
// Aggregate RelationshipLinesQcubed and RelationshipLinesSql arrays
if ($this->strRelationships) {
$strLines = explode("\n", strtolower($this->strRelationships));
if ($strLines) foreach ($strLines as $strLine) {
$strLine = trim($strLine);
if (($strLine) &&
(strlen($strLine) > 2) &&
(substr($strLine, 0, 2) != '//') &&
(substr($strLine, 0, 2) != '--') &&
(substr($strLine, 0, 1) != '#')) {
$this->strRelationshipLinesQcubed[$strLine] = $strLine;
}
}
}
if ($this->strRelationshipsScriptPath) {
if (!file_exists($this->strRelationshipsScriptPath))
$this->strErrors .= sprintf("CodeGen Settings XML Fatal Error: relationshipsScript filepath \"%s\" does not exist\r\n", $this->strRelationshipsScriptPath);
else {
$strScript = strtolower(trim(file_get_contents($this->strRelationshipsScriptPath)));
switch (strtolower($this->strRelationshipsScriptFormat)) {
case 'qcubed':
$strLines = explode("\n", $strScript);
if ($strLines) foreach ($strLines as $strLine) {
$strLine = trim($strLine);
if (($strLine) &&
(strlen($strLine) > 2) &&
(substr($strLine, 0, 2) != '//') &&
(substr($strLine, 0, 2) != '--') &&
(substr($strLine, 0, 1) != '#')) {
$this->strRelationshipLinesQcubed[$strLine] = $strLine;
}
}
break;
case 'sql':
// Separate all commands in the script (separated by ";")
$strCommands = explode(';', $strScript);
if ($strCommands) foreach ($strCommands as $strCommand) {
$strCommand = trim($strCommand);
if ($strCommand) {
// Take out all comment lines in the script
$strLines = explode("\n", $strCommand);
$strCommand = '';
foreach ($strLines as $strLine) {
$strLine = trim($strLine);
if (($strLine) &&
(substr($strLine, 0, 2) != '//') &&
(substr($strLine, 0, 2) != '--') &&
(substr($strLine, 0, 1) != '#')) {
$strLine = str_replace(' ', ' ', $strLine);
$strLine = str_replace(' ', ' ', $strLine);
$strLine = str_replace(' ', ' ', $strLine);
$strLine = str_replace(' ', ' ', $strLine);
$strLine = str_replace(' ', ' ', $strLine);
$strLine = str_replace(' ', ' ', $strLine);
$strLine = str_replace(' ', ' ', $strLine);
$strLine = str_replace(' ', ' ', $strLine);
$strLine = str_replace(' ', ' ', $strLine);
$strLine = str_replace(' ', ' ', $strLine);
$strLine = str_replace(' ', ' ', $strLine);
$strLine = str_replace(' ', ' ', $strLine);
$strCommand .= $strLine . ' ';
}
}
$strCommand = trim($strCommand);
if ((strpos($strCommand, 'alter table') === 0) &&
(strpos($strCommand, 'foreign key') !== false))
$this->strRelationshipLinesSql[$strCommand] = $strCommand;
}
}
break;
default:
$this->strErrors .= sprintf("CodeGen Settings XML Fatal Error: relationshipsScript format \"%s\" is invalid (must be either \"qcubed\" or \"sql\")\r\n", $this->strRelationshipsScriptFormat);
break;
}
}
}
$this->blnGenerateControlId = QCodeGen::LookupSetting($objSettingsXml, 'generateControlId', 'support', QType::Boolean);
$this->objModelConnectorOptions = new QModelConnectorOptions();
$this->blnAutoInitialize = QCodeGen::LookupSetting($objSettingsXml, 'createOptions', 'autoInitialize', QType::Boolean);
$this->blnPrivateColumnVars = QCodeGen::LookupSetting($objSettingsXml, 'createOptions', 'privateColumnVars', QType::Boolean);
if ($this->strErrors)
return;
$this->AnalyzeDatabase();
}
protected function AnalyzeDatabase() {
if (!QApplication::$Database) {
$this->strErrors = 'FATAL ERROR: No databases are listed in the configuration file.';
return;
}
// Set aside the Database object
if (array_key_exists($this->intDatabaseIndex, QApplication::$Database))
$this->objDb = QApplication::$Database[$this->intDatabaseIndex];
// Ensure the DB Exists
if (!$this->objDb) {
$this->strErrors = 'FATAL ERROR: No database configured at index ' . $this->intDatabaseIndex . '. Check your configuration file.';
return;
}
// Ensure DB Profiling is DISABLED on this DB
if ($this->objDb->EnableProfiling) {
$this->strErrors = 'FATAL ERROR: Code generator cannot analyze the database at index ' . $this->intDatabaseIndex . ' while DB Profiling is enabled.';
return;
}
// Get the list of Tables as a string[]
$strTableArray = $this->objDb->GetTables();
// ITERATION 1: Simply create the Table and TypeTable Arrays
if ($strTableArray) {
foreach ($strTableArray as $strTableName) {
// Do we Exclude this Table Name? (given includeTables and excludeTables)
// First check the lists of Excludes and the Exclude Patterns
if (in_array($strTableName, $this->strExcludeListArray) ||
(strlen($this->strExcludePattern) > 0 && preg_match(":" . $this->strExcludePattern . ":i", $strTableName))
) {
// So we THINK we may be excluding this table
// But check against the explicit INCLUDE list and patterns
if (in_array($strTableName, $this->strIncludeListArray) ||
(strlen($this->strIncludePattern) > 0 && preg_match(":" . $this->strIncludePattern . ":i", $strTableName))
) {
// If we're here, we explicitly want to include this table
// Therefore, do nothing
} else {
// If we're here, then we want to exclude this table
$this->strExcludedTableArray[strtolower($strTableName)] = true;
// Exit this iteration of the foreach loop
continue;
}
}
// Check to see if this table name exists anywhere else yet, and warn if it is
foreach (QCodeGen::$CodeGenArray as $objCodeGen) {
if ($objCodeGen instanceof QDatabaseCodeGen) {
foreach ($objCodeGen->objTableArray as $objPossibleDuplicate)
if (strtolower($objPossibleDuplicate->Name) == strtolower($strTableName)) {
$this->strErrors .= 'Duplicate Table Name Used: ' . $strTableName . "\r\n";
}
}
}
// Perform different tasks based on whether it's an Association table,
// a Type table, or just a regular table
$blnIsTypeTable = false;
foreach ($this->intTypeTableSuffixLengthArray as $intIndex => $intTypeTableSuffixLength) {
if (($intTypeTableSuffixLength) &&
(strlen($strTableName) > $intTypeTableSuffixLength) &&
(substr($strTableName, strlen($strTableName) - $intTypeTableSuffixLength) == $this->strTypeTableSuffixArray[$intIndex])
) {
// Let's mark, that we have type table
$blnIsTypeTable = true;
// Create a TYPE Table and add it to the array
$objTypeTable = new QTypeTable($strTableName);
$this->objTypeTableArray[strtolower($strTableName)] = $objTypeTable;
// If we found type table, there is no point of iterating for other type table suffixes
break;
// _p("TYPE Table: $strTableName<br />", false);
}
}
if (!$blnIsTypeTable) {
// If current table wasn't type table, let's look for other table types
if (($this->intAssociationTableSuffixLength) &&
(strlen($strTableName) > $this->intAssociationTableSuffixLength) &&
(substr($strTableName, strlen($strTableName) - $this->intAssociationTableSuffixLength) == $this->strAssociationTableSuffix)
) {
// Add this ASSOCIATION Table Name to the array
$this->strAssociationTableNameArray[strtolower($strTableName)] = $strTableName;
// _p("ASSN Table: $strTableName<br />", false);
} else {
// Create a Regular Table and add it to the array
$objTable = new QSqlTable($strTableName);
$this->objTableArray[strtolower($strTableName)] = $objTable;
// _p("Table: $strTableName<br />", false);
}
}
}
}
// Analyze All the Type Tables
if ($this->objTypeTableArray) foreach ($this->objTypeTableArray as $objTypeTable)
$this->AnalyzeTypeTable($objTypeTable);
// Analyze All the Regular Tables
if ($this->objTableArray) foreach ($this->objTableArray as $objTable)
$this->AnalyzeTable($objTable);
// Analyze All the Association Tables
if ($this->strAssociationTableNameArray) foreach ($this->strAssociationTableNameArray as $strAssociationTableName)
$this->AnalyzeAssociationTable($strAssociationTableName);
// Finally, for each Relationship in all Tables, Warn on Non Single Column PK based FK:
if ($this->objTableArray) foreach ($this->objTableArray as $objTable)
if ($objTable->ColumnArray) foreach ($objTable->ColumnArray as $objColumn)
if ($objColumn->Reference && !$objColumn->Reference->IsType) {
$objReference = $objColumn->Reference;
// $objReferencedTable = $this->objTableArray[strtolower($objReference->Table)];
$objReferencedTable = $this->GetTable($objReference->Table);
$objReferencedColumn = $objReferencedTable->ColumnArray[strtolower($objReference->Column)];
if (!$objReferencedColumn->PrimaryKey) {
$this->strErrors .= sprintf("Warning: Invalid Relationship created in %s class (for foreign key \"%s\") -- column \"%s\" is not the single-column primary key for the referenced \"%s\" table\r\n",
$objReferencedTable->ClassName, $objReference->KeyName, $objReferencedColumn->Name, $objReferencedTable->Name);
} else if (count($objReferencedTable->PrimaryKeyColumnArray) != 1) {
$this->strErrors .= sprintf("Warning: Invalid Relationship created in %s class (for foreign key \"%s\") -- column \"%s\" is not the single-column primary key for the referenced \"%s\" table\r\n",
$objReferencedTable->ClassName, $objReference->KeyName, $objReferencedColumn->Name, $objReferencedTable->Name);
}
}
}
protected function ListOfColumnsFromTable(QSqlTable $objTable) {
$strArray = array();
$objColumnArray = $objTable->ColumnArray;
if ($objColumnArray) foreach ($objColumnArray as $objColumn)
array_push($strArray, $objColumn->Name);
return implode(', ', $strArray);
}
protected function GetColumnArray(QSqlTable $objTable, $strColumnNameArray) {
$objToReturn = array();
if ($strColumnNameArray) foreach ($strColumnNameArray as $strColumnName) {
array_push($objToReturn, $objTable->ColumnArray[strtolower($strColumnName)]);
}
return $objToReturn;
}
public function GenerateTable(QSqlTable $objTable) {
// Create Argument Array
$mixArgumentArray = array('objTable' => $objTable);
return $this->GenerateFiles('db_orm', $mixArgumentArray);
}
public function GenerateTypeTable(QTypeTable $objTypeTable) {
// Create Argument Array
$mixArgumentArray = array('objTypeTable' => $objTypeTable);
return $this->GenerateFiles('db_type', $mixArgumentArray);
}
protected function AnalyzeAssociationTable($strTableName) {
$objFieldArray = $this->objDb->GetFieldsForTable($strTableName);
// Association tables must have 2 fields
if (count($objFieldArray) != 2) {
$this->strErrors .= sprintf("AssociationTable %s does not have exactly 2 columns.\n",
$strTableName);
return;
}
if ((!$objFieldArray[0]->NotNull) ||
(!$objFieldArray[1]->NotNull)) {
$this->strErrors .= sprintf("AssociationTable %s's two columns must both be not null",
$strTableName);
return;
}
if (((!$objFieldArray[0]->PrimaryKey) &&
($objFieldArray[1]->PrimaryKey)) ||
(($objFieldArray[0]->PrimaryKey) &&
(!$objFieldArray[1]->PrimaryKey))) {
$this->strErrors .= sprintf("AssociationTable %s only support two-column composite Primary Keys.\n",
$strTableName);
return;
}
$objForeignKeyArray = $this->objDb->GetForeignKeysForTable($strTableName);
// Add to it, the list of Foreign Keys from any Relationships Script
$objForeignKeyArray = $this->GetForeignKeysFromRelationshipsScript($strTableName, $objForeignKeyArray);
if (count($objForeignKeyArray) != 2) {
$this->strErrors .= sprintf("AssociationTable %s does not have exactly 2 foreign keys. Code Gen analysis found %s.\n",
$strTableName, count($objForeignKeyArray));
return;
}
// Setup two new ManyToManyReference objects
$objManyToManyReferenceArray[0] = new QManyToManyReference();
$objManyToManyReferenceArray[1] = new QManyToManyReference();
// Ensure that the linked tables are both not excluded
if (array_key_exists($objForeignKeyArray[0]->ReferenceTableName, $this->strExcludedTableArray) ||
array_key_exists($objForeignKeyArray[1]->ReferenceTableName, $this->strExcludedTableArray))
return;
// Setup GraphPrefixArray (if applicable)
if ($objForeignKeyArray[0]->ReferenceTableName == $objForeignKeyArray[1]->ReferenceTableName) {
// We are analyzing a graph association
$strGraphPrefixArray = $this->CalculateGraphPrefixArray($objForeignKeyArray);
} else {
$strGraphPrefixArray = array('', '');
}
// Go through each FK and setup each ManyToManyReference object
for ($intIndex = 0; $intIndex < 2; $intIndex++) {
$objManyToManyReference = $objManyToManyReferenceArray[$intIndex];
$objForeignKey = $objForeignKeyArray[$intIndex];
$objOppositeForeignKey = $objForeignKeyArray[($intIndex == 0) ? 1 : 0];
// Make sure the FK is a single-column FK
if (count($objForeignKey->ColumnNameArray) != 1) {
$this->strErrors .= sprintf("AssoiationTable %s has multi-column foreign keys.\n",
$strTableName);
return;
}
$objManyToManyReference->KeyName = $objForeignKey->KeyName;
$objManyToManyReference->Table = $strTableName;
$objManyToManyReference->Column = $objForeignKey->ColumnNameArray[0];
$objManyToManyReference->PropertyName = $this->ModelColumnPropertyName($objManyToManyReference->Column);
$objManyToManyReference->OppositeColumn = $objOppositeForeignKey->ColumnNameArray[0];
$objManyToManyReference->AssociatedTable = $objOppositeForeignKey->ReferenceTableName;
// Calculate OppositeColumnVariableName
// Do this by first making a fake column which is the PK column of the AssociatedTable,
// but who's column name is ManyToManyReference->Column
// $objOppositeColumn = clone($this->objTableArray[strtolower($objManyToManyReference->AssociatedTable)]->PrimaryKeyColumnArray[0]);
$objTable = $this->GetTable($objManyToManyReference->AssociatedTable);
$objOppositeColumn = clone($objTable->PrimaryKeyColumnArray[0]);
$objOppositeColumn->Name = $objManyToManyReference->OppositeColumn;
$objManyToManyReference->OppositeVariableName = $this->ModelColumnVariableName($objOppositeColumn);
$objManyToManyReference->OppositePropertyName = $this->ModelColumnPropertyName($objOppositeColumn->Name);
$objManyToManyReference->OppositeVariableType = $objOppositeColumn->VariableType;
$objManyToManyReference->OppositeDbType = $objOppositeColumn->DbType;
$objManyToManyReference->VariableName = $this->ModelReverseReferenceVariableName($objOppositeForeignKey->ReferenceTableName);
$objManyToManyReference->VariableType = $this->ModelReverseReferenceVariableType($objOppositeForeignKey->ReferenceTableName);
$objManyToManyReference->ObjectDescription = $strGraphPrefixArray[$intIndex] . $this->CalculateObjectDescriptionForAssociation($strTableName, $objForeignKey->ReferenceTableName, $objOppositeForeignKey->ReferenceTableName, false);
$objManyToManyReference->ObjectDescriptionPlural = $strGraphPrefixArray[$intIndex] . $this->CalculateObjectDescriptionForAssociation($strTableName, $objForeignKey->ReferenceTableName, $objOppositeForeignKey->ReferenceTableName, true);
$objManyToManyReference->OppositeObjectDescription = $strGraphPrefixArray[($intIndex == 0) ? 1 : 0] . $this->CalculateObjectDescriptionForAssociation($strTableName, $objOppositeForeignKey->ReferenceTableName, $objForeignKey->ReferenceTableName, false);
$objManyToManyReference->IsTypeAssociation = ($objTable instanceof QTypeTable);
$objManyToManyReference->Options = $this->objModelConnectorOptions->GetOptions($this->ModelClassName($objForeignKey->ReferenceTableName), $objManyToManyReference->ObjectDescription);
}
// Iterate through the list of Columns to create objColumnArray
$objColumnArray = array();
foreach ($objFieldArray as $objField) {
if (($objField->Name != $objManyToManyReferenceArray[0]->Column) &&
($objField->Name != $objManyToManyReferenceArray[1]->Column)) {
$objColumn = $this->AnalyzeTableColumn($objField, null);
if ($objColumn) {
$objColumnArray[strtolower($objColumn->Name)] = $objColumn;
}
}
}
// Make sure lone primary key columns are marked as unique
$objKeyColumn = null;
foreach ($objColumnArray as $objColumn) {
if ($objColumn->PrimaryKey) {
if ($objKeyColumn === null) {
$objKeyColumn = $objColumn;
}
else {
$objKeyColumn = false; // multiple key columns
}
}
}
if ($objKeyColumn) {
$objKeyColumn->Unique = true;
}
$objManyToManyReferenceArray[0]->ColumnArray = $objColumnArray;
$objManyToManyReferenceArray[1]->ColumnArray = $objColumnArray;
// Push the ManyToManyReference Objects to the tables
for ($intIndex = 0; $intIndex < 2; $intIndex++) {
$objManyToManyReference = $objManyToManyReferenceArray[$intIndex];
$strTableWithReference = $objManyToManyReferenceArray[($intIndex == 0) ? 1 : 0]->AssociatedTable;
$objTable = $this->GetTable($strTableWithReference);
$objArray = $objTable->ManyToManyReferenceArray;
array_push($objArray, $objManyToManyReference);
$objTable->ManyToManyReferenceArray = $objArray;
}
}
protected function AnalyzeTypeTable(QTypeTable $objTypeTable) {
// Setup the Array of Reserved Words
$strReservedWords = explode(',', QCodeGen::PhpReservedWords);
for ($intIndex = 0; $intIndex < count($strReservedWords); $intIndex++)
$strReservedWords[$intIndex] = strtolower(trim($strReservedWords[$intIndex]));
// Setup the Type Table Object
$strTableName = $objTypeTable->Name;
$objTypeTable->ClassName = $this->ModelClassName($strTableName);
// Ensure that there are only 2 fields, an integer PK field (can be named anything) and a unique varchar field
$objFieldArray = $this->objDb->GetFieldsForTable($strTableName);
if (($objFieldArray[0]->Type != QDatabaseFieldType::Integer) ||
(!$objFieldArray[0]->PrimaryKey)) {
$this->strErrors .= sprintf("TypeTable %s's first column is not a PK integer.\n",
$strTableName);
return;
}
if (($objFieldArray[1]->Type != QDatabaseFieldType::VarChar) ||
(!$objFieldArray[1]->Unique)) {
$this->strErrors .= sprintf("TypeTable %s's second column is not a unique VARCHAR.\n",
$strTableName);
return;
}
// Get the rows
$objResult = $this->objDb->Query(sprintf('SELECT * FROM %s', $strTableName));
$strNameArray = array();
$strTokenArray = array();
$strExtraPropertyArray = array();
$strExtraFields = array();
$intRowWidth = count($objFieldArray);
while ($objDbRow = $objResult->GetNextRow()) {
$strRowArray = $objDbRow->GetColumnNameArray();
$id = $strRowArray[0];
$name = $strRowArray[1];
$strNameArray[$id] = str_replace("'", "\\'", str_replace('\\', '\\\\', $name));
$strTokenArray[$id] = $this->TypeTokenFromTypeName($name);
if ($intRowWidth > 2) { // there are extra columns to process
$strExtraPropertyArray[$id] = array();
for ($i = 2; $i < $intRowWidth; $i++) {
$strFieldName = QCodeGen::TypeColumnPropertyName($objFieldArray[$i]->Name);
$strExtraFields[$i - 2] = $strFieldName;
// Get and resolve type based value
$value = $objDbRow->GetColumn($objFieldArray[$i]->Name, $objFieldArray[$i]->Type);
$strExtraPropertyArray[$id][$strFieldName] = $value;
}
}
foreach ($strReservedWords as $strReservedWord)
if (trim(strtolower($strTokenArray[$id])) == $strReservedWord) {
$this->strErrors .= sprintf("Warning: TypeTable %s contains a type name which is a reserved word: %s. Appended _ to the beginning of it.\r\n",
$strTableName, $strReservedWord);
$strTokenArray[$id] = '_' . $strTokenArray[$id];
}
if (strlen($strTokenArray[$id]) == 0) {
$this->strErrors .= sprintf("Warning: TypeTable %s contains an invalid type name: %s\r\n",
$strTableName, stripslashes($strNameArray[$id]));
return;
}
}
ksort($strNameArray);
ksort($strTokenArray);
$objTypeTable->NameArray = $strNameArray;
$objTypeTable->TokenArray = $strTokenArray;
$objTypeTable->ExtraFieldNamesArray = $strExtraFields;
$objTypeTable->ExtraPropertyArray = $strExtraPropertyArray;
$objColumn = $this->AnalyzeTableColumn ($objFieldArray[0], $objTypeTable);
$objColumn->Unique = true;
$objTypeTable->KeyColumn = $objColumn;
}
protected function AnalyzeTable(QSqlTable $objTable) {
// Setup the Table Object
$objTable->OwnerDbIndex = $this->intDatabaseIndex;
$strTableName = $objTable->Name;
$objTable->ClassName = $this->ModelClassName($strTableName);
$objTable->ClassNamePlural = $this->Pluralize($objTable->ClassName);
$objTable->Options = $this->objModelConnectorOptions->GetOptions($objTable->ClassName, QModelConnectorOptions::TableOptionsFieldName);
// Get the List of Columns
$objFieldArray = $this->objDb->GetFieldsForTable($strTableName);
// Iterate through the list of Columns to create objColumnArray
$objColumnArray = array();
if ($objFieldArray) foreach ($objFieldArray as $objField) {
$objColumn = $this->AnalyzeTableColumn($objField, $objTable);
if ($objColumn) {
$objColumnArray[strtolower($objColumn->Name)] = $objColumn;
}
}
$objTable->ColumnArray = $objColumnArray;
// Make sure lone primary key columns are marked as unique
$objKeyColumn = null;
foreach ($objColumnArray as $objColumn) {
if ($objColumn->PrimaryKey) {
if ($objKeyColumn === null) {
$objKeyColumn = $objColumn;
}
else {
$objKeyColumn = false; // multiple key columns
}
}
}
if ($objKeyColumn) {
$objKeyColumn->Unique = true;
}
// Get the List of Indexes
$objTable->IndexArray = $this->objDb->GetIndexesForTable($objTable->Name);
// Create an Index array
$objIndexArray = array();
// Create our Index for Primary Key (if applicable)
$strPrimaryKeyArray = array();
foreach ($objColumnArray as $objColumn)
if ($objColumn->PrimaryKey) {
$objPkColumn = $objColumn;
array_push($strPrimaryKeyArray, $objColumn->Name);
}
if (count($strPrimaryKeyArray)) {
$objIndex = new QIndex();
$objIndex->KeyName = 'pk_' . $strTableName;
$objIndex->PrimaryKey = true;
$objIndex->Unique = true;
$objIndex->ColumnNameArray = $strPrimaryKeyArray;
array_push($objIndexArray, $objIndex);
if (count($strPrimaryKeyArray) == 1) {
$objPkColumn->Unique = true;
$objPkColumn->Indexed = true;
}
}
// Iterate though each Index that exists in this table, set any Columns's "Index" property
// to TRUE if they are a single-column index
if ($objTable->IndexArray) foreach ($objArray = $objTable->IndexArray as $objDatabaseIndex) {
// Make sure the columns are defined
if (count ($objDatabaseIndex->ColumnNameArray) == 0)
$this->strErrors .= sprintf("Index %s in table %s indexes on no columns.\n",
$objDatabaseIndex->KeyName, $strTableName);
else {
// Ensure every column exist in the DbIndex's ColumnNameArray
$blnFailed = false;
foreach ($objArray = $objDatabaseIndex->ColumnNameArray as $strColumnName) {
if (array_key_exists(strtolower($strColumnName), $objTable->ColumnArray) &&
($objTable->ColumnArray[strtolower($strColumnName)])) {
// It exists -- do nothing
} else {
// Otherwise, add a warning
$this->strErrors .= sprintf("Index %s in table %s indexes on the column %s, which does not appear to exist.\n",
$objDatabaseIndex->KeyName, $strTableName, $strColumnName);
$blnFailed = true;
}
}
if (!$blnFailed) {
// Let's make sure if this is a single-column index, we haven't already created a single-column index for this column
$blnAlreadyCreated = false;
foreach ($objIndexArray as $objIndex)
if (count($objIndex->ColumnNameArray) == count($objDatabaseIndex->ColumnNameArray))
if (implode(',', $objIndex->ColumnNameArray) == implode(',', $objDatabaseIndex->ColumnNameArray))
$blnAlreadyCreated = true;
if (!$blnAlreadyCreated) {
// Create the Index Object
$objIndex = new QIndex();
$objIndex->KeyName = $objDatabaseIndex->KeyName;
$objIndex->PrimaryKey = $objDatabaseIndex->PrimaryKey;
$objIndex->Unique = $objDatabaseIndex->Unique;
if ($objDatabaseIndex->PrimaryKey)
$objIndex->Unique = true;
$objIndex->ColumnNameArray = $objDatabaseIndex->ColumnNameArray;
// Add the new index object to the index array
array_push($objIndexArray, $objIndex);
// Lastly, if it's a single-column index, update the Column in the table to reflect this
if (count($objDatabaseIndex->ColumnNameArray) == 1) {
$strColumnName = $objDatabaseIndex->ColumnNameArray[0];
$objColumn = $objTable->ColumnArray[strtolower($strColumnName)];
$objColumn->Indexed = true;
if ($objIndex->Unique)
$objColumn->Unique = true;
}
}
}
}
}
// Add the IndexArray to the table
$objTable->IndexArray = $objIndexArray;
// Get the List of Foreign Keys from the database
$objForeignKeys = $this->objDb->GetForeignKeysForTable($objTable->Name);
// Add to it, the list of Foreign Keys from any Relationships Script
$objForeignKeys = $this->GetForeignKeysFromRelationshipsScript($strTableName, $objForeignKeys);
// Iterate through each foreign key that exists in this table
if ($objForeignKeys) foreach ($objForeignKeys as $objForeignKey) {
// Make sure it's a single-column FK
if (count($objForeignKey->ColumnNameArray) != 1)
$this->strErrors .= sprintf("Foreign Key %s in table %s keys on multiple columns. Multiple-columned FKs are not supported by the code generator.\n",
$objForeignKey->KeyName, $strTableName);
else {
// Make sure the column in the FK definition actually exists in this table
$strColumnName = $objForeignKey->ColumnNameArray[0];
if (array_key_exists(strtolower($strColumnName), $objTable->ColumnArray) &&
($objColumn = $objTable->ColumnArray[strtolower($strColumnName)])) {
// Now, we make sure there is a single-column index for this FK that exists
$blnFound = false;
if ($objIndexArray = $objTable->IndexArray) foreach ($objIndexArray as $objIndex) {
if ((count($objIndex->ColumnNameArray) == 1) &&
(strtolower($objIndex->ColumnNameArray[0]) == strtolower($strColumnName)))
$blnFound = true;
}
if (!$blnFound) {
// Single Column Index for this FK does not exist. Let's create a virtual one and warn
$objIndex = new QIndex();
$objIndex->KeyName = sprintf('virtualix_%s_%s', $objTable->Name, $objColumn->Name);
$objIndex->Unique = $objColumn->Unique;
$objIndex->ColumnNameArray = array($objColumn->Name);
$objIndexArray = $objTable->IndexArray;
$objIndexArray[] = $objIndex;
$objTable->IndexArray = $objIndexArray;
if ($objIndex->Unique)
$this->strWarnings .= sprintf("Notice: It is recommended that you add a single-column UNIQUE index on \"%s.%s\" for the Foreign Key %s\r\n",
$strTableName, $strColumnName, $objForeignKey->KeyName);
else
$this->strWarnings .= sprintf("Notice: It is recommended that you add a single-column index on \"%s.%s\" for the Foreign Key %s\r\n",
$strTableName, $strColumnName, $objForeignKey->KeyName);
}
// Make sure the table being referenced actually exists
if ((array_key_exists(strtolower($objForeignKey->ReferenceTableName), $this->objTableArray)) ||
(array_key_exists(strtolower($objForeignKey->ReferenceTableName), $this->objTypeTableArray))) {
// STEP 1: Create the New Reference
$objReference = new QReference();
// Retrieve the Column object
$objColumn = $objTable->ColumnArray[strtolower($strColumnName)];
// Setup Key Name
$objReference->KeyName = $objForeignKey->KeyName;
$strReferencedTableName = $objForeignKey->ReferenceTableName;
// Setup IsType flag
if (array_key_exists(strtolower($strReferencedTableName), $this->objTypeTableArray)) {
$objReference->IsType = true;
} else {
$objReference->IsType = false;
}
// Setup Table and Column names
$objReference->Table = $strReferencedTableName;
$objReference->Column = $objForeignKey->ReferenceColumnNameArray[0];
// Setup VariableType
$objReference->VariableType = $this->ModelClassName($strReferencedTableName);
// Setup PropertyName and VariableName
$objReference->PropertyName = $this->ModelReferencePropertyName($objColumn->Name);
$objReference->VariableName = $this->ModelReferenceVariableName($objColumn->Name);
$objReference->Name = $this->ModelReferenceColumnName($objColumn->Name);
// Add this reference to the column
$objColumn->Reference = $objReference;
// References will not have been correctly read earlier, so try again with the reference name
$objColumn->Options = $this->objModelConnectorOptions->GetOptions($objTable->ClassName, $objReference->PropertyName) + $objColumn->Options;
// STEP 2: Setup the REVERSE Reference for Non Type-based References
if (!$objReference->IsType) {
// Retrieve the ReferencedTable object
// $objReferencedTable = $this->objTableArray[strtolower($objReference->Table)];
$objReferencedTable = $this->GetTable($objReference->Table);
$objReverseReference = new QReverseReference();
$objReverseReference->Reference = $objReference;
$objReverseReference->KeyName = $objReference->KeyName;
$objReverseReference->Table = $strTableName;
$objReverseReference->Column = $strColumnName;
$objReverseReference->NotNull = $objColumn->NotNull;
$objReverseReference->Unique = $objColumn->Unique;
$objReverseReference->PropertyName = $this->ModelColumnPropertyName($strColumnName);
$objReverseReference->ObjectDescription = $this->CalculateObjectDescription($strTableName, $strColumnName, $strReferencedTableName, false);
$objReverseReference->ObjectDescriptionPlural = $this->CalculateObjectDescription($strTableName, $strColumnName, $strReferencedTableName, true);
$objReverseReference->VariableName = $this->ModelReverseReferenceVariableName($objTable->Name);
$objReverseReference->VariableType = $this->ModelReverseReferenceVariableType($objTable->Name);
// For Special Case ReverseReferences, calculate Associated MemberVariableName and PropertyName...
// See if ReverseReference is due to an ORM-based Class Inheritence Chain
if ((count($objTable->PrimaryKeyColumnArray) == 1) && ($objColumn->PrimaryKey)) {
$objReverseReference->ObjectMemberVariable = QConvertNotation::PrefixFromType(QType::Object) . $objReverseReference->VariableType;
$objReverseReference->ObjectPropertyName = $objReverseReference->VariableType;
$objReverseReference->ObjectDescription = $objReverseReference->VariableType;
$objReverseReference->ObjectDescriptionPlural = $this->Pluralize($objReverseReference->VariableType);
// Otherwise, see if it's just plain ol' unique
} else if ($objColumn->Unique) {
$objReverseReference->ObjectMemberVariable = $this->CalculateObjectMemberVariable($strTableName, $strColumnName, $strReferencedTableName);
$objReverseReference->ObjectPropertyName = $this->CalculateObjectPropertyName($strTableName, $strColumnName, $strReferencedTableName);
// get override options for codegen
$objReverseReference->Options = $this->objModelConnectorOptions->GetOptions($objReference->VariableType, $objReverseReference->ObjectDescription);
}
$objReference->ReverseReference = $objReverseReference; // Let forward reference also see things from the other side looking back
// Add this ReverseReference to the referenced table's ReverseReferenceArray
$objArray = $objReferencedTable->ReverseReferenceArray;
array_push($objArray, $objReverseReference);
$objReferencedTable->ReverseReferenceArray = $objArray;
}
} else {
$this->strErrors .= sprintf("Foreign Key %s in table %s references a table %s that does not appear to exist.\n",
$objForeignKey->KeyName, $strTableName, $objForeignKey->ReferenceTableName);
}
} else {
$this->strErrors .= sprintf("Foreign Key %s in table %s indexes on a column that does not appear to exist.\n",
$objForeignKey->KeyName, $strTableName);
}
}
}
// Verify: Table Name is valid (alphanumeric + "_" characters only, must not start with a number)
// and NOT a PHP Reserved Word
$strMatches = array();
preg_match('/' . $this->strPatternTableName . '/', $strTableName, $strMatches);
if (count($strMatches) && ($strMatches[0] == $strTableName) && ($strTableName != '_')) {
// Setup Reserved Words
$strReservedWords = explode(',', QCodeGen::PhpReservedWords);
for ($intIndex = 0; $intIndex < count($strReservedWords); $intIndex++)
$strReservedWords[$intIndex] = strtolower(trim($strReservedWords[$intIndex]));
$strTableNameToTest = trim(strtolower($strTableName));
foreach ($strReservedWords as $strReservedWord)
if ($strTableNameToTest == $strReservedWord) {
$this->strErrors .= sprintf("Table '%s' has a table name which is a PHP reserved word.\r\n", $strTableName);
unset($this->objTableArray[strtolower($strTableName)]);
return;
}
} else {
$this->strErrors .= sprintf("Table '%s' can only contain characters that are alphanumeric or _, and must not begin with a number.\r\n", $strTableName);
unset($this->objTableArray[strtolower($strTableName)]);
return;
}
// Verify: Column Names are all valid names
$objColumnArray = $objTable->ColumnArray;
foreach ($objColumnArray as $objColumn) {
$strColumnName = $objColumn->Name;
$strMatches = array();
preg_match('/' . $this->strPatternColumnName . '/', $strColumnName, $strMatches);
if (count($strMatches) && ($strMatches[0] == $strColumnName) && ($strColumnName != '_')) {
} else {
$this->strErrors .= sprintf("Table '%s' has an invalid column name: '%s'\r\n", $strTableName, $strColumnName);
unset($this->objTableArray[strtolower($strTableName)]);
return;
}
}
// Verify: Table has at least one PK
$blnFoundPk = false;
$objColumnArray = $objTable->ColumnArray;
foreach ($objColumnArray as $objColumn) {
if ($objColumn->PrimaryKey)
$blnFoundPk = true;
}
if (!$blnFoundPk) {
$this->strErrors .= sprintf("Table %s does not have any defined primary keys.\n", $strTableName);
unset($this->objTableArray[strtolower($strTableName)]);
return;
}
}
protected function AnalyzeTableColumn(QDatabaseFieldBase $objField, $objTable) {
$objColumn = new QSqlColumn();
$objColumn->Name = $objField->Name;
$objColumn->OwnerTable = $objTable;
if (substr_count($objField->Name, "-")) {
$tableName = $objTable ? " in table " . $objTable->Name : "";
$this->strErrors .= "Invalid column name" . $tableName . ": " . $objField->Name . ". Dashes are not allowed.";
return null;
}
$objColumn->DbType = $objField->Type;
$objColumn->VariableType = $this->VariableTypeFromDbType($objColumn->DbType);
$objColumn->VariableTypeAsConstant = QType::Constant($objColumn->VariableType);
$objColumn->Length = $objField->MaxLength;
$objColumn->Default = $objField->Default;
$objColumn->PrimaryKey = $objField->PrimaryKey;
$objColumn->NotNull = $objField->NotNull;
$objColumn->Identity = $objField->Identity;
$objColumn->Unique = $objField->Unique;
$objColumn->Timestamp = $objField->Timestamp;
$objColumn->VariableName = $this->ModelColumnVariableName($objColumn);
$objColumn->PropertyName = $this->ModelColumnPropertyName($objColumn->Name);
// separate overrides embedded in the comment
// extract options embedded in the comment field
if (($strComment = $objField->Comment) &&
($pos1 = strpos ($strComment, '{')) !== false &&
($pos2 = strrpos ($strComment, '}', $pos1))) {
$strJson = substr ($strComment, $pos1, $pos2 - $pos1 + 1);
$a = json_decode($strJson, true);
if ($a) {
$objColumn->Options = $a;
$objColumn->Comment = substr ($strComment, 0, $pos1) . substr ($strComment, $pos2 + 1); // return comment without options
if (!empty ($a['Timestamp'])) {
$objColumn->Timestamp = true; // alternate way to specify that a column is a self-updating timestamp
}
if ($objColumn->Timestamp && !empty($a['AutoUpdate'])) {
$objColumn->AutoUpdate = true;
}
} else {
$objColumn->Comment = $strComment;
}
}
// merge with options found in the design editor, letting editor take precedence
$objColumn->Options = $this->objModelConnectorOptions->GetOptions($objTable->ClassName, $objColumn->PropertyName) + $objColumn->Options;
return $objColumn;
}
protected function StripPrefixFromTable($strTableName) {
// If applicable, strip any StripTablePrefix from the table name
if ($this->intStripTablePrefixLength &&
(strlen($strTableName) > $this->intStripTablePrefixLength) &&
(substr($strTableName, 0, $this->intStripTablePrefixLength - strlen($strTableName)) == $this->strStripTablePrefix))
return substr($strTableName, $this->intStripTablePrefixLength);
return $strTableName;
}
protected function GetForeignKeyForQcubedRelationshipDefinition($strTableName, $strLine) {
$strTokens = explode('=>', $strLine);
if (count($strTokens) != 2) {
$this->strErrors .= sprintf("Could not parse Relationships Script reference: %s (Incorrect Format)\r\n", $strLine);
$this->strRelationshipLinesQcubed[$strLine] = null;
return null;
}
$strSourceTokens = explode('.', $strTokens[0]);
$strDestinationTokens = explode('.', $strTokens[1]);
if ((count($strSourceTokens) != 2) ||
(count($strDestinationTokens) != 2)) {
$this->strErrors .= sprintf("Could not parse Relationships Script reference: %s (Incorrect Table.Column Format)\r\n", $strLine);
$this->strRelationshipLinesQcubed[$strLine] = null;
return null;
}
$strColumnName = trim($strSourceTokens[1]);
$strReferenceTableName = trim($strDestinationTokens[0]);
$strReferenceColumnName = trim($strDestinationTokens[1]);
$strFkName = sprintf('virtualfk_%s_%s', $strTableName, $strColumnName);
if (strtolower($strTableName) == trim($strSourceTokens[0])) {
$this->strRelationshipLinesQcubed[$strLine] = null;
return $this->GetForeignKeyHelper($strLine, $strFkName, $strTableName, $strColumnName, $strReferenceTableName, $strReferenceColumnName);
}
return null;
}
protected function GetForeignKeyForSqlRelationshipDefinition($strTableName, $strLine) {
$strMatches = array();
// Start
$strPattern = '/alter[\s]+table[\s]+';
// Table Name
$strPattern .= '[\[\`\'\"]?(' . $this->strPatternTableName . ')[\]\`\'\"]?[\s]+';
// Add Constraint
$strPattern .= '(add[\s]+)?(constraint[\s]+';
$strPattern .= '[\[\`\'\"]?(' . $this->strPatternKeyName . ')[\]\`\'\"]?[\s]+)?[\s]*';
// Foreign Key
$strPattern .= 'foreign[\s]+key[\s]*(' . $this->strPatternKeyName . ')[\s]*\(';
$strPattern .= '([^)]+)\)[\s]*';
// References
$strPattern .= 'references[\s]+';
$strPattern .= '[\[\`\'\"]?(' . $this->strPatternTableName . ')[\]\`\'\"]?[\s]*\(';
$strPattern .= '([^)]+)\)[\s]*';
// End
$strPattern .= '/';
// Perform the RegExp
preg_match($strPattern, $strLine, $strMatches);
if (count($strMatches) == 9) {
$strColumnName = trim($strMatches[6]);
$strReferenceTableName = trim($strMatches[7]);
$strReferenceColumnName = trim($strMatches[8]);
$strFkName = $strMatches[5];
if (!$strFkName)
$strFkName = sprintf('virtualfk_%s_%s', $strTableName, $strColumnName);
if ((strpos($strColumnName, ',') !== false) ||
(strpos($strReferenceColumnName, ',') !== false)) {
$this->strErrors .= sprintf("Relationships Script has a foreign key definition with multiple columns: %s (Multiple-columned FKs are not supported by the code generator)\r\n", $strLine);
$this->strRelationshipLinesSql[$strLine] = null;
return null;
}
// Cleanup strColumnName nad strreferenceColumnName
$strColumnName = str_replace("'", '', $strColumnName);
$strColumnName = str_replace('"', '', $strColumnName);
$strColumnName = str_replace('[', '', $strColumnName);
$strColumnName = str_replace(']', '', $strColumnName);
$strColumnName = str_replace('`', '', $strColumnName);
$strColumnName = str_replace(' ', '', $strColumnName);
$strColumnName = str_replace(' ', '', $strColumnName);
$strColumnName = str_replace("\r", '', $strColumnName);
$strColumnName = str_replace("\n", '', $strColumnName);
$strReferenceColumnName = str_replace("'", '', $strReferenceColumnName);
$strReferenceColumnName = str_replace('"', '', $strReferenceColumnName);
$strReferenceColumnName = str_replace('[', '', $strReferenceColumnName);
$strReferenceColumnName = str_replace(']', '', $strReferenceColumnName);
$strReferenceColumnName = str_replace('`', '', $strReferenceColumnName);
$strReferenceColumnName = str_replace(' ', '', $strReferenceColumnName);
$strReferenceColumnName = str_replace(' ', '', $strReferenceColumnName);
$strReferenceColumnName = str_replace("\r", '', $strReferenceColumnName);
$strReferenceColumnName = str_replace("\n", '', $strReferenceColumnName);
if (strtolower($strTableName) == trim($strMatches[1])) {
$this->strRelationshipLinesSql[$strLine] = null;
return $this->GetForeignKeyHelper($strLine, $strFkName, $strTableName, $strColumnName, $strReferenceTableName, $strReferenceColumnName);
}
return null;
} else {
$this->strErrors .= sprintf("Could not parse Relationships Script reference: %s (Not in ANSI SQL Format)\r\n", $strLine);
$this->strRelationshipLinesSql[$strLine] = null;
return null;
}
}
protected function GetForeignKeyHelper($strLine, $strFkName, $strTableName, $strColumnName, $strReferencedTable, $strReferencedColumn) {
// Make Sure Tables/Columns Exist, or display error otherwise
if (!$this->ValidateTableColumn($strTableName, $strColumnName)) {
$this->strErrors .= sprintf("Could not parse Relationships Script reference: \"%s\" (\"%s.%s\" does not exist)\r\n",
$strLine, $strTableName, $strColumnName);
return null;
}
if (!$this->ValidateTableColumn($strReferencedTable, $strReferencedColumn)) {
$this->strErrors .= sprintf("Could not parse Relationships Script reference: \"%s\" (\"%s.%s\" does not exist)\r\n",
$strLine, $strReferencedTable, $strReferencedColumn);
return null;
}
return new QDatabaseForeignKey($strFkName, array($strColumnName), $strReferencedTable, array($strReferencedColumn));
}
/**
* This will go through the various Relationships Script lines (if applicable) as setup during
* the __constructor() through the <relationships> and <relationshipsScript> tags in the
* configuration settings.
*
* If no Relationships are defined, this method will simply exit making no changes.
*
* @param string $strTableName Name of the table to pull foreign keys for
* @param DatabaseForeignKeyBase[] Array of currently found DB FK objects which will be appended to
* @return DatabaseForeignKeyBase[] Array of DB FK objects that were parsed out
*/
protected function GetForeignKeysFromRelationshipsScript($strTableName, $objForeignKeyArray) {
foreach ($this->strRelationshipLinesQcubed as $strLine) {
if ($strLine) {
$objForeignKey = $this->GetForeignKeyForQcubedRelationshipDefinition($strTableName, $strLine);
if ($objForeignKey) {
array_push($objForeignKeyArray, $objForeignKey);
$this->strRelationshipLinesQcubed[$strLine] = null;
}
}
}
foreach ($this->strRelationshipLinesSql as $strLine) {
if ($strLine) {
$objForeignKey = $this->GetForeignKeyForSqlRelationshipDefinition($strTableName, $strLine);
if ($objForeignKey) {
array_push($objForeignKeyArray, $objForeignKey);
$this->strRelationshipLinesSql[$strLine] = null;
}
}
}
return $objForeignKeyArray;
}
public function GenerateControlId($objTable, $objColumn) {
$strControlId = null;
if (isset($objColumn->Options['ControlId'])) {
$strControlId = $objColumn->Options['ControlId'];
} elseif ($this->blnGenerateControlId) {
$strObjectName = $this->ModelVariableName($objTable->Name);
$strClassName = $objTable->ClassName;
$strControlVarName = $this->ModelConnectorVariableName($objColumn);
$strLabelName = QCodeGen::ModelConnectorControlName($objColumn);
$strControlId = $strControlVarName . $strClassName;
}
return $strControlId;
}
/**
* Returns a string that will cast a variable coming from the database into a php type.
* Doing this in the template saves significant amounts of time over using QType::Cast() or GetColumn.
* @param QSqlColumn $objColumn
* @return string
* @throws Exception
*/
public function GetCastString (QSqlColumn $objColumn) {
switch ($objColumn->DbType) {
case QDatabaseFieldType::Bit:
return ('$mixVal = (bool)$mixVal;');
case QDatabaseFieldType::Blob:
case QDatabaseFieldType::Char:
case QDatabaseFieldType::VarChar:
case QDatabaseFieldType::Json:
return ''; // no need to cast, since its already a string or a null
case QDatabaseFieldType::Date:
return ('$mixVal = new QDateTime($mixVal, null, QDateTime::DateOnlyType);');
case QDatabaseFieldType::DateTime:
return ('$mixVal = new QDateTime($mixVal);');
case QDatabaseFieldType::Time:
return ('$mixVal = new QDateTime($mixVal, null, QDateTime::TimeOnlyType);');
case QDatabaseFieldType::Float:
case QDatabaseFieldType::Integer:
return ('$mixVal = (' . $objColumn->VariableType . ')$mixVal;');
default:
throw new Exception ('Invalid database field type');
exit;
}
}
////////////////////
// Public Overriders
////////////////////
/**
* Override method to perform a property "Get"
* This will get the value of $strName
*
* @param string strName Name of the property to get
* @return mixed
*/
public function __get($strName) {
switch ($strName) {
case 'TableArray':
return $this->objTableArray;
case 'TypeTableArray':
return $this->objTypeTableArray;
case 'DatabaseIndex':
return $this->intDatabaseIndex;
case 'CommentConnectorLabelDelimiter':
return $this->strCommentConnectorLabelDelimiter;
case 'AutoInitialize':
return $this->blnAutoInitialize;
case 'PrivateColumnVars':
return $this->blnPrivateColumnVars;
case 'objSettingsXml':
throw new QCallerException('The field objSettingsXml is deprecated');
default:
try {
return parent::__get($strName);
} catch (QCallerException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
}
}
public function __set($strName, $mixValue) {
try {
switch($strName) {
default:
return parent::__set($strName, $mixValue);
}
} catch (QCallerException $objExc) {
$objExc->IncrementOffset();
}
}
}
function array_trim(&$strValue) {
$strValue = trim($strValue);
}