ArrayExpressionBuilder.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\db\pgsql;
  8. use yii\db\ArrayExpression;
  9. use yii\db\ExpressionBuilderInterface;
  10. use yii\db\ExpressionBuilderTrait;
  11. use yii\db\ExpressionInterface;
  12. use yii\db\JsonExpression;
  13. use yii\db\Query;
  14. /**
  15. * Class ArrayExpressionBuilder builds [[ArrayExpression]] for PostgreSQL DBMS.
  16. *
  17. * @author Dmytro Naumenko <d.naumenko.a@gmail.com>
  18. * @since 2.0.14
  19. */
  20. class ArrayExpressionBuilder implements ExpressionBuilderInterface
  21. {
  22. use ExpressionBuilderTrait;
  23. /**
  24. * {@inheritdoc}
  25. * @param ArrayExpression|ExpressionInterface $expression the expression to be built
  26. */
  27. public function build(ExpressionInterface $expression, array &$params = [])
  28. {
  29. $value = $expression->getValue();
  30. if ($value === null) {
  31. return 'NULL';
  32. }
  33. if ($value instanceof Query) {
  34. list ($sql, $params) = $this->queryBuilder->build($value, $params);
  35. return $this->buildSubqueryArray($sql, $expression);
  36. }
  37. $placeholders = $this->buildPlaceholders($expression, $params);
  38. return 'ARRAY[' . implode(', ', $placeholders) . ']' . $this->getTypehint($expression);
  39. }
  40. /**
  41. * Builds placeholders array out of $expression values
  42. * @param ExpressionInterface|ArrayExpression $expression
  43. * @param array $params the binding parameters.
  44. * @return array
  45. */
  46. protected function buildPlaceholders(ExpressionInterface $expression, &$params)
  47. {
  48. $value = $expression->getValue();
  49. $placeholders = [];
  50. if ($value === null || !is_array($value) && !$value instanceof \Traversable) {
  51. return $placeholders;
  52. }
  53. if ($expression->getDimension() > 1) {
  54. foreach ($value as $item) {
  55. $placeholders[] = $this->build($this->unnestArrayExpression($expression, $item), $params);
  56. }
  57. return $placeholders;
  58. }
  59. foreach ($value as $item) {
  60. if ($item instanceof Query) {
  61. list ($sql, $params) = $this->queryBuilder->build($item, $params);
  62. $placeholders[] = $this->buildSubqueryArray($sql, $expression);
  63. continue;
  64. }
  65. $item = $this->typecastValue($expression, $item);
  66. if ($item instanceof ExpressionInterface) {
  67. $placeholders[] = $this->queryBuilder->buildExpression($item, $params);
  68. continue;
  69. }
  70. $placeholders[] = $this->queryBuilder->bindParam($item, $params);
  71. }
  72. return $placeholders;
  73. }
  74. /**
  75. * @param ArrayExpression $expression
  76. * @param mixed $value
  77. * @return ArrayExpression
  78. */
  79. private function unnestArrayExpression(ArrayExpression $expression, $value)
  80. {
  81. $expressionClass = get_class($expression);
  82. return new $expressionClass($value, $expression->getType(), $expression->getDimension()-1);
  83. }
  84. /**
  85. * @param ArrayExpression $expression
  86. * @return string the typecast expression based on [[type]].
  87. */
  88. protected function getTypehint(ArrayExpression $expression)
  89. {
  90. if ($expression->getType() === null) {
  91. return '';
  92. }
  93. $result = '::' . $expression->getType();
  94. $result .= str_repeat('[]', $expression->getDimension());
  95. return $result;
  96. }
  97. /**
  98. * Build an array expression from a subquery SQL.
  99. *
  100. * @param string $sql the subquery SQL.
  101. * @param ArrayExpression $expression
  102. * @return string the subquery array expression.
  103. */
  104. protected function buildSubqueryArray($sql, ArrayExpression $expression)
  105. {
  106. return 'ARRAY(' . $sql . ')' . $this->getTypehint($expression);
  107. }
  108. /**
  109. * Casts $value to use in $expression
  110. *
  111. * @param ArrayExpression $expression
  112. * @param mixed $value
  113. * @return JsonExpression
  114. */
  115. protected function typecastValue(ArrayExpression $expression, $value)
  116. {
  117. if ($value instanceof ExpressionInterface) {
  118. return $value;
  119. }
  120. if (in_array($expression->getType(), [Schema::TYPE_JSON, Schema::TYPE_JSONB], true)) {
  121. return new JsonExpression($value);
  122. }
  123. return $value;
  124. }
  125. }