Общаемся с базой данных

16 Январь 2008 21 Comments Иван Блинков

На этот раз хочется обсудить такой одновременно важный и несложный момент в реализации работы любого интернет-проекта, как координации работы Ваших скриптов с СУБД.

Если подойти к этому вопросу "в лоб", то код станет очень неудобен как для понимания, так и для использования: код станет переполнен различными функциями соединения с БД, отправки запросов, преобразования результатов запросов в массивы PHP, подсчета строк, которые затронул запрос, а также многие и многие другие.

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

Этим способом будет являться написание класса, реализующего интерфейс между СУБД и PHP-скриптами. Для начала стоит определиться с ассортиментом функций, которые будет призван выполнять наш класс:

  • установка соединения, а также проверка успешности выполнения этого действия;
  • отправка запросов, как заданных извне так и, возможно, из какого-либо ассортимента заранее написанных запросов;
  • обработка результатов запросов, не ограничивающаяся одним SELECT, должны быть предоставлены методы обработки результатов любых видов запросов (или хотябы большинства).

Вполне очевидным является тот факт, что методы этого класса будут использоваться практически повсеместно в большинстве проектов. Вследствии чего становится нецелесообразным создание объекта нашего класса и передача его по всем функциям и методам всех скриптов, в таких случае намного предпочтительнее делать владельцем методов и переменных сам класс, а не экземпляр класса, с помощью ключевого слова static. Это позволит пользоваться услугами нашего класса из любого места кода. Приступим-с собственно к кодингу, начать стоит с заготовки пустого класса:


В зависимости от предпочитаемой Вами СУБД набор конкретных функций, используемых в реализации нашего класса, будет вариироваться. В большинстве случаев предпочитаю пользоваться PostgreSQL, на это причин у меня несколько, но это тема для отдельного разговора. Благодаря этому факту приводимый в качестве примера код будет использовать функции для работы именно с этой СУБД. Для поклонников же других этот систем вопрос в подавляющем большинстве случаев заключается лишь в замене этих функций на аналогичные из других модулей PHP, например для популярной и широкораспространенной MySQL достаточно будет всеголишь пройтись автозаменой pg_ => mysql_ и слегка подредактировать параметры некоторых функций.

Перейдем к реализации установления соединения с СУБД, не стоит ожидать увидеть здесь ничего необычного:

static function connect()  // установка соединения
{
  self::$connection=pg_pconnect("host=localhost dbname=pgsql user=pgsql password=MyPassword");
  // не забываем менять указанные данные для авторизации на правильные
  if(!isset(self::$connection))
  {
    echo "Сайт не работает по техническим причинам.Просим прощения за доставленные неудобства.";
    exit;	// ни в коем случае не выводим более информативных сообщений об ошибке, чем это
  }
}

А вот с отправкой и обработкой результатов запросов ситуация далеко не так однозначна. Помимо простой передачи самого текста запроса в СУБД, необходимо правильно определить тип запроса и в соответствии с этим обработать результат. Можно конечно попытаться сделать это автоматически на основе вытаскивания первого слова из текста запроса, но мне всетаки кажется более предпочтительным определение "вручную" желаемого вида представление результата. Выполнение произвольных запросов может выглядеть, например, следующим образом:

static function query($str,$bool=false) // произвольный запрос
{
  //echo $str.""; // очень удобно на стадии разработки в процессе поиска ошибок
  $result=@pg_query(self::$connection,$str); // @ - для сокрытия теоретически возможных ошибок
  // or die('Query failed: '.pg_last_error());
  // не забываем убирать в комментарий в финальном варианте проекта
  // или совсем удалять
  if($result)  // Если получен результат, отличный от false
  {
    if($bool)  // Если выбран результат в виде boolean
    {
      return true;
    }
    else  // Если выбран результат в виде массива
    {
      $n=pg_num_rows($result);	// для создания универсального формата массива
      if($n==1)return pg_fetch_array($result,0,PGSQL_ASSOC);
      else  // даже когда результат содержит только одну строку
      {
        $j=pg_num_rows($result);
        $list=array();
        for($i=0;$i<$j;$i++)
        $list[]=pg_fetch_array($result,$i,PGSQL_ASSOC);
        return $list;
      }
    }
  }else return false;
}

Помимо базовой отправки запросов, в некоторых случаях имеет смысл написать несколько методов, отправляющих частоиспользуемые запросы, что в некоторых случаях позволяет сократить объем и уменьшить нагроможденность кода. Хоть я и предпочитаю не пользоваться такими вещами, но привести пример такого рода метода все же стоит:

static function selectAll($table)
{ // пример метода отправки чаcтоиспользуемых запросов
  return self::query("select * from ".$table.";");
}

Если чувствуете необходимость в подобных функциях, можно написать огромное количество, все ограничивается лишь Вашим воображением и знаниями SQL.

Что ж, осталось лишь собрать весь код в единый листинг:


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

Эта статья является частью серии статей "Джентельменский набор PHP программиста", если Вам понравилась эта статья то очень вероятно, что Вам придутся по душе и остальные статьи.

