if §constraint is passed, constraints are limited to that column * @param null| \DB\SQL\MySQL\Constraint $constraint * @return Constraint[] */ public function listConstraint($constraint = null){ $constraintName = '%'; $keys = []; if($constraint instanceof Constraint){ // list constraints for given column in this table $constraintName = $constraint->getConstraintName() . '%'; $keys = $constraint->getKeys(); } $this->db->exec("USE information_schema"); $constraintsData = $this->db->exec(" SELECT * FROM referential_constraints WHERE constraint_schema = :db AND table_name = :table AND constraint_name LIKE :constraint_name ", [ ':db' => $this->db->name(), ':table' => $this->name, ':constraint_name' => $constraintName ]); // switch back to current DB $this->db->exec("USE " . $this->db->name()); $constraints = []; foreach($constraintsData as $data){ $constraints[$data['CONSTRAINT_NAME']] = new Constraint($this, $keys, $data['REFERENCED_TABLE_NAME'] ); } return $constraints; } /** * checks whether a constraint name exists or not * -> does not check constraint params * @param \DB\SQL\MySQL\Constraint $constraint * @return bool */ public function constraintExists($constraint){ $constraints = $this->listConstraint(); return array_key_exists($constraint->getConstraintName(), $constraints); } /** * drop foreign key constraint * @param \DB\SQL\MySQL\Constraint $constraint */ public function dropConstraint($constraint){ if($constraint->isValid()){ $this->queries[] = "ALTER TABLE " . $this->db->quotekey($this->name) . " DROP FOREIGN KEY " . $this->db->quotekey($constraint->getConstraintName()); }else{ trigger_error(sprintf(self::TEXT_ConstraintNotValid, 'table: ' . $this->name . ' constraintName: ' . $constraint->getConstraintName())); } } /** * Add/Update foreign key constraint * @param \DB\SQL\MySQL\Constraint $constraint */ public function addConstraint($constraint){ if($constraint->isValid()){ if($this->constraintExists($constraint)){ // drop constraint and re-add in case something has changed $this->dropConstraint($constraint); } $this->queries[] = " ALTER TABLE " . $this->db->quotekey($this->name) . " ADD CONSTRAINT " . $this->db->quotekey($constraint->getConstraintName()) . " FOREIGN KEY (" . implode(', ', $constraint->getKeys()) . ") REFERENCES " . $this->db->quotekey($constraint->getReferencedTable()) . " (" . implode(', ', $constraint->getReferencedCols()) . ") ON DELETE " . $constraint->getOnDelete() . " ON UPDATE " . $constraint->getOnUpdate(); }else{ trigger_error(sprintf(self::TEXT_ConstraintNotValid, 'table: ' . $this->name . ' constraintName: ' . $constraint->getConstraintName())); } } } /** * Class Column * @package DB\SQL\MySQL */ class Column extends SQL\Column { const TEXT_TableNameMissing = 'Table name missing for FOREIGN KEY in `%s`'; /** * ass constraint to this column * @param Constraint $constraint */ public function addConstraint(Constraint $constraint){ $this->table->addConstraint($constraint); } /** * @see \DB\SQL\MySQL\TableModifier->constraintExists(); * @param Constraint $constraint * @return mixed */ public function constraintExists(Constraint $constraint){ return $this->table->constraintExists($constraint); } /** * get a new column based constraint * $constraintData['table'] => referenceTable name (required) * $constraintData['id'] => referenceColumns (optional) default: ['id'] * $constraintData['on-delete'] => ON DELETE action (optional) default: see \DB\SQL\MySQL\Constraint const * $constraintData['on-update'] => ON UPDATE action (optional) default: see \DB\SQL\MySQL\Constraint const * * @param array $constraintData * @return \DB\SQL\MySQL\Constraint */ public function newConstraint($constraintData){ $constraint = null; if(isset($constraintData['table'])){ if(isset($constraintData['column'])){ $constraintData['column'] = (array)$constraintData['column']; }else{ $constraintData['column'] = ['id']; } $constraint = new Constraint($this->table, $this->name, $constraintData['table'], $constraintData['column']); if(isset($constraintData['on-delete'])){ $constraint->setOnDelete($constraintData['on-delete']); } if(isset($constraintData['on-update'])){ $constraint->setOnUpdate($constraintData['on-update']); } }else{ trigger_error(sprintf(self::TEXT_TableNameMissing, $this->table->name . '->' . $this->name)); } return $constraint; } } class Constraint { // available actions const ACTIONS_DELETE = ['RESTRICT', 'CASCADE', 'SET NULL', 'NO ACTION']; const ACTIONS_UPDATE = ['RESTRICT', 'CASCADE', 'SET NULL', 'NO ACTION']; // default actions const ACTION_DELETE = 'RESTRICT'; const ACTION_UPDATE = 'RESTRICT'; const TEXT_ActionNotSupported = 'Constraint action `%s` is not supported.'; protected $table; protected $keys = []; protected $referencedTable = ''; protected $referencedCols = []; protected $onDelete = self::ACTION_DELETE; protected $onUpdate = self::ACTION_UPDATE; /** * Constraint constructor. * @param SQL\TableBuilder $table * @param array $keys * @param string $referencedTable * @param array $referencedCols */ public function __construct(SQL\TableBuilder $table, $keys = [], $referencedTable = '', $referencedCols = ['id']){ $this->table = &$table; $this->setKeys($keys); $this->setReferencedTable($referencedTable); $this->setReferencedCols($referencedCols); } /** * @param mixed $keys */ public function setKeys($keys){ $this->keys = (array)$keys; } /** * @param mixed $referencedTable */ public function setReferencedTable($referencedTable){ $this->referencedTable = $referencedTable; } /** * @param mixed $referencedCols */ public function setReferencedCols($referencedCols){ $this->referencedCols = (array)$referencedCols; } /** * @param string $onDelete */ public function setOnDelete($onDelete){ if( in_array($onDelete, self::ACTIONS_DELETE) ){ $this->onDelete = $onDelete; }else{ trigger_error(sprintf(self::TEXT_ActionNotSupported, $onDelete)); } } /** * @param string $onUpdate */ public function setOnUpdate($onUpdate){ if( in_array($onUpdate, self::ACTIONS_UPDATE) ){ $this->onUpdate = $onUpdate; }else{ trigger_error(sprintf(self::TEXT_ActionNotSupported, $onUpdate)); } } /** * @return array */ public function getKeys(){ return $this->keys; } /** * @return string */ public function getReferencedTable(){ return $this->referencedTable; } /** * @return array */ public function getReferencedCols(){ return $this->referencedCols; } /** * @return string */ public function getOnDelete(){ return $this->onDelete; } /** * @return string */ public function getOnUpdate(){ return $this->onUpdate; } /** * get a constraint name for this table. * This can either be used to generate unique constraint names for foreign keys in parent tables * or generate a "part" of a name. e.g. for db-Query all constraints of this table (ignore columns) * by "LIKE" selecting "information_schema" * -> To get a certain constraint or generate a unique constraint, ALL params are required! * @return string */ public function getConstraintName(){ $constraintName = 'fk_' . $this->table->name; if(!empty($this->getKeys())){ $constraintName .= '___' . implode('__', $this->getKeys()); if(!empty($this->getReferencedTable())){ $constraintName .= '___' . $this->getReferencedTable(); if(!empty($this->getReferencedCols())){ $constraintName .= '___' . implode('__', $this->getReferencedCols()); } } } return $constraintName; } /** * checks if constraint is valid * -> all required members must be set! * @return bool */ public function isValid(){ $valid = false; if( !empty($this->getKeys()) && !empty($this->getReferencedTable()) && !empty($this->getReferencedCols()) ){ $valid = true; } return $valid; } }