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

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


nyanorm

Розбіжності

Тут показані розбіжності між вибраною ревізією та поточною версією сторінки.

Посилання на цей список змін

Порівняння попередніх версій Попередня ревізія
Попередня ревізія
nyanorm [2019/08/05 11:59]
nyanorm [2023/06/16 13:46] (поточний)
nightfly [Що ще?]
Рядок 1: Рядок 1:
 +====== Рівень абстракції NyanORM ======
 +
 +===== Навіщо це все? =====
 +{{:nyanorm.gif? |}}
 +
 +Робить НЯКУ з вашою базою. І надає дуже зручні засоби, щоб ви докладали мінімум зусиль і мозкових звивин при роботі з БД Ubilling. Ви зможете описувати ваші моделі даних, і роботу з ними, більше не малюючи в ручну SELECT `something` FROM `tablename`... та інші остогидлі вам конструкції. Також NyanORM візьме на себе всю вашу рутинну роботу з попередньої підготовки і трансформації ваших даних. Завданням NyanORM **не бути** всеосяжним монстром, який реалізує всі можливі кейси роботи з вашими даними. Завданням NyanORM є - бути простим та зручним. З самого початку, він задумувався для того, щоб скоротити обсяги рутинного, нудного й однотипного коду, з якого складається робота із сутностями в 90-95% прикладних модулів. Власне тільки ці кейси він і повинен перекривати, не накладаючи на вас жодних додаткових обмежень.
 +
 +===== Особливості =====
 +
 + Чому саме NyanORM, а не будь-яка інша стороння бібліотека з приставкою ORM у назві? А з таких причин:
 +
 +  * Не накладає жодних обмежень на іменування табличок БД.
 +  * Не диктує жодних умов щодо полів і вмісту цих табличок.
 +  * Дозволяє без проблем працювати з уже наявними структурами даних.
 +  * Дозволяє міксувати код як з його використанням так і в старому стилі.
 +  * Не вимагає довгого і клопіткого опису моделей, десь в окремому місці, перед тим як почати працювати.
 +  * Ідеальний для надшвидкого прототипування бізнес-логіки прикладних модулів.
 +  * Доступний у будь-який момент і завжди з будь-якого вашого модулю.
 +  * Написаний у наскільки це можливо мінімалістичному стилі з максимальним використанням наявних механік Ubilling.
 +  * Мінімізує оверхеди за пам'яттю і коллбеками. Але це не точно.
 +  * Дозволяє наслідуванням модифікувати як завгодно будь-яку модель і методи роботи з нею, взагалі ніяк себе не обмежуючи.
 +  * Не виставляє жодних вимог і засобів щодо фільтрації даних. Ви можете використовувати взагалі що захочете.
 +  * Працює однаково відмінно на PHP5 і PHP7 та на PHP8. Легасі так.
 +  * Максимально очевидний синтаксис і механіки вимагають для свого розуміння мінімального рівня IQ.
 +  * Зашкалюючий рівень каваю.
 +
 +На жаль, ми не знайшли жодного наявного рішення, що відповідає **всім** особливостям/вимогам вище. Це заощадило б нам дуже багато зусиль і часу, але ні. Тому NyanORM. 
 +\\
 +\\
 +Якщо вам буде так спокійніше, то ми вдумливо подивилися на те, як працюють Eloquent, Hibernate, Doctrine, Propel, RedBean... і зробили навпаки.
 +===== Це обов'язково використовувати? =====
 +
 +**Ні! ** Ніхто вас ні в чому не обмежує і не примушує використовувати будь-що таке специфічне у вашому новому коді, і тим паче навіть не натякає, що ви маєте переписати весь ваш старий код з використанням цього об'єкта. Можливо, вам просто сподобається, який вигляд може мати ваш код без ручного збирання запитів до БД, і з використанням високорівневої абстракції.
 +
 +
 +===== З чого почати? =====
 +
 +Для прикладу, давайте розглянемо дуже типовий кейс. Нам потрібно отримати, наприклад, платежі за поточний рік, із сумою більше нуля і типом платежів "готівка". Також ми хочемо мати їх в асоціативному масиві, де ключем буде id платежу. Як це б виглядало в нашій старій реальності:
 +
 +<code php>
 +$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);
 +</code>
 +
 +Правда знайома конструкція? Остогидло, правда ж? А тепер давайте теж саме, але красиво. Для початку нам потрібна модель для таблички payments. Для цього ми або успадковуємо базову модель, використовуючи ім'я таблички як ім'я класу:
 +
 +<code php>
 +class payments extends NyanORM {}
 +$payments = new payments();
 +</code>
 +
 +або робимо те саме з використанням <del>чорної</del> магії
 +
 +<code php>
 +$payments = new nya_payments(); //воу, тут відбувається магія ;)
 +</code>
 +
 +Так, усе після префіксу **nya_** буде розгорнуто в ім'я таблички as is і для неї буде автоматично згенеровано модель.
 +
 +отримуємо всі записи
 +<code php>
 +$allPayments=$payments->getAll(); //отримуємо всі записи з моделі
 +</code>
 +
 +Так. Це типу моделька для таблички payments у ловеркейсі. Також ви можете максимально прямолінійно виставити ім'я таблички, з якою ми будемо працювати через конструктор класу. Наприклад так:
 +
 +<code php>
 +$bablo=new NyanORM('payments');
 +debarr($bablo->getAll());
 +</code>
 +
 +Ми щось відволіклися. Коротше модель у нас є. Що там із нашим початковим планом щодо вибірки цілком конкретних платежів? А ось що:
 +
 +<code php>
 +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);
 +</code>
 +
 +Давайте ще раз, на прикладі, але з чимось іншим. Нехай це будуть світчі. І нехай ми хочемо просто отримати всі світчі.
 +<code php>
 +$switches = new nya_switches();
 +$allSwitches = $switches->getAll();
 +</code>
 +
 +ну або якось так без чорної магії, в лоб:
 +<code php>
 +$switches = new NyanORM('switches');
 +$allSwitches = $switches->getAll();
 +</code>
 +
 +Куди вже простіше?
 +
 +===== Про параметри моделей =====
 +
 +Як можна було помітити, для встановлення параметрів наших запитів до моделей ми можемо використовувати метод where() з трьома досить очевидними параметрами. Також досить очевидно, що кілька where(), що йдуть послідовно, будуть збережені в кумулятивних структурах і надалі інтерпретовані як AND. А що якщо ми раптово захочемо крім платежів із готівкою отримувати їх також із типом 4 (нехай це будуть якісь штуки із самообслуговування)? Очевидно, що ми хочемо умову OR. А як її зробити? А дуже просто.
 +
 +<code php>
 +$payments->where('cashtypeid', '=', '1');
 +$payments->orWhere('cashtypeid','=','4'); //Ага, все настільки очевидно
 +</code> 
 +
 +Також якщо нам дуже захочеться пописати шматочки запитів руками, згадавши старе, або якщо хочеться зробити щось таке "таке", чого з коробки не зрозуміло, як зробити, за допомогою конструктора запитів NyanORM, тоді ви можете використовувати whereRaw('expression')/orWhereRaw('expression'), наприклад так:
 +
 +<code php>
 +$payments = new nya_payments();
 +$payments->whereRaw("`summ`>'0' AND `date` LIKE '" . curyear() . "-%' AND `cashtypeid`='1'");
 +$rawPayments = $payments->getAll('id');
 +debarr($rawPayments);
 +</code> 
 +
 +Що дасть нам цілком собі ідентичний результат. Але не красиво ж, правда?
 +===== Про очищення параметрів =====
 +
 +Слід, до речі, зауважити, що після виконання методів типу getAll(), delete() і їм подібних. Усі раніше встановлені вами параметри моделей, як-от where, limit, order, data будуть скинуті. Це зроблено з метою безпеки. Наприклад ось вибираєте ви ці платежі, розглядаєте їх, а потім, уже забувши, що ви це робили кількома екранами коду вище, ви вирішили з якоїсь причини зробити delete(). І все. Вам результат не сподобався. Саме тому за замовчуванням відбувається автоматичне очищення цих параметрів. Метод delete(), наприклад, також своєю чергою нічого не робить без зазначених явно where і кидається на вас винятками.
 +
 +\\
 +Якщо з якоїсь причини, ви не хочете, щоб відбувалося автоматичне очищення параметрів моделей, ви можете встановити параметр $flushParams цих очищувальних методів у значення false.
 +\\
 +Також у будь-який момент ви можете самостійно очистити стан будь-яких параметрів конкретної моделі, використавши відповідний сеттер з усіма порожніми параметрами. 
 +
 +<code php>
 +$payments->where();
 +$payments->limit();
 +$payments->orderBy();
 +$payments->data();
 +$payments->selectable();
 +</code>
 +
 +Помітили, як ненав'язливо ми вам показали, які ще параметри (кумулятивні структури) у моделей бувають? Так? ;)
 +
 +===== Видалення даних =====
 +
 +Ви не повірите. Усе настільки ж лінійно і просто. Давайте будемо видаляти запис із таблички abstractdevices з id 666?
 +
 +<code php>
 +$devices = new nya_abstractdevices();
 +$devices->where('id','=','666');
 +$devices->delete();
 +</code>
 +
 +===== Кумулятивна структура data() =====
 +
 +Кумулятивна структура **data** призначена для зберігання даних, які будуть надалі використані під час виклику методів **create()** або **save()**. Власне має вона лише два параметри, а саме **field** та **value**. Досить не важко здогадатися як її використовувати:
 +
 +<code php>
 +$object->data('somefield', 'new value');
 +$object->data('anotherfield', 'це теж типу якісь дані');
 +</code>
 +
 +===== Створення та зміна записів =====
 +
 +Пам'ятаєте кумулятивну структуру **data()**?  Вона нам знадобиться для створення записів у моделі або зміни наявних. Давайте створимо новий запис приблизно для такої таблички:
 +
 +<code sql>
 +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;
 +</code>
 +
 +Усе дуже прямолінійно.
 +
 +<code php>
 +$object = new nya_someobjects();
 +$object->data('name', 'а це типу ім`я');
 +$object->data('text', 'а це якби текст запису');
 +$object->create();
 +</code>
 +
 +Зауважте, ми не вказували ручками NULL для автоінкрементного поля id, як так? А так, що у методу **create()** за замовчуванням встановлений параметр $autoAiId=true, який робить це неявно. Якщо у вашій табличці немає автоінкрементного поля `id` або іншого подібного primary key, ви маєте встановити цей параметр у false. Власне ім'я поля головного ключа таблички ви завжди можете перепризначити за допомогою наслідування. Він міститься в протектед проперті **defaultPk**.  
 +\\
 +\\
 +Окей, запис створити ми створили, а як отримати його id? Для цього є зручний метод **getLastId()**, який отримує останній **defaultPk** з таблички.
 +Ось як це працює:
 +
 +<code php>
 +deb($object->getLastId()); // ой... повертає 15
 +</code>
 +
 +Окей, припустимо, ми раптово захотіли тепер змінити всі або якесь конкретне із полів у цій табличці. як бути? Усе точно так само, як і з **create()** тільки за допомогою **save()**, але тепер нам ще знадобитися **where()**. Припустимо ми будемо редагувати останній запис у цій табличці:
 +
 +<code php>
 +$idToModify=$object->getLastId();
 +$object->data('text', 'ого, це ж нове значення для text!');
 +$object->where('id', '=', $idToModify);
 +$object->save();
 +</code>
 +
 +
 +===== Увімкнення режиму відлагодження =====
 +
 +Ми знаємо. Ви звикли використовувати для налагодження ваших модулів усілякі print_r/debarr. Усе, відвикаємо. Тепер можна легко і невимушено увімкнути режим налагодження або глибокого налагодження і отримати нормальний лог і виведення всього, що відбувається з моделлю.
 +
 +<code php>
 +$model->setDebug(true);
 +</code>
 +
 +Усе, тепер усі запити до БД виводитимуться прямо у ваш стандартний в'ю, а також записуватимуться разом із часом у дебаг-лог, який ви зможете дивитися реалтайм методом
 +
 +<code bash>
 +$ tail -F exports/nyanorm.log
 +</code>
 +
 +Також вам може захотітися врубити режим глибокого відлагодження. Тоді в цей же лог, буде дампитися також стан всієї моделі цілком на кожен пчих. Робиться це так:
 +<code php>
 +$model->setDebug(true,true);
 +</code>
 +
 +===== Про виключення =====
 +
 +Якщо ви зовсім знахабнієте від вседозволеності NyanORM вам в обличчя можуть бути викинуті такі винятки:
 +
 +  * **MEOW_WHERE_STRUCT_EMPTY** - кумулятивна структура where порожня. А вона потрібна. Дуже.
 +  * **MEOW_DATA_STRUCT_EMPTY** - кумулятивна структура data порожня. І вона теж комусь дуже потрібна.
 +  * **MEOW_JOIN_WRONG_TYPE** - неправильний тип JOIN. Допустимі тільки INNER, LEFT, RIGHT.
 +  * **MEOW_NO_FIELD_NAME** - не встановлено обов'язкове ім'я поля.
 +
 +===== Принципова схема =====
 +
 +Це десь ось настільки високорівнева штука.
 +
 +{{:nyanorm_scheme0.png?|}}
 +
 +
 +Тож так, у модулях, де швидкість роботи з даними може бути вузьким місцем, можливо, доведеться використовувати більш традиційний підхід із використанням api.mysql.
 +===== Що ще? =====
 +
 +Коротше ось поки що вам практичні приклади використання цього у вигляді хеллоуворлда. Але оскільки я хеллоуворлди писати не вмію, ось вам тудушка. Як кажуть розумні люди - не вмієш писати хеллоуворлди - пиши тудушки.
 +
 +
 +Працювати наш TODO-list буде на наступній табличці в БД:
 +
 +<code sql>
 +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;
 +</code>
 +
 +А ось і весь код нашого модуля:
 +<code php>
 +
 +$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);
 +</code>
  
nyanorm.txt · Востаннє змінено: 2023/06/16 13:46 повз nightfly