Фильтры платформы API с uuid

Хорошо, поэтому я недавно создал проект, в котором я использую api-платформу в сочетании с ramsey / uuid-doctrine в соответствии с https://api-platform.com/docs/core/identifiers/.

Все основные элементы CRUD работают, но я получаю неожиданное поведение на ApiFilter.

Базовая настройка такова: у меня есть объекты платформы, которые, в свою очередь, содержат организации, которые используют абстрактный класс.

Абстрактный

class AbstaractEntity 
{   
    /**
     * @var \Ramsey\Uuid\UuidInterface
     *
     * @ORM\Id
     * @ORM\Column(type = "uuid_binary_ordered_time", unique=true)
     * @ORM\GeneratedValue(strategy = "CUSTOM")
     * @ORM\CustomIdGenerator(class = "Ramsey\Uuid\Doctrine\UuidOrderedTimeGenerator")
     * 
     * @Groups({"read", "write"})
     * @ApiProperty(iri = "https://schema.org/identifier")
     */
    protected $id;

 * The Platform to wich this object belongs.
 * 
 * @ORM\ManyToOne(targetEntity = "App\Conduction\PlatformBundle\Entity\Platform")
 * @ORM\JoinColumn(name = "platform_id", referencedColumnName = "id")
 * @Groups({"read", "write"})
 * @MaxDepth(1)
 * @ApiProperty()
 */
protected $platform;

...
}

Организация

/**
 * Organisation entity
 *
 * @ORM\Table(
 *     "organisation__organisation"
 * )
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 * @ORM\EntityListeners({"App\Conduction\OrganisationBundle\EventListener\OrganisationListener","App\Conduction\AppBundle\EventListener\EntityListener"})
 * @ApiResource(attributes = {
 *     "normalization_context" = {"groups" = {"read"}, "enable_max_depth" = "true"},
 *     "denormalization_context" = {"groups" = {"write"}}
 * })
 * @ApiFilter(SearchFilter::class, properties = {"id": "exact", "description": "partial", "name": "partial","organisation": "exact","platform": "exact"})
 * @ApiFilter(DateFilter::class, properties = {"dateCreated","datePublished","dateModified","dateDeleted"})
 * @ApiFilter(OrderFilter::class, properties = {"id", "name","dateCreated","datePublished","dateModified","dateDeleted"}, arguments = {"orderParameterName" = "order"})
 * @Gedmo\SoftDeleteable(
 *     fieldName = "dateDeleted",
 *     timeAware = true
 * )
 */
class Organisation extends \App\Conduction\AppBundle\Entity\AbstaractEntity
{
....
}

Итак, все основы в порядке, и api / organization дает следующие

