Користувальницькькі налаштування

Налаштування сайту


Сайдбар

Розділи

Загальний опис
Історія змін
Рекомендації до оновлення
Плани на майбутнє
Відомі проблеми
Онлайн демо
Відео
Допомога проекту
Люди
Трохи про безпеку

FAQ



Редагувати сайдбар

nyanorm

Рівень абстракції NyanORM

Навіщо це все?

Робить НЯКУ з вашою базою. І надає дуже зручні засоби, щоб ви докладали мінімум зусиль і мозкових звивин при роботі з БД Ubilling. Ви зможете описувати ваші моделі даних, і роботу з ними, більше не малюючи в ручну SELECT `something` FROM `tablename`… та інші остогидлі вам конструкції. Також NyanORM візьме на себе всю вашу рутинну роботу з попередньої підготовки і трансформації ваших даних. Завданням NyanORM не бути всеосяжним монстром, який реалізує всі можливі кейси роботи з вашими даними. Завданням NyanORM є - бути простим та зручним. З самого початку, він задумувався для того, щоб скоротити обсяги рутинного, нудного й однотипного коду, з якого складається робота із сутностями в 90-95% прикладних модулів. Власне тільки ці кейси він і повинен перекривати, не накладаючи на вас жодних додаткових обмежень.

Особливості

Чому саме NyanORM, а не будь-яка інша стороння бібліотека з приставкою ORM у назві? А з таких причин:

  • Не накладає жодних обмежень на іменування табличок БД.
  • Не диктує жодних умов щодо полів і вмісту цих табличок.
  • Дозволяє без проблем працювати з уже наявними структурами даних.
  • Дозволяє міксувати код як з його використанням так і в старому стилі.
  • Не вимагає довгого і клопіткого опису моделей, десь в окремому місці, перед тим як почати працювати.
  • Ідеальний для надшвидкого прототипування бізнес-логіки прикладних модулів.
  • Доступний у будь-який момент і завжди з будь-якого вашого модулю.
  • Написаний у наскільки це можливо мінімалістичному стилі з максимальним використанням наявних механік Ubilling.
  • Мінімізує оверхеди за пам'яттю і коллбеками. Але це не точно.
  • Дозволяє наслідуванням модифікувати як завгодно будь-яку модель і методи роботи з нею, взагалі ніяк себе не обмежуючи.
  • Не виставляє жодних вимог і засобів щодо фільтрації даних. Ви можете використовувати взагалі що захочете.
  • Працює однаково відмінно на PHP5 і PHP7 та на PHP8. Легасі так.
  • Максимально очевидний синтаксис і механіки вимагають для свого розуміння мінімального рівня IQ.
  • Зашкалюючий рівень каваю.

На жаль, ми не знайшли жодного наявного рішення, що відповідає всім особливостям/вимогам вище. Це заощадило б нам дуже багато зусиль і часу, але ні. Тому NyanORM.

Якщо вам буде так спокійніше, то ми вдумливо подивилися на те, як працюють Eloquent, Hibernate, Doctrine, Propel, RedBean… і зробили навпаки.

Це обов'язково використовувати?

Ні! Ніхто вас ні в чому не обмежує і не примушує використовувати будь-що таке специфічне у вашому новому коді, і тим паче навіть не натякає, що ви маєте переписати весь ваш старий код з використанням цього об'єкта. Можливо, вам просто сподобається, який вигляд може мати ваш код без ручного збирання запитів до БД, і з використанням високорівневої абстракції.

З чого почати?

Для прикладу, давайте розглянемо дуже типовий кейс. Нам потрібно отримати, наприклад, платежі за поточний рік, із сумою більше нуля і типом платежів “готівка”. Також ми хочемо мати їх в асоціативному масиві, де ключем буде id платежу. Як це б виглядало в нашій старій реальності:

$query = "SELECT * from `payments` WHERE `summ`>'0' AND `date` LIKE '" . curyear() . "-%' AND `cashtypeid`='1'";
$all = simple_queryall($query);
$rawPayments = array();
if (!empty($all)) {
    foreach ($all as $io => $each) {
        $rawPayments[$each['id']] = $each;
    }
}
 
debarr($rawPayments);

