Jii: Повноцінний Query Builder для Node.js з API від Yii 2

Вступ
Привіт всім хабровчанам, любителям Yii і Node.js. Чому об'єднані любителі PHP-фреймворку і серверного JavaScript?
Тому що Yii тепер доступний і на JavaScript (як для Node.js, так і для браузера)!

У цій статті ми розглянемо Query Builder, який повністю зберіг API від Yii2 і працює на Node.js.
Конструктор запитів — це лише одна з реалізованих частин Jii (не плутати з Yii), в даній статті я спеціально не буду розглядати фреймворк в цілому, тому що його цілком можна використовувати і частинами.

JiiЩо таке Jii?
Jii — це компонентний JavaScript MVC фреймворк, який повторює архітектурні рішення легендарного PHP-фреймворку Yii 2, у більшості випадків зберігаючи його API. Звідси походження назви Jii — JavaScript Yii.

Установка
Jii і його частини поширюються як пакети менеджера npm. Встановити його можна однією командою:

npm install jii jii-model jii-ar-sql

Після установки вам доступний простір імен і клас Jii, це єдина вхідна точка доступу до всіх класів Jii.
Пакет jii — це базовий пакет jii, який як раз оголошує простір імен і клас Jii і повертає його. Всі інші частини Jii (в тому числі jii-ar-sql) ставляться теж як пакети, але вони лише наповнюють базовий неймспейс.

СпойлерЯк ви могли помітити, в назві пакету присутній літери ar. Так, це Active Record з API від Yii2, він уже написаний і покритий купою тестів. Його я буду описувати в наступних статтях. А поки ми розглянемо тільки його Query Builder.

Всі приклади, які будуть описані нижче передбачають наявність встановленого MySQL, Node.js і приблизно такого коду:

var Jii = require('jii-ar-sql');

var db = new Jii.sql.mysql.Connection({
host: '127.0.0.1',
database: 'example',
username: 'root',
password: ",
charset: 'utf8'
});

db.open().then(function() {

(new Jii.sql.Query())
.from('user')
.where({last_name: 'Smith'})
.count('*', db)
.then(function(count) {

console.log('Records count:', count);
db.close();

});

});

Об'єкти доступу до бази даних
Ці об'єкти реалізують інтерфейс, через який можна відправляти запити до бази даних і отримувати відповіді в певному форматі. Вони використовуються конструкторами запитів і Active Record.

Кожен з об'єктів доступу до даних звертається до СУБД через драйвера, які для кожної бази свої. Всі вони реалізують єдиний API, що дозволяє змінити СУБД.

На даний момент реалізовано об'єкт доступу до MySQL, який використовує пакет-драйвер
npm mysql. Підтримка інших СУБД передбачена і планується в майбутньому.

Створення з'єднання з БД

Для доступу до бази даних необхідно створити екземпляр з'єднання Jii.sql.Connection. Потім необхідно відкрити з'єднання для завантаження схеми БД і встановлення постійного з'єднання.

var db = new Jii.sql.mysql.Connection({
host: '127.0.0.1',
database: 'example',
username: 'root',
password: ",
charset: 'utf8'
});
db.open().then(function() {
// ...
});

Якщо ви створюєте додаток Jii, то зручніше це з'єднання прописати в конфігурації програми як компонент додатка доступний через `Jii.app.db`.

module.exports = {
// ...
components: {
// ...
db: {
className: 'Jii.sql.mysql.Connection',
host: '127.0.0.1',
database: 'example',
username: 'root',
password: ",
charset: 'utf8',
}
},
// ...
};

Виконання SQL запитів
Коли у вас є примірник підключення до бази даних, ви можете виконувати SQL запит, виконавши наступні дії:
  1. Створити екземпляр Jii.sql.Command з звичайним SQL;
  2. Додати параметри запит, якщо необхідно;
  3. Викликати один з методів Jii.sql.Command.
Розглянемо кілька прикладів вибірки даних з БД:

