Comparison.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Database\Expression;
  16. use Cake\Database\ExpressionInterface;
  17. use Cake\Database\ValueBinder;
  18. /**
  19. * A Comparison is a type of query expression that represents an operation
  20. * involving a field an operator and a value. In its most common form the
  21. * string representation of a comparison is `field = value`
  22. *
  23. * @internal
  24. */
  25. class Comparison extends QueryExpression {
  26. /**
  27. * The field name or expression to be used in the left hand side of the operator
  28. *
  29. * @var string
  30. */
  31. protected $_field;
  32. /**
  33. * The value to be used in the right hand side of the operation
  34. *
  35. * @var mixed
  36. */
  37. protected $_value;
  38. /**
  39. * The type to be used for casting the value to a database representation
  40. *
  41. * @var string
  42. */
  43. protected $_type;
  44. /**
  45. * Constructor
  46. *
  47. * @param string $field the field name to compare to a value
  48. * @param mixed $value The value to be used in comparison
  49. * @param string $type the type name used to cast the value
  50. * @param string $conjunction the operator used for comparing field and value
  51. */
  52. public function __construct($field, $value, $type, $conjunction) {
  53. $this->field($field);
  54. $this->value($value);
  55. $this->type($conjunction);
  56. if (is_string($type)) {
  57. $this->_type = $type;
  58. }
  59. }
  60. /**
  61. * Sets the field name
  62. *
  63. * @param string $field The field to compare with.
  64. * @return void
  65. */
  66. public function field($field) {
  67. $this->_field = $field;
  68. }
  69. /**
  70. * Sets the value
  71. *
  72. * @param mixed $value The value to compare
  73. * @return void
  74. */
  75. public function value($value) {
  76. $this->_value = $value;
  77. }
  78. /**
  79. * Returns the field name
  80. *
  81. * @return string|Cake\Database\ExpressionInterface
  82. */
  83. public function getField() {
  84. return $this->_field;
  85. }
  86. /**
  87. * Returns the value used for comparison
  88. *
  89. * @return mixed
  90. */
  91. public function getValue() {
  92. return $this->_value;
  93. }
  94. /**
  95. * Convert the expression into a SQL fragment.
  96. *
  97. * @param \Cake\Database\ValueBinder $generator Placeholder generator object
  98. * @return string
  99. */
  100. public function sql(ValueBinder $generator) {
  101. $field = $this->_field;
  102. if ($field instanceof ExpressionInterface) {
  103. $field = $field->sql($generator);
  104. }
  105. if ($this->_value instanceof ExpressionInterface) {
  106. $template = '%s %s (%s)';
  107. $value = $this->_value->sql($generator);
  108. } else {
  109. list($template, $value) = $this->_stringExpression($generator);
  110. }
  111. return sprintf($template, $field, $this->_conjunction, $value);
  112. }
  113. /**
  114. * {@inheritDoc}
  115. *
  116. */
  117. public function traverse(callable $callable) {
  118. if ($this->_field instanceof ExpressionInterface) {
  119. $callable($this->_field);
  120. $this->_field->traverse($callable);
  121. }
  122. if ($this->_value instanceof ExpressionInterface) {
  123. $callable($this->_value);
  124. $this->_value->traverse($callable);
  125. }
  126. }
  127. /**
  128. * Returns a template and a placeholder for the value after registering it
  129. * with the placeholder $generator
  130. *
  131. * @param \Cake\Database\ValueBinder $generator The value binder to use.
  132. * @return array First position containing the template and the second a placeholder
  133. */
  134. protected function _stringExpression($generator) {
  135. $template = '%s ';
  136. if ($this->_field instanceof ExpressionInterface) {
  137. $template = '(%s) ';
  138. }
  139. if (strpos($this->_type, '[]') !== false) {
  140. $template .= '%s (%s)';
  141. $type = str_replace('[]', '', $this->_type);
  142. $value = $this->_flattenValue($this->_value, $generator, $type);
  143. // To avoid SQL erros when comparing a field to a list of empty values,
  144. // generate a condition that will always evaluate to false
  145. if ($value === '') {
  146. return ['1 != 1', ''];
  147. }
  148. } else {
  149. $template .= '%s %s';
  150. $value = $this->_bindValue($this->_value, $generator, $this->_type);
  151. }
  152. return [$template, $value];
  153. }
  154. /**
  155. * Registers a value in the placeholder generator and returns the generated placeholder
  156. *
  157. * @param mixed $value The value to bind
  158. * @param \Cake\Database\ValueBinder $generator The value binder to use
  159. * @param string $type The type of $value
  160. * @return string generated placeholder
  161. */
  162. protected function _bindValue($value, $generator, $type) {
  163. $placeholder = $generator->placeholder('c');
  164. $generator->bind($placeholder, $value, $type);
  165. return $placeholder;
  166. }
  167. /**
  168. * Converts a traversable value into a set of placeholders generated by
  169. * $generator and separated by `,`
  170. *
  171. * @param array|\Traversable $value the value to flatten
  172. * @param \Cake\Database\ValueBinder $generator The value binder to use
  173. * @param string|array $type the type to cast values to
  174. * @return string
  175. */
  176. protected function _flattenValue($value, $generator, $type = null) {
  177. $parts = [];
  178. foreach ($value as $k => $v) {
  179. $parts[] = $this->_bindValue($v, $generator, $type);
  180. }
  181. return implode(',', $parts);
  182. }
  183. /**
  184. * Returns the number of expression this class represents
  185. *
  186. * @return int
  187. */
  188. public function count() {
  189. return 1;
  190. }
  191. }