Skip to content

Releases: hecht-a/graphql-orm

v5.13.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 14:12

Added

  • operation_names: true, at the root or for a manager, names each query after its entity and the method sending it
    (query Task_findBy { ... }, mutation flush { ... }), for the logs and the monitoring of the API; off by default,
    as it changes the text of the queries. GraphqlQueryBuilder::name() names a query whatever the option.

  • The PHPStan extension tells that the mapped properties are written by the hydration and read by flush(): a
    readonly identifier needs no constructor.

Changed

  • phpstan/phpdoc-parser 2.3.1 or later is required, rather than 2.2: the tests of the PHPStan extension of the bundle
    failed with 2.2 and 2.3.0, whose nodes cannot be read back from the cache of PHPStan.
  • The hydration and flush() bypass the hooks of the properties, as Doctrine does: the set hooks no longer run when
    an entity is hydrated, and flush() sends the value a property holds rather than the one its get hook returns.
    A virtual property, holding no value of its own, can no longer be mapped: an InvalidArgumentException says so.

Fixed

  • A readonly property is no longer written again when another query returns the entity: an entity with a readonly
    identifier threw Cannot modify readonly property once met by a second query.

v5.12.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 12:51

Added

  • graphqlorm:find --dry-run shows the query as it would be sent, with its variables, the manager and the dialect,
    without sending it; --format=json gives them as an object.
  • GraphqlEntityRepository::createFindByQuery() gives the query findBy() sends, not sent yet: to read its GraphQL,
    keep its response in the result cache or send it with others through GraphqlQuery::concurrently().
  • A cookbook in the documentation: from HttpClient to the bundle, when to use it and when not, a paginated list, a form
    creating an entity, and speeding up a slow page.

Changed

  • 4.x and the versions before it are no longer maintained: they get no release, fixes included. Their documentation
    stays online under /4.x/, marked as unmaintained.

v5.11.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 08:53

Changed

  • Faster queries and hydration, measured with the benchmarks of tests/Benchmark: a query no longer copies the shared
    identity map, so that its cost no longer grows with the entities known (5,000 known: 160 µs down to 25 µs); the
    query is exported and masked for the query events only when they have listeners; the reflection of the hydrated and
    tracked properties, of the value objects, of the DTOs and of the lazy ghosts is read once; a response is decoded
    once. Hydrating 1,000 rows takes about 10% less time.

Added

  • The walkers extending AbstractGraphqlWalker format the arguments as DefaultGraphqlWalker did: a dialect overrides
    formatEnum() to print a backed enum as an enum literal, and omitsArgument() to leave out the arguments its API
    refuses.

Fixed

  • Data API Builder fetches the page after any cursor, "0" included, rather than the first page again.
  • A count or an aggregate missing from a response, or not a number, says so and names it, rather than
    expected array, got "array": InvalidGraphqlResponseException::expectedNumber().
  • The queries sent together with GraphqlQuery::concurrently() share their entities through the identity map: the
    second no longer creates another instance of an entity the first hydrated.

v5.10.2

Choose a tag to compare

@github-actions github-actions released this 19 Oct 07:30

Fixed

  • The numeric options whose null turns them off accept null once written: profiler.max_queries,
    profiler.max_query_time, slow_query_threshold (at the root or for a manager, which then reports no slow query
    whatever the root) and the lifetime of a second-level cache region. They threw an InvalidTypeException at boot.

v5.10.1

Choose a tag to compare

@github-actions github-actions released this 18 Oct 17:08
b4f3b07

Fixed

  • GraphqlEntityRepository::countMatching(), which counts the entities a filter of the query builder matches, is no
    longer internal: a test stubbing a repository no longer gets a deprecation from the debug class loader.

v5.9.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 10:37
49aa696

Added

  • The profiler suggests ->with() for a relation loaded one entity at a time, select(), hydrateAs() or
    enableResultCache() for a slow query or a large response, and the second-level cache for an entity found or loaded
    3 times or more.
  • A "Callers" tab of the profiler groups the queries by the line of the application sending them, the costliest
    first.
  • A "Replay" button of the profiler sends a query of the profile again and shows its response, in debug mode, for a
    query whose values are not masked (redact_query_values: false) and which is not a mutation. It needs the routes of
    the bundle.
  • The profiler options set the alerts of the profiler: max_queries, the queries of a request beyond which its
    toolbar turns yellow (50 by default, as Doctrine), max_query_time, the milliseconds waited for GraphQL beyond which
    it does, and repeated_queries, the times from which repeated queries, lazy loads and entities worth the
    second-level cache are reported (3 by default).
  • An "Export JSON" button of the profiler downloads the metrics, the queries and the findings of a profile, their
    values masked unless redact_query_values is false.

Changed

  • The timeline of the profiler shows the origin, the entity and the duration of each query, its hydration apart from
    the network, a graduated axis, and the time of the request before the first query.