var db = new Jii.sql.mysql.Connection(...);
db.open().then(function() {

// Повертає масив об'єктів, кожен з об'єктів є запис у таблиці,
// де ключі об'єкта - це назви стовпців, а зачения - їх відповідні значення
// рядку таблиці. При порожньому відповіді буде повернено порожній масив.
db.createCommand('SELECT * FROM post')
.queryAll()
.then(function(posts) {
});

// Повертає об'єкт, відповідному рядку таблиці (першої в результатах)
// Поверне `null` при порожньому результаті
db.createCommand('SELECT * FROM post WHERE id=1')
.queryOne()
.then(function(post) {
});

// Повертає масив, відповідній колонці таблиці (першої в результатах)
// Поверне порожній масив при порожньому результаті
db.createCommand('SELECT title FROM post')
.queryColumn()
.then(function(titles) {
});

// Повертає скаляр. `null` при порожньому результаті
db.createCommand('SELECT COUNT(*) FROM post')
.queryScalar()
.then(function(count) {
});

});

Додавання параметрів
При створенні команди з параметрами, ви повинні завжди додавати параметри через виклики методів `bindValue` або `bindValues` для запобігання атак SQL-ін'єкції. Наприклад:

db.createCommand('SELECT * FROM post WHERE id=:id AND status=:status')
.bindValue(':id', request.id)
.bindValue(':status', 1)
.queryOne()
.then(function(post) {
});

Виконання запитів не на вибірку даних
Запити на зміну даних необхідно виконувати за допомогою методу `execute()`:

db.createCommand('UPDATE post SET status=1 WHERE id=1')
.execute();

Метод Jii.sql.Command.execute() повертає об'єкт, в якому міститься інформація з результатом запитів. Кожен з об'єктів доступу може додавати в нього свої специфічні параметри, але мінімальний набір параметрів у ньому такий:
  • `affectedRows` — Кількість порушених (змінених) рядків
  • `insertId` — унікальний згенерований ідентифікатор. Повертається для запитів INSERT при наявності у колонці PK з AUTO_INCREMENT.
Для INSERT, UPDATE і DELETE запитів, замість того, щоб писати звичайні запити SQL, ви можете викликати
Jii.sql.Command.insert(), Jii.sql.Command.update(), Jii.sql.Command.delete() методи для створення
відповідних SQL. Ці методи будуть правильно екранувати імена таблиць, стовпців і значення параметрів.

// INSERT (table name, column values)
db.createCommand().insert('user', {
name: 'Sam',
age: 30
}).execute().then(function(result) {
// result.affectedRows
// result.insertId
});

// UPDATE (table name, column values, condition)
db.createCommand().update('user', {status: 1}, 'age > 30').execute();

// DELETE (table name, condition)
db.createCommand().delete('user', 'status = 0').execute();

Ви можете так само викликати Jii.sql.Command.batchInsert() для вставки декількох рядків в один запит, це буде більш
ефективно з точки зору продуктивності:

// table name, column names, column values
db.createCommand().batchInsert('user', ['name', 'age'], {
['Tom', 30],
['Jane', 20],
['Linda', 25],
}).execute();

Зміна схеми бази даних
Jii DAO надає набір методів для зміни схеми бази даних:
  • Jii.sql.Command.createTable(): створення таблиці
  • Jii.sql.Command.renameTable(): перейменування таблиці
  • Jii.sql.Command.dropTable(): видалення таблиці
  • Jii.sql.Command.truncateTable(): видалення всіх рядків з табилцы
  • Jii.sql.Command.addColumn(): додавання колонки
  • Jii.sql.Command.renameColumn(): перейменування колонки
  • Jii.sql.Command.dropColumn(): видалення колонки
  • Jii.sql.Command.alterColumn(): зміна колонки
  • Jii.sql.Command.addPrimaryKey(): додавання первинного ключа
  • Jii.sql.Command.dropPrimaryKey(): видалення первинного ключа
  • Jii.sql.Command.addForeignKey(): додавання зовнішнього ключі
  • Jii.sql.Command.dropForeignKey(): видалення зовнішнього ключа
  • Jii.sql.Command.createIndex(): створення індексу
  • Jii.sql.Command.dropIndex(): видалення індексу
Приклад використання цих методів:

// CREATE TABLE
db.createCommand().createTable('post', {
id: 'pk',
title: 'string',
text: 'text'
});

Ви також можете отримати інформацію про таблиці через метод Jii.sql.Connection.getTableSchema()

table = db.getTableSchema('post');

Метод повертає об'єкт Jii.sql.TableSchema, який містить інформацію про стовпцях таблиці, первинні ключі, зовнішні ключі, і т. д. Всі ці дані використовуються головним чином в конструкторі запитів і Active Record для спрощення роботи з БД.

Конструктор запитів


