@@ -40,7 +40,8 @@ Now this is all awfully technical, so let me come to some use-cases
4040fast to keep you motivated. Using walker implementation you can for
4141example:
4242
43-
43+ - Modify the Output walker to get the raw SQL via ``Query->getSQL() ``
44+ with interpolated parameters.
4445- Modify the AST to generate a Count Query to be used with a
4546 paginator for any given DQL query.
4647- Modify the Output Walker to generate vendor-specific SQL
@@ -50,7 +51,7 @@ example:
5051- Modify the Output walker to pretty print the SQL for debugging
5152 purposes.
5253
53- In this cookbook-entry I will show examples of the first two
54+ In this cookbook-entry I will show examples of the first three
5455points. There are probably much more use-cases.
5556
5657Generic count query for pagination
@@ -215,3 +216,77 @@ huge benefits with using vendor specific features. This would still
215216allow you write DQL queries instead of NativeQueries to make use of
216217vendor specific features.
217218
219+ Modify the Output Walker to get the raw SQL with interpolated parameters
220+ --------------------------------------------------------
221+
222+ .. code-block :: php
223+
224+ <?php
225+ use Doctrine\DBAL\ArrayParameterType;
226+ use Doctrine\DBAL\ParameterType;
227+ use Doctrine\DBAL\Types\BooleanType;
228+ use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
229+ use Doctrine\DBAL\Types\Type;
230+ use Doctrine\ORM\Query\AST;
231+ use Doctrine\ORM\Query\SqlOutputWalker;
232+
233+ class InterpolateParametersSQLOutputWalker extends SqlOutputWalker
234+ {
235+ /** {@inheritdoc} */
236+ public function walkInputParameter(AST\InputParameter $inputParam): string
237+ {
238+ $parameter = $this->getQuery()->getParameter($inputParam->name);
239+ if ($parameter === null) {
240+ return '?';
241+ }
242+
243+ $value = $parameter->getValue();
244+ /** @var ParameterType|ArrayParameterType|int|string $typeName */
245+ /** @see \Doctrine\ORM\Query\ParameterTypeInferer::inferType() */
246+ $typeName = $parameter->getType();
247+ $platform = $this->getConnection()->getDatabasePlatform();
248+ $processParameterType = static fn(ParameterType $type) => static fn($value): string =>
249+ (match ($type) { /** @see Type::getBindingType() */
250+ ParameterType::NULL => 'NULL',
251+ ParameterType::INTEGER => $value,
252+ ParameterType::BOOLEAN => (new BooleanType())->convertToDatabaseValue($value, $platform),
253+ ParameterType::STRING, ParameterType::ASCII => $platform->quoteStringLiteral($value),
254+ default => throw new ValueNotConvertible($value, $type->name)
255+ });
256+
257+ if (is_string($typeName) && Type::hasType($typeName)) {
258+ return Type::getType($typeName)->convertToDatabaseValue($value, $platform);
259+ }
260+ if ($typeName instanceof ParameterType) {
261+ return $processParameterType($typeName)($value);
262+ }
263+ if ($typeName instanceof ArrayParameterType && is_array($value)) {
264+ $type = ArrayParameterType::toElementParameterType($typeName);
265+ return implode(', ', array_map($processParameterType($type), $value));
266+ }
267+
268+ throw new ValueNotConvertible($value, $typeName);
269+ }
270+ }
271+
272+ Then you may get raw SQL with:
273+
274+ .. code-block :: php
275+
276+ <?php
277+ $query
278+ ->where('t.int IN (:ints)')->setParameter(':ints', [1, 2])
279+ ->orWhere('t.string IN (?0)')->setParameter(0, ['3', '4'])
280+ ->orWhere("t.bool = ?1")->setParameter('?1', true)
281+ ->orWhere("t.string = :string")->setParameter(':string', 'ABC')
282+ ->setHint(\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, InterpolateParametersSQLOutputWalker::class)
283+ ->getSQL();
284+
285+ The where clause of returned SQL should be like:
286+
287+ .. code-block :: sql
288+
289+ WHERE t0_.int IN (1, 2)
290+ OR t0_.string IN ('3', '4')
291+ OR t0_.bool = 1
292+ OR t0_.string = 'ABC'
0 commit comments