Запросы

Idiorm предоставляет fluent interface позволяющий построение простых запросов без единого использования SQL. Если вы использовали jQuery вообще, то будете уже знакомы с концепцией fluent interface. Просто напросто это значит что вы можете сцеплять в цепочку вызов методов вместе, один после другого. Это может сделать ваш код более читабельным, нанизывая методы друг на друга, как-будто вы составляете предложение.

Все запросы в Idiorm начинаются с вызова статического метода for_table класса ORM. Это сообщает ORM какую таблицу использовать при построении запроса.

Обратите внимание, что этот метод **не* экранирует свои параметры запроса, так что имя таблицы не должно быть передано напрямую от от вводимых пользователем данных.*

Вызовы метода добавляют фильтры и ограничения в ваш запрос, нанизываясь друг на друга. Наконец, цепочка заканчивается вызовом метода find_one() или find_many(), которые выполняют запрос и возращают результат.

Начнем с простых примеров. Скажим у нас есть таблица person содержащая столбцы id (первичный ключ записи - Idiorm предполагает что столбец первчиных ключей называется id но это можно настроить, смотри ниже), name, age и gender.

Примечание по PSR-1 и стилю camelCase

Все методы, описанные в документации могут так же быть вызваны, используя стандарт PSR-1: подчеркивания (_) заменяются на стиль написания camelCase. Далее приведен пример одной цепочки запроса, конвертированной в совместимый с PSR-1 стиль.

<?php
// документация и стиль по-умолчанию
$person = ORM::for_table('person')->where('name', 'Fred Bloggs')->find_one();

// PSR-1 совместимый стиль
$person = ORM::forTable('person')->where('name', 'Fred Bloggs')->findOne();

Как вы можете заметить, любой метод может быть изменен из формата с подчеркиваниями (_) как в документации в стиль camelCase.

Одиночные записи

Любая цепочка методов, заканчивающаяся на find_one() вернет либо единичный экземпляр класса ORM представляющий ряд из базы данных по указанному запросу, или false если не было найдено удовлетворяющих запросу записей.

Чтобы найти одиночную запись, где столбец name имеет значение “Fred Bloggs”:

<?php
$person = ORM::for_table('person')->where('name', 'Fred Bloggs')->find_one();

Грубо переводя на язык SQL, это: SELECT * FROM person WHERE name = "Fred Bloggs"

Чтобы найти единичную запись по ID, вы можете передать ID прямо в метод find_one:

<?php
$person = ORM::for_table('person')->find_one(5);

Если вы используете составной первичный ключ, то можете найти записи используя массив в качестве параметра:

<?php
$person = ORM::for_table('user_role')->find_one(array(
    'user_id' => 34,
    'role_id' => 10
));

Множество записей

Любая цепочка методов, заканчивающаяся на find_many() вернет массив экземпляров ORM-класса, по одному для каждой удовлетворяющей запросу строки. Если не было найдено ни одной строки, то будет возвращен пустой массив.

Чтобы найти все записи в таблице:

<?php
$people = ORM::for_table('person')->find_many();

Чтобы найти все записи, где gender равен female:

<?php
$females = ORM::for_table('person')->where('gender', 'female')->find_many();

Как результирующий набор

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

Итак, для примера, вместо этого:

<?php
$people = ORM::for_table('person')->find_many();
foreach ($people as $person) {
    $person->age = 50;
    $person->save();
}

Вы можете использовать это:

<?php
ORM::for_table('person')->find_result_set()
->set('age', 50)
->save();

Чтобы это сделать, замените любой вызов метода find_many() методом find_result_set().

Результирующий набор ведет себя так же, как и массив, так что вы можете использовать на нем count() и foreach как и с массивом.

<?php
foreach(ORM::for_table('person')->find_result_set() as $record) {
    echo $record->name;
}
<?php
echo count(ORM::for_table('person')->find_result_set());

Как ассоциативный массив

Так же вы можете найти множество записей в виде ассоциативного массива, вместо экземпляров Idiorm. Для этого замените любой вызов метода find_many() на метод find_array().

<?php
$females = ORM::for_table('person')->where('gender', 'female')->find_array();

