Skip to content

Commit 36a8b0d

Browse files
authored
Merge pull request #75 from andrew-demb/deprecate-target-method-annotation
✨ Support for using attribute as a target to parameter…
2 parents 2f677f6 + 0e6a7b7 commit 36a8b0d

File tree

7 files changed

+112
-22
lines changed

7 files changed

+112
-22
lines changed

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"doctrine/coding-standard": "^11.1|^12.0"
3131
},
3232
"scripts": {
33-
"phpstan": "phpstan analyse src/ -c phpstan.neon --level=7 --no-progress",
33+
"phpstan": "phpstan analyse --no-progress",
3434
"cs-check": "phpcs",
3535
"cs-fix": "phpcbf",
3636
"test": ["@cs-check", "@phpstan", "phpunit"]

phpstan.neon

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
parameters:
2+
level: 7
3+
4+
paths:
5+
- src
6+
27
ignoreErrors:
38

49
excludePaths:
@@ -7,4 +12,4 @@ parameters:
712
- .phpstan-cache
813

914
#includes:
10-
# - vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon
15+
# - vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon

src/Annotations/Assertion.php

+26-7
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,28 @@
1111

1212
use function is_array;
1313
use function ltrim;
14+
use function trigger_error;
15+
16+
use const E_USER_DEPRECATED;
1417

1518
/**
1619
* Use this annotation to validate a parameter for a query or mutation.
1720
*
21+
* Note 1: using this attribute as a target to the method (not parameter) is deprecated and will be removed in 8.0.
22+
* Note 2: support for `doctrine/annotations` will be removed in 8.0.
23+
* Note 3: this class won't implement `ParameterAnnotationInterface` in 8.0.
24+
*
1825
* @Annotation
1926
* @Target({"METHOD"})
2027
* @Attributes({
2128
* @Attribute("for", type = "string"),
2229
* @Attribute("constraint", type = "Symfony\Component\Validator\Constraint[]|Symfony\Component\Validator\Constraint")
2330
* })
2431
*/
25-
#[Attribute(Attribute::TARGET_METHOD)]
32+
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PARAMETER)]
2633
class Assertion implements ParameterAnnotationInterface
2734
{
28-
private string $for;
35+
private string|null $for = null;
2936
/** @var Constraint[] */
3037
private array $constraint;
3138

@@ -40,20 +47,32 @@ public function __construct(
4047
) {
4148
$for = $for ?? $values['for'] ?? null;
4249
$constraint = $constraint ?? $values['constraint'] ?? null;
43-
if ($for === null) {
44-
throw new BadMethodCallException('The Assert attribute must be passed a target. For instance: "#[Assert(for: "$email", constraint: new Email())"');
45-
}
4650

4751
if ($constraint === null) {
48-
throw new BadMethodCallException('The Assert attribute must be passed one or many constraints. For instance: "#[Assert(for: "$email", constraint: new Email())"');
52+
throw new BadMethodCallException('The Assertion attribute must be passed one or many constraints. For instance: "#[Assertion(constraint: new Email())"');
4953
}
5054

51-
$this->for = ltrim($for, '$');
5255
$this->constraint = is_array($constraint) ? $constraint : [$constraint];
56+
57+
if ($for === null) {
58+
return;
59+
}
60+
61+
trigger_error(
62+
"Using #[Assertion(for='" . $for . "', constaint='...')] on methods is deprecated in favor " .
63+
"of #[Assertion(constraint='...')] the parameter itself.",
64+
E_USER_DEPRECATED,
65+
);
66+
67+
$this->for = ltrim($for, '$');
5368
}
5469

5570
public function getTarget(): string
5671
{
72+
if ($this->for === null) {
73+
throw new BadMethodCallException('The Assertion attribute must be passed a target. For instance: "#[Assertion(for: "$email", constraint: new Email())"');
74+
}
75+
5776
return $this->for;
5877
}
5978

tests/Annotations/AssertionTest.php

+1-8
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,10 @@
99

1010
class AssertionTest extends TestCase
1111
{
12-
public function testException1(): void
13-
{
14-
$this->expectException(BadMethodCallException::class);
15-
$this->expectExceptionMessage('The Assert attribute must be passed a target. For instance: "#[Assert(for: "$email", constraint: new Email())"');
16-
new Assertion([]);
17-
}
18-
1912
public function testException2(): void
2013
{
2114
$this->expectException(BadMethodCallException::class);
22-
$this->expectExceptionMessage('The Assert attribute must be passed one or many constraints. For instance: "#[Assert(for: "$email", constraint: new Email())"');
15+
$this->expectExceptionMessage('The Assertion attribute must be passed one or many constraints. For instance: "#[Assertion(constraint: new Email())"');
2316
new Assertion(['for' => 'foo']);
2417
}
2518
}

tests/Fixtures/Controllers/UserController.php

+10-1
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,17 @@ public function createUser(string $email, string $password): User
3535

3636
#[Query]
3737
#[Assertion(for: '$email', constraint: new Assert\Email())]
38-
public function findByMail(string $email = '[email protected]'): User
38+
public function findByMailTargetMethod(string $email = '[email protected]'): User
3939
{
40+
// deprecated way of using the Assertion annotation - as a target of the method
41+
return new User($email, 'foo');
42+
}
43+
44+
#[Query]
45+
public function findByMail(
46+
#[Assertion(constraint: new Assert\Email())]
47+
string $email = '[email protected]',
48+
): User {
4049
return new User($email, 'foo');
4150
}
4251
}