Правда знайома конструкція? Остогидло, правда ж? А тепер давайте теж саме, але красиво. Для початку нам потрібна модель для таблички payments. Для цього ми або успадковуємо базову модель, використовуючи ім'я таблички як ім'я класу:

class payments extends NyanORM {}
$payments = new payments();

або робимо те саме з використанням чорної магії

$payments = new nya_payments(); //воу, тут відбувається магія ;)

Так, усе після префіксу nya_ буде розгорнуто в ім'я таблички as is і для неї буде автоматично згенеровано модель.

отримуємо всі записи

$allPayments=$payments->getAll(); //отримуємо всі записи з моделі

Так. Це типу моделька для таблички payments у ловеркейсі. Також ви можете максимально прямолінійно виставити ім'я таблички, з якою ми будемо працювати через конструктор класу. Наприклад так:

$bablo=new NyanORM('payments');
debarr($bablo->getAll());

Ми щось відволіклися. Коротше модель у нас є. Що там із нашим початковим планом щодо вибірки цілком конкретних платежів? А ось що:

class payments extends NyanORM { } // створюємо модель наслідуванням, оскільки це краще дружить з автокомплітом деяких IDE
$payments = new payments(); // створюємо об'єкт моделі
$payments->where('summ', '>', '0'); //тут і далі вішаємо наші умови
$payments->where('date', 'LIKE', curyear() . '%');
$payments->where('cashtypeid', '=', '1'); // так, це готівка
$rawPayments = $payments->getAll('id'); // автоматичне групування результату по id
debarr($rawPayments);

Давайте ще раз, на прикладі, але з чимось іншим. Нехай це будуть світчі. І нехай ми хочемо просто отримати всі світчі.

$switches = new nya_switches();
$allSwitches = $switches->getAll();

ну або якось так без чорної магії, в лоб:

$switches = new NyanORM('switches');
$allSwitches = $switches->getAll();

Куди вже простіше?

Про параметри моделей

Як можна було помітити, для встановлення параметрів наших запитів до моделей ми можемо використовувати метод where() з трьома досить очевидними параметрами. Також досить очевидно, що кілька where(), що йдуть послідовно, будуть збережені в кумулятивних структурах і надалі інтерпретовані як AND. А що якщо ми раптово захочемо крім платежів із готівкою отримувати їх також із типом 4 (нехай це будуть якісь штуки із самообслуговування)? Очевидно, що ми хочемо умову OR. А як її зробити? А дуже просто.

$payments->where('cashtypeid', '=', '1');
$payments->orWhere('cashtypeid','=','4'); //Ага, все настільки очевидно

Також якщо нам дуже захочеться пописати шматочки запитів руками, згадавши старе, або якщо хочеться зробити щось таке “таке”, чого з коробки не зрозуміло, як зробити, за допомогою конструктора запитів NyanORM, тоді ви можете використовувати whereRaw('expression')/orWhereRaw('expression'), наприклад так:

$payments = new nya_payments();
$payments->whereRaw("`summ`>'0' AND `date` LIKE '" . curyear() . "-%' AND `cashtypeid`='1'");
$rawPayments = $payments->getAll('id');
debarr($rawPayments);

Що дасть нам цілком собі ідентичний результат. Але не красиво ж, правда?

Про очищення параметрів

Слід, до речі, зауважити, що після виконання методів типу getAll(), delete() і їм подібних. Усі раніше встановлені вами параметри моделей, як-от where, limit, order, data будуть скинуті. Це зроблено з метою безпеки. Наприклад ось вибираєте ви ці платежі, розглядаєте їх, а потім, уже забувши, що ви це робили кількома екранами коду вище, ви вирішили з якоїсь причини зробити delete(). І все. Вам результат не сподобався. Саме тому за замовчуванням відбувається автоматичне очищення цих параметрів. Метод delete(), наприклад, також своєю чергою нічого не робить без зазначених явно where і кидається на вас винятками.


Якщо з якоїсь причини, ви не хочете, щоб відбувалося автоматичне очищення параметрів моделей, ви можете встановити параметр $flushParams цих очищувальних методів у значення false.
Також у будь-який момент ви можете самостійно очистити стан будь-яких параметрів конкретної моделі, використавши відповідний сеттер з усіма порожніми параметрами.

