Недавно я создал проект, в котором использую api-платформу в сочетании с ramsey/uuid-doctrine в соответствии с https://api-platform.com/docs/core/identifiers/.
Все базовые функции CRUD работают, но ApiFilter ведет себя неожиданно.
Основная настройка такова: у меня есть объекты платформы, которые, в свою очередь, содержат организации, обе ретранслирующие абстрактный класс.
Абстрактный
тогда можно было бы предположить, что api/organisations?platform=ba8d9958-d8d5-11e8-a9a5-5254007d3b24 дает все организации, принадлежащие этой платформе, но нет. Это дает
Или просто нулевая организация. Теперь в целях поиска ошибок я собираю все заново, но теперь использую дополнительные идентификаторы, и тогда все работает как шарм. Итак, проблема, похоже, в том, как работает 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/platforms/13be1406-d9c3-11e8-9d84-5254007d3b24, мы получим хороший ответ этого объекта платформы. Итак, анализ Ramsy\Uuid в доктрине, похоже, работает, ну, окей…. А что тогда насчет этого поля id? Если мы просто возьмем базу данных и выполним простое соединение. Мы можем присоединиться к организации и идентификатору, поэтому поля «платформа.id» и «организация__organistation.platform_id» содержат одно и то же значение. А потом самое интересное….
.../api/organisations?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/organisations?platform=/api/platforms/ba8d9958-d8d5-11e8-a9a5-5254007d3b24. Doctrine не выполняет внутреннее соединение и не может правильно декодировать UUID, но когда мы делаем api/organisations?platform.id=ba8d9958-d8d5-11e8-a9a5-5254007d3b24. Платформа API вызывает внутреннее соединение, а затем оно внезапно работает...
Хорошо, наша проблема теперь решена, но я все еще думаю, что это что-то из платформы API (которая поддерживает Ramsy\Uuid ) или Ramsy\Uuid, возможно, захочет изучить
Недавно я создал проект, в котором использую api-платформу в сочетании с ramsey/uuid-doctrine в соответствии с https://api-platform.com/docs/core/identifiers/. Все базовые функции CRUD работают, но ApiFilter ведет себя неожиданно. Основная настройка такова: у меня есть объекты платформы, которые, в свою очередь, содержат организации, обе ретранслирующие абстрактный класс. Абстрактный [code]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 which 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;
Или просто нулевая организация. Теперь в целях поиска ошибок я собираю все заново, но теперь использую дополнительные идентификаторы, и тогда все работает как шарм. Итак, проблема, похоже, в том, как работает ApiFilter. Это предполагает, что ApiFilter на самом деле не нормализует UUID перед использованием его в поиске... -- Обновление -- Хорошо, поэтому я попытался обойти это с помощью специального фильтра [code]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
// 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; } } [/code] Но затем я столкнулся с доктринальной ошибкой «[Семантическая ошибка] строка 0, столбец 76 рядом с 'platform =:platform_p1': Ошибка: 'платформа' не определена». > Это, конечно, предполагает, что платформа не определена, но, насколько я вижу, так и есть. --- ОБНОВЛЕНИЕ: проблема решена! --- [code]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"] [] [/code] Итак, здесь доктрина, очевидно, осознает, что ищет Ramsy\Uuid вместо «13be1406-d9c3-11e8-9d84-5254007d3b2» напрямую. Хорошо, а что, если мы будем искать без IRI платформы? Это дает хороший ответ из 1 объекта и следующего журнала доктрины [code][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"] [] [/code] Таким образом, разница действительно заключается в Ramsy\Uuid, особенность здесь в том, что если мы получим .. /api/platforms/13be1406-d9c3-11e8-9d84-5254007d3b24, мы получим хороший ответ этого объекта платформы. Итак, анализ Ramsy\Uuid в доктрине, похоже, работает, ну, окей…. А что тогда насчет этого поля id? Если мы просто возьмем базу данных и выполним простое соединение. Мы можем присоединиться к организации и идентификатору, поэтому поля «платформа.id» и «организация__organistation.platform_id» содержат одно и то же значение. А потом самое интересное…. .../api/organisations?platform.id=13be1406-d9c3-11e8-9d84-5254007d3b24 Работает! Так что это решает нашу проблему «да», просто неожиданным образом. Он предоставил нам журнал доктрин: [code][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\")"] [] [/code] Подождите, что? Ага. Если мы создадим файл api/organisations?platform=/api/platforms/ba8d9958-d8d5-11e8-a9a5-5254007d3b24. Doctrine не выполняет внутреннее соединение и не может правильно декодировать UUID, но когда мы делаем api/organisations?platform.id=ba8d9958-d8d5-11e8-a9a5-5254007d3b24. Платформа API вызывает внутреннее соединение, а затем оно внезапно работает... Хорошо, наша проблема теперь решена, но я все еще думаю, что это что-то из платформы API (которая поддерживает Ramsy\Uuid ) или Ramsy\Uuid, возможно, захочет изучить