Конструктор запитів використовує об'єкти доступу до бази даних (Database Access Objects), що дозволяє будувати SQL запити на мові JavaScript. Конструктор запитів покращує читаність SQL-коду і дозволяє генерувати більш безпечні запити в БД.

Використання конструктора запитів ділиться на 2 етапи:
  1. Створення екземпляра класу Jii.sql.Query для представлення різних частин SQL виразу (наприклад, `SELECT`, `FROM`).
  2. Виклик методів (наприклад, `all()`) у эклемпляра Jii.sql.Query для виконання запиту до бази даних і асинхронного отримання даних.
Наступний код показує найпростіший спосіб використання конструктора запитів:

(new Jii.sql.Query())
.select(['id', 'email'])
.from('user')
.where({last_name: 'Smith'})
.limit(10)
.all()
.then(function(rows) {
// ...
});

Наведений вище код згенерує і виконає наступний SQL-код, у якому параметр `:last_name` пов'язаний зі значенням `Smith".

SELECT `id`, `email`
FROM `user`
WHERE `last_name` = :last_name
LIMIT 10

Створення запитів
Для побудови запиту необхідно викликати різні методи об'єкта Jii.sql.Query, наповнюючи тим самим різні частини SQL команди. Імена методів схожі з назвами SQL операторів. Наприклад, щоб вказати `FROM`, необхідно викликати метод `from()`. Всі методи повертають сам об'єкт запиту, що дозволяє об'єднувати декілька викликів разом.

Далі ми опишемо використання кожного методу конструктора запитів.

Jii.sql.Query.select()
Метод Jii.sql.Query.select() метод визначає частину `SELECT` SQL-запиту. Ви можете вказати стовпці, які
будуть обрані.

query.select(['id', 'email']);

// еквівалентно:

query.select('id, email');

Імена стовпців можуть включати в себе назви таблиць та/або псевдоніми стовпців.
Наприклад,

query.select(['user.id AS user_id', 'email']);

// еквівалентно:

query.select('user.id AS user_id, email');

Ви можете передаеть об'єкт, де ключами будуть псевдоніми стовпців.
Наприклад, наведений вище код можна переписати наступним чином:

query.select({user_id: 'user.id', email: 'email'});

За замовчуванням (навіть якщо не викликати метод Jii.sql.Query.select()), у запиті буде згенерована зірочка `*`
для вибору всіх стовпців.

Крім імен стовпців, ви можете також вказувати SQL виразу. Наприклад:

query.select(["CONCAT(first_name, ' ', last_name) AS full_name", 'email']);

Так само підтримуються під-запити, для цього необхідно передати об'єкт Jii.sql.Query як один з елементів вибірки.

var subQuery = (new Jii.sql.Query()).select('COUNT(*)').from('user');

// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`
var query = (new Jii.sql.Query()).select({id: 'id', count: subQuery}).from('post');

Для додавання слова `DISTINCT" в SQL запит, необхідно викликати метод Jii.sql.Query.distinct():

// SELECT DISTINCT `user_id` ...
query.select('user_id').distinct();

Ви так само можете викликати метод Jii.sql.Query.addSelect() для додавання додаткових колонок.

query.select(['id', 'username'])
.addSelect(['email']);

Jii.sql.Query.from()
Метод Jii.sql.Query.from() наповнює фрагмент `FROM` з SQL запиту. Наприклад:

// SELECT * FROM `user`
query.from('user');

Імена таблиць можуть містити префікси та/або псевдоніми таблиць. Наприклад:

query.from(['public.user u', 'public.post p']);

// еквівалентно:

query.from('public.user u, public.post p');

При передачі об'єкта, ключі об'єкта будуть псевдонімами таблиць.

query.from({u: 'public.user', p: 'public.post'});

Крім того, імена таблиць можуть містити підпорядкований — об'єкти Jii.sql.Query.

var subQuery = (new Jii.sql.Query()).select('id').from('user').where('status=1');

// SELECT * FROM (SELECT `id` FROM `user` WHERE status=1) u
query.from({u: subQuery});

Jii.sql.Query.where()
Метод Jii.sql.Query.where() наповнює секцію `WHERE` SQL вираженні. Ви можете використовувати кілька форматів вказівки умов SQL виразу:
  • рядок, `status=1"
  • об'єкт, `{status: 1, type: 2}`
  • з зазначенням оператора, `['like', 'name', 'test']`

Рядковий формат

Рядковий формат дуже добре підходить для вказівки простих умов. Зазначена рядок записується безпосередньо в SQL-вираз.

