protected function relateI18nTableToMainTable() { $table = $this->getTable(); $i18nTable = $this->i18nTable; $pks = $this->getTable()->getPrimaryKey(); if (count($pks) > 1) { throw new EngineException('The i18n behavior does not support tables with composite primary keys'); } foreach ($pks as $column) { if (!$i18nTable->hasColumn($column->getName())) { $column = clone $column; $column->setAutoIncrement(false); $i18nTable->addColumn($column); } } if (in_array($table->getName(), $i18nTable->getForeignTableNames())) { return; } $fk = new ForeignKey(); $fk->setForeignTableCommonName($table->getCommonName()); $fk->setForeignSchemaName($table->getSchema()); $fk->setDefaultJoin('LEFT JOIN'); $fk->setOnDelete(ForeignKey::CASCADE); $fk->setOnUpdate(ForeignKey::NONE); foreach ($pks as $column) { $fk->addReference($column->getName(), $column->getName()); } $i18nTable->addForeignKey($fk); }
protected function addClosureColumn($name, Table $ct_table, Column $column) { $table = $this->getTable(); $id_fieldname = $column->getName(); $domain = $column->getDomain(); if (!$ct_table->hasColumn($name)) { $column = new Column($name); $column->setDomain($domain); $column->setPrimaryKey(true); $ct_table->addColumn($column); } else { $column = $ct_table->getColumn($name); } $ct_tablename_normalized = str_replace('_', '', $ct_table->getName()); $fk_name = $ct_tablename_normalized . '_' . $name . '_fk'; if (!$ct_table->getColumnForeignKeys($name)) { $column_fk = new ForeignKey($fk_name); $column_fk->addReference($name, $table->getColumn($id_fieldname)->getName()); $column_fk->setForeignTableCommonName($table->getName()); $column_fk->setOnUpdate('cascade'); $column_fk->setOnDelete('restrict'); $ct_table->addForeignKey($column_fk); } $column_idx_name = $fk_name . '_idx'; if (!$ct_table->hasIndex($column_idx_name)) { $column_idx = new Index($column_idx_name); $column_idx->addColumn(['name' => $column->getName()]); $ct_table->addIndex($column_idx); } }
/** * Adds all columns, indexes, constraints and additional tables. */ public function modifyTable() { $table = $this->getTable(); $tableName = $table->getName(); $foreignTableName = $this->getForeignTable(); // enable reload on insert to force the model to load the trigger generated id(s) $table->setReloadOnInsert(true); $foreignIdColumnName = $foreignTableName . '_id'; $compositeKeyColumnName = $foreignTableName . '_' . $tableName . '_id'; if ($table->hasBehavior('concrete_inheritance')) { // we're a child in a concrete inheritance $parentTableName = $table->getBehavior('concrete_inheritance')->getParameter('extends'); $parentTable = $table->getDatabase()->getTable($parentTableName); if ($parentTable->hasBehavior('\\' . __CLASS__)) { //we're a child of a concrete inheritance structure, so we're going to skip this //round here because this behavior has also been attached by the parent table. return; } } if ($table->hasColumn($foreignIdColumnName)) { $foreignIdColumn = $table->getColumn($foreignIdColumnName); } else { $foreignIdColumn = $table->addColumn(array('name' => $foreignIdColumnName, 'type' => 'integer', 'required' => true)); $compositeKeyForeignKeyName = $tableName . '_FK_' . $foreignIdColumnName; $foreignKey = new ForeignKey($compositeKeyForeignKeyName); $foreignKey->addReference($foreignIdColumnName, 'id'); $foreignKey->setForeignTableCommonName($foreignTableName); $foreignKey->setOnUpdate(ForeignKey::CASCADE); $foreignKey->setOnDelete(ForeignKey::CASCADE); $table->addForeignKey($foreignKey); } if ($table->hasColumn($compositeKeyColumnName)) { $compositeKeyColumn = $table->getColumn($compositeKeyColumnName); } else { $compositeKeyColumn = $table->addColumn(array('name' => $compositeKeyColumnName, 'type' => 'integer', 'required' => false)); } $index = new Unique($tableName . '_UQ_' . $foreignIdColumnName . '_' . $compositeKeyColumnName); $index->addColumn($foreignIdColumn); $index->addColumn($compositeKeyColumn); $table->addUnique($index); $database = $table->getDatabase(); $sequenceTableName = sprintf('%s_sequence', $foreignTableName); if (!$database->hasTable($sequenceTableName)) { $sequenceTable = $database->addTable(array('name' => $sequenceTableName, 'package' => $table->getPackage(), 'schema' => $table->getSchema(), 'namespace' => $table->getNamespace() ? '\\' . $table->getNamespace() : null, 'skipSql' => $table->isSkipSql())); $sequenceTable->addColumn(array('name' => 'table_name', 'type' => 'varchar', 'size' => 32, 'required' => true, 'primaryKey' => true)); $sequenceTable->addColumn(array('name' => $foreignIdColumnName, 'type' => 'integer', 'required' => true, 'primaryKey' => true)); $sequenceTable->addColumn(array('name' => $foreignTableName . '_max_sequence_id', 'type' => 'integer', 'required' => false, 'default' => null)); } }
protected function relateDelegateToMainTable($delegateTable, $mainTable) { $pks = $mainTable->getPrimaryKey(); foreach ($pks as $column) { $mainColumnName = $column->getName(); if (!$delegateTable->hasColumn($mainColumnName)) { $column = clone $column; $column->setAutoIncrement(false); $delegateTable->addColumn($column); } } // Add a one-to-one fk $fk = new ForeignKey(); $fk->setForeignTableCommonName($mainTable->getCommonName()); $fk->setForeignSchemaName($mainTable->getSchema()); $fk->setDefaultJoin('LEFT JOIN'); $fk->setOnDelete(ForeignKey::CASCADE); $fk->setOnUpdate(ForeignKey::NONE); foreach ($pks as $column) { $fk->addReference($column->getName(), $column->getName()); } $delegateTable->addForeignKey($fk); }
public function getForeignKeyDDL(ForeignKey $fk) { if ($fk->isSkipSql() || $fk->isPolymorphic()) { return; } $pattern = 'CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)'; $script = sprintf($pattern, $this->quoteIdentifier($fk->getName()), $this->getColumnListDDL($fk->getLocalColumnObjects()), $this->quoteIdentifier($fk->getForeignTableName()), $this->getColumnListDDL($fk->getForeignColumnObjects())); if ($fk->hasOnUpdate() && $fk->getOnUpdate() != ForeignKey::SETNULL) { $script .= ' ON UPDATE ' . $fk->getOnUpdate(); } if ($fk->hasOnDelete() && $fk->getOnDelete() != ForeignKey::SETNULL) { $script .= ' ON DELETE ' . $fk->getOnDelete(); } return $script; }
protected static function getRefRelatedBySuffix(ForeignKey $fk) { $relCol = ''; foreach ($fk->getLocalForeignMapping() as $localColumnName => $foreignColumnName) { $localTable = $fk->getTable(); $localColumn = $localTable->getColumn($localColumnName); if (!$localColumn) { throw new RuntimeException(sprintf('Could not fetch column: %s in table %s.', $localColumnName, $localTable->getName())); } $foreignKeysToForeignTable = $localTable->getForeignKeysReferencingTable($fk->getForeignTableName()); if ($fk->getForeignTableName() == $fk->getTableName()) { // self referential foreign key $relCol .= $fk->getForeignTable()->getColumn($foreignColumnName)->getPhpName(); if (count($foreignKeysToForeignTable) > 1) { // several self-referential foreign keys $relCol .= array_search($fk, $foreignKeysToForeignTable); } } elseif (count($foreignKeysToForeignTable) > 1 || count($fk->getForeignTable()->getForeignKeysReferencingTable($fk->getTableName())) > 0) { // several foreign keys to the same table, or symmetrical foreign key in foreign table $relCol .= $localColumn->getPhpName(); } } if (!empty($relCol)) { $relCol = 'RelatedBy' . $relCol; } return $relCol; }
public function getForeignKeyDDL(ForeignKey $fk) { if ($fk->isSkipSql() || !$this->foreignKeySupport) { return; } $pattern = "FOREIGN KEY (%s) REFERENCES %s (%s)"; $script = sprintf($pattern, $this->getColumnListDDL($fk->getLocalColumns()), $this->quoteIdentifier($fk->getForeignTableName()), $this->getColumnListDDL($fk->getForeignColumns())); if ($fk->hasOnUpdate()) { $script .= "\n ON UPDATE " . $fk->getOnUpdate(); } if ($fk->hasOnDelete()) { $script .= "\n ON DELETE " . $fk->getOnDelete(); } return $script; }
protected function addForeignKeys(Table $table) { $database = $table->getDatabase(); $stmt = $this->dbh->query('PRAGMA foreign_key_list("' . $table->getName() . '")'); $lastId = null; while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { if ($lastId !== $row['id']) { $fk = new ForeignKey(); $onDelete = $row['on_delete']; if ($onDelete && 'NO ACTION' !== $onDelete) { $fk->setOnDelete($onDelete); } $onUpdate = $row['on_update']; if ($onUpdate && 'NO ACTION' !== $onUpdate) { $fk->setOnUpdate($onUpdate); } $foreignTable = $database->getTable($row['table'], true); if (!$foreignTable) { continue; } $table->addForeignKey($fk); $fk->setForeignTableCommonName($foreignTable->getCommonName()); if ($table->guessSchemaName() != $foreignTable->guessSchemaName()) { $fk->setForeignSchemaName($foreignTable->guessSchemaName()); } $lastId = $row['id']; } $fk->addReference($row['from'], $row['to']); } }
protected function addRefFkScheduledForDeletionAttribute(&$script, ForeignKey $refFK) { $className = $this->getClassNameFromTable($refFK->getTable()); $fkName = lcfirst($this->getRefFKPhpNameAffix($refFK, true)); $script .= "\n /**\n * An array of objects scheduled for deletion.\n * @var ObjectCollection|{$className}[]\n */\n protected \${$fkName}ScheduledForDeletion = null;\n"; }
public function modifyTable() { $table = $this->getTable(); $parentTable = $this->getParentTable(); if ($this->isCopyData()) { // tell the parent table that it has a descendant if (!$parentTable->hasBehavior('concrete_inheritance_parent')) { $parentBehavior = new ConcreteInheritanceParentBehavior(); $parentBehavior->setName('concrete_inheritance_parent'); $parentBehavior->addParameter(array('name' => 'descendant_column', 'value' => $this->getParameter('descendant_column'))); $parentTable->addBehavior($parentBehavior); // The parent table's behavior modifyTable() must be executed before this one $parentBehavior->getTableModifier()->modifyTable(); $parentBehavior->setTableModified(true); } } // Add the columns of the parent table foreach ($parentTable->getColumns() as $column) { if ($column->getName() == $this->getParameter('descendant_column')) { continue; } if ($table->hasColumn($column->getName())) { continue; } $copiedColumn = clone $column; if ($column->isAutoIncrement() && $this->isCopyData()) { $copiedColumn->setAutoIncrement(false); } $table->addColumn($copiedColumn); if ($column->isPrimaryKey() && $this->isCopyData()) { $fk = new ForeignKey(); $fk->setForeignTableCommonName($column->getTable()->getCommonName()); $fk->setForeignSchemaName($column->getTable()->getSchema()); $fk->setOnDelete('CASCADE'); $fk->setOnUpdate(null); $fk->addReference($copiedColumn, $column); $fk->isParentChild = true; $table->addForeignKey($fk); } } // add the foreign keys of the parent table foreach ($parentTable->getForeignKeys() as $fk) { $copiedFk = clone $fk; $copiedFk->setName(''); $copiedFk->setRefPhpName(''); $this->getTable()->addForeignKey($copiedFk); } // add the indices of the parent table foreach ($parentTable->getIndices() as $index) { $copiedIndex = clone $index; $copiedIndex->setName(''); $this->getTable()->addIndex($copiedIndex); } // add the unique indices of the parent table foreach ($parentTable->getUnices() as $unique) { $copiedUnique = clone $unique; $copiedUnique->setName(''); $this->getTable()->addUnique($copiedUnique); } // add the Behaviors of the parent table foreach ($parentTable->getBehaviors() as $behavior) { if ($behavior->getName() == 'concrete_inheritance_parent' || $behavior->getName() == 'concrete_inheritance') { continue; } //validate behavior. If validate behavior already exists, clone only rules from parent if ('validate' === $behavior->getName() && $table->hasBehavior('validate')) { $table->getBehavior('validate')->mergeParameters($behavior->getParameters()); continue; } $copiedBehavior = clone $behavior; $copiedBehavior->setTableModified(false); $this->getTable()->addBehavior($copiedBehavior); } }
/** * @param string &$script The script will be modified in this method. * @param ForeignKey $refFK * @param ForeignKey $crossFK */ protected function addCrossFKDoAdd(&$script, ForeignKey $refFK, ForeignKey $crossFK) { $relatedObjectClassName = $this->getFKPhpNameAffix($crossFK, $plural = false); // lcfirst() doesn't exist in PHP < 5.3 $lowerRelatedObjectClassName = $relatedObjectClassName; $lowerRelatedObjectClassName[0] = strtolower($lowerRelatedObjectClassName[0]); $joinedTableObjectBuilder = $this->getNewObjectBuilder($refFK->getTable()); $className = $joinedTableObjectBuilder->getObjectClassname(); $tblFK = $refFK->getTable(); $foreignObjectName = '$' . $tblFK->getStudlyPhpName(); $script .= "\n /**\n * @param {$relatedObjectClassName} \${$lowerRelatedObjectClassName} The {$lowerRelatedObjectClassName} object to add.\n */\n protected function doAdd{$relatedObjectClassName}(\${$lowerRelatedObjectClassName})\n {\n {$foreignObjectName} = new {$className}();\n {$foreignObjectName}->set{$relatedObjectClassName}(\${$lowerRelatedObjectClassName});\n \$this->add{$className}({$foreignObjectName});\n }\n"; }
/** * Appends the generated <foreign-key> XML node to its parent node. * * @param ForeignKey $foreignKey The ForeignKey model instance * @param \DOMNode $parentNode The parent DOMNode object */ private function appendForeignKeyNode(ForeignKey $foreignKey, \DOMNode $parentNode) { $foreignKeyNode = $parentNode->appendChild($this->document->createElement('foreign-key')); $foreignKeyNode->setAttribute('foreignTable', $foreignKey->getForeignTableCommonName()); if ($schema = $foreignKey->getForeignSchemaName()) { $foreignKeyNode->setAttribute('foreignSchema', $schema); } $foreignKeyNode->setAttribute('name', $foreignKey->getName()); if ($phpName = $foreignKey->getPhpName()) { $foreignKeyNode->setAttribute('phpName', $phpName); } if ($refPhpName = $foreignKey->getRefPhpName()) { $foreignKeyNode->setAttribute('refPhpName', $refPhpName); } if ($defaultJoin = $foreignKey->getDefaultJoin()) { $foreignKeyNode->setAttribute('defaultJoin', $defaultJoin); } if ($onDeleteBehavior = $foreignKey->getOnDelete()) { $foreignKeyNode->setAttribute('onDelete', $onDeleteBehavior); } if ($onUpdateBehavior = $foreignKey->getOnUpdate()) { $foreignKeyNode->setAttribute('onUpdate', $onUpdateBehavior); } for ($i = 0, $size = count($foreignKey->getLocalColumns()); $i < $size; $i++) { $refNode = $foreignKeyNode->appendChild($this->document->createElement('reference')); $refNode->setAttribute('local', $foreignKey->getLocalColumnName($i)); $refNode->setAttribute('foreign', $foreignKey->getForeignColumnName($i)); } foreach ($foreignKey->getVendorInformation() as $vendorInformation) { $this->appendVendorInformationNode($vendorInformation, $foreignKeyNode); } }
private function addSnapshotTable() { $table = $this->getTable(); $primaryKeyColumn = $table->getFirstPrimaryKeyColumn(); $database = $table->getDatabase(); $snapshotTableName = $this->getParameter(self::PARAMETER_SNAPSHOT_TABLE) ?: $this->getDefaultSnapshotTableName(); if ($database->hasTable($snapshotTableName)) { $this->snapshotTable = $database->getTable($snapshotTableName); return; } $snapshotTable = $database->addTable(['name' => $snapshotTableName, 'phpName' => $this->getParameter(self::PARAMETER_SNAPSHOT_PHPNAME), 'package' => $table->getPackage(), 'schema' => $table->getSchema(), 'namespace' => $table->getNamespace() ? '\\' . $table->getNamespace() : null]); $addSnapshotAt = true; $hasTimestampableBehavior = 0 < count(array_filter($database->getBehaviors(), function (Behavior $behavior) { return 'timestampable' === $behavior->getName(); })); if ($hasTimestampableBehavior) { $addSnapshotAt = false; $timestampableBehavior = clone $database->getBehavior('timestampable'); $timestampableBehavior->setParameters(array_merge($timestampableBehavior->getParameters(), ['create_column' => $this->getParameter(self::PARAMETER_SNAPSHOT_AT_COLUMN), 'disable_updated_at' => 'true'])); $snapshotTable->addBehavior($timestampableBehavior); } $snapshotTable->isSnapshotTable = true; $idColumn = $snapshotTable->addColumn(['name' => 'id', 'type' => 'INTEGER']); $idColumn->setAutoIncrement(true); $idColumn->setPrimaryKey(true); $idColumn->setNotNull(true); $columns = $table->getColumns(); foreach ($columns as $column) { if ($column->isPrimaryKey()) { continue; } $columnInSnapshotTable = clone $column; $columnInSnapshotTable->setNotNull(false); if ($columnInSnapshotTable->hasReferrers()) { $columnInSnapshotTable->clearReferrers(); } if ($columnInSnapshotTable->isAutoincrement()) { $columnInSnapshotTable->setAutoIncrement(false); } $snapshotTable->addColumn($columnInSnapshotTable); } $foreignKeyColumn = $snapshotTable->addColumn(['name' => $this->getParameter(self::PARAMETER_REFERENCE_COLUMN), 'type' => $primaryKeyColumn->getType(), 'size' => $primaryKeyColumn->getSize()]); $index = new Index(); $index->setName($this->getParameter(self::PARAMETER_REFERENCE_COLUMN)); if ($primaryKeyColumn->getSize()) { $index->addColumn(['name' => $this->getParameter(self::PARAMETER_REFERENCE_COLUMN), 'size' => $primaryKeyColumn->getSize()]); } else { $index->addColumn(['name' => $this->getParameter(self::PARAMETER_REFERENCE_COLUMN)]); } $snapshotTable->addIndex($index); $foreignKey = new ForeignKey(); $foreignKey->setName(vsprintf('fk_%s_%s', [$snapshotTable->getOriginCommonName(), $this->getParameter(self::PARAMETER_REFERENCE_COLUMN)])); $foreignKey->setOnUpdate('CASCADE'); $foreignKey->setOnDelete('SET NULL'); $foreignKey->setForeignTableCommonName($this->getTable()->getCommonName()); $foreignKey->addReference($foreignKeyColumn, $primaryKeyColumn); $snapshotTable->addForeignKey($foreignKey); if ($this->getParameter(self::PARAMETER_LOG_SNAPSHOT_AT) == 'true' && $addSnapshotAt) { $snapshotTable->addColumn(['name' => $this->getParameter(self::PARAMETER_SNAPSHOT_AT_COLUMN), 'type' => 'TIMESTAMP']); } $indices = $table->getIndices(); foreach ($indices as $index) { $copiedIndex = clone $index; $snapshotTable->addIndex($copiedIndex); } // copy unique indices to indices // see https://github.com/propelorm/Propel/issues/175 for details $unices = $table->getUnices(); foreach ($unices as $unique) { $index = new Index(); $index->setName($unique->getName()); $columns = $unique->getColumns(); foreach ($columns as $columnName) { if ($size = $unique->getColumnSize($columnName)) { $index->addColumn(['name' => $columnName, 'size' => $size]); } else { $index->addColumn(['name' => $columnName]); } } $snapshotTable->addIndex($index); } $behaviors = $database->getBehaviors(); foreach ($behaviors as $behavior) { $behavior->modifyDatabase(); } $this->snapshotTable = $snapshotTable; }
public function providerForTestGetForeignKeysDDL() { $db = new Database(); $db->setIdentifierQuoting(true); $table1 = new Table('foo'); $db->addTable($table1); $column1 = new Column('bar_id'); $column1->getDomain()->copy(new Domain('FOOTYPE')); $table1->addColumn($column1); $table2 = new Table('bar'); $db->addTable($table2); $column2 = new Column('id'); $column2->getDomain()->copy(new Domain('BARTYPE')); $table2->addColumn($column2); $fk = new ForeignKey('foo_bar_fk'); $fk->setForeignTableCommonName('bar'); $fk->addReference($column1, $column2); $fk->setOnDelete('CASCADE'); $table1->addForeignKey($fk); $column3 = new Column('baz_id'); $column3->getDomain()->copy(new Domain('BAZTYPE')); $table1->addColumn($column3); $table3 = new Table('baz'); $db->addTable($table3); $column4 = new Column('id'); $column4->getDomain()->copy(new Domain('BAZTYPE')); $table3->addColumn($column4); $fk = new ForeignKey('foo_baz_fk'); $fk->setForeignTableCommonName('baz'); $fk->addReference($column3, $column4); $fk->setOnDelete('SETNULL'); $table1->addForeignKey($fk); return array(array($table1)); }
/** * Adds a relation from logTable to origin table. * * @param Table $logTable */ protected function addForeignKey(Table $logTable) { $table = $this->getTable(); if ($table->getForeignKeysReferencingTable($table->getName())) { //if already a foreignKey exist to origin table then don't add a second. return; } // create the foreign key $fk = new ForeignKey(); $fk->setForeignTableCommonName($table->getCommonName()); $fk->setForeignSchemaName($table->getSchema()); $fk->setPhpName('Origin'); $fk->setOnDelete('CASCADE'); $fk->setOnUpdate('CASCADE'); foreach ($table->getPrimaryKey() as $column) { $fk->addReference($logTable->getColumn($column->getName()), $column); } $logTable->addForeignKey($fk); }
$fkAuthorPost->setDefaultJoin('Criteria::LEFT_JOIN'); $fkAuthorPost->setOnDelete('CASCADE'); $fkCategoryPost = new ForeignKey('fk_post_has_category'); $fkCategoryPost->addReference('category_id', 'id'); $fkCategoryPost->setForeignTableCommonName('blog_category'); $fkCategoryPost->setRefPhpName('Posts'); $fkCategoryPost->setPhpName('Category'); $fkCategoryPost->setDefaultJoin('Criteria::INNER_JOIN'); $fkCategoryPost->setOnDelete('SETNULL'); $fkPostTag = new ForeignKey('fk_post_has_tags'); $fkPostTag->addReference('post_id', 'id'); $fkPostTag->setForeignTableCommonName('blog_post'); $fkPostTag->setPhpName('Post'); $fkPostTag->setDefaultJoin('Criteria::LEFT_JOIN'); $fkPostTag->setOnDelete('CASCADE'); $fkTagPost = new ForeignKey('fk_tag_has_posts'); $fkTagPost->addReference('tag_id', 'id'); $fkTagPost->setForeignTableCommonName('blog_tag'); $fkTagPost->setPhpName('Tag'); $fkTagPost->setDefaultJoin('Criteria::LEFT_JOIN'); $fkTagPost->setOnDelete('CASCADE'); /* Regular Indexes */ $pageContentFulltextIdx = new Index('page_content_fulltext_idx'); $pageContentFulltextIdx->setColumns([['name' => 'content']]); $pageContentFulltextIdx->addVendorInfo(new VendorInfo('mysql', ['Index_type' => 'FULLTEXT'])); /* Unique Indexes */ $authorUsernameUnique = new Unique('author_password_unique_idx'); $authorUsernameUnique->setColumns([['name' => 'username', 'size' => '8']]); /* Behaviors */ $timestampableBehavior = new TimestampableBehavior(); $timestampableBehavior->setName('timestampable');
public function getReferrerVersionsColumn(ForeignKey $fk) { $fkTableName = $fk->getTable()->getName(); $fkIdsColumnName = $fkTableName . '_versions'; return $this->versionTable->getColumn($fkIdsColumnName); }
public function getForeignKeyDDL(ForeignKey $fk) { $pattern = "\n-- SQLite does not support foreign keys; this is just for reference\n-- FOREIGN KEY (%s) REFERENCES %s (%s)\n"; return sprintf($pattern, $this->getColumnListDDL($fk->getLocalColumns()), $fk->getForeignTableName(), $this->getColumnListDDL($fk->getForeignColumns())); }
/** * Load foreign keys for this table. * * @param Table $table The Table model class to add FKs to */ protected function addForeignKeys(Table $table) { // local store to avoid duplicates $foreignKeys = array(); /* @var StatementInterface $stmt */ $stmt = $this->dbh->query("SELECT CONSTRAINT_NAME, DELETE_RULE, R_CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE CONSTRAINT_TYPE = 'R' AND TABLE_NAME = '" . $table->getName() . "'"); while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { // Local reference /* @var StatementInterface $stmt2 */ $stmt2 = $this->dbh->query("SELECT COLUMN_NAME FROM USER_CONS_COLUMNS WHERE CONSTRAINT_NAME = '" . $row['CONSTRAINT_NAME'] . "' AND TABLE_NAME = '" . $table->getName() . "'"); $localReferenceInfo = $stmt2->fetch(\PDO::FETCH_ASSOC); // Foreign reference $stmt2 = $this->dbh->query("SELECT TABLE_NAME, COLUMN_NAME FROM USER_CONS_COLUMNS WHERE CONSTRAINT_NAME = '" . $row['R_CONSTRAINT_NAME'] . "'"); $foreignReferenceInfo = $stmt2->fetch(\PDO::FETCH_ASSOC); if (!isset($foreignKeys[$row['CONSTRAINT_NAME']])) { $fk = new ForeignKey($row['CONSTRAINT_NAME']); $fk->setForeignTableCommonName($foreignReferenceInfo['TABLE_NAME']); $onDelete = 'NO ACTION' === $row['DELETE_RULE'] ? 'NONE' : $row['DELETE_RULE']; $fk->setOnDelete($onDelete); $fk->setOnUpdate($onDelete); $fk->addReference(array('local' => $localReferenceInfo['COLUMN_NAME'], 'foreign' => $foreignReferenceInfo['COLUMN_NAME'])); $table->addForeignKey($fk); $foreignKeys[$row['CONSTRAINT_NAME']] = $fk; } } }
/** * Adds a new foreign key to this table. * * @param ForeignKey|array * @return ForeignKey */ public function addForeignKey($fkdata) { if ($fkdata instanceof ForeignKey) { $fk = $fkdata; $fk->setTable($this); $this->foreignKeys[] = $fk; if (!in_array($fk->getForeignTableName(), $this->foreignTableNames)) { $this->foreignTableNames[] = $fk->getForeignTableName(); } return $fk; } $fk = new ForeignKey(); $fk->setTable($this); $fk->loadMapping($fkdata); return $this->addForeignKey($fk); }
/** * A utility function to create a new foreign key * from attrib and add it to this table. */ public function addForeignKey($fkdata) { if ($fkdata instanceof ForeignKey) { $fk = $fkdata; $fk->setTable($this); $this->foreignKeys[] = $fk; if ($this->foreignTableNames === null) { $this->foreignTableNames = array(); } if (!in_array($fk->getForeignTableName(), $this->foreignTableNames)) { $this->foreignTableNames[] = $fk->getForeignTableName(); } return $fk; } else { $fk = new ForeignKey(); $fk->setTable($this); $fk->loadFromXML($fkdata); return $this->addForeignKey($fk); } }
public function testToString() { $tableA = new Table('A'); $tableB = new Table('B'); $diff = new TableDiff($tableA, $tableB); $diff->addAddedColumn('id', new Column('id', 'integer')); $diff->addRemovedColumn('category_id', new Column('category_id', 'integer')); $colFoo = new Column('foo', 'integer'); $colBar = new Column('bar', 'integer'); $tableA->addColumn($colFoo); $tableA->addColumn($colBar); $diff->addRenamedColumn($colFoo, $colBar); $columnDiff = new ColumnDiff($colFoo, $colBar); $diff->addModifiedColumn('foo', $columnDiff); $fk = new ForeignKey('category'); $fk->setTable($tableA); $fk->setForeignTableCommonName('B'); $fk->addReference('category_id', 'id'); $fkChanged = clone $fk; $fkChanged->setForeignTableCommonName('C'); $fkChanged->addReference('bla', 'id2'); $fkChanged->setOnDelete('cascade'); $fkChanged->setOnUpdate('cascade'); $diff->addAddedFk('category', $fk); $diff->addModifiedFk('category', $fk, $fkChanged); $diff->addRemovedFk('category', $fk); $index = new Index('test_index'); $index->setTable($tableA); $index->setColumns([$colFoo]); $indexChanged = clone $index; $indexChanged->setColumns([$colBar]); $diff->addAddedIndex('test_index', $index); $diff->addModifiedIndex('test_index', $index, $indexChanged); $diff->addRemovedIndex('test_index', $index); $string = (string) $diff; $expected = ' A: addedColumns: - id removedColumns: - category_id modifiedColumns: A.FOO: modifiedProperties: renamedColumns: foo: bar addedIndices: - test_index removedIndices: - test_index modifiedIndices: - test_index addedFks: - category removedFks: - category modifiedFks: category: localColumns: from ["category_id"] to ["category_id","bla"] foreignColumns: from ["id"] to ["id","id2"] onUpdate: from to CASCADE onDelete: from to CASCADE '; $this->assertEquals($expected, $string); }
/** * Adds the useFkQuery method for this object. * @param string &$script The script will be modified in this method. * @param ForeignKey $fk */ protected function addUseRefFkQuery(&$script, ForeignKey $fk) { $fkTable = $this->getTable()->getDatabase()->getTable($fk->getTableName()); $fkQueryBuilder = $this->getNewStubQueryBuilder($fkTable); $queryClass = $this->getClassNameFromBuilder($fkQueryBuilder, true); $relationName = $this->getRefFKPhpNameAffix($fk); $joinType = $this->getJoinType($fk); $this->addUseRelatedQuery($script, $fkTable, $queryClass, $relationName, $joinType); }
public function testCompareSort() { $c1 = new Column('Foo'); $c2 = new Column('Bar'); $c3 = new Column('Baz'); $c4 = new Column('Faz'); $fk1 = new ForeignKey(); $fk1->addReference($c1, $c3); $fk1->addReference($c2, $c4); $t1 = new Table('Baz'); $t1->addForeignKey($fk1); $fk2 = new ForeignKey(); $fk2->addReference($c2, $c4); $fk2->addReference($c1, $c3); $t2 = new Table('Baz'); $t2->addForeignKey($fk2); $this->assertFalse(PropelForeignKeyComparator::computeDiff($fk1, $fk2)); }
public function getForeignKeyDDL(ForeignKey $fk) { if ($fk->isSkipSql() || $fk->isPolymorphic()) { return; } $pattern = "CONSTRAINT %s\n FOREIGN KEY (%s) REFERENCES %s (%s)"; $script = sprintf($pattern, $this->quoteIdentifier($fk->getName()), $this->getColumnListDDL($fk->getLocalColumnObjects()), $this->quoteIdentifier($fk->getForeignTableName()), $this->getColumnListDDL($fk->getForeignColumnObjects())); if ($fk->hasOnDelete()) { $script .= "\n ON DELETE " . $fk->getOnDelete(); } return $script; }
public function testCompareModifiedFks() { $db1 = new Database(); $db1->setPlatform($this->platform); $c1 = new Column('Foo'); $c2 = new Column('Bar'); $fk1 = new ForeignKey(); $fk1->addReference($c1, $c2); $t1 = new Table('Baz'); $t1->addForeignKey($fk1); $db1->addTable($t1); $t1->doNaming(); $db2 = new Database(); $db2->setPlatform($this->platform); $c3 = new Column('Foo'); $c4 = new Column('Bar2'); $fk2 = new ForeignKey(); $fk2->addReference($c3, $c4); $t2 = new Table('Baz'); $t2->addForeignKey($fk2); $db2->addTable($t2); $t2->doNaming(); $tc = new TableComparator(); $tc->setFromTable($t1); $tc->setToTable($t2); $nbDiffs = $tc->compareForeignKeys(); $tableDiff = $tc->getTableDiff(); $this->assertEquals(1, $nbDiffs); $this->assertEquals(1, count($tableDiff->getModifiedFks())); $this->assertEquals(array('Baz_FK_1' => array($fk1, $fk2)), $tableDiff->getModifiedFks()); }
public function getDropForeignKeyDDL(ForeignKey $fk) { if (!$this->supportsForeignKeys($fk->getTable())) { return ''; } if ($fk->isSkipSql()) { return; } $pattern = "\nALTER TABLE %s DROP FOREIGN KEY %s;\n"; return sprintf($pattern, $this->quoteIdentifier($fk->getTable()->getName()), $this->quoteIdentifier($fk->getName())); }
/** * Load foreign keys for this table. */ protected function addForeignKeys(Table $table, $oid) { $database = $table->getDatabase(); $stmt = $this->dbh->prepare("SELECT\n conname,\n confupdtype,\n confdeltype,\n CASE nl.nspname WHEN 'public' THEN cl.relname ELSE nl.nspname||'.'||cl.relname END as fktab,\n array_agg(DISTINCT a2.attname) AS fkcols,\n CASE nr.nspname WHEN 'public' THEN cr.relname ELSE nr.nspname||'.'||cr.relname END as reftab,\n array_agg(DISTINCT a1.attname) AS refcols\n FROM pg_constraint ct\n JOIN pg_class cl ON cl.oid=conrelid\n JOIN pg_class cr ON cr.oid=confrelid\n JOIN pg_namespace nl ON nl.oid = cl.relnamespace\n JOIN pg_namespace nr ON nr.oid = cr.relnamespace\n LEFT JOIN pg_catalog.pg_attribute a1 ON a1.attrelid = ct.confrelid\n LEFT JOIN pg_catalog.pg_attribute a2 ON a2.attrelid = ct.conrelid\n WHERE\n contype='f'\n AND conrelid = ?\n AND a2.attnum = ANY (ct.conkey)\n AND a1.attnum = ANY (ct.confkey)\n GROUP BY conname, confupdtype, confdeltype, fktab, reftab\n ORDER BY conname"); $stmt->bindValue(1, $oid); $stmt->execute(); $foreignKeys = array(); while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $name = $row['conname']; $localTable = $row['fktab']; $localColumns = explode(',', trim($row['fkcols'], '{}')); $foreignTableName = $row['reftab']; $foreignColumns = explode(',', trim($row['refcols'], '{}')); // On Update switch ($row['confupdtype']) { case 'c': $onupdate = ForeignKey::CASCADE; break; case 'd': $onupdate = ForeignKey::SETDEFAULT; break; case 'n': $onupdate = ForeignKey::SETNULL; break; case 'r': $onupdate = ForeignKey::RESTRICT; break; default: case 'a': // NOACTION is the postgresql default $onupdate = ForeignKey::NONE; break; } // On Delete switch ($row['confdeltype']) { case 'c': $ondelete = ForeignKey::CASCADE; break; case 'd': $ondelete = ForeignKey::SETDEFAULT; break; case 'n': $ondelete = ForeignKey::SETNULL; break; case 'r': $ondelete = ForeignKey::RESTRICT; break; default: case 'a': // NOACTION is the postgresql default $ondelete = ForeignKey::NONE; break; } $foreignTable = $database->getTable($foreignTableName); $localTable = $database->getTable($localTable); if (!$foreignTable) { continue; } if (!isset($foreignKeys[$name])) { $fk = new ForeignKey($name); $fk->setForeignTableCommonName($foreignTable->getCommonName()); if ($table->guessSchemaName() != $foreignTable->guessSchemaName()) { $fk->setForeignSchemaName($foreignTable->getSchema()); } $fk->setOnDelete($ondelete); $fk->setOnUpdate($onupdate); $table->addForeignKey($fk); $foreignKeys[$name] = $fk; } $max = count($localColumns); for ($i = 0; $i < $max; $i++) { $foreignKeys[$name]->addReference($localTable->getColumn($localColumns[$i]), $foreignTable->getColumn($foreignColumns[$i])); } } }
/** * Compute the difference between two Foreign key objects * * @param ForeignKey $fromFk * @param ForeignKey $toFk * * @param boolean $caseInsensitive Whether the comparison is case insensitive. * False by default. * * @return boolean false if the two fks are similar, true if they have differences */ public static function computeDiff(ForeignKey $fromFk, ForeignKey $toFk, $caseInsensitive = false) { // Check for differences in local and remote table $test = $caseInsensitive ? strtolower($fromFk->getTableName()) != strtolower($toFk->getTableName()) : $fromFk->getTableName() != $toFk->getTableName(); if ($test) { return true; } $test = $caseInsensitive ? strtolower($fromFk->getForeignTableName()) != strtolower($toFk->getForeignTableName()) : $fromFk->getForeignTableName() != $toFk->getForeignTableName(); if ($test) { return true; } // compare columns $fromFkLocalColumns = $fromFk->getLocalColumns(); sort($fromFkLocalColumns); $toFkLocalColumns = $toFk->getLocalColumns(); sort($toFkLocalColumns); if (array_map('strtolower', $fromFkLocalColumns) != array_map('strtolower', $toFkLocalColumns)) { return true; } $fromFkForeignColumns = $fromFk->getForeignColumns(); sort($fromFkForeignColumns); $toFkForeignColumns = $toFk->getForeignColumns(); sort($toFkForeignColumns); if (array_map('strtolower', $fromFkForeignColumns) != array_map('strtolower', $toFkForeignColumns)) { return true; } // compare on if ($fromFk->normalizeFKey($fromFk->getOnUpdate()) != $toFk->normalizeFKey($toFk->getOnUpdate())) { return true; } if ($fromFk->normalizeFKey($fromFk->getOnDelete()) != $toFk->normalizeFKey($toFk->getOnDelete())) { return true; } // compare skipSql if ($fromFk->isSkipSql() != $toFk->isSkipSql()) { return true; } return false; }
/** * Adds a new foreign key to this table. * * @param ForeignKey|array $foreignKey The foreign key mapping * @return ForeignKey */ public function addForeignKey($foreignKey) { if ($foreignKey instanceof ForeignKey) { $fk = $foreignKey; $fk->setTable($this); $name = $fk->getPhpName() ?: $fk->getName(); if (isset($this->foreignKeysByName[$name])) { throw new EngineException(sprintf('Foreign key "%s" declared twice in table "%s". Please specify a different php name!', $name, $this->getName())); } $this->foreignKeys[] = $fk; $this->foreignKeysByName[$name] = $fk; if (!in_array($fk->getForeignTableName(), $this->foreignTableNames)) { $this->foreignTableNames[] = $fk->getForeignTableName(); } return $fk; } $fk = new ForeignKey(isset($foreignKey['name']) ? $foreignKey['name'] : null); $fk->setTable($this); $fk->loadMapping($foreignKey); return $this->addForeignKey($fk); }