Skip to content

Commit d305d5e

Browse files
authored
Merge pull request #609 from davidgv88/foreignkey_forge
Add support for foreign keys to the Forge
2 parents 0364a8a + e50fed0 commit d305d5e

File tree

9 files changed

+619
-10
lines changed

9 files changed

+619
-10
lines changed

application/Controllers/Checks.php

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,140 @@ public function index()
1515
{
1616
session()->start();
1717
}
18+
19+
public function forge()
20+
{
21+
echo '<h1>MySQL</h1>';
22+
23+
log_message('debug', 'MYSQL TEST');
24+
25+
$forge_mysql = \Config\Database::forge();
26+
27+
$forge_mysql->getConnection()->query('SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;');
28+
29+
$forge_mysql->dropTable('users', true);
30+
31+
$forge_mysql->getConnection()->query('SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;');
32+
33+
$forge_mysql->addField([
34+
'id' => [
35+
'type' => 'INTEGER',
36+
'constraint' => 11
37+
],
38+
'name' => [
39+
'type' => 'VARCHAR',
40+
'constraint' => 50,
41+
]
42+
]);
43+
$forge_mysql->addKey('id', true);
44+
$attributes = array('ENGINE' => 'InnoDB');
45+
$forge_mysql->createTable('users', true, $attributes);
46+
47+
$data_insert = array(
48+
'id' => 1,
49+
'name' => 'User 1',
50+
);
51+
$forge_mysql->getConnection()->table('users')->insert($data_insert);
52+
53+
$drop = $forge_mysql->dropTable('invoices', true);
54+
55+
$forge_mysql->addField([
56+
'id' => [
57+
'type' => 'INTEGER',
58+
'constraint' => 11,
59+
],
60+
'users_id' => [
61+
'type' => 'INTEGER',
62+
'constraint' => 11
63+
],
64+
'other_id' => [
65+
'type' => 'INTEGER',
66+
'constraint' => 11
67+
]
68+
]);
69+
$forge_mysql->addKey('id', true);
70+
71+
$forge_mysql->addForeignKey('users_id','users','id','CASCADE','CASCADE');
72+
$forge_mysql->addForeignKey('other_id','users','id','CASCADE','CASCADE');
73+
74+
$attributes = array('ENGINE' => 'InnoDB');
75+
$res = $forge_mysql->createTable('invoices', true,$attributes);
76+
77+
if(!$res){
78+
var_dump($forge_mysql->getConnection()->mysqli);
79+
}else{
80+
echo '<br><br>OK';
81+
82+
var_dump($forge_mysql->getConnection()->getForeignKeyData('invoices'));
83+
}
84+
85+
$res = $forge_mysql->dropForeignKey('invoices','invoices_other_id_foreign');
86+
87+
88+
echo '<h1>PostgreSQL</h1>';
89+
90+
$forge_pgsql = \Config\Database::forge('pgsql');
91+
92+
$forge_pgsql->dropTable('users',true, true);
93+
94+
$forge_pgsql->addField([
95+
'id' => [
96+
'type' => 'INTEGER',
97+
'constraint' => 11,
98+
'auto_increment' => true,
99+
],
100+
'name' => [
101+
'type' => 'VARCHAR',
102+
'constraint' => 50,
103+
]
104+
]);
105+
$forge_pgsql->addKey('id', true);
106+
$forge_pgsql->createTable('users', true);
107+
108+
109+
$data_insert = array(
110+
'id' => 1,
111+
'name' => 'User 1',
112+
);
113+
$forge_pgsql->getConnection()->table('users')->insert($data_insert);
114+
115+
$forge_pgsql->dropTable('invoices',true);
116+
$forge_pgsql->addField([
117+
'id' => [
118+
'type' => 'INTEGER',
119+
'constraint' => 11,
120+
'auto_increment' => true,
121+
],
122+
'users_id' => [
123+
'type' => 'INTEGER',
124+
'constraint' => 11
125+
],
126+
'other_id' => [
127+
'type' => 'INTEGER',
128+
'constraint' => 11
129+
],
130+
'another_id' => [
131+
'type' => 'INTEGER',
132+
'constraint' => 11
133+
]
134+
]);
135+
$forge_pgsql->addKey('id', true);
136+
137+
$forge_pgsql->addForeignKey('users_id','users','id','CASCADE','CASCADE');
138+
$forge_pgsql->addForeignKey('other_id','users','id');
139+
140+
$res = $forge_pgsql->createTable('invoices', true);
141+
142+
if(!$res){
143+
var_dump($forge_pgsql->getConnection()->mysqli);
144+
}else{
145+
echo '<br><br>OK';
146+
var_dump($forge_pgsql->getConnection()->getForeignKeyData('invoices'));
147+
}
148+
149+
//$res = $forge_pgsql->dropForeignKey('invoices','invoices_other_id_foreign');
150+
151+
}
18152