query.where('status=1');

// або із зазначенням параметрів
query.where('status=:status', {':status': status});

Ви можете додавати параметри до запиту через методи Jii.sql.Query.params() або Jii.sql.Query.addParams().

query.where('status=:status')
.addParams({':status': status});

Умова як об'єкт (хеш)

Об'єкт краще всього використовувати, щоб вказати кілька об'єднаних (`AND`) подусловий, кожен з яких має
просте рівність. Ключами об'єкта є стовпці, а значення — відповідні значення, які передаються умова.
Наприклад:

// ...WHERE`status` = 10) AND (`type` IS NULL) AND (`id` IN (4, 8, 15))
query.where({
status: 10,
type: null,
id: [4, 8, 15]
});

Конструктор запитів досить розумний, щоб правильно обробляти в якості значень `NULL` і масиви.

Ви також можете використовувати підпорядкований з хеш-форматі:

var userQuery = (new Jii.sql.Query()).select('id').from('user');

// ...WHERE `id` IN (SELECT `id` FROM `user`)
query.where({id: userQuery});

Формат із зазначенням оператора

Даний формат дозволяє задавати довільні умови в програмному вигляді. Загальний формат такий:

[operator, operand1, operand2, ...]

де операнди можуть бути один вказаний у форматі рядка, об'єкта або із зазначенням оператора. Оператор може бути одним
з наступних:
  • `and`: операнди повинні бути об'єднані разом, використовуючи `AND`. Наприклад, `['and', 'ID = 1', 'ID = 2']` буде генерувати `ID = 1 AND ID = 2`. Якщо операнд масиву, він буде перетворений в рядок, використовуючи правила, описані тут. Наприклад, `['and', 'type=1', ['or', 'id=1', 'id=2']]` згенерує `type=1 AND (id=1 OR id=2)`.
    Метод не виконує екранування.
  • `or`: схожий на `AND` оператора, за винятком того, що операнди з'єднуються за допомогою `OR`.
  • `between`: операнд 1 — ім'я стовпця, а операнди 2 і 3 — початкові і кінцеві значення діапазону, в яких знаходяться значення колонки.
    Наприклад, `['between', 'ID', 1, 10]` згенерує вираз `id BETWEEN 1 AND 10`.
  • `not between` так `between`, але `BETWEEN` замінюється на `NOT BETWEEN` у створеному вираженні.
  • `IN`: операнд 1 повинен бути стовпцем або SQL-вираз (expression). Другий операнд може бути або масивом або об'єктом `Jii.sql.Query`. Наприклад, `['in', 'id', [1, 2, 3]]` згенерує `id IN(1, 2, 3)`.
    Метод екранує ім'я стовпця і обробляє значення в діапазоні.
    Оператор `IN` також підтримує складові стовпці. У цьому випадку операнд 1 повинен бути масивом стовпців, в той час як операнд 2 повинен бути масивом масивів або `Jii.sql.Query` об'єктом, який представляє діапазон стовпців.
  • `NOT IN`: схожий на `IN` оператора, за винятком того, що `IN` замінюється на `NOT IN` у створеному вираженні.
  • `Like`: Перший операнд повинен бути стовпцем або SQL виразом, а операнд 2 — рядок або масив, що представляє собою значення для пошуку. Наприклад, `['like', 'name', 'tester']` згенерує `name LIKE '%tester%".
    У разі завдання масиву буде згенеровано кілька `LIKE`, об'єднані оператором `AND`. Наприклад, `['like', 'name', ['test', 'sample']]` згенерує `name LIKE '%test%' AND name LIKE '%sample%".
  • `or like`: схожий на `like`, але для об'єднання використовується оператор `OR`, коли другим операндом переданий масив.
  • `not like`: схожий на оператор `like`, за винятком того, що `LIKE` замінюється на `NOT LIKE` у створеному вираженні.
  • `or not like`: схожий на `not like`, але для об'єднання використовується оператор `OR`, коли другим операндом переданий масив.
  • `exists`: потрібно один операнд, який повинен бути екземпляром Jii.sql.Query. Згенерує вираз
    `EXISTS (sub-query)`.
  • `not exists`: схожий на оператор `exists`, генерує вираз `NOT EXISTS (sub-query)`.
  • `>`, `<=`, або будь-який інший оператор БД. Перший операнд повинен бути ім'ям стовпця, а другий — значенням. Наприклад, `['>', 'age', 10]` згенерує `age>10`.