Не забываем подписываться на RSS блога, а также на ленту комментариев.

21 comments

  • Над этой проблемой люди задумались давно, написана масса врапперов, позволяющих выполнять запросики в СУБД-независимом стиле. Под PHP вам будет полезно взглянуть на PEAR::MDB2.

    А теперь задумайтесь над другой проблемой.
    Какого хрена, я, как разработчик веб-приложения (а не извращенного шаблона) должен вообще думать об SQL? Меня волнуют, например блог, — статьи, комментарии, а не строки и поля базы данных. Решение в том, чтобы хранить объекты. Например, комментарий — это объект, который обладает:
    свойствами — имя автора, текст (публичные), адрес эл. почты, IP-адрес (скрытые),
    методами — публикация, скрытие, удаление,
    связями — со статьей, с пользователем, если последний также является объектом в вашей архитектуре.
    Это теория. На практике мы либо изобретаем хитрую СУБД (например, Zope Object Database), которая сразу хранит объекты, либо изобретаем прослойку, которая позволяет хранить объекты в традиционных реляционных базах. Такие прослойки получили название ORM. Советую взглянуть на SQLAlchemy. В любом случае, вы пишете код для доступа к объектам, вас не волнует как именно они хранятся. Это просто, удобно и очень приятно.

  • Да-да, и для этого аспекта веб-разработки тоже можно найти массу готовеньких решений на блюдечке с голубой каёмочкой, если есть желание — почему бы и нет? С таким же успехом можно и готовые веб-приложения найти — вариант тоже очень неплохой, особенно если задача тривиальна, и по-моему даже он не смотря на свои массу недостатков на порядок лучше использования большинства framework’ов (мое мнение именно таково). Пример такой тривиальной задачи Вы как раз и привели — блог, например этот очень неплохо себя чувствует на готовом решении под названием WordPress.

  • Давайте рассмотрим недостатки.

  • [quote comment="129"]Давайте рассмотрим недостатки.[/quote]
    Чего именно? Framework’ов? CMS? Собственноручно написанного кода?

  • 1. Использования фреймворков для создания веб-приложений. Допустим, у нас задача хитрее, чем блог, т.е. готового веб-приложения нет.
    2. Использования готовых DBA-прослоек (таких, как PEAR::MDB2) вместо самописных. Как частный случай фреймворков.

  • [quote comment="144"]1. Использования фреймворков для создания веб-приложений. Допустим, у нас задача хитрее, чем блог, т.е. готового веб-приложения нет.
    2. Использования готовых DBA-прослоек (таких, как PEAR::MDB2) вместо самописных. Как частный случай фреймворков.[/quote]
    При желании можно было бы даже целый пост (и далеко не один) написать, описывая преимущества и/или недостатки того или иного framework’a, но если говорить вкратце, то основными недостатками, которые я могу назвать (по крайней мере лично для меня):
    1. отсутствие понимания (или излишне высокие издержки его получения) и полного контроля (да и порой даже частичного) над принципами работы тех частей функционала, которые работают без моего ведома (например автоматически построенные MVC модели и прочие вещи, призванные облегчить жизнь обленившегося программиста).
    2. универсальность; по крайней мере те из framework’ов, с которыми мне доводилось сталкиваться, решали поставленную задачу более чем неэффективно именно благодаря ей.
    3. издержки связанные с обучением; я лично порой страдаю «изобретением велосипедов» именно потому, что понимая принцип работы требуемой функции порой бывает быстрее (и иногда, но не всегда, эффективнее) ее написать самому, чем напрягать лишний раз Google в попытках найти «готовенькое» или то, как этим «готовеньким» можно воспользоваться.
    4. свобода; независимость от программистов, написавших framework, и их решений относительно каких-либо вопросов, связанных с вариантами реализации чего-либо или чем-нибудь еще.

    Вот это были мои мысли насчет framework’ов в целом, не могу похвастаться богатым опытом работы с ними, так что что-то более конкретное про многие из них я сказать врядли смогу, PEAR можно по большому счету тоже отнести к сказанным выше словам, но я ими вообще предпочитаю лишний раз не пользоваться без особой необходимости (которой пока ни разу не возникало).

  • Выглядит, как будто “я не пробовал, но вроде отстой”.

  • bez:

    незнаю про ORMы все одно мнение — для реальных проектов ЭТО ВСЕ ХЛАМ) по той причине что с ними нет возможности иметь сложные взаимосвязи между данными, по сути это взаимодействие всегда сводиться к ХИТРОВЫЕ..АНОМУ запросу к БД) который никак не разложить по объектам которые каждый привязан к своей таблице и т.д. к томуже МОДУЛЬНОСТЬ — это очень узко, это подразумевает модули которые вещи сами в себе а часто необходима сложная взаимосвязь между модулями и гибкость.

  • bez:

    к томуже ORM всегда подразумевает строгую реляционную модель данных) что для высоконагруженных проектов является НЕРЕАЛЬНЫМ

  • [quote comment="154"]Выглядит, как будто “я не пробовал, но вроде отстой”.[/quote]
    Нет, выглядит как мое мнение о теме в целом, не вдаваясь в какие-либо подробности о конкретном продукте, с которым мне доводилось иметь дело, пускай и недолгое.

  • [quote comment="158"]незнаю про ORMы все одно мнение — для реальных проектов ЭТО ВСЕ ХЛАМ) по той причине что с ними нет возможности иметь сложные взаимосвязи между данными, по сути это взаимодействие всегда сводиться к ХИТРОВЫЕ..АНОМУ запросу к БД)
    который никак не разложить по объектам[/quote]
    Можно пример? А то не очень понятно о чем речь. Ваш запрос не может быть отражен в объекты(ы)? Заведите такой объект в который он может быть отражен. ORM не отрицает использование SQL запросов.
    [quote]которые каждый привязан к своей таблице[/quote]
    С чего бы ему быть привязанным? Это скорее проблемы тех решений, с которыми вы лично работали.
    [quote] и т.д. к томуже МОДУЛЬНОСТЬ — это очень узко, это подразумевает модули которые вещи сами в себе а часто необходима сложная взаимосвязь между модулями и гибкость.[/quote]
    ORM не имеет ни какого отношения к модулям.

  • Екимов Сергей:

    есть отличная библиотека DbSimple
    http://dklab.ru/lib/DbSimple

  • Fritz:

    Традиционный парадокс — квалификация и продуктивность любого программиста на PHP выше чем квалификация любого другого программиста на PHP либо группы таких программистов.

    ORM используют в массе гораздо более реальных проектов чем большинство таковых на ПХП

  • [quote comment="323"]Традиционный парадокс — квалификация и продуктивность любого программиста на PHP выше чем квалификация любого другого программиста на PHP либо группы таких программистов.[/quote]Извините, из-за крайнего недостатка запятых мне не удалось уловить мысль этого предложения :(

    А насчет остального обсуждения, я пожалуй «постою в сторонке», так как не являюсь большим любителем темы разговора…

  • Костя:

    Получил ответ от nginx:
    502 bad gateway

    После F5 страница загрузилась. Что у вас в бэкенде крутится?

  • [quote comment="389"]Получил ответ от nginx:
    502 bad gateway

    После F5 страница загрузилась. Что у вас в бэкенде крутится?[/quote]
    Shared хостинг за 150р./мес., со всеми вытекающими последствиями…

    А на нем соответственно nginx, PHP, и MySQL, а также WordPress в роли CMS.

  • Костя:

    А из-за чего в вашем случае такая ошибка могла произойти? А то я сейчас хочу перевести один сайт с apache + cgi (perl) на nginx + fastcgi, а теперь даже и не знаю стоит ли… Вдруг такие же ошибки будут иногда всплывать?

    А какая версия nginx у вас?

  • [quote comment="396"]А из-за чего в вашем случае такая ошибка могла произойти? А то я сейчас хочу перевести один сайт с apache + cgi (perl) на nginx + fastcgi, а теперь даже и не знаю стоит ли… Вдруг такие же ошибки будут иногда всплывать?

    А какая версия nginx у вас?[/quote]
    Не в версии дело, просто хостинг-провайдер размещает слишком много сайтов на одном сервере. Это, видимо, одно из следствий…

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

    Server: nginx/0.5.35

  • Привет всем!
    1. Мне кажется, что спор заварился по той причине, что до конца не было понято о чем спрорим. Спорить о применении готовых решений и реализации своего, мотивируя тем, что код лучше контролируется глупость по нескольким причинам. Одна из них потому что каждое решение в пределе приводит к абсурду: либо брать все готовое и не программировать, а заниматься настройкой и кастомизацией, либо программировать все что только можно самому (включая ОС) потому что так можно лучше разобраться. А так как разработчикам хочется быть аля изобретателями-учеными, но сильно при этом не напрягаясь, то приходиться искать золотую середину. (Как всегда ;-) ) И на мой взгляд, это единственно верное решение. Как с точки бизнеса так и с точки разработчика и получения им удовлетворения от своей работы.

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

    В защиту ORM могу привести такие продукты как Propel & Hibernate. По моему популярность последнего все говорит сама за себя.

    Так же есть альтернатива ORM модели называемая DAO (Data Access Object) и не плохая ее реализации в SqlMap framework, который является частью Prado framework.

    Всем спасибо за интересную дискуссию разгоревшуюся на не очень актуальной (для меня) теме.

  • Alex:

    Приветствую всех.

    Данная статья очень напоминает изобретение велосипеда.

    Если обратиться к классикам, то стоит взглянуть на книгу
    «Разработка Web-приложений с помощью PHP и MySQL», которую написали Л. Веллинг и Л.Томпсон (третье! издание было издано в 2005! году).
    Авторы данной книги исповедуют подход выделения кода обращения к БД
    в отдельные функции (которые можно многократно использовать и в других проектах).
    Таким образом код не захламляется ненужными повторениями и
    обладает отличной наглядностью.

    П.С. Как вы боретесь с дублированием страниц в индексе поисковых систем, если все урл сайта содержат идентификатор сессии?

  • Интересно. Значит надо какие-нибудь поправки вносить.

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>