19153

20154
public function escape()

system/Database/BaseConnection.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1598,7 +1598,7 @@ public function fieldExists($fieldName, $tableName)
15981598
*/
15991599
public function getFieldData(string $table)
16001600
{
1601-
$fields = $this->_fieldData($this->protectIdentifiers($table, true, null, false));
1601+
$fields = $this->_fieldData($this->protectIdentifiers($table, true, false, false));
16021602

16031603
return $fields ?? false;
16041604
}
@@ -1618,6 +1618,21 @@ public function getIndexData(string $table)
16181618
return $fields ?? false;
16191619
}
16201620

1621+
//--------------------------------------------------------------------
1622+
1623+
/**
1624+
* Returns an object with foreign key data
1625+
*
1626+
* @param string $table the table name
1627+
* @return array
1628+
*/
1629+
public function getForeignKeyData(string $table)
1630+
{
1631+
$fields = $this->_foreignKeyData($this->protectIdentifiers($table, true, false, false));
1632+
1633+
return $fields ?? false;
1634+
}
1635+
16211636
//--------------------------------------------------------------------
16221637

16231638
/**

system/Database/Forge.php

Lines changed: 108 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ class Forge
6969
* @var array
7070
*/
7171
protected $primaryKeys = [];
72+
73+
/**
74+
* List of foreign keys.
75+
*
76+
* @var type
77+
*/
78+
protected $foreignKeys = [];
7279

7380
/**
7481
* Character set used.
@@ -330,7 +337,63 @@ public function addField($field)
330337
}
331338

332339
//--------------------------------------------------------------------
340+
341+
/**
342+
* Add Foreign Key
343+
*
344+
* @param array $field
345+
*
346+
* @return \CodeIgniter\Database\Forge
347+
*/
348+
public function addForeignKey($fieldName= '',$tableName = '', $tableField = '', $onUpdate = false, $onDelete = false)
349+
{
350+
351+
if( ! isset($this->fields[$fieldName]))
352+
{
353+
throw new \RuntimeException('Field "'.$fieldName.'" not exist');
354+
}
355+
356+
$this->foreignKeys[$fieldName] = [
357+
'table' => $tableName,
358+
'field' => $tableField,
359+
'onDelete' => $onDelete,
360+
'onUpdate' => $onUpdate
361+
];
362+
363+
364+
return $this;
365+
}
333366