$payments->where();
$payments->limit();
$payments->orderBy();
$payments->data();
$payments->selectable();

Помітили, як ненав'язливо ми вам показали, які ще параметри (кумулятивні структури) у моделей бувають? Так? ;)

Видалення даних

Ви не повірите. Усе настільки ж лінійно і просто. Давайте будемо видаляти запис із таблички abstractdevices з id 666?

$devices = new nya_abstractdevices();
$devices->where('id','=','666');
$devices->delete();

Кумулятивна структура data()

Кумулятивна структура data призначена для зберігання даних, які будуть надалі використані під час виклику методів create() або save(). Власне має вона лише два параметри, а саме field та value. Досить не важко здогадатися як її використовувати:

$object->data('somefield', 'new value');
$object->data('anotherfield', 'це теж типу якісь дані');

Створення та зміна записів

Пам'ятаєте кумулятивну структуру data()? Вона нам знадобиться для створення записів у моделі або зміни наявних. Давайте створимо новий запис приблизно для такої таблички:

CREATE TABLE IF NOT EXISTS `someobjects` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(255) NOT NULL,
  `text` text,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

Усе дуже прямолінійно.

$object = new nya_someobjects();
$object->data('name', 'а це типу ім`я');
$object->data('text', 'а це якби текст запису');
$object->create();

Зауважте, ми не вказували ручками NULL для автоінкрементного поля id, як так? А так, що у методу create() за замовчуванням встановлений параметр $autoAiId=true, який робить це неявно. Якщо у вашій табличці немає автоінкрементного поля `id` або іншого подібного primary key, ви маєте встановити цей параметр у false. Власне ім'я поля головного ключа таблички ви завжди можете перепризначити за допомогою наслідування. Він міститься в протектед проперті defaultPk.

Окей, запис створити ми створили, а як отримати його id? Для цього є зручний метод getLastId(), який отримує останній defaultPk з таблички. Ось як це працює:

deb($object->getLastId()); // ой... повертає 15

Окей, припустимо, ми раптово захотіли тепер змінити всі або якесь конкретне із полів у цій табличці. як бути? Усе точно так само, як і з create() тільки за допомогою save(), але тепер нам ще знадобитися where(). Припустимо ми будемо редагувати останній запис у цій табличці:

$idToModify=$object->getLastId();
$object->data('text', 'ого, це ж нове значення для text!');
$object->where('id', '=', $idToModify);
$object->save();

Увімкнення режиму відлагодження

Ми знаємо. Ви звикли використовувати для налагодження ваших модулів усілякі print_r/debarr. Усе, відвикаємо. Тепер можна легко і невимушено увімкнути режим налагодження або глибокого налагодження і отримати нормальний лог і виведення всього, що відбувається з моделлю.

$model->setDebug(true);

Усе, тепер усі запити до БД виводитимуться прямо у ваш стандартний в'ю, а також записуватимуться разом із часом у дебаг-лог, який ви зможете дивитися реалтайм методом

$ tail -F exports/nyanorm.log

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

$model->setDebug(true,true);

Про виключення

Якщо ви зовсім знахабнієте від вседозволеності NyanORM вам в обличчя можуть бути викинуті такі винятки:

  • MEOW_WHERE_STRUCT_EMPTY - кумулятивна структура where порожня. А вона потрібна. Дуже.
  • MEOW_DATA_STRUCT_EMPTY - кумулятивна структура data порожня. І вона теж комусь дуже потрібна.
  • MEOW_JOIN_WRONG_TYPE - неправильний тип JOIN. Допустимі тільки INNER, LEFT, RIGHT.
  • MEOW_NO_FIELD_NAME - не встановлено обов'язкове ім'я поля.

Принципова схема

Це десь ось настільки високорівнева штука.

Тож так, у модулях, де швидкість роботи з даними може бути вузьким місцем, можливо, доведеться використовувати більш традиційний підхід із використанням api.mysql.

Що ще?

