thecodingmachine/graphqlite-symfony-validator-bridge
Bridge between GraphQLite and Symfony Validator: validate GraphQL inputs and arguments using Symfony constraints, returning validation errors in GraphQL responses. Integrates with GraphQLite’s validation features for Symfony-based projects.
Install the package in your Laravel project:
composer require thecodingmachine/graphqlite-symfony-validator-bridge
Register the bridge in your GraphQLite schema configuration:
use TheCodingMachine\GraphQLite\Validation\SymfonyValidatorBridge;
use Symfony\Component\Validator\Validator\ValidatorInterface;
// In your GraphQLite schema builder
$validator = app(ValidatorInterface::class); // Laravel's built-in validator
$schema->setValidator(new SymfonyValidatorBridge($validator));
First use case: Validate a GraphQL mutation input with Symfony constraints.
use Symfony\Component\Validator\Constraints as Assert;
#[Assert\NotBlank]
#[Assert\Email]
private ?string $email;
ValidatorInterface.// GraphQL Input Type
#[GraphQLInputType]
class CreateUserInput {
#[Assert\NotBlank]
#[Assert\Length(min: 3, max: 50)]
public string $name;
#[Assert\Email]
public ?string $email = null;
}
// Example: Convert Laravel's 'required|email' to Symfony
#[Assert\NotBlank]
#[Assert\Email]
private ?string $email;
Default, Create, Update).$schema->setValidator(
new SymfonyValidatorBridge($validator, ['Default', 'Create'])
);
Constraint class for complex rules.use Symfony\Component\Validator\Constraint;
class UniqueUsername extends Constraint {
public string $message = 'This username is already taken.';
}
#[UniqueUsername]
private ?string $username;
$schema->setErrorFormatter(function (ValidationException $e) {
return [
'errors' => array_map(function ($error) {
return [
'message' => $error->getMessage(),
'path' => $error->getPropertyPath(),
];
}, $e->getErrors())
];
});
Service Provider Setup Ensure the Symfony Validator is bound to Laravel’s container:
// In AppServiceProvider
public function register() {
$this->app->extend(\Symfony\Component\Validator\Validator\ValidatorInterface::class,
function ($app, $validator) {
return $validator;
});
}
Testing Validations Use Laravel’s testing helpers to assert validation errors:
public function test_user_creation_validation() {
$response = $this->post('/graphql', [
'query' => 'mutation { createUser(input: { name: "", email: "invalid" }) }'
]);
$response->assertJsonValidationErrors([
'name' => ['This value should not be blank.'],
'email' => ['This value is not a valid email address.']
]);
}
Performance Optimization
$validator = Cache::remember('graphql.validator', now()->addHours(1), function () {
return app(ValidatorInterface::class);
});
Dynamic Validation Rules
Use Symfony’s Callback constraint for dynamic validation:
#[Assert\Callback]
public function validate(ExecutionContextInterface $context, $payload) {
if ($payload->email === 'admin@example.com') {
$context->buildViolation('Admin email is restricted.')
->atPath('email')
->addViolation();
}
}
PHP Version Mismatch
#[Assert\Email]) require PHP 8+. If using PHP 7.x, fall back to YAML/XML constraints.validator: constraints in config/validation.yaml:
App\GraphQL\Types\CreateUserInput:
constraints:
- NotBlank: { fields: name }
- Email: { fields: email }
Circular Dependencies
User has Address, Address has User), Symfony’s validator may throw circular reference errors.ValidationBuilder to exclude circular references:
$validator = $this->validator->withRootConstraints();
Laravel’s Validator vs. Symfony’s Validator
Validator facade is not the same as Symfony’s ValidatorInterface. Directly inject ValidatorInterface to avoid confusion.$validator = app(\Symfony\Component\Validator\Validator\ValidatorInterface::class);
Validation Overhead
Error Message Localization
config/validator.php:
'translation_domain' => 'validators',
Enable Debug Mode Symfony’s validator provides detailed errors in debug mode:
$validator->setDebug(true);
Log Validation Errors Extend the bridge to log errors for debugging:
$schema->setValidator(new class($validator) extends SymfonyValidatorBridge {
public function validate($data, array $groups = null) {
try {
return parent::validate($data, $groups);
} catch (\Exception $e) {
\Log::error('Validation failed: ' . $e->getMessage());
throw $e;
}
}
});
Test Constraints Isolated
Use Symfony’s Validator directly to test constraints before integrating with GraphQLite:
$validator = app(ValidatorInterface::class);
$errors = $validator->validate($inputData, null, ['Default']);
Check for Deprecated Constraints
@Assert\Email (use @Assert\Email from symfony/validator).Custom Constraint Validators
Extend Symfony’s ConstraintValidatorInterface for complex logic:
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class UniqueUsernameValidator extends ConstraintValidator {
public function validate($value, Constraint $constraint) {
if (User::where('username', $value)->exists()) {
$this->context->buildViolation($constraint->message)
->addViolation();
}
}
}
Override Error Formatting Customize how validation errors are returned in GraphQL responses:
$schema->setErrorFormatter(function (ValidationException $e) {
return [
'errors' => array_map(function ($error) {
return [
'message' => __($error->getMessage()),
'path' => explode('.', $error->getPropertyPath()),
'code' => $error->getCode(),
];
}, $e->getErrors())
];
});
Integrate with Laravel’s Form Requests
Reuse Laravel’s FormRequest validation logic in GraphQL:
use Illuminate\Validation\Rule;
#[Assert\NotBlank]
#[Assert\Length(min: 8)]
#[Assert\Regex('/^(?=.*[A-Z])(?=.*\d).+$/')] // At least 1 uppercase and 1 digit
private ?string $password;
Dynamic Constraint Loading
How can I help you explore Laravel packages today?