Skip to content

Add support for foreign keys to the Forge #609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Oct 12, 2017
134 changes: 134 additions & 0 deletions application/Controllers/Checks.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,140 @@ public function index()
{
session()->start();
}

public function forge()
{
echo '<h1>MySQL</h1>';

log_message('debug', 'MYSQL TEST');

$forge_mysql = \Config\Database::forge();

$forge_mysql->getConnection()->query('SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;');

$forge_mysql->dropTable('users', true);

$forge_mysql->getConnection()->query('SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;');

$forge_mysql->addField([
'id' => [
'type' => 'INTEGER',
'constraint' => 11
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 50,
]
]);
$forge_mysql->addKey('id', true);
$attributes = array('ENGINE' => 'InnoDB');
$forge_mysql->createTable('users', true, $attributes);

$data_insert = array(
'id' => 1,
'name' => 'User 1',
);
$forge_mysql->getConnection()->table('users')->insert($data_insert);

$drop = $forge_mysql->dropTable('invoices', true);

$forge_mysql->addField([
'id' => [
'type' => 'INTEGER',
'constraint' => 11,
],
'users_id' => [
'type' => 'INTEGER',
'constraint' => 11
],
'other_id' => [
'type' => 'INTEGER',
'constraint' => 11
]
]);
$forge_mysql->addKey('id', true);

$forge_mysql->addForeignKey('users_id','users','id','CASCADE','CASCADE');
$forge_mysql->addForeignKey('other_id','users','id','CASCADE','CASCADE');

$attributes = array('ENGINE' => 'InnoDB');
$res = $forge_mysql->createTable('invoices', true,$attributes);

if(!$res){
var_dump($forge_mysql->getConnection()->mysqli);
}else{
echo '<br><br>OK';

var_dump($forge_mysql->getConnection()->getForeignKeyData('invoices'));
}

$res = $forge_mysql->dropForeignKey('invoices','invoices_other_id_foreign');


echo '<h1>PostgreSQL</h1>';

$forge_pgsql = \Config\Database::forge('pgsql');

$forge_pgsql->dropTable('users',true, true);

$forge_pgsql->addField([
'id' => [
'type' => 'INTEGER',
'constraint' => 11,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 50,
]
]);
$forge_pgsql->addKey('id', true);
$forge_pgsql->createTable('users', true);


$data_insert = array(
'id' => 1,
'name' => 'User 1',
);
$forge_pgsql->getConnection()->table('users')->insert($data_insert);

$forge_pgsql->dropTable('invoices',true);
$forge_pgsql->addField([
'id' => [
'type' => 'INTEGER',
'constraint' => 11,
'auto_increment' => true,
],
'users_id' => [
'type' => 'INTEGER',
'constraint' => 11
],
'other_id' => [
'type' => 'INTEGER',
'constraint' => 11
],
'another_id' => [
'type' => 'INTEGER',
'constraint' => 11
]
]);
$forge_pgsql->addKey('id', true);

$forge_pgsql->addForeignKey('users_id','users','id','CASCADE','CASCADE');
$forge_pgsql->addForeignKey('other_id','users','id');

$res = $forge_pgsql->createTable('invoices', true);

if(!$res){
var_dump($forge_pgsql->getConnection()->mysqli);
}else{
echo '<br><br>OK';
var_dump($forge_pgsql->getConnection()->getForeignKeyData('invoices'));
}

//$res = $forge_pgsql->dropForeignKey('invoices','invoices_other_id_foreign');

}


public function escape()
Expand Down
17 changes: 16 additions & 1 deletion system/Database/BaseConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -1598,7 +1598,7 @@ public function fieldExists($fieldName, $tableName)
*/
public function getFieldData(string $table)
{
$fields = $this->_fieldData($this->protectIdentifiers($table, true, null, false));
$fields = $this->_fieldData($this->protectIdentifiers($table, true, false, false));

return $fields ?? false;
}
Expand All @@ -1618,6 +1618,21 @@ public function getIndexData(string $table)
return $fields ?? false;
}

//--------------------------------------------------------------------

/**
* Returns an object with foreign key data
*
* @param string $table the table name
* @return array
*/
public function getForeignKeyData(string $table)
{
$fields = $this->_foreignKeyData($this->protectIdentifiers($table, true, false, false));

return $fields ?? false;
}

//--------------------------------------------------------------------

/**
Expand Down
114 changes: 108 additions & 6 deletions system/Database/Forge.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ class Forge
* @var array
*/
protected $primaryKeys = [];

/**
* List of foreign keys.
*
* @var type
*/
protected $foreignKeys = [];

