IsUnique.php 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\ORM\Rule;
  17. use Cake\Datasource\EntityInterface;
  18. /**
  19. * Checks that a list of fields from an entity are unique in the table
  20. */
  21. class IsUnique
  22. {
  23. /**
  24. * The list of fields to check
  25. *
  26. * @var string[]
  27. */
  28. protected $_fields;
  29. /**
  30. * The unique check options
  31. *
  32. * @var array
  33. */
  34. protected $_options = [
  35. 'allowMultipleNulls' => false,
  36. ];
  37. /**
  38. * Constructor.
  39. *
  40. * ### Options
  41. *
  42. * - `allowMultipleNulls` Allows any field to have multiple null values. Defaults to false.
  43. *
  44. * @param string[] $fields The list of fields to check uniqueness for
  45. * @param array $options The options for unique checks.
  46. */
  47. public function __construct(array $fields, array $options = [])
  48. {
  49. $this->_fields = $fields;
  50. $this->_options = $options + $this->_options;
  51. }
  52. /**
  53. * Performs the uniqueness check
  54. *
  55. * @param \Cake\Datasource\EntityInterface $entity The entity from where to extract the fields
  56. * where the `repository` key is required.
  57. * @param array $options Options passed to the check,
  58. * @return bool
  59. */
  60. public function __invoke(EntityInterface $entity, array $options): bool
  61. {
  62. if (!$entity->extract($this->_fields, true)) {
  63. return true;
  64. }
  65. $fields = $entity->extract($this->_fields);
  66. if ($this->_options['allowMultipleNulls'] && array_filter($fields, 'is_null')) {
  67. return true;
  68. }
  69. $alias = $options['repository']->getAlias();
  70. $conditions = $this->_alias($alias, $fields);
  71. if ($entity->isNew() === false) {
  72. $keys = (array)$options['repository']->getPrimaryKey();
  73. $keys = $this->_alias($alias, $entity->extract($keys));
  74. if (array_filter($keys, 'strlen')) {
  75. $conditions['NOT'] = $keys;
  76. }
  77. }
  78. return !$options['repository']->exists($conditions);
  79. }
  80. /**
  81. * Add a model alias to all the keys in a set of conditions.
  82. *
  83. * @param string $alias The alias to add.
  84. * @param array $conditions The conditions to alias.
  85. * @return array
  86. */
  87. protected function _alias(string $alias, array $conditions): array
  88. {
  89. $aliased = [];
  90. foreach ($conditions as $key => $value) {
  91. $aliased["$alias.$key IS"] = $value;
  92. }
  93. return $aliased;
  94. }
  95. }