Фильтры платформы 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
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.

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