Caution
Like all other releases, this one was done to the best of one's ability. However, it has not been tested as thoroughly as previous releases. It is thus to be considered even more experimental than the version number already implies.
BC BREAK: pass conditions and sort methods through the application as specific types, instead of template parameters
When a request is received that contains filters or sorting definitions, they are at some point (and in some form) passed to the data source, to apply them.
Previously those definitions were immediately converted to instances supporting specific data sources, when the request was received. The implementation of these instances depended on a factory instance provided to the request handling.
E.g. if the data source was based on Doctrine, the application developer would choose a factory that converted the given filter into conditions that could be used for Doctrine queries. The created conditions where then passed through the library down to the data source, where they could be directly applied. The same was true for the sorting definitions.
Using template parameters to type-hint the instances wherever they were passed through, allowed to ensure that static code checking tools like phpstan could detect incompatibilities between the factory chosen by the developer and the data source chosen by the developer. However, the disadvantage was the considerable complexity overhead due to the type-hinting.
The current version changes the approach and passes a predefined format through the application. Logic directly responsible to access the data source, receiving filter or sorting definitions, must be able to handle this predefined format to use it for that specific data source. E.g. if the developer previously chose a condition factory compatible with his Doctrine data source, they now have to convert the predefined format to such Drupal conditions before accessing Doctrine.
Due to the flexibility required in the format (especially for filters) it is significantly more difficult to fully validate it early, which is needed for easily understandable error messages. For now the logic evaluating the format to access the data source must throw an exception if parts of the definitions are not supported. In future versions the logic accessing the data source may be allowed to pass validation information (like supported filter operators) up, allowing to the request handling, to validate the definitions early.
Beside the extensive adjustments of type-hinting within the library, the following changes were done to the developer API.
Note that the following examples show only the migration from one of many previous possibility to one of many current possibilities. If in doubt, you can manually verify your method/constructor usages against the corresponding docblock type-hinting, or verify it automatically using static code checkers like phpstan on the appropriate levels.
Otherwise, you must ensure that the filter operators supported by the request handling are supported by the data source as well, as explained above, the new approach currently can not automatically verify this aspect any longer.
ListRequest
instances still need a DrupalFilterParser
and JsonApiSortingParser
instance.
However, how these instances are created needs to be adjusted.
Instead of selecting the implementations yourself
// both implementations chosen by you
$sortMethodFactory = new \EDT\DqlQuerying\SortMethodFactories\SortMethodFactory();
$conditionFactory = new \EDT\DqlQuerying\ConditionFactories\DqlConditionFactory();
$sortingTransformer = new \EDT\JsonApi\RequestHandling\JsonApiSortingParser($sortMethodFactory);
$drupalConditionFactory = new \EDT\Querying\ConditionParsers\Drupal\PredefinedDrupalConditionFactory($conditionFactory);
$filterTransformer = new \EDT\Querying\ConditionParsers\Drupal\DrupalFilterParser(
$conditionFactory,
new \EDT\Querying\ConditionParsers\Drupal\DrupalConditionParser($drupalConditionFactory)
);
you must now use specific implementations:
// implementations required by the library
$sortMethodFactory = new \EDT\ConditionFactory\ConditionFactory();
$conditionFactory = new \EDT\Querying\SortMethodFactories\SortMethodFactory();
$sortingTransformer = new \EDT\JsonApi\RequestHandling\JsonApiSortingParser($sortMethodFactory);
$drupalConditionFactory = new \EDT\Querying\ConditionParsers\Drupal\PredefinedDrupalConditionFactory($conditionFactory)
$filterTransformer = new \EDT\Querying\ConditionParsers\Drupal\DrupalFilterParser(
$conditionFactory,
new \EDT\Querying\ConditionParsers\Drupal\DrupalConditionParser($drupalConditionFactory)
);
These specific implementations must also be used when creating conditions or sort methods to be applied to resources or entities.
As the needed implementations for the condition factory and sort method factory are now fixed, they are no longer needed as constructor parameters in DefaultProcessorConfig
.
Instead of
// both implementations chosen by you
$sortMethodFactory = new \EDT\DqlQuerying\SortMethodFactories\SortMethodFactory();
$conditionFactory = new \EDT\DqlQuerying\ConditionFactories\DqlConditionFactory();
$defaultProcessorConfig = new \EDT\JsonApi\Requests\DefaultProcessorConfig(
$validator,
$eventDispatcher,
$router,
$conditionFactory,
$sortMethodFactory
);
simply use
new \EDT\JsonApi\Requests\DefaultProcessorConfig(
$validator,
$eventDispatcher,
$router
);
The interfaces PathsBasedConditionFactoryInterface
and PathsBasedConditionGroupFactoryInterface
were removed. If you implemented any of them directly, you can simply replace the parent with ConditionFactoryInterface
or ConditionGroupFactoryInterface
respectively.
This affects the classes closest to the data source. As they now receive the predefined condition and sort method types, these must be converted to types usable for the data source.
OffsetEntityProviderInterface
is still generic (i.e. takes the type of conditions and sort methods as template parameters) and its implementations (PrefilledEntityProvider
and DoctrineOrmEntityProvider
) are still bound to specific types of conditions and sort methods.
I.e. PrefilledEntityProvider
expects instances of FunctionInterface
and SortMethodInterface
, which can be applied on PHP objects without additional data source like a DBMS.
Likewise, DoctrineOrmEntityProvider
expects instances of ClauseInterface
and OrderByInterface
, which it uses to create Doctrine queries.
This means that you can not call those two provider classes with the predefined definition instances.
Where you used them, you should instead use the new MappingEntityProvider
class instead.
// your entity class
$entityClass = Book::class;
// the factory creating conditions that can be applied in your data source, e.g. Doctrine
$conditionFactory = new \EDT\DqlQuerying\ConditionFactories\DqlConditionFactory();
// the factory creating sort methods that can be applied in your data source, e.g. Doctrine
$sortMethodFactory = new \EDT\DqlQuerying\SortMethodFactories\SortMethodFactory();
// create the entity provider like you did previously, e.g. one that accesses Doctrine
$queryBuilderPreparer = new \EDT\DqlQuerying\Utilities\QueryBuilderPreparer($entityClass);
$doctrineEntityProvider = new \EDT\DqlQuerying\ObjectProviders\DoctrineOrmEntityProvider(
$doctrineEntityManager,
$queryBuilderPreparer,
$entityClass
);
// create a `MappingEntityProvider` instance that automatically converts the predefined
// conditions and sort methods into ones suited for your entity provider defined above.
$conditionConverter = \EDT\JsonApi\InputHandling\ConditionConverter::createDefault($validator, $conditionFactory);
$sortMethodConverter = \EDT\JsonApi\InputHandling\SortMethodConverter::createDefault($validator, $sortMethodFactory);
$entityProvider = new \EDT\Querying\Contracts\MappingEntityProvider(
$conditionConverter,
$sortMethodConverter,
$doctrineEntityProvider
);
Previously the type of conditions and sort methods accepted by a FluentQuery
instance depended on the OffsetEntityProviderInterface
it was initialized with.
Now it only accepts the predefined condition and sort method types and consequently its constructor only accepts an OffsetEntityProviderInterface
that has these types set as template parameters, e.g. MappingEntityProvider
.
To re-iterate: its constructor does no longer accept PrefilledEntityProvider
nor DoctrineOrmEntityProvider
.
Similar to FluentQuery
, the methods in RepositoryInterface
do no longer accept any type of conditions and sort methods, but the predefined ones only.
You will need to adjust your implementation to convert the predefined types to the ones needed by your data source.
To do so you may use MappingEntityProvider
directly or follow the approach used in its implementation, depending on your use case.
Property config types were affected by the removal of the condition and sort method template parameters as well.
When manually implementing config classes, you don't need to specify these template parameters anymore.
The following example assumes you used a data source taking FunctionInterface<bool>
and OrderBySortMethodInterface
as condition/sort method implementations.
I.e. instead of
use \EDT\DqlQuerying\Contracts\OrderBySortMethodInterface;
/**
* @template-extends MagicResourceConfigBuilder<FunctionInterface<bool>,OrderBySortMethodInterface,EntityAInterface>
*
* @property-read AttributeConfigBuilderInterface<FunctionInterface<bool>,EntityAInterface> $propertyA
* @property-read ToOneRelationshipConfigBuilderInterface<FunctionInterface<bool>,OrderBySortMethodInterface,EntityAInterface,EntityBInterface> $propertyB
* @property-read ToManyRelationshipConfigBuilderInterface<FunctionInterface<bool>,OrderBySortMethodInterface,EntityAInterface,EntityBInterface> $propertyC
*/
class EntityAConfig extends MagicResourceConfigBuilder
simply remove the condition/sort method template parameters and use
/**
* @template-extends MagicResourceConfigBuilder<EntityAInterface>
*
* @property-read AttributeConfigBuilderInterface<EntityAInterface> $propertyA
* @property-read ToOneRelationshipConfigBuilderInterface<EntityAInterface,EntityBInterface> $propertyB
* @property-read ToManyRelationshipConfigBuilderInterface<EntityAInterface,EntityBInterface> $propertyC
*/
class EntityAConfig extends MagicResourceConfigBuilder
Also, when generating these classes the template parameters do no longer need to be set.
Instead of
$conditionClass = ClassOrInterfaceType::fromFqcn(
FunctionInterface::class,
[NonClassOrInterfaceType::fromRawString('bool')]
);
$sortingClass = ClassOrInterfaceType::fromFqcn(OrderBySortMethodInterface::class);
$interfaceClass = ClassOrInterfaceType::fromFqcn(EntityAInterface::class);
$parentClass = ClassOrInterfaceType::fromFqcn(
MagicResourceConfigBuilder::class,
[$conditionClass, $sortingClass, $interfaceClass]
);
new \EDT\DqlQuerying\ClassGeneration\ResourceConfigBuilderFromEntityGenerator(
$conditionClass,
$sortingClass,
$parentClass,
$traitEvaluator
);
simply use
$interfaceClass = ClassOrInterfaceType::fromFqcn(EntityAInterface::class);
$parentClass = ClassOrInterfaceType::fromFqcn(
MagicResourceConfigBuilder::class,
[$interfaceClass]
);
new \EDT\DqlQuerying\ClassGeneration\ResourceConfigBuilderFromEntityGenerator(
$parentClass,
$traitEvaluator
);
Caution
Like all other releases, this one was done to the best of one's ability. However, it has not been tested as thoroughly as previous releases. It is thus to be considered even more experimental than the version number already implies.
This affects all implementations of ConditionFactoryInterface
as well as ConditionDefinition
.
Creating those conditions by not passing any values to check against is not only unnecessary, as the result is known even before execution, but it may also cause mistakes due to confusions. Thus, an empty list of values in no longer allowed. To migrate, simply check your list before calling these methods, like shown in the following example.
$condition = [] === $values
? $conditionFactory->false()
: $conditionFactory->propertyHasAnyOfValues($values, ['foo']);
Instead of providing a RequestStack
instance to the PaginatorFactory
constructor, now the createPaginatorAdapter
method takes the Request
instance as second parameter.
I.e. instead of
$paginatorFactory = new \EDT\JsonApi\RequestHandling\PaginatorFactory(
$requestStack,
$router
);
$paginatorFactory->createPaginatorAdapter($paginator);
use
$paginatorFactory = new \EDT\JsonApi\RequestHandling\PaginatorFactory(
$router
);
$request = $requestStack->getCurrentRequest();
Assert::notNull($request);
$paginatorFactory->createPaginatorAdapter($paginator, $request);
BC BREAK: refactor RequestTransformer
to abstract RequestWithBody
, functioning as parent of request classes
Beside the RequestTransformer
class itself, this affects ListRequest
, CreationRequest
and UpdateRequest
.
Instead of
$requestTransformer = new \EDT\JsonApi\RequestHandling\RequestTransformer(
$requestStack,
$typeProvider,
$validator,
$requestConstraintFactory
);
$listRequest = new \EDT\JsonApi\Requests\ListRequest(
$filterParser,
$sortingParser,
$paginationFactory,
$paginationParser,
$requestTransformer,
$schemaPathProcessor,
$eventDispatcher,
$sortValidator
);
$creationRequest = new \EDT\JsonApi\Requests\CreationRequest(
$requestTransformer,
$eventDispatcher
)
$updateRequest = new \EDT\JsonApi\Requests\UpdateRequest(
$requestTransformer,
$eventDispatcher
);
use
$request = $requestStack->getCurrentRequest();
\Webmozart\Assert\Assert::notNull($request);
$maxBodyNestingDepth = 512;
$listRequest = new \EDT\JsonApi\Requests\ListRequest(
$filterParser,
$sortingParser,
$paginationFactory,
$paginationParser,
$request,
$schemaPathProcessor,
$eventDispatcher,
$sortValidator
);
$creationRequest = new \EDT\JsonApi\Requests\CreationRequest(
$eventDispatcher,
$request,
$validator,
$requestConstraintFactory,
$maxBodyNestingDepth
)
$updateRequest = new \EDT\JsonApi\Requests\UpdateRequest(
$eventDispatcher,
$request,
$validator,
$requestConstraintFactory,
$maxBodyNestingDepth
);
BC BREAK: The GetRequest
and DeletionRequest
constructors no longer take a RequestTransformer
instance
Previously the RequestTransformer
instance was not actually used anyway in the implementation of GetRequest
and DeletionRequest
.
Hence, the constructors was adjusted and do no longer accept such instance.
If you call the constructors manually or define the parameter manually in your dependency injection configuration (e.g. in Symfony), you need to adjust these places. If you extend from GetRequest
or DeletionRequest
, you also need to adjust the child class constructors that call the constructor as their parent.
Multiple problems existed in the OpenAPISchemaGenerator
class.
- erroneous: recursion loop for bidirectional relationships
- hardcoded: coupling to specific translation keys
- limited: no distinction between different kind of exposures (get/list/create/update/delete)
To mitigate these problems, the class and its usage was reworked.
The class itself was renamed to OpenApiDocumentBuilder
, but it should now be used via the newly introduced Manager
class anyway.
Previously you would initialize OpenAPISchemaGenerator
yourself and retrieve an OpenApi
instance (containing the OpenApi document data) via getOpenAPISpecification
.
Now you create a Manager
instance and fill it with the types that should be directly accessible (i.e. not only reachable via includes).
Afterward its Manager::createOpenApiDocumentBuilder
can be used to retrieve an OpenApiDocumentBuilder
.
In that OpenApiDocumentBuilder
instance, you can set configurations for actions you are interested in (e.g. get
and list
).
After the configuration of the builder, you can call OpenApiDocumentBuilder::buildDocument
to retrieve the OpenApi
instance.
This seemingly increased complexity is mainly the result of keeping the generation more generic, without assuming specific translation keys to exist.
Besides that, it also allows to re-use the same instance to generate the documentation in different languages and introduces the Manager
class as future major entry point into the library.
The following shows the adjustment needed to migrate from the old approach to the new one.
Note that no distinction was possible between types available via get
and list
.
I.e. it was not possible to expose a type for JSON:API get
actions only, like it was not possible to expose it for JSON:API list
actions only as well.
If either exposure was wanted, the documentation would state the type as exposed with the other action too.
Also, the OpenApiSchemaGenerator
implementation would simply assume specific translation keys to be available via the given translator.
$types = [
/* your resource type instances, that shall be directly accessible via JSON:API `get` or `list` */
];
$schemaGenerator = new \EDT\JsonApi\ApiDocumentation\OpenApiSchemaGenerator(
$types,
$router, // \Symfony\Component\Routing\RouterInterface
new \EDT\JsonApi\ApiDocumentation\SchemaStore(),
$translator, // \Symfony\Contracts\Translation\TranslatorInterface
$defaultPageSize
);
$openApiDocument = $schemaGenerator->getOpenAPISpecification();
To keep the migration easy, the old behavior can be configured using prepared configuration classes as shown below. Those are however set as deprecated from the start, as applications should provide their own implementation instead.
$getableTypes = [
/* your resource type instances that shall be accessible via JSON:API `get` actions */
];
$listableTypes = [
/* your resource type instances that shall be accessible via JSON:API `list` actions */
];
$manager = new \EDT\JsonApi\Manager();
$manager->setPaginationDefaultPageSize($defaultPageSize);
$manager->registerGetableTypes($getableTypes);
$manager->registerListableTypes($listableTypes);
$schemaGenerator = $manager->createOpenApiDocumentBuilder();
$schemaGenerator->setGetActionConfig(
new \EDT\JsonApi\ApiDocumentation\GetActionConfig($router, $translator)
);
$schemaGenerator->setListActionConfig(
new \EDT\JsonApi\ApiDocumentation\ListActionConfig($router, $translator)
);
$schemaGenerator->buildDocument(new \EDT\JsonApi\ApiDocumentation\OpenApiWording($translator));
BC BREAK: Allow to pass null
paths into DrupalConditionFactoryInterface::createConditionWithoutValue
and createConditionWithValue
If you did not override these two methods, you don't need to do anything.
If you did override either of them, you need to adjust the signatures of your overriding method to accept not only array
as path but also null
.
Accordingly, your logic needs to handle a null
value as path.
However, it is valid to just throw an exception if your implementation does not support it.
BC BREAK: Adjust constructor of DynamicTransformer
to take some specific values instead of a whole TransferableTypeInterface
instance
Previously the DynamicTransformer
constructor required an instance of TransferableTypeInterface
from which it retrieved data internally.
Now the constructor does no longer accept a TransferableTypeInterface
instance, but requires the values it retrieved internally instead.
I.e., instead of calling
$transformer = new \EDT\JsonApi\OutputHandling\DynamicTransformer(
$type
);
you can now call it like this:
$transformer = new \EDT\JsonApi\OutputHandling\DynamicTransformer(
$type->getTypeName(),
$type->getEntityClass(),
$type->getReadability(),
);
BC BREAK: Require the implementation of five other methods instead of getResourceConfig
in children of AbstractResourceType
Previously, the AbstractResourceType
implementation did define the abstract method getResourceConfig(): ResourceConfigInterface
to implement multiple methods defined by its parent interfaces by itself.
Classes extending from AbstractResourceType
were required to implement the getResourceConfig
method.
Instead, the AbstractResourceType
now leaves the implementation of the parent interface methods to extending classes and thus does no longer define getResourceConfig
at all.
To keep the previous behavior, the following code can be added to the classes directly extending from AbstractResourceType
:
public function getReadability(): ResourceReadability
{
return $this->getResourceConfig()->getReadability();
}
public function getFilteringProperties(): array
{
return $this->getResourceConfig()->getFilteringProperties();
}
public function getSortingProperties(): array
{
return $this->getResourceConfig()->getSortingProperties();
}
public function getUpdatability(): ResourceUpdatability
{
return $this->getResourceConfig()->getUpdatability();
}
protected function getInstantiability(): ResourceInstantiability
{
return $this->getResourceConfig()->getInstantiability();
}
BC BREAK: Replace RequiredRelationshipConstructorBehavior
constructor boolean parameter with Cardinality
enum
Instead of taking a simple bool $toOne
parameter in the constructor, you now need to use the Cardinality
enum. I.e. calls with true
needs to be replaced with Cardinality::TO_ONE
and calls with false
needs to be replaced with Cardinality::TO_MANY
.
BC BREAK: Remove *ConstructorBehaviorFactory
classes, you can use added static createFactory
methods in the corresponding *Behavior
classes instead
When configuring resource properties you can connect them to behaviors, which is done by passing a factory instance into the corresponding property config method, which is internally used to create the behavior to be connected. There are many different kind of properties with different behavior implementations provided by the library, which required many corresponding factory classes. To make the behavior configuration more approachable, all factory class implementations were moved as anonymous classes into their corresponding behavior, hidden from the configuring developer. E.g. instead of using
$config->title->addConstructorBehavior(
new AttributeConstructorBehaviorFactory(null, null));
you would now use
$config->title->addConstructorBehavior(
AttributeConstructorBehavior::createFactory(null, OptionalField::NO, null));
The additional OptionalField::NO
parameter is independent of this change and explained further down below.
To migrate to the new approach, search in your application for all usages of classes that end with ConstructorBehaviorFactory
.
For each one, a corresponding class ending with ConstructorBehavior
exists, providing a createFactory
method, as shown above.
BC BREAK: Adjust *SetBehavior
and *SetBehaviorFactory
class constructors to take the OptionalField
enum instead of bool
Previously, some classes ending with the name SetBehavior
or SetBehaviorFactory
took a boolean to imply if the properties they were configured for are required in the request data or not.
Now creating instances of these classes requires an OptionalField
enum instead.
I.e. a call with true
is to be replaced with OptionalField::YES
.
A call with false
is to be replaced with OptionalField::NO
.
Previously, some classes ending with the name Readability
took a boolean to imply if the corresponding property should be present in the response if no fieldset was specified in the request.
Now creating instances of these classes requires a DefaultField
enum instead.
I.e. a call with true
is to be replaced with DefaultField::YES
.
A call with false
is to be replaced with DefaultField::NO
.
Its only method readable
is still present in all of its former child classes and interfaces.
The root interface in which the signature is defined is now AttributeOrRelationshipBuilderInterface
.
If you used ReadablePropertyConfigBuilderInterface
directly in your application you need to adjust your inheritance hierarchy.
If you did not use ReadablePropertyConfigBuilderInterface
directly, you don't need to do anything.
If you used FilteringTypeInterface
directly in your application you can now remove the previously necessary template parameters. If you did not use the interface directly or did not specify its template parameters anyway, you don't need to do anything.
Using the wrong template parameters will not affect your application at runtime. However, on certain phpstan levels, the static code checker may raise concerns.
- feature: add classes to programmatically create Drupal filters
- feature: provide assertion methods to subclasses of
PredefinedDrupalConditionFactory
- refactor: (bc break) improve return of
PredefinedDrupalConditionFactory::getSupportedOperators
, instead of just the names of the supported operators, it is able to provide additionalConstraint
s too; however, this remains currently unused and unsupported; applyarray_keys
to the return value as quick migration - refactor: (bc break) split
PredefinedDrupalConditionFactory::getOperatorFunctions
intogetOperatorFunctionsWithValue
andgetOperatorFunctionsWithoutValue
; overriding implementations must be adjusted accordingly - refactor: (bc break) start to separate validation from filter transformation;
DrupalFilterParser::parseFilter
will no longer automatically validate the given filter, add a call toDrupalFilterParser::validateFilter
as quick migration - refactor: introduce operator constants in
StandardOperator
and prefer them over string usage - refactor: introduce and use
ExpectedPropertyCollectionInterface
- feature: add early break, to potentially improve performance depending on
RepositoryInterface
implementation
- feature: allow setting identifiers, attributes or relationships as readable, filterable or sortable by default
- feature: relax
composer.json
dependencies to allow Symfony 6 (experimental) - fix: use correct comparison for 0.24.37 fix
- fix: provide client with backend-created IDs
- refactor: allow to change max request body JSON nesting depth via injection
- rollback: lock pagination in list requests to page-based again, as others are not yet supported
- feature: make attribute validation more flexible
- to allow arrays as attributes, the
RequestConstraintFactory
must now be configured to define how they should be handled
- to allow arrays as attributes, the
- fix: increase JSON request body max depth to allow for non-primitive attributes (i.e. array structures)
- fix: handle a to-one relationship with no items correctly on update and creation requests
- fix: handle required properties correctly in more validation places
- refactor: allow
array
as attribute type, but for string lists only for now - refactor: improve some validation messages
- feature: use Symfony validators to validate
sort
query parameterListRequest
now requiresSortValidator
as constructor parameter
- fix: handle required properties correctly in validation
- feature: provide request body in creation and update events
- feature: rework "instance of" implementation to be properly usable (though support for execution in
ConditionEvaluator
is still missing)
- feature: add
IsTargetEntityNotInstanceOf
clause
- fix: solve logical problem in
IsInstanceOfTargetEntity
and rename it toIsTargetEntityInstanceOf
- feature: provide target entity alias to clauses
- feature: add
IsInstanceOfTargetEntity
condition
- fix: do not add unnecessary
use
statements when generating classes
- feature: allow to used different entity references in property types when generating classes
- feature: allow to disable comment generation for properties when generating classes
- fix: solve various simple bugs
- fix: enable filtering for resource identifier set as filterable
- fix: create property builder correctly
- fix: do not automatically create config builders for all properties detected by
MagicResourceConfigBuilder
, as otherwise unused relationships will have no relationship type set when needed
- fix: check array size correctly
- refactor: provide
getFullyQualifiedName
andgetTemplateParameter
methods in allTypeInterface
implementations, adjust usages accordingly - fix: add support for non-classes/non-interfaces to
DocblockPropertyByTraitEvaluator
- feature: improve docblock parsing support, Types in tags like
propert-read
now support more cases of template parameter usages - refactor: move
TypeInterface
and its implementations in different namespace - refactor: reduce phpstan concerns
- fix: consider type correctly in assertion
- fix: add missing method call
- fix: implement necessary interface
- fix: add missing type check
- fix: use correct return type
- feature: add
Iso8601PropertyAccessor
to automatically convert Doctrinedatetime
to ISO8601 strings
- feature: move logic into separate functions to improve support for adjustments in child classes
- feature: when setting to-many relationship properties with
ProxyPropertyAccessor
, automatically convertarray
s to DoctrineCollection
s - refactor:
PropertySetBehaviorInterface::executeBehavior
must now return the list of properties that were not adjusted according to the request instead of the previously returnedbool
that implied if any properties were not adjusted according to the request- due to these changes the following classes will now expect callables in their constructors returning such list:
FixedSetBehavior
CallbackAttributeSetBehaviorFactory
CallbackAttributeSetBehavior
CallbackToOneRelationshipSetBehaviorFactory
CallbackToOneRelationshipSetBehavior
CallbackToManyRelationshipSetBehaviorFactory
CallbackToManyRelationshipSetBehavior
- due to these changes the
initializable
andupdatable
methods of the following classes will now expect (still optional) callables which returns such listsAttributeConfigBuilderInterface
ToOneRelationshipConfigBuilderInterface
ToManyRelationshipConfigBuilderInterface
- due to these changes the following classes will now expect callables in their constructors returning such list:
- refactor: for similar reasons as stated above, the
callable
parameters in following classes must now include a list of properties that were not set according to the request in their returnAttributeConstructorBehaviorFactory
AttributeConstructorBehavior
FixedConstructorBehaviorFactory
RequiredRelationshipConstructorBehavior
RequiredToOneRelationshipConstructorBehaviorFactory
ToManyRelationshipConstructorBehavior
ToManyRelationshipConstructorBehaviorFactory
ToOneRelationshipConstructorBehavior
ToOneRelationshipConstructorBehaviorFactory
- feature: add support for general update behaviors
- feature: add support for general creation behaviors
- feature: allow creation-configuration of ID config builders
- feature: add support for redundant attributes/annotations
- refactor: use interfaces instead of callables
- feature: improve exception message
- fix: generate correct docblock
param
tag
- feature: restore previously removed exception methods for backward compatibility
- refactor: refactor: restructure classes and API for resource property configuration
- refactor: apply object-oriented approach for entity modification
- consolidate up class/interface naming
- provide entity changing instances with more general data, thus allowing more abstraction and moving specific behavior into objects
- split constructor argument instances from setability instances for entity initialization
- add convenience methods to
WrapperObject
- adjust
PropertyBuilder
(nowPropertyConfig
) API
- fix: allow
id
andtype
fields in update/creation request, as originally intended - refactor: setting relationship properties as default include when
readable
must now be done onreadable
call and takes the place of the previously removedbool
to disable sanity checks - feature: add new approach to configure resource properties
- feature: preserve context information in exception
- fix: use correct template parameter
- fix: consider access conditions when deleting resources
- refactor: adjust exceptions, interfaces and events
Simplify exception handling in requests: Wrapping every exception to provide information the caller already has is unnecessarily complicated.
Add events (again): Events to execute code before or after request handling were added. These will probably be the target of heavy refactoring in the foreseeable future.
Restructure/rename interfaces/namespaces: Using type interfaces directly corresponding to requests should make the hierarchy more understandable. The namespaces were adjusted for clarity.
- feature: allow read usage of
DocblockTagParser
properties
- refactor: allow child classes of
ClauseFunction
andOrderBy
interface inDoctrineOrmEntityProvider
template parameters
- refactor: use reflection class as parameter instead of class name
- feature: allow classes extending from
ReflectionSegmentFactory
to reuse parts of its logic
- refactor: revoke previously added event support in favor of objects
- feature: integrate event dispatching for resource creation and update into
AbstractResourceType
; implementation ofgetEventDispatcher
is now required
- refactor: require
AbstractResourceType::getIdentifierPropertyPath
to be implemented - refactor: restructure the "Type" interface hierarchy
- refactor: rename
AbstractResourceType::getAccessCondition
togetAccessConditions
and return list of conditions instead of a single (already merged) one - feature: apply more centralized and stricter request validation
- refactor: remodel logic to be more flexible for different use cases
- refactor: conditions/sort methods returned by
getAccessCondition
/getDefaultSortMethods
must now access the schema of the backing entity instead of the schema of the type (i.e. the automatic de-aliasing was removed) - feature: a type property defined in
getFilterableProperties
andgetSortableProperties
can now use different property in the backing entity for filtering and sorting - refactor: use type readabilities/updatabilities/initializabilities instances instead of using a predefined algorithm
- refactor: separate
id
property from attributes; attributes MUST NOT return anid
property but implementIdentifiableTypeInterface::getIdentifierReadability
instead - fix: use correct property list when reading to-one relationships in
WrapperObject
- refactor: require enum to define relevant tags on docblock parsing
- fix: allow
DocblockTagParser
to find interfaces - fix: allow creation of
IS NULL
andIS NOT NULL
Drupal conditions
- fix: update monorepo dependencies to solve monorepo-split bug
- fix: update
thecodingmachine/safe
dependency for PHP 8 support - refactor: update dev-dependencies
- fix: update
composer.json
files
- remove
getTransformer
fromResourceTypeInterface
, transformation is now always done viaDynamicTransformer
, regardless ofTypeInterface
implementation, thus- remove
getReadableResourceTypeProperties
,getWrapperFactory
,getLogger
andgetMessageFormatter
fromResourceTypeInterface
/AbstractResourceType
- the
WrapperObjectFactory
,LoggerInterface
andMessageFormatter
implementations returned by the methods above now need to be injected into theAbstractApiService
- remove
- remove static
ResourceTypeInterface::getName()
method, the non-staticTransferableTypeInterface::getIdentifier()
method needs to be implemented instead - feature: rework resource property configuration
CreatableTypeInterface
:- remove
getPropertiesRequiredForCreation
, its information is now available viagetInitializableProperties
- change return type of
getInitializableProperties
- remove
- change signature and return type of
TransferableTypeInterface::getUpdatableProperties
; when extending fromAbstractResourceType
, updatability should be configured viaconfigureProperties()
now instead of overridinggetUpdatableProperties
UpdatableRelationship
:- split class into
AttributeUpdatability
/ToOneRelationshipUpdatability
/ToManyRelationshipUpdatability
depending on the context - change constructor parameters
- rename
getValueConditions()
method - allow to use custom write functions
- split class into
- disallow
null
as$value
inCachingPropertyReader::determineToOneRelationshipValue
- partially validate values read from entities in
WrapperArrayFactory
andWrapperObject
against configuration retrieved from type instances, if they don't match expectations an exception is thrown - respect
Updatability
entity conditions when updating values inWrapperObject
- respect
Updatability
value conditions when updating relationship values (conditions for attribute values are not yet supported and will be ignored) DynamicTransformer
- couple to
ResourceTypeInterface
, to reduce complexity - change constructor parameters
- validate
transform
$entity
parameter - partially validate attribute and relationship values read from
$entity
- validate
include…
parameters
- couple to
AbstractResourceType
- change
getUpdatableProperties
default implementation from returning an empty array to respecting the settings one viaconfigureProperties
- change return type and signature of
processProperties
- remove
getTypeProvider
- remove
getPropertyCollection
, implementconfigureProperties
instead
- change
- reduce caching done in
CachingResourceType
- ease initialization of valid path segments with factories by accepting
PropertyPathInterface
instances, a single non-empty-string or a list of non-empty-strings
- feature: allow the usage of
PropertyPathInterface
to create sort methods
- refactor: adjust
PathTransformer::prefixPathsList
signature - refactor: use more specific type-hints where possible
- refactor: switch the parameter order of the callables returned by
DrupalConditionFactoryInterface
- refactor: change the path parameter type to create conditions from
non-empty-string $property, non-empty-string $properties
tonon-empty-list<non-empty-string>|PropertyPathInterface $properties
- refactor: change the path parameter type to create sort methods from
non-empty-string $property, non-empty-string $properties
tonon-empty-list<non-empty-string> $properties
- feature: add minimal validation for the type of the value passed via the
value
field in a Drupal filter condition
- feature: add JSON:API sort format validation class
- fix: use correct property path delimiter
- fix: disallow
null
conjunction inDrupalFilterValidator
- refactor:
getAsNames
/getAsNamesInDotNotation
inPropertyAutoPathTrait
andPropertyAutoPathInterface
must not return an empty list/empty-string respectively; initializing instances with an empty property path is allowed, but ensure they are not used for anything else but further path-building - refactor: require implementations using
PropertyAutoPathTrait
to also implementPropertyAutoPathInterface
- refactor: assert
ExposableRelationshipTypeInterface
implementation and itsisExposedAsRelationship
fortrue
inAbstractResourceType
forgetReadableProperties
,getSortableProperties
andgetFilterableProperties
but almost nowhere else - refactor: require
TransferableTypeInterface
to createWrapperObject
instances - refactor: change methods and signatures in
AbstractProcessorConfig
and removePropertyPathProcessor::getPropertyTypeIdentifier
- refactor: require
TransferableTypeInterface::getReadableProperties
to returnTransferableTypeInterface
instances - refactor: require
TypeInterface::getInternalProperties
to returnTypeInterface
instances - refactor: require
SortableTypeInterface::getSortableProperties
to returnSortableTypeInterface
instances - refactor: require
FilterableTypeInterface::getFilterableProperties
to returnFilterableTypeInterface
instances - refactor: require
TransferableTypeInterface::getUpdatableProperties
to returnUpdatableRelationship
instances - refactor: merge
ReadableTypeInterface
andUpdatableTypeInterface
intoTransferableTypeInterface
- refactor: remove obsolete
TypeAccessor
- refactor: avoid type identifier in
PropertyPathProcessor
- feature: attempt parallel PHP 8 support
- feature: add
PropertyBuilder::getName()
method - refactor: deprecate
ExposableRelationshipTypeInterface::isExposedAsRelationship
, evaluate the conditions when returning relationships in methods likeReadableTypeInterface::getReadableProperties()
instead
- refactor: adjust
CreatableTypeInterface
template parameters - refactor: remove
TypeRetrievalAccessException
static constructors:unknownTypeIdentifier
,noNameWithImplementation
,typeExistsButNotAvailable
andtypeExistsButNotReferencable
- refactor: require
ResourceTypeInterface
to implementExposableRelationshipTypeInterface
andExposablePrimaryResourceTypeInterface
, each should not only correspond toisReferencable
/isDirectlyAccessible
respectively but include the logic inisAvailable
too - fix: use stricter path processing; every relationship in paths used by external callers must now return
true
inExposableRelationshipTypeInterface::isExposedAsRelationship
; this for example affects type wrappers (WrapperObjectFactory
/WrapperArrayFactory
) and JSON:API filtering, reading and sorting - refactor: remove
TypeInterface::isAvailable
, useExposableRelationshipTypeInterface::isExposedAsRelationship
orExposablePrimaryResourceTypeInterface::isExposedAsPrimaryResource
instead,AbstractApiService
was adjusted to requireisExposedAsPrimaryResource
to returntrue
for the primary accessed resource types - refactor: remove
TypeInterface::isAvailable
requirement from type wrapper factories (WrapperObjectFactory
/WrapperArrayFactory
), calls must decide by themselves on the restriction of the root type (e.g.ExposablePrimaryResourceTypeInterface::isExposedAsPrimaryResource
), relationships will automatically be checked fortrue
return inExposableRelationshipTypeInterface::isExposedAsRelationship
- refactor: remove
TypeInterface::isDirectlyAccessible
,ExposablePrimaryResourceTypeInterface::isExposedAsPrimaryResource
can be used instead - refactor: remove
TypeInterface::isReferencable
,ExposableRelationshipTypeInterface::isExposedAsRelationship
can be used instead - refactor: on external accesses to filterable, readable and sortable properties, require the corresponding type to be an exposed relationship
- refactor: rename
AbstractTypeAccessor
toAbstractProcessorConfig
- refactor: rename
AbstractTypeAccessor::getType
togetRelationshipType
- refactor: require
array
instead of varargs forPropertyPath
initialization
- refactor: use PHP 7.4 property types where possible
- chore: require at least PHP 7.4 as dependency
- refactor: remove deprecated
SchemaPathProcessor::mapSortMethods
, useSchemaPathProcessor::processDefaultSortMethods()
andSchemaPathProcessor::mapSorting()
instead - refactor: remove deprecated
SchemaPathProcessor::mapConditions
, useSchemaPathProcessor::mapFilterConditions()
andSchemaPathProcessor::processAccessCondition()
instead - refactor: remove deprecated
TypeRestrictedEntityProvider
, use the individual components manually and optimize them for your use-case - refactor: remove deprecated
GenericEntityFetcher
, use the individual components manually and optimize them for your use-case
- refactor: require
QueryBuilderPreparer
forDoctrineOrmEntityProvider
initialization - refactor: remove
QueryGenerator
,QueryBuilderPreparer
orDoctrineOrmEntityProvider
can be used instead - refactor: require
JoinFinder
forQueryBuilderPreparer
initialization - refactor: require
ConditionEvaluator
andSorter
forPropertyReader
initialization - refactor: require
TableJoiner
forSorter
initialization - refactor: require
ConditionEvaluator
andSorter
forPrefilledObjectProvider
initialization - refactor: require
TableJoiner
forConditionEvaluator
initialization
- refactor: drop
TCondition
type requirement from root condition factories and introduce separate interfaces instead - refactor: separate grouping methods into their own interface
- feature: allow more fine-grained adjustments in
DrupalFilterValidator
subclasses - remove unneeded factory injection in
DrupalConditionParser
- refactor: rename
OperatorProviderInterface
andPredefinedOperatorProvider
for clarity - feature: validate Drupal filter for sane
path
andmemberOf
values
- refactor: revert code back to version 0.12.16
- feature: rename
getAccessCondition
and let it return a list
- refactor: rename
OffsetBasedEntityProviderInterface
toOffsetPaginatingEntityProviderInterface
- feature: add pagination parsers
- refactor: rename pagination classes for simplicity
- refactor: remove
illuminate/collections
dependency due to missing template type support - refactor: rename template parameters to new naming pattern
- refactor: rework resource type property handling
- feature: add nullable support to
TypeRequirement
- feature: improve
DynamicTransformer
exception message - refactor: rename
SliceException
to more genericPaginationException
- refactor: improve API
- refactor: improve implementation
- refactor: adjust
split
parameters and behavior - refactor: remove problematic static constructor method
- refactor: improve naming/type-hinting and remove assertions
- feature: separate logic into new method to allow overriding
- feature: allow injection of
PropertyPathProcessor
implementation
- feature: allow validation of external read-paths
- feature: validate paths on alias processing
- feature: add basic
fields
validator - refactor: require URL parameters in API-request handling
- feature: inject message formatting logic to allow adjustments
- feature: validate Drupal filter names
- feature: add
CachingPropertyReader
- refactor: improve types and type-hint usage
- fix: avoid parameter count error in
null
check
- fix: postpone request retrieval until needed
- refactor: decouple
jsonapi
package fromextra
package - refactor: prefer to handle Drupal root conditions as array
- feature: add
WrapperObject::getPropertyValue
- fix: ignore unavailable source code when parsing property-read tags
- fix: use matching parameter naming
- fix: increase pagerfanta requirement to ^2.7
- feature: use less strict dependency requirements
- feature: add interface method to check for type creatability
- build composer package from
jsonapi
implementation
- feature: add initial
jsonapi
package implementation - fix: restore accidentally removed PHP 7.4 support
- chore: remove temporary
phpstan-baseline.neon
, its content is covered inphpstan.neon
- refactor: improve code and documentation based on tool concerns and add remaining phpstan (level 8) concerns as baseline to be worked on
- refactor: use safe functions from
thecodingmachine/safe
instead of PHP build-ins - refactor: always return int-indexed list by
ObjectProvider
- refactor: remove
AllTrue
PHP class, it can be constructed usingAllEqual
instead - refactor: replace
ExtendedReflectionClass
withnikic/php-parser
and thus fix edt-path tests - refactor: replace trait usage in clauses with inheritance and composition
- refactor: replace trait usage in functions and clauses with inheritance and composition
- chore: remove currently unused tooling configs
- feature: disallow to-many relationships usage for sort methods in DQL
- feature: add DQL support for custom selects
- feature: add basic right join support for DQL building
- chore: improve documentation
- fix: use correct table name after refactoring
- fix: set correct edt-queries version number
- fix: handle associations when detecting DQL joins correctly
- refactor: mark setter methods in
PropertyAutoPathTrait
as internal
- chore: Minor deployment changes related to splitting out the packages
- fix: avoid possibly unwanted
TypeRetrievalAccessException
when reading or updating a relationship
- prepare for public release
- Fix release tagging
- Fix interdep version constraints
- Fix the subtree splitting
- Configure default branch name
- combine edt packages into monorepo with automatic subtree splitting
- Minor changes
- First tagged release