Fixed

  • The time in GraphQL of the profiler and of its toolbar counts once the queries sent together, which the request
    waited for at the same time; their sum is shown apart.
  • The profiler describes the entities read by the queries sent after the response (a kernel.terminate listener, a
    streamed response), and marks these queries "after the response".
  • The profiler marks the queries of a sub-request (a fragment, a forward) with its path.
  • A profile keeps the detail of the first 500 queries only: the others are counted in the metrics and the alerts,
    without their text, variables, AST nor response, so that a page sending thousands of queries does not bloat it.

v5.8.1

Choose a tag to compare

@github-actions github-actions released this 18 Oct 09:59
342be3f

Changed

  • graphqlorm:make:entity generates the mapping attributes through a namespace alias, as Doctrine:
    use GraphqlOrm\Mapping as GraphqlOrm; and #[GraphqlOrm\Entity(name: 'tasks', repositoryClass: ...)]. The
    documentation maps the entities the same way.
  • The messages of the exceptions, of the commands and of the profiler name the mapping attributes the same way:
    #[GraphqlOrm\Cache], #[GraphqlOrm\Embedded]...

v5.8.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 09:29
c291108

Added

  • $repository->with('assignee.company') names the relations find(), findOneBy(), findBy() and findAll() load,
    as with() of the query builder: it gives a copy of the repository, which is left as it is.
  • graphqlorm:query --hydrate=Task hydrates the response as the entity and prints the entities as graphqlorm:find,
    as a table or JSON (--format=json).
  • The profiler tells the relations a query loads: those named by with(), or up to max_depth.
  • The schema validation reports a property typed with a value object that no type converter converts, and checks the
    fields a converter converts use one of the scalars of its #[AsTypeConverter(..., scalars: ['Int'])].
  • graphqlorm:make:entity --from-schema types a custom scalar of the API with the value object of the type converter
    telling it (scalars: ['Siret']).
  • Value objects spread over several fields, as the embeddables of Doctrine: a class marked #[Embeddable], its fields
    mapped with #[Field], held by a property mapped with #[Embedded], a GraphQL object (price { amount currency })
    or fields of the entity with #[Embedded(prefix: 'total_')]. Created through its constructor, selected, filtered
    and ordered by the path of its fields (total.amount), kept in the second-level cache, hydrated for the DTOs, checked
    by the schema validation and the PHPStan rules, listed by the profiler.

v5.7.1

Choose a tag to compare

@github-actions github-actions released this 18 Oct 02:54
79e96cd

Fixed

  • The #[AfterHydrate] hooks of an entity completed after being hydrated in part threw a PartialEntityException with
    PHP 8.4, whose ReflectionProperty::isInitialized() runs the initializer of a lazy object.
  • No deprecation with symfony/dependency-injection 8.1, which deprecates the index and priority methods of the tagged
    iterators given by position.

v5.7.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 02:23
a43d43a

Added

  • with() on the query builder names the relations a query loads, nested ones by their path
    (with('assignee.company', 'label')), in place of max_depth: the relations not named only get their identifier
    selected. A relation mapped with Fetch::Lazy is loaded when named, and the relations named are added to the fields
    of select().
  • graphqlorm:query sends a raw GraphQL query to the API of a manager (--manager), with its variables
    (--variables='{"id": 1}') or from the standard input, and prints the JSON response. A response holding errors fails
    the command; the query is logged and profiled, its values masked there as any other.
  • hydrateAs() on the query builder hydrates the result as read-only DTOs, created through their constructor, rather
    than entities: the query selects their fields only. A parameter maps the field of its name, or another one with
    #[MapFrom('assignee.name')]; one typed with a class maps a relation to a nested DTO, an array typed list<Dto> a
    collection. Neither identity map nor second-level cache; pagination, indexBy() and the result cache work as with
    entities. The profiler tells the queries hydrated as DTOs, with the DTO and the number created.
  • Type converters turn a scalar of the API into a value object (Money, Email, Siret) and back: a service
    implementing TypeConverterInterface, autoconfigured, converts the properties typed with the class of its
    #[AsTypeConverter], or those whose #[Field(converter: ...)] names it. The values of the entities and of the DTOs
    are converted, and the value objects of the filters, criteria and indexBy sent back. The "Entities" tab of the
    profiler tells the converter of each field.
  • graphqlorm:cache:warmup [entity] loads the entities marked #[Cache] into the second-level cache, page by page
    (--batch-size, 100 by default), those of an entity or every one, of a manager (--manager) or all, and tells how
    many were hydrated and kept.
  • GraphqlCache::evictQueryResults() evicts every result of a manager, with any pool, even shared: the results get a new
    key, as the entities with evictEntityRegion(). The results kept before stay valid until a first eviction.
  • graphqlorm:cache:clear evicts the result cache and the second-level cache: both by default, or --results,
    --key=..., --entities, --class=..., of a manager (--manager) or all.
  • graphqlorm:cache:stats describes the caches of each manager: the pool, adapter and lifetime of the result cache and
    of each region, the entities marked #[Cache], and how many times they were evicted.