Додавання умов

Ви можете використовувати методи Jii.sql.Query.andWhere() або Jii.sql.Query.orWhere() для додавання умов в
існуючий запит. Ви можете викликати ці методи кілька разів, наприклад:

var status = 10;
var search = 'jii';

query.where({status: status});

if (search) {
query.andWhere(['like', 'title', search]);
}

Якщо `search` не порожній, то приведений вище код сгенерирет наступний SQL-запит:

... WHERE`status` = 10) AND (`title` LIKE '%jii%')

Фільтрація умов

При будівництві `WHERE` умови, заснованого на даних користувача, як правило, необхідно ігнорувати порожні
значення. Наприклад, у пошуковій формі, яка дозволяє здійснювати пошук по імені і по електронній пошті, потрібно
ігнорувати полі, якщо в нього користувач нічого не ввів. Це можна зробити за допомогою методу
Jii.sql.Query.filterWhere():

// Дані полів username та email беруться з форми
query.filterWhere({
username: username,
email: email,
});

Відмінності між Jii.sql.Query.filterWhere() Jii.sql.Query.where() полягає в тому, що перший буде ігнорувати
порожні значення.
Значення вважається пустим, якщо це `null`, `false`, масив пустий, порожній рядок або рядок, що складається лише з прогалин.
Подібно методам Jii.sql.Query.andWhere() Jii.sql.Query.orWhere(), ви можете використовувати
Jii.sql.Query.andFilterWhere() Jii.sql.Query.orFilterWhere() для добавленя додаткових умов.

Jii.sql.Query.orderBy()

Метод Jii.sql.Query.orderBy() додає частина `ORDER BY` SQL-запиту. Наприклад:

// ... ORDER BY `id` ASC, `name` DESC
query.orderBy({
id: 'asc',
name: 'desc',
});

У наведеному вище коді, ключами об'єкта є імена стовпців, а значення — відповідного напряму сортування.

Для додавання умов сортування використовуйте метод Jii.sql.Query.addOrderBy().
Наприклад:

query.orderBy('id ASC')
.addOrderBy('name DESC');

Jii.sql.Query.groupBy()
Метод Jii.sql.Query.orderBy() додає частина `GROUP BY` SQL-запиту. Наприклад,

// ... GROUP BY `id`, `status`
query.groupBy(['id', 'status']);

Якщо `GROUP BY` включає тільки прості імена стовпців, ви можете вказати його, використовуючи рядок, як при написанні звичайного SQL. Наприклад:

query.groupBy('id status');

Ви можете використовувати метод Jii.sql.Query.addGroupBy() для додавання додаткових колонок до частини `GROUP BY`.
Наприклад:

query.groupBy(['id', 'status'])
.addGroupBy('age');

Jii.sql.Query.having()
Метод Jii.sql.Query.having() визначає частину `HAVING` SQL-вирази. Цей метод працює так само, як метод Jii.sql.Query.where(). Наприклад,

// ... HAVING `status` = 1
query.having({status: 1});

Додавайте додаткові умови з допомогою методів Jii.sql.Query.andHaving() або Jii.sql.Query.orHaving().
Наприклад:

// ... HAVING (`status` = 1) AND (`age` > 30)
query.having({status: 1})
.andHaving(['>', 'age', 30]);

Jii.sql.Query.limit() and Jii.sql.Query.offset()
Методи Jii.sql.Query.limit() Jii.sql.Query.offset() наповнюють частини `LIMIT` і `OFFSET` SQL-вирази. Наприклад:

// ... LIMIT 10 OFFSET 20
query.limit(10).offset(20);

Якщо ви передасте неправильні значення `limit` і `offset`, то вони будуть проігноровані.

Jii.sql.Query.join()
Метод Jii.sql.Query.join() наповнює частина `JOIN` SQL-вирази. Наприклад:

// ... LEFT JOIN `post` ON `post`.`user_id` = `user`.`id`
query.join('LEFT JOIN', 'post', 'post.user_id = user.id');