Это полезно, если вам нужно преобразовать результат запроса в последовательную форму записи(сериализация массива) для JSON, и вам не нужно дополнительной возможности обновлять возвращаемые данные.

Подсчет результатов

Для подсчета числа строк, возвращаемых запросом, вызовите метод count().

<?php
$number_of_people = ORM::for_table('person')->count();

Фильтрация результатов

Idiorm предоставляет семейство методов, позволяющих извлечь только те записи, которые удовлетворяют определенному условию(ям). Эти методы можно вызывать множество раз для построения запроса, а fluent interface у Idiorm позволяет строить цепочку из таких методов, для построения читабельных и простых к пониманию запросов.

Предостережения

Только подмножество доступных условий, поддерживаемых SQL доступны при использовании Idiorm. Кроме того, все пункты WHERE будут соединены с использованием AND при выполнении запроса. Поддержка OR в пунктах WHERE в настоящее время отстутствует.

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

Некоторая поддержка более сложных условий и запросов реализована в методах where_raw и raw_query (смотрите ниже). Если вы поймете, что чаще нуждаетесь в в большем функционале, нежели содержит Idiorm, то возможно пришло время рассмотреть более полнофункциональный ORM.

Равенство: where, where_equal, where_not_equal

По-умолчанию, вызывая where с двумя параметрами (название столбца и значение), они будут соединены, используя оператор равенства (=). Например, вызов where('name', 'Fred') вернет следующее: WHERE name = "Fred".

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

Метод where_not_equal добавляет пункт WHERE column != "value" к вашему запросу.

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

<?php
$people = ORM::for_table('person')
            ->where(array(
                'name' => 'Fred',
                'age' => 20
            ))
            ->find_many();

// Создаст следующий запрос SQL:
SELECT * FROM `person` WHERE `name` = "Fred" AND `age` = "20";

Короткая запись: where_id_is

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

Короткая запись: where_id_in

Этот вспомагательный метод аналогичен where_id_is, но он ожидает массив первичных ключей для выборки. Так же понимает и составной первичный ключ.

Меньше чем / больше чем: where_lt, where_gt, where_lte, where_gte