tests/Fixtures/InvalidControllers/InvalidController.php

+4-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
class InvalidController
1313
{
1414
#[Query]
15-
#[Assertion(for: '$resolveInfo', constraint: new Assert\Email())]
16-
public function invalid(ResolveInfo $resolveInfo): string
17-
{
15+
public function invalid(
16+
#[Assertion(for: '$resolveInfo', constraint: new Assert\Email())]
17+
ResolveInfo $resolveInfo,
18+
): string {
1819
return 'foo';
1920
}
2021
}

tests/IntegrationTest.php

+64-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ public function testEndToEndAssert(): void
108108

109109
$errors = $result->toArray(DebugFlag::RETHROW_UNSAFE_EXCEPTIONS)['errors'];
110110

111-
// TODO: find why message is not in French...
112111
$this->assertSame('This value is not a valid email address.', $errors[0]['message']);
113112
$this->assertSame('email', $errors[0]['extensions']['field']);
114113
$this->assertSame('Validate', $errors[0]['extensions']['category']);
@@ -151,6 +150,70 @@ public function testEndToEndAssert(): void
151150
$this->assertSame('[email protected]', $data['findByMail']['email']);
152151
}
153152

153+
public function testEndToEndAssertForDeprecatedWay(): void
154+
{
155+
$schema = $this->getSchema();
156+
$schema->assertValid();
157+
158+
$queryString = '
159+
{
160+
findByMailTargetMethod(email: "notvalid") {
161+
email
162+
}
163+
}
164+
';
165+
166+
$result = GraphQL::executeQuery(
167+
$schema,
168+
$queryString,
169+
);
170+
$result->setErrorsHandler([WebonyxErrorHandler::class, 'errorHandler']);
171+
$result->setErrorFormatter([WebonyxErrorHandler::class, 'errorFormatter']);
172+
173+
$errors = $result->toArray(DebugFlag::RETHROW_UNSAFE_EXCEPTIONS)['errors'];
174+
175+
$this->assertSame('This value is not a valid email address.', $errors[0]['message']);
176+
$this->assertSame('email', $errors[0]['extensions']['field']);
177+
$this->assertSame('Validate', $errors[0]['extensions']['category']);
178+
179+
$queryString = '
180+
{
181+
findByMailTargetMethod(email: "[email protected]") {
182+
email
183+
}
184+
}
185+
';
186+
187+
$result = GraphQL::executeQuery(
188+
$schema,
189+
$queryString,
190+
);
191+
$result->setErrorsHandler([WebonyxErrorHandler::class, 'errorHandler']);
192+
$result->setErrorFormatter([WebonyxErrorHandler::class, 'errorFormatter']);
193+
194+
$data = $result->toArray(DebugFlag::RETHROW_UNSAFE_EXCEPTIONS)['data'];
195+
$this->assertSame('[email protected]', $data['findByMailTargetMethod']['email']);
196+
197+
// Test default parameter
198+
$queryString = '
199+
{
200+
findByMailTargetMethod {
201+
email
202+
}
203+
}
204+
';
205+
206+
$result = GraphQL::executeQuery(
207+
$schema,
208+
$queryString,
209+
);
210+
$result->setErrorsHandler([WebonyxErrorHandler::class, 'errorHandler']);
211+
$result->setErrorFormatter([WebonyxErrorHandler::class, 'errorFormatter']);
212+
213+
$data = $result->toArray(DebugFlag::RETHROW_UNSAFE_EXCEPTIONS)['data'];
214+
$this->assertSame('[email protected]', $data['findByMailTargetMethod']['email']);
215+
}
216+
154217
public function testException(): void
155218
{
156219
$schemaFactory = $this->getSchemaFactory();

0 commit comments

Comments
 (0)