Работа с хранимыми процедурами

В данный момент, на текущем проекте, backend у нас разделен на 2 части. Это непосредственно команда backend и команда DBA, которые полностью заведуют всеми базами данных. И доступ к базе у нас осуществляется только посредством хранимых процедур.

Как раз о работе с ними я сегодня и хотел поговорить.

На самом деле это очень круто, когда есть такое разделение. Нам, на backend не приходится запариваться по поводу структуры таблиц, правильного и быстрого построения запросов, грамотно настроенных индексов и всего-всего связанного с этим. На самом деле это не значит, что знания по sql не пригодятся разработчикам на php. Как раз наоборот. Но мне, скажем прямо, очень повезло.

На самом деле с одной стороны это хорошо, но с другой… Symfony очень тесно связана с ORM, по умолчанию Doctrine. И если взять и заменить ORM на любую другую достаточно просто, то полностью выпилить ее, лишив Symfony полного контроля над базой не так то и легко.

Сущности

В случае с ORM, при выборке мы получаем просто набор объектов сущностей, со всеми необходимыми связями, с которыми так удобно работать.

В случае с хранимыми процедурами такого нет. И тут есть либо 2 пути: либо полностью отказаться от сущностей, либо маппить все данные на них вручную.

Так как по большей части, за отдачу нужных данных отвечает DBA, нам пришлось отказаться от использования сущностей. Потому что каждая хранимка отдает какой-то определенных набор данных. Например при редактировании профиля, пользователю нужны все данные. А при отображении его для на главной, нужно только 2–3 поля. Но User от этого не перестает быть другой сущностью, не перестает находится в другой таблице. Но, по итогу, мы получаем пустую сущность с парой–тройкой заполненных параметров, и для того, чтобы понять, какие именно параметры мы получили из хранимки, а какие нет, придется лезть достаточно глубоко в код. Это крайне неудобно, и это было самой веской причиной, по которой мы практически полностью отказались от сущностей. Полностью отказаться не смогли, потому что некоторые сущности нужны для других компонентов Symfony. Взять хотя бы того же User, который нужен для компонента Security

Forms и Repositories

Исходя из прошлого пункта, об отказе от сущностей, формы с хранилищами так же не представляется возможность использовать. Если с хранилищами все ясно, то от форм мы отказались по той же причине что и от сущностей. Несмотря на то, что они не требуют привязки к сущностям, а легко работают и с простыми массивами данных, для каждой хранимой процедуры на обновление потребовалась бы своя форма. Либо конфигурирование их переросло бы в такую лапшу, с которой невозможно было бы разобраться.

На смену формам и хранилищам пришли мапперы и валидаторы

Mappers и Validators

Мапперы, в нашей структуре, по своей сути и представляют несколько видоизмененные хранилища. С той лишь разницей, что работают они не с ORM, а с библиотекой для работы с хранимыми процедурами. Но если посмотреть снаружи, то ничего не изменилось – мы передаем заданные параметры и получаем набор данных из базы.

С валидаторами все несколько сложнее.

У нас нет возможности полностью провалидировать отправляемые данные, так как часть валидации (например, проверка на то, что в базе уже есть такая почта) мы выполнить не можем. То есть, конечно можем, но для этого придется увеличивать количество хранимок, да и с точки зрения построения системы это не слишком логично.

Структура библиотеки, с которой мы работаем — pgFunс, позволяет на любое исключение, полученное из хранимки создавать собственное исключение, так что процесс отлавливания ошибок состоит из 2х частей.

Первым делом отрабатывают валидаторы, и отлавливают ошибочные данные, которые можно точно идентифицировать. Например текст, вместо id, неправильная маска телефона и прочее.

if (!$validator->validate()) {
    return JsonResponse::userError($validator->getErrors());
}

Если данные валидные, то они проверяются в хранимых процедурах. Если в них что-то пошло не так, то мы получаем исключение

PgFunc\Exception\Specified

Или то, которое мы укажем.

Осталось только не забыть обернуть его в try/catch. Так как Symfony пропагандирует использование тонких контроллеров, то можно сразу весь контроллер обернуть в try/catch

try {
    // do any action
} catch (PgFuncException $exception) {
    return JsonResponse::userError($exception);
} catch (\Throwable $exception) {
    return JsonResponse::error($exception);
}

PgFunc и StoredProcedureBundle

Изначально, у нас была только библиотека, которую написал мой коллега, для работы с хранимыми процедурами в postgres. Библиотека достаточно удобная и он ее постоянно совершенствует и расширяет. А мной к ней был дописан свой бандл для более удобной работы в Symfony.

Подробнее о работе с бандлом можно ознакомиться на Github. К сожалению, не могу сказать того же о библиотеке, но как только я полностью изучу все ее нюансы, я выложу отдельный обзор на нее.

 

[Всего голосов: 1    Средний: 5/5]

Добавить комментарий

Ваш e-mail не будет опубликован.