{
    "@context": "/api/contexts/Organisation",
    "@id": "/api/organisations",
    "@type": "hydra:Collection",
    "hydra:member": [
        {
            "@id": "/api/organisations/ba8dc018-d8d5-11e8-bcb6-5254007d3b24",
            "@type": "Organisation",
            "slug": null,
            "organisation": "/api/organisations/ba8dc018-d8d5-11e8-bcb6-5254007d3b24",
            "children": [
                "/api/organisations/ba8dc018-d8d5-11e8-bcb6-5254007d3b24"
            ],
            "id": "ba8dc018-d8d5-11e8-bcb6-5254007d3b24",
            "name": "Conduction",
            "description": "This is the defeault platform",
            "platform": {
                "@id": "/api/platforms/ba8d9958-d8d5-11e8-a9a5-5254007d3b24",
                "@type": "Platform",
                "approveUsers": true,
                "validateUsers": true,
                "organisation": "/api/organisations/ba8dc018-d8d5-11e8-bcb6-5254007d3b24",
                "id": "ba8d9958-d8d5-11e8-a9a5-5254007d3b24",
                "name": "Conduction",
                "description": "This is the defeault platform",
                "platform": "/api/platforms/ba8d9958-d8d5-11e8-a9a5-5254007d3b24",
                "dateCreated": "2018-10-26T06:15:09+02:00",
                "datePublished": "2018-10-26T06:15:09+02:00",
                "dateModified": "2018-10-26T06:15:10+02:00"
            },
            "dateCreated": "2018-10-26T06:15:09+02:00",
            "datePublished": "2018-10-26T06:15:09+02:00",
            "dateModified": "2018-10-26T06:15:10+02:00"
        },
        {
            "@id": "/api/organisations/5f87e080-d8d6-11e8-9809-5254007d3b24",
            "@type": "Organisation",
            "slug": null,
            "organisation": "/api/organisations/5f87e080-d8d6-11e8-9809-5254007d3b24",
            "children": [
                "/api/organisations/5f87e080-d8d6-11e8-9809-5254007d3b24"
            ],
            "id": "5f87e080-d8d6-11e8-9809-5254007d3b24",
            "name": "test 1",
            "description": "<p>test 1&nbsp;</p>",
            "platform": null,
            "dateCreated": "2018-10-26T06:19:47+02:00",
            "datePublished": "2018-10-26T06:19:47+02:00",
            "dateModified": "2018-10-26T06:19:47+02:00"
        },
        {
            "@id": "/api/organisations/f2079af4-d8d6-11e8-ba9a-5254007d3b24",
            "@type": "Organisation",
            "slug": null,
            "organisation": "/api/organisations/f2079af4-d8d6-11e8-ba9a-5254007d3b24",
            "children": [
                "/api/organisations/f2079af4-d8d6-11e8-ba9a-5254007d3b24"
            ],
            "id": "f2079af4-d8d6-11e8-ba9a-5254007d3b24",
            "name": "test 1",
            "description": "<p>test 1</p>",
            "platform": {
                "@id": "/api/platforms/f20723bc-d8d6-11e8-8ffb-5254007d3b24",
                "@type": "Platform",
                "approveUsers": true,
                "validateUsers": true,
                "organisation": "/api/organisations/f2079af4-d8d6-11e8-ba9a-5254007d3b24",
                "id": "f20723bc-d8d6-11e8-8ffb-5254007d3b24",
                "name": "test 1",
                "description": "<p>test 1</p>",
                "platform": "/api/platforms/f20723bc-d8d6-11e8-8ffb-5254007d3b24",
                "dateCreated": "2018-10-26T06:23:52+02:00",
                "datePublished": "2018-10-26T06:23:52+02:00",
                "dateModified": "2018-10-26T06:23:52+02:00"
            },
            "dateCreated": "2018-10-26T06:23:52+02:00",
            "datePublished": "2018-10-26T06:23:52+02:00",
            "dateModified": "2018-10-26T06:23:52+02:00"
        }
    ],
    "hydra:totalItems": 3,
    "hydra:search": {
        "@type": "hydra:IriTemplate",
        "hydra:template": "/api/organisations{?id,id[],description,name,organisation,organisation[],platform,platform[],dateCreated[before],dateCreated[strictly_before],dateCreated[after],dateCreated[strictly_after],datePublished[before],datePublished[strictly_before],datePublished[after],datePublished[strictly_after],dateModified[before],dateModified[strictly_before],dateModified[after],dateModified[strictly_after],dateDeleted[before],dateDeleted[strictly_before],dateDeleted[after],dateDeleted[strictly_after],order[id],order[name],order[dateCreated],order[datePublished],order[dateModified],order[dateDeleted]}",
        "hydra:variableRepresentation": "BasicRepresentation",
        "hydra:mapping": [
            {
                "@type": "IriTemplateMapping",
                "variable": "id",
                "property": "id",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "id[]",
                "property": "id",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "description",
                "property": "description",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "name",
                "property": "name",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "organisation",
                "property": "organisation",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "organisation[]",
                "property": "organisation",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "platform",
                "property": "platform",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "platform[]",
                "property": "platform",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateCreated[before]",
                "property": "dateCreated",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateCreated[strictly_before]",
                "property": "dateCreated",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateCreated[after]",
                "property": "dateCreated",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateCreated[strictly_after]",
                "property": "dateCreated",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "datePublished[before]",
                "property": "datePublished",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "datePublished[strictly_before]",
                "property": "datePublished",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "datePublished[after]",
                "property": "datePublished",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "datePublished[strictly_after]",
                "property": "datePublished",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateModified[before]",
                "property": "dateModified",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateModified[strictly_before]",
                "property": "dateModified",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateModified[after]",
                "property": "dateModified",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateModified[strictly_after]",
                "property": "dateModified",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateDeleted[before]",
                "property": "dateDeleted",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateDeleted[strictly_before]",
                "property": "dateDeleted",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateDeleted[after]",
                "property": "dateDeleted",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "dateDeleted[strictly_after]",
                "property": "dateDeleted",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "order[id]",
                "property": "id",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "order[name]",
                "property": "name",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "order[dateCreated]",
                "property": "dateCreated",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "order[datePublished]",
                "property": "datePublished",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "order[dateModified]",
                "property": "dateModified",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "order[dateDeleted]",
                "property": "dateDeleted",
                "required": false
            }
        ]
    }
}

Тогда можно предположить, что api / organization? platform = ba8d9958-d8d5-11e8-a9a5-5254007d3b24 предоставляет все организации, принадлежащие этой платформе, но нет. Это дает

{
    "@context": "/api/contexts/Organisation",
    "@id": "/api/organisations",
    "@type": "hydra:Collection",
    "hydra:member": [],
    "hydra:totalItems": 0,
    "hydra:view": {
        "@id": "/api/organisations?platform=ba8d9958-d8d5-11e8-a9a5-5254007d3b24",
        "@type": "hydra:PartialCollectionView"
    },
    "hydra:search": {
        "@type": "hydra:IriTemplate",
        "hydra:template": "/api/organisations{?id,id[],description,name,organisation,organisation[],platform,platform[],dateCreated[before],dateCreated[strictly_before],dateCreated[after],dateCreated[strictly_after],datePublished[before],datePublished[strictly_before],datePublished[after],datePublished[strictly_after],dateModified[before],dateModified[strictly_before],dateModified[after],dateModified[strictly_after],dateDeleted[before],dateDeleted[strictly_before],dateDeleted[after],dateDeleted[strictly_after],order[id],order[name],order[dateCreated],order[datePublished],order[dateModified],order[dateDeleted]}",
        "hydra:variableRepresentation": "BasicRepresentation",
        "hydra:mapping": [
            {
                "@type": "IriTemplateMapping",
                "variable": "id",
                "property": "id",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "id[]",
                "property": "id",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "description",
                "property": "description",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "name",
                "property": "name",
                "required": false
            },
            {
                "@type": "IriTemplateMapping",
                "variable": "organisation",
                "property": "organisation",
                "required": false
            },
            {
            "@type": "IriTemplateMapping",
            "variable": "organisation[]",
            "property": "organisation",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "platform",
            "property": "platform",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "platform[]",
            "property": "platform",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateCreated[before]",
            "property": "dateCreated",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateCreated[strictly_before]",
            "property": "dateCreated",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateCreated[after]",
            "property": "dateCreated",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateCreated[strictly_after]",
            "property": "dateCreated",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "datePublished[before]",
            "property": "datePublished",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "datePublished[strictly_before]",
            "property": "datePublished",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "datePublished[after]",
            "property": "datePublished",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "datePublished[strictly_after]",
            "property": "datePublished",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateModified[before]",
            "property": "dateModified",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateModified[strictly_before]",
            "property": "dateModified",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateModified[after]",
            "property": "dateModified",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateModified[strictly_after]",
            "property": "dateModified",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateDeleted[before]",
            "property": "dateDeleted",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateDeleted[strictly_before]",
            "property": "dateDeleted",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateDeleted[after]",
            "property": "dateDeleted",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "dateDeleted[strictly_after]",
            "property": "dateDeleted",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "order[id]",
            "property": "id",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "order[name]",
            "property": "name",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "order[dateCreated]",
            "property": "dateCreated",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "order[datePublished]",
            "property": "datePublished",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "order[dateModified]",
            "property": "dateModified",
            "required": false
        },
        {
            "@type": "IriTemplateMapping",
            "variable": "order[dateDeleted]",
            "property": "dateDeleted",
            "required": false
        }
    ]
}

}

Или просто поставьте нулевую организацию. Теперь, для поиска ошибок, я снова создаю все это целиком, но теперь использую инкрементные идентификаторы, и тогда это работает как шарм. Итак, проблема, похоже, заключается в том, как работает ApiFilter. Это может означать, что ApiFilter на самом деле не нормализует UUID перед его использованием в поиске ...

