Skip to content

groupProductDataSections: null и пустая строка в sort_order трактуются как 0, а отсутствие ключа — как «в конец» #656

Closed

Description

@biz87

Проблема

groupProductDataSections() по-разному трактует «значение не задано» в зависимости от того, каким именно способом оно не задано.

vueManager/src/utils/groupProductDataSections.js:39-40:

const sortA = Number.isFinite(Number(a.sort_order)) ? Number(a.sort_order) : Number.MAX_SAFE_INTEGER

Number() даёт:

sort_order Number() результат
0 0 в начало
null 0 в начало
'' 0 в начало
отсутствует / undefined NaN в конец
'abc' NaN в конец

То есть null и пустая строка молча превращаются в приоритет 0 и уезжают в начало списка, а отсутствующий ключ — в конец. Интуитивно все три означают одно и то же «порядок не задан», и вести себя должны одинаково.

Насколько это опасно сегодня

Не опасно. На текущем источнике данных не воспроизводится:

  • колонка объявлена как null="false" default="0" (core/components/minishop3/schema/minishop3.mysql.schema.xml:562), то есть NULL в базе быть не может;
  • ConfigService::getSections() кастует значение в int;
  • оба пути записи — сид-миграция 20251021000000_seed_page_sections.php (10/20/30/40) и ConfigService::saveSections(), где sortOrder = $index — всегда проставляют осмысленное целое.

Это спящий риск, а не активный баг.

Почему всё же стоит починить

Функция вынесена в utils/ как переиспользуемая и не содержит никаких предположений об источнике данных. Первый же вызов с другим источником — ответ стороннего аддона, кэш, ручной ввод, будущий эндпоинт, отдающий null вместо 0 — даст молча неверный порядок секций без единой ошибки в консоли.

Цена починки сейчас — одна строка и один тест. Цена диагностики потом — как обычно у таких багов, несоизмеримо больше: порядок «просто неправильный», исключений нет.

Предлагаемое решение

Считать «не заданным» всё, что не является конечным числом, включая null и пустую строку:

const toSort = v => (v === null || v === '' || v === undefined || !Number.isFinite(Number(v)))
  ? Number.MAX_SAFE_INTEGER
  : Number(v)

Важно сохранить текущее корректное поведение для 0: автор верно использовал Number.isFinite, а не truthy-проверку, поэтому sort_order: 0 сейчас обрабатывается как валидное значение — эту деталь при правке не потерять.

Критерии приёмки

  • null, '' и отсутствующий ключ дают одинаковый результат
  • sort_order: 0 по-прежнему трактуется как валидное значение и идёт в начало
  • Тай-брейк при одинаковом sort_order по-прежнему стабилен (по числовому id, затем по строке)
  • Тесты на все перечисленные случаи, включая тай-брейк — сейчас он единственная нетривиальная ветка функции без явного покрытия

Контекст

Найдено при ревью PR #620 (issue #611). Сам PR корректен и уже влит — эта находка касается граничных случаев, которые на текущих данных не возникают.

Activity

  1. added
    priority: lowНизкий приоритет, когда будет время
    javascriptPull requests that update javascript code
    tech-debtMaintainability / refactor / architecture debt
    on Sep 7, 2026
  2. self-assigned this
    on Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

javascriptPull requests that update javascript codepriority: lowНизкий приоритет, когда будет времяtech-debtMaintainability / refactor / architecture debt

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions