{"id":22211509,"url":"https://github.com/whitedigital-eu/entity-resource-mapper","last_synced_at":"2025-07-27T11:32:22.728Z","repository":{"id":37205109,"uuid":"487742329","full_name":"whitedigital-eu/entity-resource-mapper","owner":"whitedigital-eu","description":"Entity Resource Mapper for API Platform","archived":false,"fork":false,"pushed_at":"2024-04-12T12:48:34.000Z","size":398,"stargazers_count":1,"open_issues_count":8,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-12T16:59:54.574Z","etag":null,"topics":["api-platform","php","symfony-bundle"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/whitedigital-eu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-05-02T06:39:29.000Z","updated_at":"2024-04-15T08:54:16.787Z","dependencies_parsed_at":"2024-04-15T09:04:50.896Z","dependency_job_id":null,"html_url":"https://github.com/whitedigital-eu/entity-resource-mapper","commit_stats":{"total_commits":164,"total_committers":6,"mean_commits":"27.333333333333332","dds":"0.45731707317073167","last_synced_commit":"dcfb275da751f76cffa690b3d67d2bccaf64bd05"},"previous_names":[],"tags_count":92,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whitedigital-eu%2Fentity-resource-mapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whitedigital-eu%2Fentity-resource-mapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whitedigital-eu%2Fentity-resource-mapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/whitedigital-eu%2Fentity-resource-mapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/whitedigital-eu","download_url":"https://codeload.github.com/whitedigital-eu/entity-resource-mapper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227800740,"owners_count":17821872,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-platform","php","symfony-bundle"],"created_at":"2024-12-02T20:33:56.631Z","updated_at":"2024-12-02T20:33:57.253Z","avatar_url":"https://github.com/whitedigital-eu.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Entity Resource Mapper Bundle\n\n1) Extends Symfony / Api Platform functionality by helping to map Doctrine entity objects with Api Platform resource objects and offers other helpers such as filters, JSON Functions, etc.\n\n2) Implements AuthorizationService which centralizes all authorization configuration and provides methods for authorizing resources in:\n- Data provider - collection get\n- Data provider - item get\n- Data persister - item post/put/patch\n- Data persister - item delete\n- Individual resource in EntityToResourceMapper\n\n## Requirements\n\nPHP 8.1+  \nSymfony 6.3+\n\n## Installation\nThe recommended way to install is via Composer:\n```bash\ncomposer require whitedigital-eu/entity-resource-mapper-bundle\n```\n\n## Configuration\n\n### ClassMapper service ###\nYou should create ClassMapper service configuration file, for example:\n\n```php\nnamespace App\\Service;\n\nuse App\\Dto\\CustumerDto;\nuse App\\Entity\\Customer;\n\nuse WhiteDigital\\EntityResourceMapper\\Mapper\\ClassMapperConfiguratorInterface;\nuse WhiteDigital\\EntityResourceMapper\\Mapper\\ClassMapper;\n\nclass ClassMapperConfigurator implements ClassMapperConfiguratorInterface\n{\n    public function __invoke(ClassMapper $classMapper)\n    {\n        $classMapper-\u003eregisterMapping(CustomerResource::class, Customer::class);\n        // with Callback - must return true for mapping to be active\n        $classMapper-\u003eregisterMapping(PublicHtmlResource::class, Html::class, callback: static fn (array $context) =\u003e !self::isAdmin($context));\n        $classMapper-\u003eregisterMapping(AdminHtmlResource::class, Html::class, callback: static fn (array $context) =\u003e self::isAdmin($context));\n    }\n    \n    /**\n     * IsAdmin or else IsPublic.\n     */\n    private static function isAdmin(array $context): bool\n    {\n        return array_key_exists('request_uri', $context) \u0026\u0026 str_starts_with($context['request_uri'], '/api/admin');\n    }\n}\n\n```\nand register it as configurator for ClassMapper service in your services.yaml file:\n```yaml\n    WhiteDigital\\EntityResourceMapper\\Mapper\\ClassMapperConfiguratorInterface:\n      class: App\\Service\\ClassMapperConfigurator\n```\nAdditionally, you can use Mapping attribute to register mapping:\n```php\nuse App\\Dto\\CustumerDto;\nuse Doctrine\\ORM\\Mapping as ORM;\nuse WhiteDigital\\EntityResourceMapper\\Attribute\\Mapping;\n\n#[ORM\\Entity]\n#[Mapping(CustumerDto::class)]\nclass Customer ...\n```\n```php\nuse WhiteDigital\\EntityResourceMapper\\Attribute\\Mapping;\nuse App\\Entity\\Customer;\n\n#[Mapping(Customer::class)]\nclass CustumerDto ...\n```\n\n### Filters ###\nFollowing filters are currently available (filters works as described in Api Platform docs, except for comments below): \n- ResourceBooleanFilter\n- ResourceDateFilter _(throws exception, if value is not a valid DateTime object)_\n- ResourceEnumFilter _(same as SearchFilter but with explicit documentation)_\n- ResourceExistsFilter\n- ResourceJsonFilter _(new filter)_\n- ResourceNumericFilter\n- ResourceOrderFilter _(allows ordering by json values)_\n- ResourceOrderCustomFilter _(Order filter which will order by custom SELECT fields, which are not included in root alias nor joins)_\n- ResourceRangeFilter\n- ResourceSearchFilter\n\n### JSON Functions ### \nFollowing PostgreSQL functions are available in Doctrine and used in ResourceJsonFilter and ResourceOrderFilter:\n- JSONB_PATH_EXISTS(%s, %s) - PostgreSQL function jsonb_path_exists(%s::jsonb, %s)\n- JSON_GET_TEXT(%s, %s) - PostgreSQL alias for %s-\u003e\u003e%s\n- JSON_ARRAY_LENGTH(%s) - PostgreSQL function json_array_length(%s)\n- JSON_CONTAINS(%s, %s) - PostgreSQL alias for %s::jsonb @\u003e '%s'\n\n### DBAL Types ###\nThis bundle comes with and autoconfigures following dbal types to use UTC time zone:\n- date\n- datetime\n- date_immutable\n- datetime_immutable\n\n### Security ### \nAvailable operation types:\n- `AuthorizationService::ALL` Includes all of the below\n- `AuthorizationService::COL_GET` Collection GET \n- `AuthorizationService::ITEM_GET` Item GET\n- `AuthorizationService::COL_POST` Collection POST\n- `AuthorizationService::ITEM_PATCH` Item PUT + PATCH\n- `AuthorizationService::ITEM_DELETE` Item DELETE\n\nAvailable grant types:\n- `GrantType::ALL` resource fully available  \n- `GrantType::LIMITED` resource is available with limitations\n- `GrantType::NONE` resource not available\n\nAuthorizationService Configurator must be implemented.\n\n```php\n// src/Service/Configurator/AuthorizationServiceConfigurator.php\n\nuse WhiteDigital\\EntityResourceMapper\\Resource\\BaseResource;  \nuse WhiteDigital\\EntityResourceMapper\\Security\\AuthorizationServiceConfiguratorInterface;  \n\nfinal class AuthorizationServiceConfigurator implements AuthorizationServiceConfiguratorInterface\n{\n    public function __invoke(AuthorizationService $service): void\n    {\n        $service-\u003esetAuthorizationOverride(static fn (BaseEntity|BaseResource|null $object = null) =\u003e 'cli' === strtolower(PHP_SAPI) \u0026\u0026 'test' !== $_ENV['APP_ENV']);\n\n        $service-\u003esetResources([\n            ActivityResource::class =\u003e [\n                AuthorizationService::ALL =\u003e ['ROLE_SUPER_ADMIN' =\u003e GrantType::ALL, 'ROLE_KAM' =\u003e GrantType::ALL],\n                AuthorizationService::COL_GET =\u003e [, 'ROLE_JUNIOR_KAM' =\u003e GrantType::OWN],\n                AuthorizationService::ITEM_GET =\u003e [, 'ROLE_JUNIOR_KAM' =\u003e GrantType::GROUP],\n                AuthorizationService::COL_POST =\u003e [],\n                AuthorizationService::ITEM_PATCH =\u003e [],\n                AuthorizationService::ITEM_DELETE =\u003e [],\n            ]]);\n    \n        //either mainResource or roles key must be set\n        $service-\u003esetMenuStructure(\n                [\n                    ['name' =\u003e 'ACTIVITIES',\n                        'mainResource' =\u003e ActivityResource::class,\n                    ],\n                    ['name' =\u003e 'REPORTS',\n                        'roles' =\u003e ['ROLE_SUPER_ADMIN', 'ROLE_KAM'],\n                    ],\n                 ]);\n    }\n}\n```\nregister it as service:\n```\nWhiteDigital\\EntityResourceMapper\\Security\\AuthorizationServiceConfiguratorInterface:\n    class: AuthorizationServiceConfigurator\n```\nIf `setAuthorizationOverride` closure is set, it will be called with current object (resource or entity) and if it returns true, authorization will be skipped.\n \nUse following methods:  \n- In DataProvider, getCollection:\n```php\n$this-\u003eauthorizationService-\u003elimitGetCollection($resourceClass, $queryBuilder); // This will affect queryBuilder object\n```\n- In DataProvider, getItem:\n```php\n$this-\u003eauthorizationService-\u003eauthorizeSingleObject($entity, AuthorizationService::ITEM_GET); // This will throw AccessDeniedException if not authorized\n```\n- In DataPersister, persist:\n```php\n$this-\u003eauthorizationService-\u003eauthorizeSingleObject($data, AuthorizationService::ITEM_PATCH); // This will throw AccessDeniedException if not authorized\n// or\n$this-\u003eauthorizationService-\u003eauthorizeSingleObject($data, AuthorizationService::COL_POST; // This will throw AccessDeniedException if not authorized\n```\n- In DataPersister, remove:\n```php\n$this-\u003eauthorizationService-\u003eauthorizeSingleObject($data, AuthorizationService::ITEM_DELETE); // This will throw AccessDeniedException if not authorized\n```\n- In any Resource, if you have defined its grant as LIMITED, you must add attribute to BaseResource class, to define access resolver configurations for each of the resource classes\n```php\n#[AuthorizeResource(accessResolvers: [\n    new AccessResolverConfiguration(className: OwnerPropertyAccessResolver::class, config: ['ownerPropertyPath' =\u003e 'supervisor']),\n])]\n```\n\nSame class must also set following property with correct normalization group:\n\n```php\n    #[Groups('deal_read')]\n    #[ApiProperty(attributes: [\"openapi_context\" =\u003e [\"description\" =\u003e \"If Authorization GrantType::OWN or GROUP is calculated, resource can be restricted.\"]])]\n    public bool $isRestricted = false;\n```\n### Property visibility check\nSometimes you want to return all items in endpoint but want to limit properties returned based on user roles. To do so, you need to set `GrantType::LIMITED` to\nrole and operation you want to have this visibility check and add `#[VisibleProperty]` attribute to resource where this check should be done. \n`#[VisibleProperty]` attribute takes 2 parameters: `ownerProperty` and `properties`. `properties` is an array of all properties you want to `SHOW`. `ownerProperty` is\nthe name of property which to check against current logged in user.  \n\u003e **IMPORTANT**: If resource has GrantType::LIMITED to some role for get or get_collection operations, at least one access resolver or `#[VisibleProperty]` must be set!\n\n### Explicit check if all roles are configured in authorization service\n\nIf you want explicitly check if all project defined roles are fully configured in authorization service, you can configure this check by passing BackedEnum containing\nall needed roles to configuration.\n\u003e Default value is `[]`, so without this configuration check will not be triggered.  \n```php\n\u003c?php declare(strict_types = 1);\n\nuse App\\Constants\\Enum\\Roles;\nuse Symfony\\Config\\EntityResourceMapperConfig;\n\nreturn static function (EntityResourceMapperConfig $config): void {\n    $config\n        -\u003erolesEnum(Roles::class);\n};\n```\nor\n```yaml\nentity_resource_mapper:\n    roles_enum: App\\Constants\\Enum\\Roles\n```\nThis enum must be backed and contain all needed roles with `ROLE_` prefix like this:\n```php\n\u003c?php declare(strict_types = 1);\n\nnamespace App\\Constants\\Enum;\n\nenum Roles: string\n{\n    case ROLE_USER = 'ROLE_USER';\n    case ROLE_ADMIN = 'ROLE_ADMIN'\n}\n```\nNow if you don't have ROLE_USER or ROLE_ADMIN grants configured for any resource operation you passed in `AuthorizationService-\u003esetServices()`, exception will be thrown.\n\n### Public resource access ###\n\nIf it is required to access any resource without authorization (by default this is forbidden), you can use `AuthorizationServiceConfigurator` \nto allow specific operations for `AuthenticatedVoter::PUBLIC_ACCESS`. To do so, configure needed operations using `GrantType::ALL`. Only\n`GrantType::ALL` is allowed to be used (no option for `GrantType::LIMITED`) and you do not need to set `GrantType::NONE` for public \naccess.\nExample:\n```php\n// src/Service/Configurator/AuthorizationServiceConfigurator.php\n\nuse WhiteDigital\\EntityResourceMapper\\Security\\AuthorizationServiceConfiguratorInterface;\nuse WhiteDigital\\EntityResourceMapper\\Security\\Enum\\GrantType;\nuse Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AuthenticatedVoter;\n\nfinal class AuthorizationServiceConfigurator implements AuthorizationServiceConfiguratorInterface\n{\n    public function __invoke(AuthorizationService $service): void\n    {\n        $service-\u003esetResources([\n            ActivityResource::class =\u003e [\n                AuthorizationService::ALL =\u003e ['ROLE_SUPER_ADMIN' =\u003e GrantType::ALL, 'ROLE_KAM' =\u003e GrantType::ALL],\n                AuthorizationService::COL_GET =\u003e ['ROLE_JUNIOR_KAM' =\u003e GrantType::LIMITED],\n                AuthorizationService::ITEM_GET =\u003e [AuthenticatedVoter::PUBLIC_ACCESS =\u003e GrantType::ALL],\n                AuthorizationService::COL_POST =\u003e [],\n                AuthorizationService::ITEM_PATCH =\u003e [],\n                AuthorizationService::ITEM_DELETE =\u003e [],\n            ]]);\n    }\n}\n```\n\n### Menu Builder ### \n\nThis package ships with a menu builder functionality, that allows to define the overall menu structure and allows for\ndynamic menu building based on current user limitations (authorization, rules)\n\nTo use menu builder service, you must first create a configurator class for this service that implements \nWhiteDigital\\EntityResourceMapper\\Interface\\MenuBuilderServiceConfiguratorInterface\n\n```php\n\nuse WhiteDigital\\EntityResourceMapper\\MenuBuilder\\Interface\\MenuBuilderServiceConfiguratorInterface;\nuse WhiteDigital\\EntityResourceMapper\\MenuBuilder\\Services\\MenuBuilderService;\n\nfinal class MenuBuilderServiceConfigurator implements MenuBuilderServiceConfiguratorInterface\n{\n    public function __invoke(MenuBuilderService $service): void\n    {\n        //either mainResource or roles key must be set\n        $service-\u003esetMenuStructure(\n                [\n                    [\n                        'name' =\u003e 'ACTIVITIES',\n                        'mainResource' =\u003e ActivityResource::class,\n                    ],\n                    [\n                        'name' =\u003e 'REPORTS',\n                        'roles' =\u003e ['ROLE_SUPER_ADMIN', 'ROLE_KAM'],\n                    ],\n                 ]);\n    }\n}\n```\nRegister the configurator class as a service:\n```\nWhiteDigital\\EntityResourceMapper\\MenuBuilder\\Interface\\MenuBuilderServiceConfiguratorInterface:\n    class: MenuBuilderServiceConfigurator\n```\nAnd finally you can use the menubuilder and retrieve the filtered menu by calling the MenuBuilderService like so:\n\n```php\n\nuse WhiteDigital\\EntityResourceMapper\\MenuBuilder\\Services\\MenuBuilderService;\n\nclass SomeClass\n{\n    public function someFunction(MenuBuilderService $service): void\n    {\n        $data = $service-\u003egetMenuForCurrentUser();\n    }\n}\n```\n\n### Base provider and processor\nIn most cases way how to read or write data to database is the same, so this package provides `AbstractDataProcessor` \nand `AbstractDataProvider` that implements base logic for api platform.\nMaker part of this package uses these clases for generation as well. Using these abstractions will take away need to\nduplicate code for each entity/resource. As these are abstractions, you can always override any function of them when\nneeded.\n\n### Extended api resource\nOther `whitedigital-eu` packages may come with api resources that with some configuration may not be suited for\nstraight away usage in a project. This is why `ExtendedApiResource` is useful to override part of options defined\nin default attributes.\n\nFor example, take a look at `WhiteDigital\\Audit\\ApiResource\\AuditResource` class. It defines api resource. \nIf you want iri to be `/api/vendor/audits`, you have to do the following:\n1. Create new class that extends resource you want to override\n2. Add `ExtendedApiResouce` attribute insted of `ApiResource` attribute\n3. Pass only those options that you want to override, others will be taken from resource you are extending\n```php\nnamespace App\\ApiResource;\n\nuse WhiteDigital\\EntityResourceMapper\\Attribute\\ExtendedApiResource;\n\n#[ExtendedApiResource(routePrefix: '/vendor')]\nclass AuditResource extends WhiteDigital\\Audit\\ApiResource\\AuditResource\n{\n}\n```\n`ExtendedApiResouce` attribute checks which resource you are extending and overrides options given in extension,\nkeeping other options same as in parent resource.\n\n\u003e **IMPORTANT**: You need to disable bundled resource using api_platform.openapi.factory decorator, otherwise you will have 2 instances of audit\n\u003e resource: one with `/api/audits` iri and one with `/api/vendor/audits` iri.\n\n### ApiResource maker\nDefault configuration options comes based on `api-platform`|`symfony` recommendations but you can override them like this (default values shown):\n```yaml\napi_resource:\n    namespaces:\n        api_resource: ApiResource\n        class_map_configurator: Service\\\\Configurator # required by whitedigital-eu/entity-resource-mapper-bundle\n        data_processor: DataProcessor\n        data_provider: DataProvider\n        entity: Entity\n        root: App\n    defaults:\n        api_resource_suffix: Resource\n        role_separator: ':'\n        space: '_'\n```\n```php\nuse Symfony\\Config\\EntityResourceMapperConfig;\n\nreturn static function (EntityResourceMapperConfig $config): void {\n    $namespaces = $config\n        -\u003enamespaces();\n\n    $namespaces\n        -\u003eapiResource('ApiResource')\n        -\u003eclassMapConfigurator('Service\\\\Configurator') # required by whitedigital-eu/entity-resource-mapper-bundle\n        -\u003edataProcessor('DataProcessor')\n        -\u003edataProvider('DataProvider')\n        -\u003eentity('Entity')\n        -\u003eroot('App');\n        \n    $defaults = $config\n        -\u003edefaults();\n        \n    $defaults\n        -\u003eapiResourceSuffix('Resource')\n        -\u003eroleSeparator(':')\n        -\u003espace('_');\n};\n```\n`namespaces` are there to set up different directories for generated files. So, if you need to put files in different directories/namespaces, you can chnage it as such.\n\n`roleSeparator` and `space` from `defaults` are added to configure separators for groups used in api resource. For example, `UserRole` with defaults will become `user_role:read` for read group.  \n`apiResourcrSuffix` defines suffix for api resource class name. For example, by default `User` entity will make `UserResource` api resource class.\n\n### Usage\nSimply run `make:api-resource \u003cEntityName\u003e` where EntityName is entity you want to create api resource for.\nExample, `make:api-resource User` to make UserResource, UserDataProcessor and UserDataProvider for User entity.\n\nMaker command generates resource properties based on entity variables. This could sometimes be incorrect or not needed, so you can pass `--no-properties` option to not generate properties.  \nBy default, maker command will throw an error if you are trying to generate classes that already exist. If for some reason you want to rewrite generated classes, you can pass `--delete-if-exists`\noption.  \nThis option comes in handy on occasion when you have 2 entities, that have relation. Because of specific logical impossibility, to generate resources for both classes automatically, you should:  \n1. run `make:api-resource Entity1 --no-properties`  \n2. run `make:api-resource Entity2`  \n3. run `make:api-resource Entity1 --delete-if-exists`\n\nThis command automatically generates ApiFilters for given entity. Default value is to generate them is for first level fields. Like this:\n\n```php\nuse ApiPlatform\\Metadata\\ApiFilter;\nuse ApiPlatform\\Metadata\\ApiResource;\nuse WhiteDigital\\EntityResourceMapper\\Filters\\ResourceDateFilter;\nuse WhiteDigital\\EntityResourceMapper\\Resource\\BaseResource;\n\n#[\n    ApiResource (\n        shortName: 'User'\n    ),\n    ApiFilter(ResourceDateFilter::class, properties: ['createdAt', 'updatedAt', ]),\n]\nclass UserResource extends BaseResource \n{\n    public ?DateTimeImmutable $createdAt = null;\n\n    public ?DateTimeImmutable $updatedAt = null;\n    \n    public ?UserResource $parent = null;\n}\n```\nIf you don't want to generate any filters, run command by passing `level 0`:  \n```shell\nbin/console make:api-resource User --level 0\n```\n\nIf you want generate filters for more levels for subresources, like, parent.createdAt, pass higher level:  \n```shell\nbin/console make:api-resource User --level 2\n```\n```php\nuse ApiPlatform\\Metadata\\ApiFilter;\nuse ApiPlatform\\Metadata\\ApiResource;\nuse WhiteDigital\\EntityResourceMapper\\Filters\\ResourceDateFilter;\nuse WhiteDigital\\EntityResourceMapper\\Resource\\BaseResource;\n\n#[\n    ApiResource (\n        shortName: 'User'\n    ),\n    ApiFilter(ResourceDateFilter::class, properties: ['createdAt', 'updatedAt', 'parent.createdAt', 'parent.updatedAt']),\n]\nclass UserResource extends BaseResource \n{\n    public ?DateTimeImmutable $createdAt = null;\n\n    public ?DateTimeImmutable $updatedAt = null;\n    \n    public ?UserResource $parent = null;\n}\n```\nHigher level -\u003e deeper subresource filters.  \nIt is obvious that you probably do not need all generated filters, but it is easier to remove than it is to add.  \n\nIf you want to exclude specific type of filters, you can pass `--exclude-\u003cfilter\u003e` to skip generation of those filters.  \n```shell\nbin/console make:api-resource User --level 2 --exclude-array --exclude-numeric --exclude-range\n```\nAvailable filters are:  \n+ `array`: `ResourceJsonFilter`\n+ `bool`: `ResourceBooleanFilter`\n+ `date`: `ResourceDateFilter`\n+ `enum`: `ResourceEnumFilter`\n+ `numeric`: `ResourceNumericFilter`\n+ `range`: `ResourceRangeFilter`\n+ `search`: `ResourceSearchFilter`\n\n`ResourceOrderFilter` is created from non-excluded `numeric`, `search`, `date` and `array` filters.  \n\n### PHP CS Fixer\n\u003e **IMPORTANT**: When running php-cs-fixer, make sure not to format files in `skeleton` folder. Otherwise maker\n\u003e command will stop working.\n\n### Validators\nThis library contains validators for Classifiers. These will only work if Entity/Resource have following structure (not less than this):\nEntity:\n\n```php\nuse Doctrine\\ORM\\Mapping\\Entity;\n\n#[Entity]\nclass Classifier\n{\n    private ?int $id = null;\n    private ?string $value = null;\n    private ?array $data = [];\n    private ?ClassifierType $type = null;\n}\n```\nResource:\n```php\nuse ApiPlatform\\Metadata\\ApiResource;\n\n#[ApiResource]\nclass ClassifierResource\n{\n    public mixed $id = null;\n    public ?string $value = null;\n    public ?array $data = [];\n    public ?ClassifierType $type = null;\n}\n```\nAs seen in these examples, you need to have a Backed enum (here called `ClassifierType`) that you need to validate against.  \nExample of `ClassifierType` is something like this:\n```php\nenum ClassifierType: string\n{\n    case ONE = 'ONE';\n    case TWO = 'TWO';\n}\n```\nNow you can use either `CorrectClassifierType` or `ClassifierRequiredDataIsSet` validator:  \n\n---\n\n`CorrectClassifierType`:\nCorrectClassifierType checks if in related resource Classifier is given with correct type:\n\n```php\nuse ApiPlatform\\Metadata\\ApiResource;\nuse WhiteDigital\\EntityResourceMapper\\Validator\\Constraints as WDAssert;\n\n#[ApiResource]\nclass TestResource\n{\n    #[WDAssert\\CorrectClassifierType(ClassifierType::ONE)]\n    public ?ClassifierResource $one = null;\n}\n```\nNow if you pass resource that has any other type (like `ClassifierType::TWO` for example) to this resource, error will be thrown.  \n\n---\n\n`ClassifierRequiredDataIsSet`:\nSometimes you may need extra data in Classifier and may be mandatory. For this ClassifierRequiredDataIsSet can be used to check if this \ndata is passed. This is used on `ClassifierResource`:\n\n```php\nuse ApiPlatform\\Metadata\\ApiResource;\nuse WhiteDigital\\EntityResourceMapper\\Validator\\Constraints as WDAssert;\n\n#[ApiResource]\n#[WDAssert\\ClassifierRequiredDataIsSet(ClassifierType::ONE, ['test1'])]\nclass ClassifierResource\n{\n    public mixed $id = null;\n    public ?string $value = null;\n    public ?array $data = [];\n    public ?ClassifierType $type = null;\n}\n```\nAnd now when creating a new Classifier with type `ONE`, an error will be thrown if data does not contain value with key `test1`\n\n## Tests\n\nRun tests by:\n```bash\n$ vendor/bin/phpunit\n```\n\n## TODO ##\n- performance improvements\n- explicit joins on dataprovider\n- computed properties as querybuilder methods\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhitedigital-eu%2Fentity-resource-mapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwhitedigital-eu%2Fentity-resource-mapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwhitedigital-eu%2Fentity-resource-mapper/lists"}