-- Обновлять --

Хорошо, поэтому я попытался пропустить это с помощью настраиваемого фильтра

namespace App\Conduction\AppBundle\Filter;

use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\ORM\QueryBuilder;
use Ramsey\Uuid\Uuid;

final class UuidFilter extends AbstractContextAwareFilter 
{
    protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
    {

        // otherwise filter is applied to order and page as well
        if (
                !$this->isPropertyEnabled($property, $resourceClass) ||
                !$this->isPropertyMapped($property, $resourceClass)
                ) {
                    return;
                }

                $value =pack("h*", str_replace('-', '', $value));

                $parameterName = $queryNameGenerator->generateParameterName($property); // Generate a unique parameter name to avoid collisions with other filters

                $queryBuilder
                ->andWhere("$property =:$parameterName")
                ->setParameter($parameterName, $value);
    }

    // This function is only used to hook in documentation generators (supported by Swagger and Hydra)
    public function getDescription(string $resourceClass): array
    {
        if (!$this->properties) {
            return [];
        }

        $description = [];
        foreach ($this->properties as $property => $strategy) {
            $description["uuid"] = [
                    'property' => $property,
                    'type' => 'string',
                    'required' => false,
                    'swagger' => [
                            'description' => 'This filter allows to search the api based on object uuids',
                            'name' => 'UUID',
                            'type' => 'Primary identifier',
                    ],
            ];
        }

        return $description;
    }
}

Но затем я столкнулся с ошибкой доктрины: «[Семантическая ошибка] строка 0, столбец 76 рядом с 'platform =: platform_p1': Ошибка: 'платформа' не определена».

Это, конечно, предполагает, что платформа не определена, но, насколько я могу судить, это так.

--- ОБНОВЛЕНИЕ: Решено! ---

Interesting thought, but unfortunately that also returns zero results. But interesting stuff is happening. So using the IRI doctrine does get the proper intel -> as seen in the logs as:
[2018-10-30 10:00:50] doctrine.DEBUG: SELECT o0_.slug AS slug_0, o0_.id AS id_1, o0_.name AS name_2, o0_.description AS description_3, o0_.date_created AS date_created_4, o0_.date_published AS date_published_5, o0_.date_modified AS date_modified_6, o0_.date_deleted AS date_deleted_7, o0_.date_checkout AS date_checkout_8, o0_.version AS version_9, o0_.address_id AS address_id_10, o0_.organisation_id AS organisation_id_11, o0_.default_tax_id AS default_tax_id_12, o0_.member_group_id AS member_group_id_13, o0_.admin_group_id AS admin_group_id_14, o0_.image_id AS image_id_15, o0_.platform_id AS platform_id_16, o0_.user_owned AS user_owned_17, o0_.user_created AS user_created_18, o0_.user_updated AS user_updated_19, o0_.user_checkout AS user_checkout_20 FROM organisation__organisation o0_ WHERE o0_.platform_id = ? AND o0_.date_deleted IS NULL AND o0_.date_published < ? ORDER BY o0_.id ASC ["[object] (Ramsey\\Uuid\\Uuid: \"13be1406-d9c3-11e8-9d84-5254007d3b24\")","2018-10-30 10:00:50"] []

Итак, доктрина явно знает, что ищет Ramsy \ Uuid напрямую вместо «13be1406-d9c3-11e8-9d84-5254007d3b2». Хорошо, а что, если мы будем искать без платформы IRI? Это дает хороший ответ 1 объект и следующий журнал доктрины

[2018-10-30 10:09:46] doctrine.DEBUG: SELECT o0_.slug AS slug_0, o0_.id AS id_1, o0_.name AS name_2, o0_.description AS description_3, o0_.date_created AS date_created_4, o0_.date_published AS date_published_5, o0_.date_modified AS date_modified_6, o0_.date_deleted AS date_deleted_7, o0_.date_checkout AS date_checkout_8, o0_.version AS version_9, o0_.address_id AS address_id_10, o0_.organisation_id AS organisation_id_11, o0_.default_tax_id AS default_tax_id_12, o0_.member_group_id AS member_group_id_13, o0_.admin_group_id AS admin_group_id_14, o0_.image_id AS image_id_15, o0_.platform_id AS platform_id_16, o0_.user_owned AS user_owned_17, o0_.user_created AS user_created_18, o0_.user_updated AS user_updated_19, o0_.user_checkout AS user_checkout_20 FROM organisation__organisation o0_ WHERE o0_.date_deleted IS NULL AND o0_.date_published < ? ORDER BY o0_.id ASC ["2018-10-30 10:09:46"] []