/**
* Character set used.
Expand Down Expand Up @@ -330,7 +337,63 @@ public function addField($field)
}

//--------------------------------------------------------------------

/**
* Add Foreign Key
*
* @param array $field
*
* @return \CodeIgniter\Database\Forge
*/
public function addForeignKey($fieldName= '',$tableName = '', $tableField = '', $onUpdate = false, $onDelete = false)
{

if( ! isset($this->fields[$fieldName]))
{
throw new \RuntimeException('Field "'.$fieldName.'" not exist');
}

$this->foreignKeys[$fieldName] = [
'table' => $tableName,
'field' => $tableField,
'onDelete' => $onDelete,
'onUpdate' => $onUpdate
];


return $this;
}

//--------------------------------------------------------------------

/**
* Foreign Key Drop
*
* @param string $table Table name
* @param string $foreign_name Foreign name
*
* @return bool
*/
public function dropForeignKey($table, $foreign_name)
{

$sql = sprintf($this->dropConstraintStr,$this->db->escapeIdentifiers($this->db->DBPrefix.$table),$this->db->escapeIdentifiers($this->db->DBPrefix.$foreign_name));

if ($sql === false)
{
if ($this->db->DBDebug)
{
throw new DatabaseException('This feature is not available for the database you are using.');
}

return false;
}

return $this->db->query($sql);
}

//--------------------------------------------------------------------

/**
* Create Table
*
Expand Down Expand Up @@ -425,8 +488,10 @@ protected function _createTable($table, $if_not_exists, $attributes)
$columns[$i] = ($columns[$i]['_literal'] !== false) ? "\n\t" . $columns[$i]['_literal'] : "\n\t" . $this->_processColumn($columns[$i]);
}

$columns = implode(',', $columns)
. $this->_processPrimaryKeys($table);
$columns = implode(',', $columns);

$columns .= $this->_processPrimaryKeys($table);
$columns .= $this->_processForeignKeys($table);

// Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL)
if ($this->createTableKeys === true)
Expand Down Expand Up @@ -472,11 +537,12 @@ protected function _createTableAttributes($attributes)
*
* @param string $table_name Table name
* @param bool $if_exists Whether to add an IF EXISTS condition
* @param bool $cascade Whether to add an CASCADE condition
*
* @return mixed
* @throws \CodeIgniter\DatabaseException
*/
public function dropTable($table_name, $if_exists = false)
public function dropTable($table_name, $if_exists = false, $cascade = false)
{
if ($table_name === '')
{
Expand All @@ -488,13 +554,14 @@ public function dropTable($table_name, $if_exists = false)
return false;
}


// If the prefix is already starting the table name, remove it...
if (strpos($table_name, $this->db->DBPrefix) === 0)
{
$table_name = substr($table_name, strlen($this->db->DBPrefix));
}

if (($query = $this->_dropTable($this->db->DBPrefix . $table_name, $if_exists)) === true)
if (($query = $this->_dropTable($this->db->DBPrefix . $table_name, $if_exists, $cascade)) === true)
{
return true;
}
Expand Down Expand Up @@ -523,10 +590,11 @@ public function dropTable($table_name, $if_exists = false)
*
* @param string $table Table name
* @param bool $if_exists Whether to add an IF EXISTS condition
* @param bool $cascade Whether to add an CASCADE condition
*
* @return string
*/
protected function _dropTable($table, $if_exists)
protected function _dropTable($table, $if_exists, $cascade)
{
$sql = 'DROP TABLE';

Expand All @@ -545,7 +613,9 @@ protected function _dropTable($table, $if_exists)
}
}

return $sql . ' ' . $this->db->escapeIdentifiers($table);
$sql = $sql . ' ' . $this->db->escapeIdentifiers($table);

return $sql;
}

//--------------------------------------------------------------------
Expand Down Expand Up @@ -1076,6 +1146,38 @@ protected function _processIndexes($table)
}

//--------------------------------------------------------------------
/**
* Process foreign keys
*
* @param string $table Table name
*
* @return string
*/
protected function _processForeignKeys($table) {
$sql = '';

$allowActions = array('CASCADE','SET NULL','NO ACTION','RESTRICT','SET DEFAULT');

if (count($this->foreignKeys) > 0){
foreach ($this->foreignKeys as $field => $fkey) {
$name_index = $table.'_'.$field.'_foreign';

$sql .= ",\n\tCONSTRAINT " . $this->db->escapeIdentifiers($name_index)
. ' FOREIGN KEY(' . $this->db->escapeIdentifiers($field) . ') REFERENCES '.$this->db->escapeIdentifiers($this->db->DBPrefix.$fkey['table']).' ('.$this->db->escapeIdentifiers($fkey['field']).')';

if($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions)){
$sql .= " ON DELETE ".$fkey['onDelete'];
}

if($fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions)){
$sql .= " ON UPDATE ".$fkey['onDelete'];
}

}
}

return $sql;
}
//--------------------------------------------------------------------

/**
Expand Down
Loading