Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Graphqlite Symfony Validator Bridge Laravel Package

besmartand-pro/graphqlite-symfony-validator-bridge

Bridge package connecting Symfony Validator with GraphQLite, enabling automatic validation of GraphQL input/arguments using Symfony constraints and returning structured validation errors in GraphQL responses. Suitable for Symfony apps using GraphQLite.

View on GitHub
Deep Wiki
Context7

Implementation Patterns

1. Schema-Level Validation (Declarative Approach)

Leverage GraphQLite’s schema definitions to embed Symfony constraints directly, reducing resolver clutter.

Example: Annotated Input Types

use GraphQL\Type\Definition\InputType;
use GraphQL\Type\Definition\Type;
use Symfony\Component\Validator\Constraints as Assert;
use Besmartand\GraphQLite\SymfonyValidatorBridge\Annotation\ValidatedInput;

/**
 * @ValidatedInput({
 *     "email": {"constraints": {"Email": {}}},
 *     "password": {
 *         "constraints": {
 *             "NotBlank": {},
 *             "Length": {"min": 8}
 *         }
 *     }
 * })
 */
$inputType = new InputType([
    'name' => 'UserInput',
    'fields' => [
        'email' => Type::string(),
        'password' => Type::string(),
    ],
]);

Pros:

  • Validation rules live with the schema (single source of truth).
  • IDE support for Symfony annotations (e.g., PHPStorm hints).

Cons:

  • Requires annotation parsing (may need doctrine/annotations).
  • Less flexible for dynamic rules.

2. Dynamic Validation (Runtime Rules)

Use the bridge to apply validation rules dynamically in resolvers.

Example: Conditional Validation

use Besmartand\GraphQLite\SymfonyValidatorBridge\ValidatorBridge;

public function resolve($root, $args)
{
    $validator = app(ValidatorBridge::class);

    // Dynamic rules based on context
    $rules = [
        'email' => 'required|email',
        'password' => $args['isAdmin'] ? 'required|min:12' : 'required|min:8',
    ];

    $errors = $validator->validate($args, $rules);
    if ($errors) {
        throw new GraphQLValidationError($errors);
    }

    // Proceed
}

Pros:

  • Flexible for context-aware validation (e.g., admin vs. user roles).
  • No schema changes required.

Cons:

  • Rules are scattered across resolvers (harder to maintain).
  • Risk of inconsistent validation logic.

3. Reusable Validation Services

Extract validation logic into services to avoid duplication.

Example: Dedicated Validator Service

// app/Services/UserInputValidator.php
class UserInputValidator
{
    protected $validator;

    public function __construct(ValidatorBridge $validator)
    {
        $this->validator = $validator;
    }

    public function validate(array $input): array
    {
        return $validator->validate($input, [
            'email' => [
                new Assert\NotBlank(),
                new Assert\Email(),
            ],
            'password' => [
                new Assert\NotBlank(),
                new Assert\Length(['min' => 8]),
            ],
        ]);
    }
}

Usage in Resolver:

public function resolve($root, $args)
{
    $validator = app(UserInputValidator::class);
    $errors = $validator->validate($args);
    if ($errors) {
        throw new GraphQLValidationError($errors);
    }
    // ...
}

Pros:

  • DRY (Don’t Repeat Yourself) validation logic.
  • Easier to test and mock.

Cons:

  • Adds a layer of abstraction (may confuse junior devs).

4. Nested Object Validation

Validate complex input structures (e.g., arrays, objects).

Example: Validating an Array of Items

$validator->validate($args, [
    'items' => new Assert\All([
        new Assert\Valid(),
        new Assert\Type('array'),
        new Assert\Count(['min' => 1]),
    ]),
    'items.*.name' => 'required|string|max:255',
    'items.*.price' => 'type:float|min:0',
]);

Pros:

  • Supports deeply nested validation.
  • Reuses Symfony’s constraint composition.

Cons:

  • Complex rule syntax can be error-prone.
  • Performance overhead for large arrays.

5. Integration with Laravel’s Form Requests

Reuse existing Laravel validation logic in GraphQL.

Example: Adapt a Form Request

use App\Http\Requests\StoreUserRequest;
use Besmartand\GraphQLite\SymfonyValidatorBridge\ValidatorBridge;

