В MODX предусмотрены 2 основных способа работы с базой данных — через xPDO и через PDO. По первому способу информации достаточно много. Как правило, разработчики работают с БД именно через xPDO. Но бывают такие случаи, когда нужно использовать PDO. Например, быстро добавить какое-нибудь значение в свою таблицу или написать сложный запрос. В этом случае разработчик должен понимать, что он делает, потому что в связи между таблицами, в отличие от xPDO, не работают и могут возникнуть проблемы со ссылочной целостностью.

При работе с xPDO используются классы и схемы, в которых указаны типы данных, их связи. Поэтому, если вы хотите обратиться к пользовательским классам, вам предварительно нужно загрузить модель данных и только потом строить запросы. А в случае с PDO, вы можете написать обычный SQL-запрос и отправить его на сервер.

Основные методы PDO — xPDO::query($sql), xPDO::exec($sql) и xPDO::prepare()/PDOStatement::execute(). По ним и пробежимся.

xPDO::exec($sql)

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

// Удаляем все неактивные записи из произвольной таблицы
$sql = "DELETE FROM myTable WHERE active = 0";
$count = $modx->exec($sql);
// Получим количество удаленных записей
print("Удалено $count записей.");

Важно запомнить, что этот метод возвращает только количество строк. Т.е. оператор SELECT в данном случае используется, как если бы запрос был SELECT COUNT(*).

$sql = "SELECT * FROM modx_users WHERE sudo = 1";
$count = $modx->exec($sql);
// Получим количество администраторов с правами SUDO
print("Количество пользователей с неограниченными правами - $count.");

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

xPDO::query($sql)

Этот метод выполняет SQL запрос и возвращает результирующий набор в виде объекта PDOStatement. Опять же, данные должны быть уже подготовлены и экранированы. Иначе можно пропустить SQL инъекцию.

PDO::query() возвращает объект PDOStatement или FALSE, если запрос выполнить не удалось.

$sql = "SELECT * FROM modx_users WHERE active = 1";
// Вариант 1.
$statement = $modx->query($sql);
$users = $statement->fetchAll(PDO::FETCH_ASSOC);
foreach ($users as $user) {
    print $user['username'] .'<br/>';
}
// Вариант 2. Если не нужно зачитывать данные в массив
foreach ($modx->query($sql) as $user) {
    print $user['username'] .'<br/>';
}

Узнать количество записей, задействованных в последнем запросе, можно с помощью специального метода PDOStatement::rowCount().

$count = $statement->rowCount();

Если в запросе используются переменные или запрос планируется использовать несколько раз, то для этих случаев подойдут подготовленные выражения (prepared statements).

xPDO::prepare($sql) и PDOStatement::execute()

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

  • Не нужно подготавливать запрос каждый раз. Немного повышает производительность системы.
  • Запрос не нужно экранировать, драйвер БД может делает это автоматически — что значительно снижает риск получить SQL инъекцию.

PDO поддерживает 2 вида плейсхолдеров — позиционные (?), для которых важен порядок передаваемых переменных, и именованные (:name), для которых порядок не важен.

// 1. Позиционный плейсхолдер
$sql = 'SELECT name FROM modx_users WHERE email = ?';
// Подготавливаем шаблон SQL запроса
$statement = $modx->prepare($sql);
// Выполняем запрос подставляя данные
if ( $statement->execute(array($_GET['email'])) ) {
    $result = $statement->fetchAll(PDO::FETCH_ASSOC);
}

// 2. Именованный плейсхолдер
$sql = 'SELECT name FROM modx_users WHERE email = :email';
// Подготавливаем шаблон SQL запроса
$statement = $modx->prepare($sql);
// Выполняем запрос подставляя данные
if ( $statement->execute(array('email'=>$_GET['email'])) ) {
    $result = $statement->fetchAll(PDO::FETCH_ASSOC);
}

При таком подходе невозможно подсунуть SQL инъекцию, даже если подставлять необработанные данные (см. пример). Кроме того, подготовив один раз запрос, можно несколько раз выполнить метод execute() подставляя разные значения.

Ещё передать данные в подготовленный запрос можно используя специальные методы PDOStatement::bindParam() и PDOStatement::bindValue(). Подробнее про них можно прочитать в официальной документации.

Важно понимать, что работая с БД через PDO ответственность за корректность SQL запроса, ссылочную целостность данных и безопасность ложится целиком на программиста. Поэтому я советую использовать возможности xPDO.
04 февраля 2016, 10:10   7115     6

Комментарии ()

  1. Василий Столейков 11 февраля 2016, 10:00 # 0
    А можно пример с PDOStatement::rowCount()?
    1. Сергей Шлоков 11 февраля 2016, 10:13 # +1
      $sql = "SELECT * FROM modx_users WHERE active = 1";
      $statement = $modx->query($sql);
      print 'Количество записей - '.$statement->rowCount();
      
      1. Василий Столейков 11 февраля 2016, 10:22 # 0
        Спасибо, помогло!
    2. Андрей 20 декабря 2017, 17:57 # 0
      Добрый день.
      Помогите сформировать запрос такого характера:
      1. У меня есть таблица с некими записями, в которой есть поля: id (AI), vid, pid, aid
      2. Мне нужно получить следующую запрись из БД, зная о текущей все (id, vid, pid, aid), но чтобы у следующей записи последние 3 параметра (vid, pid, aid) были такие же, как у текущей.
      3. Сложность для меня в том, что это не обязательно может быть соседний id, может он вообще будет через десяток записей.
      Прошу помощи с формированием запроса))
      1. Сергей Шлоков 20 декабря 2017, 19:33 # 0
        Давайте размышлять последовательно: вам нужны записи, у которых должны быть определённые параметры. Для этого нужно использовать WHERE с соответствующими равенствами.
        Далее, нужна одна из следующих по порядку записей, а не предыдущая. Т.е. у неё id должен быть больше id текущей. Значит в WHERE нужно добавить условие неравенства.
        Ну и наконец, запись нужна только одна. Поэтому нужно ограничить запрос через LIMIT. И обязательно нужно удостовериться, что это именно следующая запись, а не вторая или третья по очереди. Для чего нужно сделать соответствующую сортировку ORDER BY.
        Осталось всё это совместить.
        А следом попробовать сформировать этот запрос через xPDO.
        1. Андрей 20 декабря 2017, 19:49 # 0
          Все, сделал. Спасибо за подсказку!

      Вы должны авторизоваться, чтобы оставлять комментарии.

      Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.