Skip to main content

Upgrade Guides

This section contains upgrade guides between major versions.

v2 to v3

Breaking Changes

  • PHP 7.4 required, was 7.2. Docker image, CI jobs and the composer platform config move with it. firebase/php-jwt widens to ^6.10 || ^7.0, so PHP 8 consumers pull the patched v7.
  • Resource controllers enforce their HTTP method. AbstractUpdateResourceController requires a PATCH route and AbstractDeleteResourceController a DELETE route, checked when the route is built. An update controller returning a PUT route now throws. Partial-update semantics were always what these controllers documented; the check makes a mismatching route fail instead of publishing a contract the controller does not implement.
  • Filters declare operators instead of being one. A field is one filter with the operators it accepts, rather than one filter per operator:
    The named constructors (::equals(), ::in(), ::like(), ::contains(), ::greaterThan(), ::lessThan()) are gone. Declaring the same field twice now throws, where v2 silently kept the last one, which means a v2 route that declared two filters for one field was only ever applying one of them.
  • Reading filters changed. getType(), the isType*() family, getValue() and getRawValue() are replaced by typed properties plus has() and raw():
    Property names are equal, notEqual, in, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, like, contains and isNull.
  • The filter[...] query wrapper is gone. Clients send ?status[eq]=active, one bracket level, with the operator inside. ?filter[status]=active no longer resolves. Flat notation is unchanged: ?status=active still works and maps to the first operator a field declares, so v2 clients that never used the wrapper need no change. Filter field names may no longer collide with order_by, offset, cursor, page or limit, since filters and framework parameters now share one namespace. A colliding declaration throws when the route is built.
  • Filters are validated per operator. An operator a field does not declare, and combinations that can never match (value[gte]=100&value[lte]=10, eq equal to neq, an empty in list), answer 422 instead of being ignored.
  • OpenAPI output targets 3.2.0, was 3.1.1. A filter field with several operators becomes one deepObject parameter whose properties are its allowed operators. A field with a single operator is documented flat, with the operator named in the description. Toolchains pinned to 3.1 will reject the document.
  • AbstractApivalkController::__invoke() is no longer abstract. Existing controllers keep working, ApivalkRequestInterface is still a valid parameter type. What changes is that you can now name your own request class, and should, since the generated shapes for path(), body() and filtering() only resolve against the concrete class:
    A controller without __invoke() is no longer a compile-time error. It is rejected at runtime by RouteCacheFactory and ApivalkControllerFactory instead. See Typing $request.
  • getRequestClass() and getResponseClasses() are removed. __invoke() is the single source of truth for both. Its parameter names the request class, and the response classes are read from what its body returns:
    Delete both methods, and the @extends AbstractApivalkController<GetPetRequest> annotation with them, the template is gone. The same applies to the five resource controller bases, which no longer declare either. Check every migrated controller against this, it is where the migration silently loses documentation: the scan is syntactic, so a response only stays documented if it is constructed with a literal new inside __invoke() itself. return $this->buildResponse($pet); and $response = $this->service->list(); return $response; both documented fine while getResponseClasses() listed them by hand, and both document nothing now. Neither fails, the endpoint simply loses that response from its spec, and a success response built in a helper leaves the operation with no 2xx at all. Only two mistakes are loud, both a LogicException naming the controller: returning an anonymous class, and an __invoke() that yields no response class whatsoever. A controller that builds some of its responses in helpers passes and ships an incomplete document. Worth doing once across the codebase before you publish the new spec: run ControllerResponseScanner::scan() over every controller and compare the result against the getResponseClasses() list you are deleting. The diff is exactly the set of responses you are about to lose, minus the framework error responses that now come from the route instead. See Documented Responses.
  • Framework error responses follow the route. 401, 404, 422 and 429 were appended to every operation; each is now documented only where the route can produce it, and 403 is documented wherever an authorization declares scopes or permissions. Generated specs change accordingly: authorized routes gain 403, and routes without a rate limit, path parameter or anything to validate lose 429, 404 and 422. See Automated Error Responses.

New Features

  • QUERY transport (RFC 10008). $route->enableQuery() makes a filtered route additionally reachable as a QUERY request whose JSON body carries the filters. Opt-in per route. See Filtering.
  • New operators: neq, gte, lte and null (?end_date[null]=true matches rows without a value).
  • Per-field IDE shapes. DocBlockGenerator emits one interface per filter field listing only the operators that field declares.
  • excludeFromDocumentation() keeps a route out of the generated OpenAPI document, for internal endpoints that should not reach public docs or generated SDKs.

v1 to v2

This section lists major changes and steps required to upgrade from Apivalk v1 to v2.

Breaking Changes

  • Renamed MODE_EDIT to MODE_UPDATE: All occurrences of MODE_EDIT in AbstractPropertyCollection and related security permissions (e.g., asset:edit to asset:update) have been renamed.
  • Renamed “Ordering” to “Sorting”: The terminology and related classes/methods have been updated from “Ordering” to “Sorting” (e.g., Order is now Sort).
  • Property System Refactor: NumberProperty has been split into IntegerProperty and FloatProperty. StringProperty now has specialized variants like EnumProperty, DateProperty, DateTimeProperty, ByteProperty, and BinaryProperty.

New Features

  • Resource System: Introduced AbstractResource and a CRUD controller hierarchy (Create, View, List, Update, Delete) to streamline resource management.
  • Modular Pagination: Added support for multiple pagination strategies (Cursor, Offset, Page) with a new PaginatorFactory.
  • Comprehensive Filtering & Sorting: A new robust system for defining filters and sorts on routes, with automatic population from query parameters.
  • Property Serializer: Introduced PropertySerializer for full round-trip serialization of properties, including validators.
  • Improved OpenAPI & DocBlock Generation: Enhanced generators to support the new resource system, pagination, filters, and sorts.
  • Request Population Strategies: Refactored request population into a modular strategy collection.

Deprecation marks

This section will contain all features/elements which are deprecated and which will be removed in a certain upcoming version.

Upcoming removes

Removed deprecations