Есть четыре метода, доступные для неравенств:

  • Меньше чем (less than): $people = ORM::for_table('person')->where_lt('age', 10)->find_many();
  • Больше чем (greater than): $people = ORM::for_table('person')->where_gt('age', 5)->find_many();
  • Меньше или равен (less than or equal): $people = ORM::for_table('person')->where_lte('age', 10)->find_many();
  • Больше или равен (greater than or equal_: $people = ORM::for_table('person')->where_gte('age', 5)->find_many();

Сравнение строк: where_like и where_not_like

Для добавления пункта WHERE ... LIKE, используйте:

<?php
$people = ORM::for_table('person')->where_like('name', '%fred%')->find_many();

Аналогично и для WHERE ... NOT LIKE, используйте:

<?php
$people = ORM::for_table('person')->where_not_like('name', '%bob%')->find_many();

Множественные условия OR

Можно добавить простое условие OR в тот же пункт WHERE используя where_any_is. Если вам нужно указать множество условий, используйте массив элементов. Каждый элемент будет ассоциативным массивом, содержащим множество условий.

<?php
$people = ORM::for_table('person')
            ->where_any_is(array(
                array('name' => 'Joe', 'age' => 10),
                array('name' => 'Fred', 'age' => 20)))
            ->find_many();

// Создаст SQL запрос:
SELECT * FROM `widget` WHERE (( `name` = 'Joe' AND `age` = '10' ) OR ( `name` = 'Fred' AND `age` = '20' ));

По-умолчанию, оператор равенства используется для каждого столбца, но его можно переопределить для любого столбца, используя второй параметр:

<?php
$people = ORM::for_table('person')
            ->where_any_is(array(
                array('name' => 'Joe', 'age' => 10),
                array('name' => 'Fred', 'age' => 20)), array('age' => '>'))
            ->find_many();

// Создаст SQL запрос:
SELECT * FROM `widget` WHERE (( `name` = 'Joe' AND `age` > '10' ) OR ( `name` = 'Fred' AND `age` > '20' ));

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

<?php
$people = ORM::for_table('person')
            ->where_any_is(array(
                array('score' => '5', 'age' => 10),
                array('score' => '15', 'age' => 20)), '>')
            ->find_many();

// Создаст SQL запрос:
SELECT * FROM `widget` WHERE (( `score` > '5' AND `age` > '10' ) OR ( `score` > '15' AND `age` > '20' ));

Определение принадлежности: where_in и where_not_in

Для добавления пунктов WHERE ... IN () или WHERE ... NOT IN (), используйте методы where_in и where_not_in соответственно.

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

<?php
$people = ORM::for_table('person')->where_in('name', array('Fred', 'Joe', 'John'))->find_many();

Работа с NULL значениями: where_null и where_not_null

Для добавления пункта WHERE column IS NULL или WHERE column IS NOT NULL, используйте методы where_null и where_not_null соответственно. Оба метода принимают один параметр: название столбца для сравнения.

Необработанный WHERE

Если вам необходимо создать более сложный запрос, то можно использовать метод where_raw для указания нужного SQL-фрагмента для пункта WHERE. Данный метод принимает два аргумента: строку, добавляемую к запросу, и (опционально) массив параметров, который будет связан со строкой. Если параметры были переданы, строка должна содержать знаки вопроса (?) как плейсхолдеры, для подстановки вместо них значений из массива, а массив должен содержать значения, которые будут подставлены в строку в соответствующем порядке.

Данный метод можно использовать в цепочке методов вместе с другими методами where_* а так же с методами вроде offset, limit и order_by_*. Содержимое переданной строки будет соединено с предыдущими и последующими пунктами WHERE с AND в качестве соединения.

<?php
$people = ORM::for_table('person')
            ->where('name', 'Fred')
            ->where_raw('(`age` = ? OR `age` = ?)', array(20, 25))
            ->order_by_asc('name')
            ->find_many();

// Создаст SQL запрос:
SELECT * FROM `person` WHERE `name` = "Fred" AND (`age` = 20 OR `age` = 25) ORDER BY `name` ASC;

Обратите внимание, что этот метод поддерживает только синтакс “плейсхолдера в виде вопроса”, а НЕ синтакс “именной плейсхолдер”. Все потому, что PDO не позволяет создавать запросы, содержащие смешанные типы плейсхолдеров. Так же, необходимо убедиться в том, что число вопросов-плейсхолдеров в строке соответствует числу элементов в массиве.

Если вам нужно ещё больше гибкости, вы можете вручную указать всю строку запроса. Смотрите Необработанные запросы ниже.

Limit и offset

Обратите внимание, что эти методы **не* экранируют свои паораметры в запросе, поэтому они не должны передаваться напрямую от пользователя.*

Методы limit и offset очень похожи на эквивалентные им в SQL.

<?php
$people = ORM::for_table('person')->where('gender', 'female')->limit(5)->offset(10)->find_many();

Порядок

Обратите внимание, что эти методы **не* экранируют свои паораметры в запросе, поэтому они не должны передаваться напрямую от пользователя.*

Доступны два метода для добавления к запросу пункта ORDER BY. Это order_by_desc и order_by_asc, каждый из которых принимает название столбца для сортировки. Имена столбцов будут писаться в кавычках.

<?php
$people = ORM::for_table('person')->order_by_asc('gender')->order_by_desc('name')->find_many();

Если вы хотите упорядочить по какому-то другому признаку, отличному от названия столбца, то используйте метод order_by_expr для добавления SQL выражения без кавычек, как в пункте ORDER BY.

<?php
$people = ORM::for_table('person')->order_by_expr('SOUNDEX(`name`)')->find_many();

Группировка

Обратите внимание, что эти методы **не* экранируют свои паораметры в запросе, поэтому они не должны передаваться напрямую от пользователя.*

Для добавления пункта GROUP BY в строку запроса, вызовите метод group_by передав название столца в качестве аргумента. Можно вызывать этот метод множество раз для добавления большего числа колонок.

<?php
$people = ORM::for_table('person')->where('gender', 'female')->group_by('name')->find_many();

Так же возможно использование GROUP BY с выражениями из базы данных:

<?php
$people = ORM::for_table('person')->where('gender', 'female')->group_by_expr("FROM_UNIXTIME(`time`, '%Y-%m')")->find_many();

Having

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

HAVING работает точно таким же способом, что и все методы where* в Idiorm. Замените where_ на having_ для использования этих функций.

Например:

<?php
$people = ORM::for_table('person')->group_by('name')->having_not_like('name', '%bob%')->find_many();

Столбцы в результате

По-умолчанию, все столбцы в выражении SELECT будут возвращены после запроса. То есть, вызывая:

<?php
$people = ORM::for_table('person')->find_many();

В результате сформирует запрос:

<?php
SELECT * FROM `person`;

Метод select дает контроль над тем, какие столбцы будут возвращены. Вызовите select несколько раз для указания нужных столбцов или используйте select_many для указания нескольких столбцов за раз.

<?php
$people = ORM::for_table('person')->select('name')->select('age')->find_many();

В результате сформирует запрос:

<?php
SELECT `name`, `age` FROM `person`;

Опционально, можно передать второй аргумент в select для указания алиаса столбца:

<?php
$people = ORM::for_table('person')->select('name', 'person_name')->find_many();

В результате сформирует запрос:

<?php
SELECT `name` AS `person_name` FROM `person`;

Названия столбцов, переданные в select автоматически заносятся в кавычки, даже если содержат идентификаторы в стиле table.column:

<?php
$people = ORM::for_table('person')->select('person.name', 'person_name')->find_many();

В результате сформирует запрос:

<?php
SELECT `person`.`name` AS `person_name` FROM `person`;

Если вы хотите переопределить это поведение (например, для передачи выражения из базы данных) то вместо этого метода нужно использовать метод select_expr. Он так же принимает алиас в качестве второго аргумента. Можно указать несколько выражений, вызвав select_expr несколько раз или использовать select_many_expr для указания нескольких выражений за раз.

<?php
// Примечание: Только для иллюстрации. Для выполнения подсчется, используйте метод count().
$people_count = ORM::for_table('person')->select_expr('COUNT(*)', 'count')->find_many();

В результате сформирует запрос:

<?php
SELECT COUNT(*) AS `count` FROM `person`;

Короткая запись для указания множества столбцов

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

<?php
$people = ORM::for_table('person')->select_many('name', 'age')->find_many();

В результате сформирует запрос:

<?php
SELECT `name`, `age` FROM `person`;

Для указания алиасов, вам нужно передать массив (алиас будет называться так же, как ключ в ассоциативном массиве):

<?php
$people = ORM::for_table('person')->select_many(array('first_name' => 'name'), 'age', 'height')->find_many();

В результате сформирует запрос:

<?php
SELECT `name` AS `first_name`, `age`, `height` FROM `person`;

Вы можете использовать такой стиль при передаче аргументов в select_many и select_many_expr путем смешивания и сопоставления массивов и параметров:

<?php
select_many(array('alias' => 'column', 'column2', 'alias2' => 'column3'), 'column4', 'column5')
select_many('column', 'column2', 'column3')
select_many(array('column', 'column2', 'column3'), 'column4', 'column5')

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

<?php
$people = ORM::for_table('person')->select_many('name', 'age', 'height')->select_expr('NOW()', 'timestamp')->find_many();

В результате сформирует запрос:

<?php
SELECT `name`, `age`, `height`, NOW() AS `timestamp` FROM `person`;

DISTINCT

Для добавления ключевого слова DISTINCT перед списком результирующих столбцов в запросе, добавьте вызов метода distinct() в цепочку запроса.

<?php
$distinct_names = ORM::for_table('person')->distinct()->select('name')->find_many();

В результате сформирует запрос:

<?php
SELECT DISTINCT `name` FROM `person`;

Соединения Join

Idiorm содержит семейство методов для добавления различных типов JOIN(объединение) при построении запросов:

Методы: join, inner_join, left_outer_join, right_outer_join, full_outer_join.

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

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

<?php
$results = ORM::for_table('person')->join('person_profile', array('person.id', '=', 'person_profile.person_id'))->find_many();

Так же возможно указать условие в виде строки, которая будет добавлена в запрос как есть. Однако, в этом случае имена столбцов не будет экранированы, так что этот метод нужно использовать с осторожностью.

<?php
// Не рекомендуется, потому что условие объединения не экранируется.
$results = ORM::for_table('person')->join('person_profile', 'person.id = person_profile.person_id')->find_many();

Методы join так же принимают необязательный третий параметр, который служит как alias для таблицы в запросе. Это полезно, если нужно соединить таблицу с собой для создания иерархической структуры. В этом случае, лучше всего скомбинировать с методом table_alias, который добавит алиас к основной таблице, связанной с ORM, и методом select для управления возвращаемыми столбцами.

<?php
$results = ORM::for_table('person')
    ->table_alias('p1')
    ->select('p1.*')
    ->select('p2.name', 'parent_name')
    ->join('person', array('p1.parent', '=', 'p2.id'), 'p2')
    ->find_many();

Необработанные соединения JOIN

Если вам нужно построить более сложный запрос, то можно использовать метод raw_join для указания SQL-фрагмента для пункта JOIN. Этот метод принимает четыре обязательных аргумента: строку, добавляемую к запросу, условия в виде массива, содержащего три компонента: первый столбец, оператор, второй столбец, алиас таблицы и (необязательно) массив параметров. Если параметры переданы, строка должна содержать знаки вопроса (?) для представления подставляемых значений, и массив параметров должен создержать значения для подстановки в строку в корректной последовательности.

Этот метод может использоваться в цепочке наравне с другими методами *_join так же, как и методы вроде offset, limit и order_by_*. Содержимое переданной строки будет соединено с предшествующими и последующими пунктами JOIN.

<?php
$people = ORM::for_table('person')
            ->raw_join(
                'JOIN (SELECT * FROM role WHERE role.name = ?)',
                array('person.role_id', '=', 'role.id'),
                'role',
                array('role' => 'janitor'))
            ->order_by_asc('person.name')
            ->find_many();

// Создаст SQL запрос:
SELECT * FROM `person` JOIN (SELECT * FROM role WHERE role.name = 'janitor') `role` ON `person`.`role_id` = `role`.`id` ORDER BY `person`.`name` ASC

Обратите внимание, этот метод поддерживает только синтаксис “плейсхолдеров в виде знака вопроса”, а не синтаксис “именованных плейсхоледров”. Потмоу что PDO не позволяет создавать запросы с различным типом плейсхоледров. Так же нужно убедиться, что число знаков вопроса в строке совпадает с числом передаваемых значений в массиве.

Если вам нужно ещё больше гибкости, вы можете вручную написать весь запрос. Смотрите Необработанные запросы ниже.

Агрегатные функции

Существует поддержка MIN, AVG, MAX и SUM в дополнение к COUNT (описанно ранее).

Для получения минимального значения столбца, вызовите метод min().

<?php
$min = ORM::for_table('person')->min('height');

Остальные функции (AVG, MAX and SUM) работают таким же способом. Укажите название столбца для выполнения агрегатных функций, и в результате получите значение integer.

Необработанные запросы

Если вам нужно выполнять более сложные запросы, то можно полностью указать текст запроса, используя метод raw_query. Данный метод принимает строку и необязательный массив параметров. Строка может содержать плейсхолдеры, либо в виде знака вопроса, либо именованный плейсхолдер, который будет использоваться для внедрения параметров в запрос.

<?php
$people = ORM::for_table('person')->raw_query('SELECT p.* FROM person p JOIN role r ON p.role_id = r.id WHERE r.name = :role', array('role' => 'janitor'))->find_many();

Возвращаемый экземпляр(ы) класса ORM будет содержать данные для всех столбцов, возвращаемых запросом. Обратите внимание, что все так же нужно вызывать метод for_table для связки экземпляра с нужной таблицей, даже если нет ограничений на установку другой таблицы в запросе. Это сделано для того, чтобы при желании выполнить метод save, ORM знал какую таблицу обновлять.

Нужно так же иметь в виду, что использование raw_query требует дополнительных навыков и может быть опасным, и Idiorm не будет пытаться защитить вас от ошибок, которые могут появиться при использовании этого метода. Если вы заметите у себя частое используете raw_query, то возможно вы неправильно поняли цель использования ORM, или ваше приложение может быть слишком сложным, для использоваия Idiorm. Рассмотрите возможность использования более полнофункциональной системы абстракции базы данных (ORM).