Skip to content

Commit 534b163

Browse files
Merge pull request #11 from MacPaw/develop
Release
2 parents 184f9a8 + f3afae2 commit 534b163

File tree

2 files changed

+500
-2
lines changed

2 files changed

+500
-2
lines changed

src/Context/ORMContext.php

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Doctrine\ORM\EntityManagerInterface;
1010
use Doctrine\ORM\NonUniqueResultException;
1111
use Doctrine\ORM\NoResultException;
12+
use Doctrine\ORM\QueryBuilder;
13+
use JsonException;
1214
use RuntimeException;
1315

1416
final class ORMContext implements Context
@@ -22,6 +24,8 @@ public function __construct(EntityManagerInterface $manager)
2224

2325
/**
2426
* @And I see :count entities :entityClass
27+
*
28+
* @param class-string $entityClass
2529
*/
2630
public function andISeeInRepository(int $count, string $entityClass): void
2731
{
@@ -30,6 +34,8 @@ public function andISeeInRepository(int $count, string $entityClass): void
3034

3135
/**
3236
* @Then I see :count entities :entityClass
37+
*
38+
* @param class-string $entityClass
3339
*/
3440
public function thenISeeInRepository(int $count, string $entityClass): void
3541
{
@@ -38,6 +44,8 @@ public function thenISeeInRepository(int $count, string $entityClass): void
3844

3945
/**
4046
* @And I see entity :entity with id :id
47+
*
48+
* @param class-string $entityClass
4149
*/
4250
public function andISeeEntityInRepositoryWithId(string $entityClass, string $id): void
4351
{
@@ -46,6 +54,8 @@ public function andISeeEntityInRepositoryWithId(string $entityClass, string $id)
4654

4755
/**
4856
* @Then I see entity :entity with id :id
57+
*
58+
* @param class-string $entityClass
4959
*/
5060
public function thenISeeEntityInRepositoryWithId(string $entityClass, string $id): void
5161
{
@@ -54,6 +64,8 @@ public function thenISeeEntityInRepositoryWithId(string $entityClass, string $id
5464

5565
/**
5666
* @Then I see entity :entity with properties:
67+
*
68+
* @param class-string $entityClass
5769
*/
5870
public function andISeeEntityInRepositoryWithProperties(string $entityClass, PyStringNode $string): void
5971
{
@@ -62,6 +74,7 @@ public function andISeeEntityInRepositoryWithProperties(string $entityClass, PyS
6274
}
6375

6476
/**
77+
* @param class-string $entityClass
6578
* @param array<string, mixed> $params
6679
*
6780
* @throws NonUniqueResultException
@@ -74,12 +87,20 @@ private function seeInRepository(int $count, string $entityClass, ?array $params
7487
->select('count(e)');
7588

7689
if (null !== $params) {
90+
$metadata = $this->manager->getClassMetadata($entityClass);
91+
7792
foreach ($params as $columnName => $columnValue) {
7893
if ($columnValue === null) {
7994
$query->andWhere(sprintf('e.%s IS NULL', $columnName));
8095
} else {
81-
$query->andWhere(sprintf('e.%s = :%s', $columnName, $columnName))
82-
->setParameter($columnName, $columnValue);
96+
if ($this->isJsonField($metadata, $columnName)) {
97+
// Handle JSON fields with proper DQL
98+
$this->addJsonFieldCondition($query, $columnName, $columnValue);
99+
} else {
100+
// Regular field comparison
101+
$query->andWhere(sprintf('e.%s = :%s', $columnName, $columnName))
102+
->setParameter($columnName, $columnValue);
103+
}
83104
}
84105
}
85106
}
@@ -93,4 +114,74 @@ private function seeInRepository(int $count, string $entityClass, ?array $params
93114
);
94115
}
95116
}
117+
118+
/**
119+
* Check if a field is mapped as JSON type
120+
*
121+
* @param \Doctrine\ORM\Mapping\ClassMetadata<object> $metadata
122+
*/
123+
private function isJsonField(\Doctrine\ORM\Mapping\ClassMetadata $metadata, string $fieldName): bool
124+
{
125+
if (!$metadata->hasField($fieldName)) {
126+
return false;
127+
}
128+
129+
$fieldMapping = $metadata->getFieldMapping($fieldName);
130+
131+
return \in_array($fieldMapping['type'], ['json', 'json_array'], true);
132+
}
133+
134+
/**
135+
* Add JSON field condition using DQL-compatible functions
136+
* Uses CONCAT for PostgreSQL to convert JSON to string for comparison
137+
*
138+
* @param mixed $expectedValue
139+
*/
140+
private function addJsonFieldCondition(QueryBuilder $query, string $fieldName, $expectedValue): void
141+
{
142+
$platform = $this->manager->getConnection()->getDatabasePlatform();
143+
$platformName = $platform->getName();
144+
145+
// Normalize JSON value - ensure consistent encoding
146+
$expectedJson = $this->normalizeJsonValue($expectedValue);
147+
$paramName = $fieldName . '_json';
148+
149+
if ($platformName === 'postgresql') {
150+
// PostgreSQL: Use CONCAT to convert JSON to string for comparison
151+
// CONCAT('', field) effectively casts JSON to text in a DQL-compatible way
152+
$query->andWhere(sprintf('CONCAT(\'\', e.%s) = :%s', $fieldName, $paramName))
153+
->setParameter($paramName, $expectedJson);
154+
} elseif ($platformName === 'mysql') {
155+
// MySQL: Use JSON_UNQUOTE to extract JSON as string
156+
$query->andWhere(sprintf('JSON_UNQUOTE(e.%s) = :%s', $fieldName, $paramName))
157+
->setParameter($paramName, $expectedJson);
158+
} else {
159+
// Fallback for other databases (SQLite, etc.)
160+
$query->andWhere(sprintf('e.%s = :%s', $fieldName, $paramName))
161+
->setParameter($paramName, $expectedJson);
162+
}
163+
}
164+
165+
/**
166+
* Normalize JSON value to ensure consistent comparison
167+
* This handles arrays, objects, and already-encoded JSON strings
168+
*
169+
* @param mixed $value
170+
*/
171+
private function normalizeJsonValue($value): string
172+
{
173+
if (is_string($value)) {
174+
// If it's already a JSON string, decode and re-encode for normalization
175+
try {
176+
$decoded = json_decode($value, true, 512, JSON_THROW_ON_ERROR);
177+
return json_encode($decoded, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
178+
} catch (JsonException $e) {
179+
// If it's not valid JSON, treat as regular string
180+
return json_encode($value, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
181+
}
182+
}
183+
184+
// For arrays/objects, encode with consistent flags
185+
return json_encode($value, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
186+
}
96187
}

0 commit comments

Comments
 (0)