Skip to content

onerror/funpay

Repository files navigation

Тестовое задание. Написать функцию формирования sql-запросов (MySQL) из шаблона и значений параметров.

Места вставки значений в шаблон помечаются вопросительным знаком, после которого может следовать спецификатор преобразования. Спецификаторы: ?d - конвертация в целое число ?f - конвертация в число с плавающей точкой ?a - массив значений ?# - идентификатор или массив идентификаторов

Если спецификатор не указан, то используется тип переданного значения, но допускаются только типы string, int, float, bool (приводится к 0 или 1) и null. Параметры ?, ?d, ?f могут принимать значения null (в этом случае в шаблон вставляется NULL). Строки и идентификаторы автоматически экранируются.

Массив (параметр ?a) преобразуется либо в список значений через запятую (список), либо в пары идентификатор и значение через запятую (ассоциативный массив). Каждое значение из массива форматируется в зависимости от его типа (идентично универсальному параметру без спецификатора).

Также необходимо реализовать условные блоки, помечаемые фигурными скобками. Если внутри условного блока есть хотя бы один параметр со специальным значением, то блок не попадает в сформированный запрос. Специальное значение должно возвращаться методом skip. Нужно выбрать подходящее значение на своё усмотрение. Условные блоки не могут быть вложенными.

При ошибках в шаблонах или значениях выбрасывать исключения.

Для упрощения задачи предполагается, что в передаваемом шаблоне не будут использоваться комментарии sql.

В файле Database.php находится заготовка класса с заглушками в виде исключений. Нужно реализовать методы buildQuery и skip. В файле DatabaseTest.php находятся примеры (тесты). Тесты обязательно должны быть успешными (в противном случае код рассматриваться не будет).

Код должен работать с php 8.3.

Комментарии к решению

  1. Из задания не было ясно, могу ли я изменять существующие классы и интерфейсы и могу ли добавлять новые. Поэтому я решил, что сигнатуры существующих методов я трогать не имею права, тем более задание включало в себя только реализацию двух методов, заготовки которых уже усть, а также чтобы проходили существующие тесты. Также я решил не трогать структуру проекта и не подключать composer, потому что структура файлов в ТЗ не совсем корректна с точки зрения PSR, да и нечего подключать композером. Но я решил, что поскольку ничего не сказано про добавление новых классов, и не помешает разнести код по ним. Если в рамках ТЗ всё же принципиально ничего нового не добавлять, а именно реализовать всю логику в одном методе buildQuery, то к этому легко вернуться, сделав автоматический рефакторинг типа "inline method" несколько раз, дело нескольких минут. Просто код тогда будет неудобочитаемый и неподдерживаемый.
  2. Подключение к базе данных уже было реализовано в коде, приложенном к ТЗ через mysqli и инкапсулировано в Database, и не было сказано, что я могу это менять. Я бы не стал использовать mysqli, а перевёл решение как минимум на PDO. Также я выделил бы вопросы подключения к БД в отдельный класс. Однако, в ТЗ было сказано реализовать только ва метода, и я решил, что вопросы подключения к БД не входят в ТЗ, и я не имею права это менять в коде. А в коде идёт хардкорная инциализация свойства в конструкторе, а это плохо - коннект задан жёстко, и сейчас нужно его от объекта к объекту.
  3. Также ничего не известно про то, как внутри реализован float и что делать с null в select-запросах, какие есть требования к валидации шаблонов и нужна ли она вообще, или шаблоны у нас приходят из внутреннего источника, где они уже гарантированно валидны. Поэтому я решил, что валидацию шаблонов и значений делать не буду, и буду считать, что они всегда валидны. Также я решил, что null в select-запросах (начинающихся с ключевого слова SELECT) нужно всегда трактовать как IS NULL или IS NOT NULL, в зависимости от того, используется ли оператор = или один из операторов неравенства. В ТЗ прямо не указывается, что делать с NULL в SELECT-запросах, и этот момент я включил только для иллюстрации, потому что иначе теряется всякий смысл операции, так что SelectQueryBuilder написан для примера дальнейшего пути развития, потому что кейс в любом случае не исчерпан - SELECT может встретиться и в подзапросах, и в CTE, например. Также пока не ясно, какую номенклуатуру запросов должен обсслуживать этот парсер. Также я решил, что float в select-запросах является валидным значением, и его нужно просто вставлять в запрос как есть.
  4. Из контекста не ясно, как в проекте организовано управление зависимостями, какова практика, применяемая в проекте - используется ли какой-то фреймфорк или библиотека для управления зависимостями, DI-контейнер, или паттерн Registry, или антипаттерн Service Locator (может, там легаси на проекте), или фабрики какие-то, это непонятно. QueryBuilder сейчас у меня хардкорно создается прямо в Database, но это не совсем правильно. Правильно было бы вообще не иметь методов buildQuery и skip и инициализацию коннекта к БД в классе Database, это нарушает SOLID и другие распространённые принципы. Но с той заготовкой кода, которая имеется на руках, пожалуй, ничего лучше не придумать.
  5. Избыточные комментарии к методам, в том числе автогенерируемые PHPStorm, я удалил намеренно - сейчас это мейнстримная практика, чтобы не загромождать код, который должен быть читаем благодаря правильным сигнатурам и названиям методов. Оставил комментарии только там, где они имеют смысл и не мешают читаемости кода.

About

A test task solution for FunPay

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages