This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a GraphQL Type Driver for Doctrine ORM that integrates with webonyx/graphql-php. It's a framework-agnostic library that creates GraphQL types from Doctrine ORM entities using PHP attributes.
Key Point: This library does NOT redefine how webonyx/graphql-php works. It creates types to be used within that framework's existing patterns.
# Run all tests and quality checks (parallel-lint, phpcs, psalm, phpstan, phpunit)
composer test
# Run only PHPUnit tests
vendor/bin/phpunit
# Run a single test file
vendor/bin/phpunit test/Feature/Type/EntityTest.php
# Run a specific test method
vendor/bin/phpunit --filter testMethodName
# Generate code coverage report (requires Xdebug)
composer coverage# Run PHP CodeSniffer (Doctrine Coding Standard)
vendor/bin/phpcs
# Run Psalm (level 4)
vendor/bin/psalm
# Run PHPStan (level 5)
vendor/bin/phpstan analyze src --level=5
# Run PHP Parallel Lint
vendor/bin/parallel-lint ./src/ ./testThe Driver class (src/Driver.php) is the main entry point. It extends Container and uses the Services trait to provide:
type(string $id)- Get a GraphQL type (entity or custom type)connection(string $id)- Wrap an entity type in a Connection typefilter(string $id)- Get filter InputObjectType for an entitypagination()- Get pagination typeresolve(string $id)- Get resolve closure for an entityinput(string $entityClass, array $requiredFields, array $optionalFields)- Create InputObjectType for mutationscompleteConnection(string $id)- Returns a complete GraphQL endpoint definition with type, args, and resolve
The library uses a custom PSR-11 compliant Container (src/Container.php) that:
- Stores services and types in a case-insensitive registry
- Supports lazy initialization via closures
- Allows for buildable types (types that depend on other types)
- Enables registration of custom types
Key containers:
Driver- Main container for the entire libraryEntityTypeContainer- Manages entity GraphQL types (uses lazy ghost objects)TypeContainer- Manages custom GraphQL types (DateTime, Blob, etc.)HydratorContainer- Manages Doctrine Laminas Hydrators for entities
Metadata is extracted from entity attributes and stored in a Metadata object (ArrayObject wrapper):
MetadataFactorybuilds metadata from PHP attributes on entitiesGlobalEnablefeature enables all fields/associations without attributes (useful for development)- Metadata is cached per entity and includes field mappings, association mappings, limits, filters, etc.
Attributes are in src/Attribute/:
#[Entity]- Marks an entity for GraphQL exposure#[Field]- Exposes a field#[Association]- Exposes an association (relationship)#[ComputedField]- Exposes derived values from entity methods (placed on public methods)#[ExcludeFilters]- Excludes specific filters
Uses league/event (v3.0) for PSR-14 event dispatching. Key events in src/Event/:
EntityDefinition- Fired when an entity GraphQL type is created (allows modification of type definition)QueryBuilder- Fired when QueryBuilder is created for entity resolution (allows custom query modifications)Criteria- Fired for association filteringMetadata- Fired when metadata is built
Events can have custom event names via $eventName parameter in Driver methods.
Filters are auto-generated for all exposed fields and associations (src/Filter/):
FilterFactorycreates filter InputObjectTypesFiltersenum defines available filter types (eq, neq, lt, lte, gt, gte, isnull, between, in, notin, startwith, endswith, contains, sort, sortPriority)- Filters are context-aware based on field type
- Can be excluded globally via Config or per-entity/field via attributes
ResolveEntityFactorycreates resolve closures for entity queriesResolveCollectionFactorycreates resolve closures for associationsFieldResolverresolves individual fields- Uses Doctrine Laminas Hydrator for extracting entity data to arrays
- Supports extraction strategies (e.g., ToBoolean, ToFloat, ToInteger, Collection handling)
Config class (src/Config.php) supports:
group- Allows multiple GraphQL schemas from same entitiesgroupSuffix- Custom suffix for type namesuseHydratorCache- Cache hydrator results per requestlimit- Hard limit for collections (default: 1000)globalEnable- Enable all fields/associations without attributesignoreFields- Fields to ignore with globalEnableglobalByValue- Extract by value vs referenceentityPrefix- Remove prefix from type namessortFields- Sort fields alphabeticallyexcludeFilters- Globally exclude specific filters
Tests use in-memory SQLite database with test entities in test/Entity/:
Artist,Performance,Recording,User- Relational test dataTypeTest- Tests all Doctrine data types
Base test class TestCase (test/TestCase.php):
- Sets up EntityManager with attribute metadata
- Populates test data (Grateful Dead, Phish, etc.)
- Uses
setUp()for per-test isolation
Test organization:
- Feature tests in
test/Feature/organized by functionality - Tests verify GraphQL schema generation, query resolution, filtering, pagination, events, etc.
- Lazy Initialization: Entity types use PHP 8.4 Lazy Ghost objects to defer construction
- Type Registry: All types registered in containers by lowercase ID
- Buildable Types: Types that depend on other types (e.g., Connection wraps Entity type)
- Event-Driven Customization: Events allow modification at key points (type definition, query building)
- Attribute-Based Configuration: PHP 8 attributes configure GraphQL exposure
- Complete Connection Model: Follows GraphQL pagination spec with edges/nodes/pageInfo
- PHP 8.4+ required
- Doctrine ORM 3.6+ required
- Main branch is
12.5.x - This library is framework-agnostic (can be used with Laravel, Symfony, etc.)
- Type names are suffixed with group name by default (can be customized via groupSuffix config)