public function resolve($root, $args)
{
    $validator = app(ValidatorBridge::class);
    $request = new StoreUserRequest();
    $request->merge($args);

    $validator->validate($request->validated(), $request->rules());
    // ...
}

Pros:

  • Leverages existing Laravel validation rules.
  • Consistent validation across REST and GraphQL.

Cons:

  • Tight coupling to Laravel’s request system.
  • May not fit GraphQL’s stateless nature.

6. Custom Error Formatting

Map Symfony validation errors to GraphQL-friendly formats.

Example: Custom Error Handler

use Symfony\Component\Validator\ConstraintViolationListInterface;
use GraphQL\Error\FormattedError;

public function handleValidationErrors(ConstraintViolationListInterface $errors): array
{
    return array_map(function ($error) {
        return new FormattedError([
            'message' => $error->getMessage(),
            'path' => $error->getPropertyPath(),
            'code' => 'VALIDATION_ERROR',
            'extensions' => [
                'constraint' => get_class($error->getConstraint()),
            ],
        ]);
    }, iterator_to_array($errors));
}

Usage:

try {
    $validator->validate($args, $rules);
} catch (ValidationException $e) {
    throw new GraphQLValidationError($this->handleValidationErrors($e->getErrors()));
}

Pros:

  • Client-friendly error messages.
  • Standardized error structure.

Cons:

  • Adds boilerplate to error handling.

7. Caching Validated Inputs

Optimize performance for repeated validations (e.g., in loops).

Example: Cached Validation

use Illuminate\Support\Facades\Cache;

public function validateCached(array $input, array $rules, string $cacheKey)
{
    return Cache::remember($cacheKey, now()->addMinutes(10), function () use ($input, $rules) {
        return app(ValidatorBridge::class)->validate($input, $rules);
    });
}

Usage:

$errors = $this->validateCached($args, $rules, "validation_{$args['email']}");

Pros:

  • Reduces validation overhead for repeated inputs.
  • Useful for rate-limited endpoints.

Cons:

  • Cache invalidation can be tricky.
  • Not suitable for dynamic rules.

8. Testing Validation Logic

Write focused tests for validation rules.

Example: PHPUnit Test

use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;

class UserInputValidatorTest extends ConstraintValidatorTestCase
{
    public function testValidInput()
    {
        $validator = app(ValidatorBridge::class);
        $input = ['email' => 'test@example.com', 'password' => 'secure123'];
        $errors = $validator->validate($input, $this->getRules());
        $this->assertEmpty($errors);
    }

    public function testInvalidEmail()
    {
        $validator = app(ValidatorBridge::class);
        $input = ['email' => 'invalid-email', 'password' => 'secure123'];
        $errors = $validator->validate($input, $this->getRules());
        $this->assertCount(1, $errors);
    }

    protected function getRules(): array
    {
        return [
            'email' => 'required|email',
            'password' => 'required|min:8',
        ];
    }
}

Pros:

  • Ensures validation rules work as expected.
  • Catches regressions early.

Cons:

  • Requires maintaining test cases alongside rules.

Gotchas and Tips

Pitfalls

  1. Schema Validation Mismatch

    • Issue: Validation rules in the schema may not align with resolver logic (e.g., partial updates).
    • Fix: Document validation expectations in schema comments or use a tool like graphql-php to enforce consistency.
  2. Performance Overhead

    • Issue: Symfony Validator can be slow for high-throughput endpoints.
    • Fix:
      • Cache validation results (see Caching Validated Inputs).
      • Use @Cache decorators for resolvers.
      • Profile with tideways/xhprof to identify bottlenecks.
  3. Error Handling Gaps

    • Issue: Unhandled ValidationException can crash GraphQL responses.
    • Fix:
      • Always catch ValidationException in resolvers.
      • Use a global error handler to standardize validation errors:
        $schema->setErrorHandler(function ($errors) {
            return array_map(function ($error) {
                if ($error instanceof ValidationException) {
                    return $this->handleValidationErrors($error->getErrors());
                }
                return $error;
            }, $errors);
        });
        
  4. Annotation Parsing Failures

    • Issue: @ValidatedInput annotations may not parse correctly.
    • Fix:
      • Ensure doctrine/annotations is installed:
        composer require doctrine/
        
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky