1<?php 2 3namespace Doctrine\DBAL\Schema; 4 5use Doctrine\DBAL\Platforms\AbstractPlatform; 6 7use function array_keys; 8use function array_map; 9use function in_array; 10use function strrpos; 11use function strtolower; 12use function strtoupper; 13use function substr; 14 15/** 16 * An abstraction class for a foreign key constraint. 17 */ 18class ForeignKeyConstraint extends AbstractAsset implements Constraint 19{ 20 /** 21 * Instance of the referencing table the foreign key constraint is associated with. 22 * 23 * @var Table 24 */ 25 protected $_localTable; 26 27 /** 28 * Asset identifier instances of the referencing table column names the foreign key constraint is associated with. 29 * array($columnName => Identifier) 30 * 31 * @var Identifier[] 32 */ 33 protected $_localColumnNames; 34 35 /** 36 * Table or asset identifier instance of the referenced table name the foreign key constraint is associated with. 37 * 38 * @var Table|Identifier 39 */ 40 protected $_foreignTableName; 41 42 /** 43 * Asset identifier instances of the referenced table column names the foreign key constraint is associated with. 44 * array($columnName => Identifier) 45 * 46 * @var Identifier[] 47 */ 48 protected $_foreignColumnNames; 49 50 /** 51 * Options associated with the foreign key constraint. 52 * 53 * @var mixed[] 54 */ 55 protected $_options; 56 57 /** 58 * Initializes the foreign key constraint. 59 * 60 * @param string[] $localColumnNames Names of the referencing table columns. 61 * @param Table|string $foreignTableName Referenced table. 62 * @param string[] $foreignColumnNames Names of the referenced table columns. 63 * @param string|null $name Name of the foreign key constraint. 64 * @param mixed[] $options Options associated with the foreign key constraint. 65 */ 66 public function __construct( 67 array $localColumnNames, 68 $foreignTableName, 69 array $foreignColumnNames, 70 $name = null, 71 array $options = [] 72 ) { 73 if ($name !== null) { 74 $this->_setName($name); 75 } 76 77 $this->_localColumnNames = $this->createIdentifierMap($localColumnNames); 78 79 if ($foreignTableName instanceof Table) { 80 $this->_foreignTableName = $foreignTableName; 81 } else { 82 $this->_foreignTableName = new Identifier($foreignTableName); 83 } 84 85 $this->_foreignColumnNames = $this->createIdentifierMap($foreignColumnNames); 86 $this->_options = $options; 87 } 88 89 /** 90 * @param string[] $names 91 * 92 * @return Identifier[] 93 */ 94 private function createIdentifierMap(array $names): array 95 { 96 $identifiers = []; 97 98 foreach ($names as $name) { 99 $identifiers[$name] = new Identifier($name); 100 } 101 102 return $identifiers; 103 } 104 105 /** 106 * Returns the name of the referencing table 107 * the foreign key constraint is associated with. 108 * 109 * @return string 110 */ 111 public function getLocalTableName() 112 { 113 return $this->_localTable->getName(); 114 } 115 116 /** 117 * Sets the Table instance of the referencing table 118 * the foreign key constraint is associated with. 119 * 120 * @param Table $table Instance of the referencing table. 121 * 122 * @return void 123 */ 124 public function setLocalTable(Table $table) 125 { 126 $this->_localTable = $table; 127 } 128 129 /** 130 * @return Table 131 */ 132 public function getLocalTable() 133 { 134 return $this->_localTable; 135 } 136 137 /** 138 * Returns the names of the referencing table columns 139 * the foreign key constraint is associated with. 140 * 141 * @return string[] 142 */ 143 public function getLocalColumns() 144 { 145 return array_keys($this->_localColumnNames); 146 } 147 148 /** 149 * Returns the quoted representation of the referencing table column names 150 * the foreign key constraint is associated with. 151 * 152 * But only if they were defined with one or the referencing table column name 153 * is a keyword reserved by the platform. 154 * Otherwise the plain unquoted value as inserted is returned. 155 * 156 * @param AbstractPlatform $platform The platform to use for quotation. 157 * 158 * @return string[] 159 */ 160 public function getQuotedLocalColumns(AbstractPlatform $platform) 161 { 162 $columns = []; 163 164 foreach ($this->_localColumnNames as $column) { 165 $columns[] = $column->getQuotedName($platform); 166 } 167 168 return $columns; 169 } 170 171 /** 172 * Returns unquoted representation of local table column names for comparison with other FK 173 * 174 * @return string[] 175 */ 176 public function getUnquotedLocalColumns() 177 { 178 return array_map([$this, 'trimQuotes'], $this->getLocalColumns()); 179 } 180 181 /** 182 * Returns unquoted representation of foreign table column names for comparison with other FK 183 * 184 * @return string[] 185 */ 186 public function getUnquotedForeignColumns() 187 { 188 return array_map([$this, 'trimQuotes'], $this->getForeignColumns()); 189 } 190 191 /** 192 * {@inheritdoc} 193 * 194 * @see getLocalColumns 195 */ 196 public function getColumns() 197 { 198 return $this->getLocalColumns(); 199 } 200 201 /** 202 * Returns the quoted representation of the referencing table column names 203 * the foreign key constraint is associated with. 204 * 205 * But only if they were defined with one or the referencing table column name 206 * is a keyword reserved by the platform. 207 * Otherwise the plain unquoted value as inserted is returned. 208 * 209 * @see getQuotedLocalColumns 210 * 211 * @param AbstractPlatform $platform The platform to use for quotation. 212 * 213 * @return string[] 214 */ 215 public function getQuotedColumns(AbstractPlatform $platform) 216 { 217 return $this->getQuotedLocalColumns($platform); 218 } 219 220 /** 221 * Returns the name of the referenced table 222 * the foreign key constraint is associated with. 223 * 224 * @return string 225 */ 226 public function getForeignTableName() 227 { 228 return $this->_foreignTableName->getName(); 229 } 230 231 /** 232 * Returns the non-schema qualified foreign table name. 233 * 234 * @return string 235 */ 236 public function getUnqualifiedForeignTableName() 237 { 238 $name = $this->_foreignTableName->getName(); 239 $position = strrpos($name, '.'); 240 241 if ($position !== false) { 242 $name = substr($name, $position + 1); 243 } 244 245 return strtolower($name); 246 } 247 248 /** 249 * Returns the quoted representation of the referenced table name 250 * the foreign key constraint is associated with. 251 * 252 * But only if it was defined with one or the referenced table name 253 * is a keyword reserved by the platform. 254 * Otherwise the plain unquoted value as inserted is returned. 255 * 256 * @param AbstractPlatform $platform The platform to use for quotation. 257 * 258 * @return string 259 */ 260 public function getQuotedForeignTableName(AbstractPlatform $platform) 261 { 262 return $this->_foreignTableName->getQuotedName($platform); 263 } 264 265 /** 266 * Returns the names of the referenced table columns 267 * the foreign key constraint is associated with. 268 * 269 * @return string[] 270 */ 271 public function getForeignColumns() 272 { 273 return array_keys($this->_foreignColumnNames); 274 } 275 276 /** 277 * Returns the quoted representation of the referenced table column names 278 * the foreign key constraint is associated with. 279 * 280 * But only if they were defined with one or the referenced table column name 281 * is a keyword reserved by the platform. 282 * Otherwise the plain unquoted value as inserted is returned. 283 * 284 * @param AbstractPlatform $platform The platform to use for quotation. 285 * 286 * @return string[] 287 */ 288 public function getQuotedForeignColumns(AbstractPlatform $platform) 289 { 290 $columns = []; 291 292 foreach ($this->_foreignColumnNames as $column) { 293 $columns[] = $column->getQuotedName($platform); 294 } 295 296 return $columns; 297 } 298 299 /** 300 * Returns whether or not a given option 301 * is associated with the foreign key constraint. 302 * 303 * @param string $name Name of the option to check. 304 * 305 * @return bool 306 */ 307 public function hasOption($name) 308 { 309 return isset($this->_options[$name]); 310 } 311 312 /** 313 * Returns an option associated with the foreign key constraint. 314 * 315 * @param string $name Name of the option the foreign key constraint is associated with. 316 * 317 * @return mixed 318 */ 319 public function getOption($name) 320 { 321 return $this->_options[$name]; 322 } 323 324 /** 325 * Returns the options associated with the foreign key constraint. 326 * 327 * @return mixed[] 328 */ 329 public function getOptions() 330 { 331 return $this->_options; 332 } 333 334 /** 335 * Returns the referential action for UPDATE operations 336 * on the referenced table the foreign key constraint is associated with. 337 * 338 * @return string|null 339 */ 340 public function onUpdate() 341 { 342 return $this->onEvent('onUpdate'); 343 } 344 345 /** 346 * Returns the referential action for DELETE operations 347 * on the referenced table the foreign key constraint is associated with. 348 * 349 * @return string|null 350 */ 351 public function onDelete() 352 { 353 return $this->onEvent('onDelete'); 354 } 355 356 /** 357 * Returns the referential action for a given database operation 358 * on the referenced table the foreign key constraint is associated with. 359 * 360 * @param string $event Name of the database operation/event to return the referential action for. 361 * 362 * @return string|null 363 */ 364 private function onEvent($event) 365 { 366 if (isset($this->_options[$event])) { 367 $onEvent = strtoupper($this->_options[$event]); 368 369 if (! in_array($onEvent, ['NO ACTION', 'RESTRICT'])) { 370 return $onEvent; 371 } 372 } 373 374 return null; 375 } 376 377 /** 378 * Checks whether this foreign key constraint intersects the given index columns. 379 * 380 * Returns `true` if at least one of this foreign key's local columns 381 * matches one of the given index's columns, `false` otherwise. 382 * 383 * @param Index $index The index to be checked against. 384 * 385 * @return bool 386 */ 387 public function intersectsIndexColumns(Index $index) 388 { 389 foreach ($index->getColumns() as $indexColumn) { 390 foreach ($this->_localColumnNames as $localColumn) { 391 if (strtolower($indexColumn) === strtolower($localColumn->getName())) { 392 return true; 393 } 394 } 395 } 396 397 return false; 398 } 399} 400