Коротше ось поки що вам практичні приклади використання цього у вигляді хеллоуворлда. Але оскільки я хеллоуворлди писати не вмію, ось вам тудушка. Як кажуть розумні люди - не вмієш писати хеллоуворлди - пиши тудушки.

Працювати наш TODO-list буде на наступній табличці в БД:

CREATE TABLE IF NOT EXISTS `todo` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `text` text,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

А ось і весь код нашого модуля:

$todo = new nya_todo(); //Створюємо модель даних за допомогою магії. 
                       //Власне todo після префікса nya_ це і є наша табличка.
 
$moduleBaseUrl = '?module=testing'; //базовий URL нашого модулю
$messages = new UbillingMessageHelper(); //хочемо штатні красиві повідомлення
$result = '';
 
//Рендеримо формочку створення, використовуючи інлайнову збірку за допомогою [[apiastral|Astral]].
$inputs = wf_TextInput('newtasktext', __('Task'), '', false, 40);
$inputs .= wf_Submit(__('Create'));
$creationForm = wf_Form('', 'POST', $inputs, 'glamour');
show_window(__('Create new task'), $creationForm);
 
//Ловимо непорожній newtasktext як сигнал для створення нового запису
if (ubRouting::checkPost(array('newtasktext'))) {
    //заповнюємо новими даними структуру data, попередньо відфільтрувавши їх засобами ubRouting
    $todo->data('text', ubRouting::post('newtasktext', 'mres'));
    $todo->create(); //кажемо моделі що "створи запис" на підставі структури вище.
    ubRouting::nav($moduleBaseUrl); //повертаємось у морду модуля
}
 
//Ловимо запит на видалення тудушки, цього разу GET-ом.
if (ubRouting::checkGet('deletetodo')) {
    //виставляємо параметр where у id, котра видаляється, попередньо переконавшись, що це буде циферка
    $todo->where('id', '=', ubRouting::get('deletetodo', 'int'));
    $todo->delete(); //кажемо моделі "вдалися"
    ubRouting::nav($moduleBaseUrl);
}
 
//Ловимо запит на зміну наявного запису. Для сигналізації про початок чекаємо не порожній текст і айдишку.
if (ubRouting::checkPost(array('edittodoid', 'edittodotext'))) {
    //Далі ж усе очевидно, правда? Виставляємо де, виставляємо, що поміняти, фільтруємо, говоримо "збережися".
    $todo->where('id', '=', ubRouting::post('edittodoid', 'int'));
    $todo->data('text', ubRouting::post('edittodotext', 'mres'));
    $todo->save();
    ubRouting::nav($moduleBaseUrl);
}
 
//Показуємо наявні завдання, які нам потрібно зробити.
$todo->orderBy('id', 'DESC'); //хочемо сортування від свіжих до древніх
$allTodos = $todo->getAll(); //дістаємо взагалі все, що бачимо з моделі.
 
if (!empty($allTodos)) {
    $cells = wf_TableCell(__('Text'));
    $cells .= wf_TableCell(__('Actions'));
    $rows = wf_TableRow($cells, 'row1');
    foreach ($allTodos as $io => $each) {
        $cells = wf_TableCell($each['text']);
        $actControls = wf_JSAlert($moduleBaseUrl . '&deletetodo=' . $each['id'], web_delete_icon(), $messages->getDeleteAlert());
        //Прямо тут, збираємо формочку редагування кожного завдання і пихаємо її в контроли.
        $editInputs = wf_HiddenInput('edittodoid', $each['id']);
        $editInputs .= wf_TextInput('edittodotext', __('Text'), $each['text'], false, 40);
        $editInputs .= wf_Submit(__('Save'));
        $editForm = wf_Form('', 'POST', $editInputs, 'glamour');
        $actControls .= wf_modalAuto(web_edit_icon(), __('Edit'), $editForm);
        //Фу так робити.
        $cells .= wf_TableCell($actControls);
        $rows .= wf_TableRow($cells, 'row5');
    }
    $result .= wf_TableBody($rows, '100%', 0, 'sortable');
} else {
    $result .= $messages->getStyledMessage(__('Nothing to show'), 'warning');
}
show_window(__('Sample TODO list'), $result);
nyanorm.txt · Востаннє змінено: 2023/06/16 13:46 повз nightfly