Комментарии 67
А чем они вам мешают?
Тем, что сначала выполняется join запрос, а потом in.
В Yii 1.1 мы пытались вытащить связанные модели из одного JOIN
запроса. В большинстве случаев это работало, но не всегда. Особенно для запросов с GROUP
, HAVING
и т.д.
Код при этом был довольно сложный. Из за этого возникали непоправимые баги.
В 2.0 путём переделки запросов на WHERE IN(...)
удалось упростить код, обрабатывающий результат в разы и уйти от всех проблем, которые нас приследовали в 1.1.
Это про причины такого поведения в 2.0.
Зачем выполнять запрос два раза?
Затем, что иногда всё-таки надо применить условие по таблице из JOIN.
Неужели никто не сталкивался с ограничением длины запроса?
Нет. ActiveRecord не стоит пользоваться для импорта-экспорта. В нормальных ситуациях в WHERE IN
оказывается не более сотни ID-шников. Плюс-минус.
Почему же тогда для каждой ячейки каждой строки не выполнять отдельный запрос?
Потому что нет необходимости? :)
Так вот вопрос: WHERE IN используется в основном вместо join для того, чтобы было легче написать код билдера и отойти от некоторых багов при этом производительность примерно одинаковая (что JOIN, что WHERE IN)?
Да. Плюс можно делать аналог JOIN между двумя не связанными хранилищами. Например, MySQL и Redis.
Но остается такая задняя мысль, что по скольку нет возможности поставить внешний ключ, мы не можем гарантировать целостность базы и пользоваться фишечками типа каскад, делет и тп. На сколько это вообще оправданный функционал?
Бывает, конечно, что и кстати, но да, без внешний ключей как-то не очень.
На тему сложных отношений есть вот такое: https://github.com/yii2tech/ar-role
Павел Климов из core team сделал расширение чтобы убирать дополнительные запросы в качестве эксперимента. Вот что у этого расширения в аннотации:
3) Despite extra query removal, this extension may not actually increase overall performance. Regular Yii eager join query is very simple and fast, while this extension consumes extra memory and performs extra calculations. Thus in result performance remain almost the same. In most cases usage of this extension is a tradeoff: it reduces load on Database side, while increases it on PHP side.
Для действительно сложных запросов действительно стоит писать SQL. Но AR — штука полезная потому как большинство запросов в любом приложении очень простые.
+ Как многие утверждают: за ранее мы не можем узнать, что и где просядет при нагрузках. Тоесть подстава может быть в самом простом запросе счетчика.
Вот была одна большая неприятность у сотрудника — он из-за своей невнимательности условие запроса записал в Model::find($condition) вместо Model::find()->where($condition) и получая ActiveQuery думал, что условие запомнилось. Дальше по нему выполнялся метод delete(). И вместо того, что бы удалить несколько записей из БД (по условию) он удалил все записи… а это был продакшн и печаль получилось… Дак вот круто было, что бы из коробки Yii2 он кидать исключение о том, что в find() нет аргументов при их использовании…
Не помню как в yii, но у меня есть delete у модельки. Тут не напортачишь. А если вызывается метод массового удаления, то надо быть максимально внимательным и да, правильно говорят — не забывать тестировать.
Массовое удаление это такая вещь на которой многие спотыкались. И много думалось о том как лучше сделать.
Лично мое мнение — надо на прикладном уровне в модельке/билдере для удаления делать «корзину», а ядро не трогать, и лишних исключений не делать.
Оптимизация безопасности delete (и update к слову тоже, а то типа никто никогда не затирал важных атрибутов не тем данным) заканчивается когда делают что без условия ничего не меняется/ну удаляется (хотя на find* спокойно ставится условие вроде where 1=1). Всё остальное как правило из области «одной рукой лечим, другой калечим».
Нет, просто «Нет WHERE блока — исключительная ситуация».
Но это не так просто — взял да и запилил.
Тут нужно продумать, а где бросать исключение?
На низком уровне оно по идее и так не пропустит, скорее всего подставив дефолтное «1=1».
У меня в фреймворке для изменяющих запросов по умолчанию подставляется FALSE, что на низком уровне превращается в «1=2».
И если тут не так, то может и стоит подумать о другом дефолтном поведении на низком уровне.
Кидать исключение на высоком уровне? Разве что при выполнении/компиляции запроса. Но тут метод универсальный, и делить его на изменяющий и неизменяющий — та еще работа.
А на уровне построителя — пока на выполнение/генерацию запроса не послали, так в любой момент могут условие добавить, так что некошер. Но ок. Вставили мы куда-то исключение, всё супер. Даже не перетрудились и ничего не поломали. Результат какой? Перечитайте еще раз сообщение с которого начался этот тред — наши пляски с бубном ему бы не помогли. Ведь он потерял условие у find. А тут обязательное условие — однозначное зло ибо ломает целый вагон кейсов где оно не нужно.
Фреймворки и библиотеки должны по возможности защищать от типичных ошибок. DELETE и UPDATE без WHERE блока в 99% случаев — ошибочная ситуация. Ну на крайняк WARNING кидать неплохо бы.
http://php.net/security.magicquotes
Или register_globals
Раньше все дрочили на это.
Сейчас все проклинают.
Автоэкранирование не панацея от XSS.
Все это нужно для низкоквалифицированных разработчиков.
На самом деле, автоэкранирование в шаблонах по сути своей намного ближе к emulated prepares в pdo.
У обеих сторон есть свои за и против. Но это не имеет отношения к теме обсуждения просто потому что хорошее оно или плохое автоэкранирование в шаблонах, но цель его не в защите от дурака, а в упрощении работы программиста.
С чего бы?
Подготовленные выражения работают явно.
Мы вызываем функционал, который будет экранировать.
Да и htmlspecialchars не экранирует…
htmlspecialchars преобразует специальные символы в HTML-сущности.
«Автоэкранирование» не панацея.
А иногда оно лишнее.
Например нужно вывести строку, где должен быть кусок html, а остальные данные с базы.
Если понядеятся на «автоэкранирование», то html выведется испорченный.
Если отключить «автоэкранирование», то с базы может пролезть html.
Экранировать нужно вручную то, что нужно.
Если используется кеширование, то заэкранировать лучше один раз и сохранить в кеш.
Я шаблоны вообще не кеширую, кеширую только «контроллеры».
Часто в базе хранится уже обработанный html, или про-htmlspecialchars-енный, или про-strip_tags-енный. Повторная обработка испортит его.
А так да, для клепания говносайтов низкоквалифицированными разработчиками автоэкранирование нужно. :)
Часто в базе хранится уже обработанный html, или про-htmlspecialchars-енный, или про-strip_tags-енный. Повторная обработка испортит его.
Это зря.
обработанный html — для таких особых случаев в шаблонизаторе есть особые методы, которые позволяют выводить чистый html.
про-htmlspecialchars-енный — а зачем это хранить? Если легаси или он таким приходит, то можно использовать htmlspecialchars_decode
strip_tags — а если тексте есть строка " " (неразрывный пробел)? Без htmlspecialchars выведется просто пробел.
1) PDO prepares:
SELECT * FROM users WHERE username = :username
2) Template engine:
Username: {{ username }}
И в том, и в другом случае в результате получается выражение с корректно отформатированным строковым литералом на целевом языке — SQL и HTML соответственно.
Иногда надо и в SQL-запрос подставить SQL-выражение, и в HTML кусок HTML. Это в обоих случаях записывается особым образом.
Таких мест обычно во всех фреймворках десятки где массив и не обязателен, а подойдет и объект с ArrayAccess да править лень, ибо редко когда на что-то влияет.
Или делайте преобразование перед лоад или к issues пулреквест прикладывайте.
Лично я бы на месте мейнтейнеров yii забил бы на такой issues а пулреквест скорее всего принял бы.
Да, об этом действительно забыли, если вы про возросшую производительность и возможность её нарастить ещё :)
Или вы про то, что надо composer global update
сделать?
Позор, что их до сих пор не было.
Yii не следует SemVer? Немного странно видеть UPGRADE.md для патчей
Это не патчи. Патчи были бы 2.0.8.1.
А что тогда означают 2.0.9?
Согласно документации:
Given a version number MAJOR.MINOR.PATCH, increment the:
MAJOR version when you make incompatible API changes,
MINOR version when you add functionality in a backwards-compatible manner, and
PATCH version when you make backwards-compatible bug fixes.
Вероятно сложно съехать с уже проложенных рельс, уже распланировано что куда, созданы ветки, куча микроскопической, но работы.
Это где такое осталось? https://github.com/yiisoft/yii2/blob/master/docs/internals/versions.md
То цитата из документации SemVer.
Теперь вижу, что не следуете. Точнее следуете по шаблону 2.MAJOR.MINOR.PATCH.
Только вот в composer разве можно указать "2.0.0.*"? В случае какой-то критической ошибки в безопасности появятся версии 2.0.0.1, 2.0.1.1 и тд? Или она будет устранена в 2.0.10?
Yii 2.0.9