Таким образом, разница на самом деле заключается в Ramsy \ Uuid, особенность здесь в том, что если мы получим .. / api / platform / 13be1406-d9c3-11e8-9d84-5254007d3b24, мы получим хороший ответ этого платформенного объекта. доктрины Рамси \ Uuid, похоже, работает, ну ладно…. Так что насчет этого поля id? Если мы просто возьмем базу данных и сделаем простое соединение. Мы можем присоединиться к организации и id, поэтому поля platform.id и organization__organistation.platform_id действительно содержат одно и то же значение. А затем для настоящего кикера….

... / api / organization? platform.id = 13be1406-d9c3-11e8-9d84-5254007d3b24

Работает! Так что это решает нашу проблему «да», но неожиданным образом. Он представляет нам журнал доктрины:

[2018-10-30 10:19:15] doctrine.DEBUG: SELECT t0.editable AS editable_1, t0.memberGroup AS memberGroup_2, t0.id AS id_3, t0.name AS name_4, t0.description AS description_5, t0.date_created AS date_created_6, t0.date_published AS date_published_7, t0.date_modified AS date_modified_8, t0.date_deleted AS date_deleted_9, t0.date_checkout AS date_checkout_10, t0.version AS version_11, t0.organisation_id AS organisation_id_12, t13.slug AS slug_14, t13.id AS id_15, t13.name AS name_16, t13.description AS description_17, t13.date_created AS date_created_18, t13.date_published AS date_published_19, t13.date_modified AS date_modified_20, t13.date_deleted AS date_deleted_21, t13.date_checkout AS date_checkout_22, t13.version AS version_23, t13.address_id AS address_id_24, t13.organisation_id AS organisation_id_25, t13.default_tax_id AS default_tax_id_26, t13.member_group_id AS member_group_id_27, t13.admin_group_id AS admin_group_id_28, t13.image_id AS image_id_29, t13.platform_id AS platform_id_30, t13.user_owned AS user_owned_31, t13.user_created AS user_created_32, t13.user_updated AS user_updated_33, t13.user_checkout AS user_checkout_34, t0.parent_id AS parent_id_35, t0.image_id AS image_id_36, t0.platform_id AS platform_id_37, t0.user_owned AS user_owned_38, t0.user_created AS user_created_39, t0.user_updated AS user_updated_40, t0.user_checkout AS user_checkout_41 FROM organisation__organisation_group t0 LEFT JOIN organisation__organisation t13 ON t0.organisation_id = t13.id INNER JOIN organisation__organisation_group_member ON t0.id = organisation__organisation_group_member.group_id WHERE organisation__organisation_group_member.user_id = ? ["[object] (Ramsey\\Uuid\\Uuid: \"154f07d0-d9c3-11e8-bf04-5254007d3b24\")"] []

Подожди, а? Ага. Если мы сделаем api / organization? Platform = / api / platform / ba8d9958-d8d5-11e8-a9a5-5254007d3b24. Doctrine не выполняет внутреннее соединение и не может правильно декодировать UUID, но когда мы делаем api / organization? Platform.id = ba8d9958-d8d5-11e8-a9a5-5254007d3b24. Платформа API принудительно выполняет внутреннее соединение, а затем оно внезапно срабатывает …… ..

Хорошо, наша проблема теперь решена, но я все еще думаю, что это то, что может захотеть изучить либо платформа API (которая поддерживает Ramsy \ Uuid), либо Ramsy \ Uuid.

Спасибо! сэкономил мне много времени. Для тех, кто приходит сюда и кому нужно знать ответ (поскольку сообщение довольно длинное) - установите фильтр на .id связанного объекта, а не на корневой объект. Например, #[ApiFilter(SearchFilter::class, properties: ['platform.id' => 'exact'])] vs #[ApiFilter(SearchFilter::class, properties: ['platform' => 'exact'])] Затем используйте uuid вместо IRI

