Переход с MySQL на PostgreSQL в проекте Symfony2 (ShareOBJ.com)

postgresql-600x600Статья написана для Symfony2 разработчика или новичка. Проектирование базы в Doctrine и ошибки, которых можно было избежать.
Для тех, кто только начал проектировать базу или только наполняет свои Repository классы методами с запросами. Пожалуйста, используйте DQL. Вам самим будет потом легче.
Не верьте, что та база, с которой вы сейчас работаете, будет вечной, всегда в тренде и всегда её мощностей хватит. Рано или поздно придётся перебираться на более производительные решения.

Использовать какие-то специальные фишечки конкретной базы данных, прямо в своём классе, ради прироста производительности – это шаг не самый правильный. Правильнее написать отдельный bundle для конкретной фишечки и подключать её отдельно, не меняя самого кода репозиторий класса.

Представьте, что на всех своих новых проектах, я могу спокойно изменить в parameters.yml параметры соединения с базой такие как pdo_driver и иже с ними.
Понимаете? В одном файле. Я меняю pdo_mysql на pdo_pgsql и производительность взлетает из-за того, что PostgreSQL работает быстрее чем MySQL. Для языка DQL не приниципально какая именно база крутится, запросы будут транслироваться в те, которые нужны.

Это на новых проектах так, но один из моих старых проектов, начинался на заре моего изучения Symfony2 и Doctrine, потому я грешил добавлением вставок, которые понимает только MySQL и теперь с большим скрипом и болью я переезжал с MySQL базы на PostgreSQL. Возможно, даже не полностью переехал, так как этот проект не покрыт полностью тестами из-за спешки, и что где упало, я увижу позже.

Какие именно я допускал ошибки?

Неправильно:
$qb->andWhere('o.isPublic = 1');

Правильно:
$qb->andWhere($qb->expr()->eq('o.isPublic',$qb->expr()->literal(true)));

Разумеется это не аналогичные запросы. Логика в них разная. Но сама суть в том, чтобы использовать Expressions и саму библиотеку Doctrine, а не пытаться писать глубокие вставки с большим количеством AND в одну строчку. И даже такие казалось бы маленькие и локаничные вещи выглядят уродливо, но сберегут уйму времени в случае переноса проекта на другие рельсы.

Вот ещё кусочек запроса поиска объекта в радиусе (ромбе) действия поиска из “было” и “стало”. Запрос на самом деле больше, а это одна из частей:

Неправильно:
$qb->andWhere('o.lat > :downO AND o.lat < :upO AND o.lon > :leftO AND o.lon < :rightO')
->setParameter('downO', floatval($outerBounds['downLat']))->setParameter('upO', floatval($outerBounds['upLat']))
->setParameter('leftO', floatval($outerBounds['leftLng']))->setParameter('rightO', floatval($outerBounds['rightLng']));

Правильно:
$qb->andWhere($qb->expr()->gt('o.lat', ':downO'))
->setParameter('downO', floatval($outerBounds['downLat']));
$qb->andWhere($qb->expr()->lt('o.lat', ':upO'))
->setParameter('upO', floatval($outerBounds['upLat']));
$qb->andWhere($qb->expr()->gt('o.lon', ':leftO'))
->setParameter('leftO', floatval($outerBounds['leftLng']));
$qb->andWhere($qb->expr()->lt('o.lon', ':rightO'))
->setParameter('rightO', floatval($outerBounds['rightLng']));

Если когда-нибудь, Ваш PM(Project Manager) вам скажет: “делай скорее, плевать на тесты, скорей бы закончить и сдать” – знайте, это не настоящий PM, это бодишопер однодневка, который не собирается брать этот проект на поддержку. Он не будет отвечать за код и не будет его никогда поддерживать. В случае чего это будете делать Вы или тот, кому останется эдакое наследство.

Потому программисту нужно взять за правило: покрывать всё тестами и писать запросы в базу языком DQL. И мегаопытных распильщиков, которым плевать на имидж фирмы, нужно люстрировать перед более высоким начальством и рассказывать о реальных последствиях идущих далеко вперёд.

Для user stories или так называемых тестов, я рекомендую использовать Behat (в PHP он добрался в таком виде из Ruby on Rails. Там он называется Cucumber).
А DQL вы уже знаете, если Вы Symfony2 разработчик.