367+
//--------------------------------------------------------------------
368+
369+
/**
370+
* Foreign Key Drop
371+
*
372+
* @param string $table Table name
373+
* @param string $foreign_name Foreign name
374+
*
375+
* @return bool
376+
*/
377+
public function dropForeignKey($table, $foreign_name)
378+
{
379+
380+
$sql = sprintf($this->dropConstraintStr,$this->db->escapeIdentifiers($this->db->DBPrefix.$table),$this->db->escapeIdentifiers($this->db->DBPrefix.$foreign_name));
381+
382+
if ($sql === false)
383+
{
384+
if ($this->db->DBDebug)
385+
{
386+
throw new DatabaseException('This feature is not available for the database you are using.');
387+
}
388+
389+
return false;
390+
}
391+
392+
return $this->db->query($sql);
393+
}
394+
395+
//--------------------------------------------------------------------
396+
334397
/**
335398
* Create Table
336399
*
@@ -425,8 +488,10 @@ protected function _createTable($table, $if_not_exists, $attributes)
425488
$columns[$i] = ($columns[$i]['_literal'] !== false) ? "\n\t" . $columns[$i]['_literal'] : "\n\t" . $this->_processColumn($columns[$i]);
426489
}
427490

428-
$columns = implode(',', $columns)
429-
. $this->_processPrimaryKeys($table);
491+
$columns = implode(',', $columns);
492+
493+
$columns .= $this->_processPrimaryKeys($table);
494+
$columns .= $this->_processForeignKeys($table);
430495

431496
// Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL)
432497
if ($this->createTableKeys === true)
@@ -472,11 +537,12 @@ protected function _createTableAttributes($attributes)
472537
*
473538
* @param string $table_name Table name
474539
* @param bool $if_exists Whether to add an IF EXISTS condition
540+
* @param bool $cascade Whether to add an CASCADE condition
475541
*
476542
* @return mixed
477543
* @throws \CodeIgniter\Database\Exceptions\DatabaseException
478544
*/
479-
public function dropTable($table_name, $if_exists = false)
545+
public function dropTable($table_name, $if_exists = false, $cascade = false)
480546
{
481547
if ($table_name === '')
482548
{
@@ -488,13 +554,14 @@ public function dropTable($table_name, $if_exists = false)
488554
return false;
489555
}
490556

557+
491558
// If the prefix is already starting the table name, remove it...
492559
if (! empty($this->db->DBPrefix) && strpos($table_name, $this->db->DBPrefix) === 0)
493560
{
494561
$table_name = substr($table_name, strlen($this->db->DBPrefix));
495562
}
496563

497-
if (($query = $this->_dropTable($this->db->DBPrefix . $table_name, $if_exists)) === true)
564+
if (($query = $this->_dropTable($this->db->DBPrefix . $table_name, $if_exists, $cascade)) === true)
498565
{
499566
return true;
500567
}
@@ -523,10 +590,11 @@ public function dropTable($table_name, $if_exists = false)
523590
*
524591
* @param string $table Table name
525592
* @param bool $if_exists Whether to add an IF EXISTS condition
593+
* @param bool $cascade Whether to add an CASCADE condition
526594
*
527595
* @return string
528596
*/
529-
protected function _dropTable($table, $if_exists)
597+
protected function _dropTable($table, $if_exists, $cascade)
530598
{
531599
$sql = 'DROP TABLE';
532600

@@ -545,7 +613,9 @@ protected function _dropTable($table, $if_exists)
545613
}
546614
}
547615

548-
return $sql . ' ' . $this->db->escapeIdentifiers($table);
616+
$sql = $sql . ' ' . $this->db->escapeIdentifiers($table);
617+
618+
return $sql;
549619
}
550620

551621
//--------------------------------------------------------------------
@@ -1076,6 +1146,38 @@ protected function _processIndexes($table)
10761146
}
10771147

10781148
//--------------------------------------------------------------------
1149+
/**
1150+
* Process foreign keys
1151+
*
1152+
* @param string $table Table name
1153+
*
1154+
* @return string
1155+
*/
1156+
protected function _processForeignKeys($table) {
1157+
$sql = '';
1158+
1159+
$allowActions = array('CASCADE','SET NULL','NO ACTION','RESTRICT','SET DEFAULT');
1160+
1161+
if (count($this->foreignKeys) > 0){
1162+
foreach ($this->foreignKeys as $field => $fkey) {
1163+
$name_index = $table.'_'.$field.'_foreign';
1164+
1165+
$sql .= ",\n\tCONSTRAINT " . $this->db->escapeIdentifiers($name_index)
1166+
. ' FOREIGN KEY(' . $this->db->escapeIdentifiers($field) . ') REFERENCES '.$this->db->escapeIdentifiers($this->db->DBPrefix.$fkey['table']).' ('.$this->db->escapeIdentifiers($fkey['field']).')';
1167+
1168+
if($fkey['onDelete'] !== false && in_array($fkey['onDelete'], $allowActions)){
1169+
$sql .= " ON DELETE ".$fkey['onDelete'];
1170+
}
1171+
1172+
if($fkey['onUpdate'] !== false && in_array($fkey['onUpdate'], $allowActions)){
1173+
$sql .= " ON UPDATE ".$fkey['onDelete'];
1174+
}
1175+
1176+
}
1177+
}
1178+
1179+
return $sql;
1180+
}
10791181
//--------------------------------------------------------------------
10801182

10811183
/**

0 commit comments

Comments
 (0)