doz87 08.07.2021 07:14
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
1 734
4

Ответы 4

Я почти уверен, что "правильный" поиск будет таким:

api/organisations?platform=/api/platforms/ba8d9958-d8d5-11e8-a9a5-5254007d3b24.

Вам нужно использовать только iri, а не id.

Спасибо, у меня это не сработало, но определенно подтолкнуло меня в правильном направлении. Я обновил сообщение своими выводами.

Ruben van der Linde 30.10.2018 10:27

Вы можете создать собственный фильтр:

services.yml

    App\Api\Filter\UlidFilter:
        tags: [ 'api_platform.filter' ]

Приложение \ Api \ Filter \ UlidFilter.php

namespace App\Api\Filter;

use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Uid\Ulid;

final class UlidFilter extends AbstractContextAwareFilter
{
    protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
    {
        // otherwise filter is applied to order and page as well
        if (
            !$this->isPropertyEnabled($property, $resourceClass) ||
            !$this->isPropertyMapped($property, $resourceClass, true)
        ) {
            return;
        }

        // Generate a unique parameter name to avoid collisions with other filters
        $parameterName = $queryNameGenerator->generateParameterName($property);
        $queryBuilder
            ->andWhere(sprintf('o.%s = :%s', $property, $parameterName))
            ->setParameter($parameterName, (new Ulid($value))->toBinary());
    }

    // This function is only used to hook in documentation generators (supported by Swagger and Hydra)
    public function getDescription(string $resourceClass): array
    {
        if (!$this->properties) {
            return [];
        }

        $description = [];
        foreach ($this->properties as $property => $strategy) {
            $description[$property] = [
                'property' => $property,
                'type' => 'string',
                'required' => false,
                'swagger' => [
                    'description' => 'Filter Ulid property.',
                    'name' => 'Ulid Search filter',
                    'type' => '',
                ],
            ];
        }

        return $description;
    }
}

В вашем объекте:

use App\Api\Filter\UlidFilter;
...
/**
* @ApiResource()
* @ApiFilter(UlidFilter::class, properties = {"Field": "exact"})
**/

У меня была такая же проблема, и я нашел то, что сработало для меня.

У меня есть две сущности: экспорт и листинг.

OneToMany в листинге -> Экспорт.

Я хотел отфильтровать экспорт по идентификатору объявления (UUID).

#[ApiFilter(SearchFilter::class, properties: ['listing' => 'exact'])]

Я обнаружил, что если я сделал запрос на api/exports?listing = {insert IRI or uuid}, он не работал, однако если я изменил параметр запроса api/exports?listing.id = {insert IRI or uuid}, он работал.

И наоборот, если я поменяю фильтр на этот -

#[ApiFilter(SearchFilter::class, properties: ['listing.id' => 'exact'])]

Затем я могу просто запросить /api/exports?listing = {insert IRI or uuid}, и он отлично работает.

Чего я не понимал, так это того, что это работает для всех свойств отношения, поэтому, если вы просто укажете listing в качестве фильтра, вы можете отфильтровать любое свойство на Listing из параметров запроса, и он будет работать (например, list.id = {} илиisting.type и т. д.)

Интересно, поскольку он открывает другие возможности, но для этой конкретной проблемы он решает проблему uuid.

Я столкнулся с этой проблемой, и у меня была аналогичная проблема, но я решил ее без настраиваемого фильтра. Вам нужно только обновить определение @ApiFilter ().

Я изменил эту строку в модели вашей организации с:

 * @ApiFilter(SearchFilter::class, properties = {"id": "exact", "description": "partial", "name": "partial","organisation": "exact","platform": "exact"})

К

 * @ApiFilter(SearchFilter::class, properties = {"id": "exact", "description": "partial", "name": "partial","organisation": "exact","platform.uuid": "exact"})

Обратите внимание на изменение определения платформы. Это все, что мне нужно, чтобы исправить мой вариант использования.

Я вижу, что в другом комментарии говорится об использовании .id, но это не сработало у меня, так как это будет искать только в столбце id, а не в uuid.

Другие вопросы по теме