Если вы храните чувствительные данные в базе в зашифрованном виде, рано или поздно возникает вопрос: где в Yii2 правильно делать дешифровку?
Почти все начинают одинаково:
- сначала — afterFind() в модели,
- потом — дешифровка прямо в GridView::value,
- кто-то идёт дальше и вешает Behavior.
Я прошёл все эти этапы.
Все эти подходы работают.
Но с ростом проекта начинают проявляться архитектурные и производственные проблемы.
И в итоге пришёл к выводу, что все они решают проблему не на том уровне.
В этой статье я хочу показать архитектурно корректный подход: централизованную дешифровку данных на уровне ActiveDataProvider.
Типовые решения и их ограничения
afterFind() в модели
Самый распространённый вариант — выполнять дешифровку сразу после выборки:
- просто реализовать;
- не требует изменений в UI;
- кажется “логичным”.
Но у этого подхода есть серьёзные минусы: afterFind() вызывается всегда, независимо от контекста:
- веб-интерфейс;
- API;
- консоль;
- фоновые задачи.
- модель теряет исходные (зашифрованные) данные;
- невозможно контролировать, где дешифровка действительно нужна;
- при массовых выборках дешифровка происходит даже там, где она не используется.
В результате модель начинает выполнять инфраструктурную работу, не относящуюся к бизнес-логике.
Дешифровка в GridView
Другой популярный вариант — выполнять дешифровку при рендере:
Этот подход переносит логику из модели в представление, но создаёт другую проблему.
Если таблица содержит:
- десятки строк,
- несколько колонок с чувствительными данными,
- то дешифровка будет выполняться многократно — по сути, в каждой ячейке.
Недостатки:
- большое количество повторных операций;
- логика размазана по UI;
- сложнее централизовать обработку ошибок;
- представление начинает знать о криптографии.
Behavior на модели
Использование Behavior технически возможно, но архитектурно это всё ещё уровень модели.
Минусы:
- неявное поведение;
- сложность отладки;
- отсутствие контроля контекста;
- та же проблема “всегда и везде”.
Во всех этих подходах проблема одна и та же: дешифровка размещена не на том уровне абстракции.
Где должна жить дешифровка
Дешифровка данных:
- не является бизнес-логикой;
- не относится к представлению;
- не должна быть обязанностью модели.
Это инфраструктурная задача. В Yii2 есть слой, который идеально для неё подходит — DataProvider.
Почему именно он:
- данные уже выбраны из базы;
- известен объём данных (например, текущая страница);
- есть единая точка подготовки данных;
- UI получает уже готовый результат.
Идея простая:
Модель отвечает за данные.
DataProvider — за их подготовку к отображению.
Его ответственность:
- определить, какие поля требуют дешифровки;
- собрать значения один раз;
- выполнить пакетную дешифровку;
- аккуратно подставить результат обратно в модели.
При этом:
- модель не знает о дешифровке;
- GridView не содержит никакой логики;
- криптография полностью изолирована.
Основные моменты реализации
Метод prepareModels() — это оптимальная точка для обработки данных. В этом месте данные уже выбраны и подготовлены моделью для рендеринга, что уже позволяет работать постранично, собрать значения для дешифровки, централизованно дешифровать, не вмешиваясь в бизнес-логику.
Если в модели остается старая логика дешифровки через afterFind(), мы ее отключаем, что бы избежать двойной дешифровки. Это позволяет сохранить совместимость в других местах, поведение кода останется предсказуемым.
Дешифровка производится пакетным способом, что позволяет сократить количество задержек.
Сервис возвращает ассоциативный массив вида [encrypted=>decrypted], который провайдер раскладывает обратно в модель.
Интеграция провайдера выполняется так же как и в исходном виде:
После этого:
1). модели остаются “чистыми” и не содержат криптологики;
2). GridView отображает уже подготовленные значения;
3). разработки в интерфейсе не требуют дополнительных обработчиков и callback’ов.