Метод має 4 значення:
  • `type`: тип, наприклад, `INNER JOIN", `LEFT JOIN".
  • `table`: ім'я приєднуваної таблиці.
  • `on`: (необов'язковий) умова, частина `ON` SQL-вирази. Синтаксис аналогічний методу Jii.sql.Query.where().
  • `params`: (необов'язковий), параметри умови (`ON` частини).
Ви можете використовувати наступні методи, для вказівки `INNER JOIN`, `LEFT JOIN` і `RIGHT JOIN` відповідно.
  • Jii.sql.Query.innerJoin()
  • Jii.sql.Query.leftJoin()
  • Jii.sql.Query.rightJoin()
Наприклад,

query.leftJoin('post', 'post.user_id = user.id');

Щоб приєднати декілька стовпців, необхідно викликати `join` методи кілька разів.

Крім того, ви можете приєднувати під-запити. У цьому випадку вам необхідно передати об'єкт, де ключ буде псевдонімом приєднується запиту. Наприклад:

var subQuery = (new Jii.sql.Query()).from('post');
query.leftJoin({u: subQuery}, 'u.id = author_id');

Jii.sql.Query.union()
Метод Jii.sql.Query.union() наповнює частина `UNION` SQL-запиту. Наприклад,

var query1 = (new Jii.sql.Query())
.select('id, category_id AS type, name')
.from('post')
.limit(10);

var query2 = (new Jii.sql.Query())
.select('id, type, name')
.from('user')
.limit(10);

query1.union(query2);

Ви можете викликати даний метод кілька разів для додавання декількох `UNION` фрагментів.

Методи запиту
Клас Jii.sql.Query надає цілий набір методів для різних резельтатов запиту:
  • Jii.sql.Query.all(): повертає масив об'єктів, де ключами є назви стовпців.
  • Jii.sql.Query.one(): повертає перший запит — об'єкт, відповідний знайденої рядку.
  • Jii.sql.Query.column(): повертає масив, відповідний значеннями першого стовпця результату запиту.
  • Jii.sql.Query.scalar(): повертає скалярне значення, розташоване в першій комірці результату.
  • Jii.sql.Query.exists(): повертає булевое значення, яке вказує, чи містить запит який-небудь результат.
  • Jii.sql.Query.count(): повертає кількість знайдених рядків.
  • Інші методи агрегації запит, в тому числі Jii.sql.Query.sum(q), Jii.sql.Query.average(q),
    Jii.sql.Query.max(q), Jii.sql.Query.min(q). Параметр `q` є обов'язковим для цих методів
    і може бути або іменем стовпця, або SQL виразом.
Всі ці методи повертають примірник `Promise` для обробки асинхронного відповіді

Наприклад:

// SELECT `id`, `email` FROM `user`
(new Jii.sql.Query())
.select(['id', 'email'])
.from('user')
.all().then(function(rows) {
// ...
});

// SELECT * FROM `user` WHERE `username` LIKE `%test%`
(new Jii.sql.Query())
.from('user')
.where(['like', 'username', 'test'])
.one().then(function(row) {
// ...
});

Всі ці методи приймають необов'язковий параметр `db`, представляє Jii.sql.Connection. Якщо цей параметр не
заданий, буде використовуватися компонент програми `db` для підключення до БД. Нижче ще один приклад використання `count()` методу:

// executes SQL: SELECT COUNT(*) FROM `user` WHERE `last_name`=:last_name
(new Jii.sql.Query())
.from('user')
.where({last_name: 'Smith'})
.count()
.then(function(count) {
// ...
})

Індекси в результатах запиту
Коли ви викликаєте Jii.sql.Query.all(), то він поверне масив рядків, які індексуються послідовними цілими числами. Але ви можете індексувати їх по-різному, наприклад, конкретним значенням стовпця або вираження за допомогою методу Jii.sql.Query.indexBy(), викликаного перед методом Jii.sql.Query.all(). У цьому випадку буде повернуто об'єкт.
Наприклад:

// returns {100: {id: 100 username: '...', ...}, 101: {...}, 103: {...}, ...}
var query = (new Jii.sql.Query())
.from('user')
.limit(10)
.indexBy('id')
.all();

Для вказівки складних індексів, ви можете передати в метод Jii.sql.Query.indexBy() анонімну функцію:

var query = (new Jii.sql.Query())
.from('user')
.indexBy(function (row) {
return row.id + row.username;
}).all();

Анонімна функція приймає параметр `row` яка містить дані поточного рядка і повинна повернути рядок або число, яке буде використовуватися в якості значення індексу (ключа об'єкта) для поточного рядка.

завершення


Jii — опенсорсний проект, тому я буду дуже радий, якщо хтось приєднається до розробки Jii. Пишіть на affka@affka.ru.
У Jii вже багато чого реалізовано і наступній статті я планую описати Active Record.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.