<?php
/**
 * -------------------------------------------------------------------------
 *
 * Модуль для модели Vacancies.
 *
 * -------------------------------------------------------------------------
 *
 * Эта модель представляет собой таблицу "vacancies" базы данных. А в классе
 * её модуля, реализованного ниже, имеются следующие методы для работы с
 * этой таблицей:
 *
 *     PUBLIC  onInstall    ( $db                                  )  -->  чтобы произвести необходимые действия при первом запуске приложения
 *     PUBLIC  getStructure (                                      )  -->  чтобы получить структуру записи для формы редактора в админпанели
 *     PUBLIC  addColumn    ( $name[, $after]                      )  -->  чтобы добавить колонку в таблицу
 *     PUBLIC  add          (      $row                            )  -->  чтобы добавить новую запись в таблицу
 *     PUBLIC  getBy        ( $id                                  )  -->  чтобы извлечь запись по её системному идентификатору
 *     PUBLIC  findBy       ( $pid[,                  $forAdmin]   )  -->  чтобы извлечь запись по её публичному идентификатору
 *     PUBLIC  select       (        [$page[, $size[, $forAdmin]]] )  -->  чтобы получить список записей, находящихся на некоторой странице
 *     PUBLIC  selectBy     ( $town[, $page[, $size[, $forAdmin]]] )  -->  чтобы получить список записей города, находящихся на некоторой странице
 *     PUBLIC  getSitemap   (                                      )  -->  чтобы получить список URL-ов страниц вакансий для карты сайта
 *     PUBLIC  remove       ( $id                                  )  -->  чтобы удалить запись по её системному идентификатору
 *     PUBLIC  removeOld    (                                      )  -->  чтобы удалить записи с истёкшим сроком давности
 *     PUBLIC  update       ( $id, $row                            )  -->  чтобы обновить запись по её системному идентификатору
 *
 * -------------------------------------------------------------------------
 *
 * @package    MimimiFramework
 * @subpackage Examples / Repost Vacancies
 * @license    GPL-2.0
 *             https://opensource.org/license/gpl-2-0/
 * @copyright  2022 MiMiMi Community
 *             https://mimimi.software/
 *
 * -------------------------------------------------------------------------
 */

    /**
     * ---------------------------------------------------------------------
     *
     * Подключаем из папки ядра фреймворка файл "mimimi.core/Module.php".
     * Там объявлен класс "MimimiModule", являющийся простейшей модульной
     * заготовкой. Этот класс подходит как основа для реализуемого ниже
     * модуля.
     *
     * ---------------------------------------------------------------------
     */

    mimimiInclude( 'Module.php' );

    /**
     * ---------------------------------------------------------------------
     *
     * Создаём на основе класса той заготовки новый класс, в котором напишем
     * программный код текущего модуля. Обратите внимание как задано имя
     * нового класса - оно сложено из имени класса вышестоящего модуля, то
     * есть "MyMimimiModels", и имени текущего PHP-файла без расширения.
     *
     * ---------------------------------------------------------------------
     */

    class MyMimimiModelsVacancies extends MimimiModule {

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Обработчик первого запуска приложения.
         *
         * -----------------------------------------------------------------
         *
         * Вызов этого метода происходит лишь в ситуации, когда файл SQLite3
         * базы данных ещё не существовал и только что создан. Поэтому здесь
         * мы просто создаём таблицу "vacancies", описывая желаемую структуру
         * записей. Для понимания этого нюанса обратитесь к файлу:
         *
         *     mimimi.modules/Sqlite/Sqlite.php  -->  метод __construct()
         *                           Sqlite.php  -->  метод autoCreate()
         *
         * Колонки "id", "url_root", "created" используем как технические,
         * полагаясь только на их дефолтные значения. Использование значения
         * по умолчанию для колонки "active" является простейшей защитой от
         * теоретически возможных ошибок неконтролируемого добавления записи
         * с неполным набором колонок в случае кривого кода в алгоритме
         * обработки формы ввода, отсеивающей неизменившиеся поля.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @return  void
         *
         * -----------------------------------------------------------------
         */

        public function onInstall ( $db ) {
            $db->exec ( 'CREATE TABLE `vacancies` ( `id`             INTEGER      PRIMARY KEY  AUTOINCREMENT,'
                                                . ' `url_root`       VARCHAR(16)  DEFAULT "vacancies/",'
                                                . ' `vacancy_id`     VARCHAR(64),'
                                                . ' `module`         VARCHAR(64),'
                                                . ' `region`         VARCHAR(32),'
                                                . ' `district`       VARCHAR(32),'
                                                . ' `town`           VARCHAR(64),'
                                                . ' `content`        VARCHAR(32768),'
                                                . ' `source_url`     VARCHAR(512),'
                                                . ' `active`         BOOLEAN      DEFAULT FALSE,'
                                                . ' `via_task`       INTEGER      DEFAULT 0,'
                                                . ' `tg_message_id`  VARCHAR(32),'
                                                . ' `max_message_id` VARCHAR(32),'
                                                . ' `killtime`       DATETIME,'
                                                . ' `created`        DATETIME     DEFAULT CURRENT_TIMESTAMP )' );
        }

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Получить структуру записи для формы редактора в админпанели.
         *
         * -----------------------------------------------------------------
         *
         * Здесь обозначены колонки записи о вакансии, которые позволено
         * редактировать администратору в форме ввода. Элементы данного
         * массива означают следующее: имя колонки записи, её значение по
         * умолчанию, тип необходимого поля в форме ввода, надпись над полем
         * ввода, дополнительные атрибуты у поля ввода. Для понимания этого
         * нюанса обратитесь к файлу:
         *
         *     repost.vacancies/Themes/default/snippets/vacancy-form.tpl
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @return  array
         *
         * -----------------------------------------------------------------
         */

        public function getStructure ( ) {
            return [
                [ 'active', 0, 'number', 'Активно/нет', 'min="0" max="1"' ]
            ];
        }

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Добавить колонку в таблицу.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @param   string  $name   Имя колонки (без кавычек). 
         *                          После имени через пробел допускается указать тип колонки.
         * @param   string  $after  (необязательный) Имя колонки (без кавычек), после которой произойдёт вставка.
         * @return  void
         *
         * -----------------------------------------------------------------
         */

        public function addColumn ( $name, $after = '' ) {
            $name  = preg_replace ( '~^\s*([a-z\d_]+)(\s|$)~ui', '`$1`$2', $name );
            $after = $after == '' ? ''
                                  : ( 'AFTER `' . $after . '`' );
            $this->owner->exec ( 'ALTER TABLE `vacancies` '
                               . 'ADD ' . $name . ' '
                               . $after );
        }

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Добавить запись вакансии.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @param   array  $row  Массив, индексированный именами колонок сохраняемой записи.
         * @return  void
         *
         * -----------------------------------------------------------------
         */

        public function add ( $row ) {
            $this->owner->addFor ( 'vacancies', $row );
        }

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Получить запись вакансии по её системному ИД.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @param   int    $id  Системный идентификатор запрашиваемой записи.
         * @return  array       Массив, индексированный именами колонок извлечённой записи.
         *
         * -----------------------------------------------------------------
         */

        public function getBy ( $id ) {
            return $this->owner->getByFor ( 'vacancies', $id );
        }

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Получить запись вакансии по её публичному ИД.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @param   int    $vacancy_id  Публичный идентификатор запрашиваемой записи.
         * @param   bool   $hasAdmin    (необязательный) TRUE  если извлекаем запись для админа, то есть не учитывая её состояние.
         *                                               FALSE если извлекаем только активную запись.
         * @return  array               Массив, индексированный именами колонок извлечённой записи.
         *
         * -----------------------------------------------------------------
         */

        public function findBy ( $vacancy_id, $hasAdmin = FALSE ) {
            $where = $hasAdmin ? ''
                               : 'AND `active` = 1';
            return $this->owner->queryRow ( 'SELECT * '
                                          . 'FROM `vacancies` '
                                          . 'WHERE `vacancy_id` = ' . $this->owner->bind ( $vacancy_id ) . ' '
                                          .        $where );
        }

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Получить список активных вакансий па указанной странице.
         *
         * -----------------------------------------------------------------
         *
         * Обратите внимание, что в конце списка всегда добавляется пустая
         * (FALSE) запись, если за этим фрагментом существует продолжение.
         * Такая пустая запись помогает в макете пагинации вывести кнопку
         * "Далее" без необходимости передавать в шаблон дополнительную
         * переменную об отрисовке кнопки. Для понимания этого нюанса
         * обратитесь к следующему макету:
         *
         *     repost.vacancies/Themes/default/snippets/pagination.tpl
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @param   int    $pagenum   (необязательный) Номер интересуемой страницы.
         * @param   int    $pagesize  (необязательный) Сколько карточек возможно на странице.
         * @param   bool   $hasAdmin  (необязательный) TRUE  если извлекаем записи для админа, то есть не учитывая их состояние.
         *                                             FALSE если извлекаем только активные записи.
         * @return  array               Массив массивов, индексированных именами колонок извлечённых записей.
         *
         * -----------------------------------------------------------------
         */

        public function select ( $pagenum = 1, $pagesize = 10, $hasAdmin = FALSE ) {
            $offset = ( $pagenum - 1 ) * $pagesize;
            $where  = $hasAdmin ? ''
                                : 'WHERE `active` = 1 ';
            $rows   = $this->owner->queryRows ( 'SELECT * '
                                              . 'FROM `vacancies` '
                                              . $where
                                              . 'LIMIT '  . $this->owner->bind ( $pagesize + 1 ) . ' '
                                              . 'OFFSET ' . $this->owner->bind ( $offset       ) );
            if ( $rows ) {
                if ( count ( $rows ) > $pagesize ) {
                    array_pop ( $rows );
                    $rows[ ] = FALSE;
                }
            }
            return $rows;
        }

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Получить список активных вакансий города па указанной странице.
         *
         * -----------------------------------------------------------------
         *
         * Обратите внимание, что в конце списка всегда добавляется пустая
         * (FALSE) запись, если за этим фрагментом существует продолжение.
         * Такая пустая запись помогает в макете пагинации вывести кнопку
         * "Далее" без необходимости передавать в шаблон дополнительную
         * переменную об отрисовке кнопки. Для понимания этого нюанса
         * обратитесь к следующему макету:
         *
         *     repost.vacancies/Themes/default/snippets/pagination.tpl
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @param   string  $code      Идентифкатор желаемого города.
         * @param   int     $pagenum   (необязательный) Номер интересуемой страницы.
         * @param   int     $pagesize  (необязательный) Сколько карточек возможно на странице.
         * @param   bool    $hasAdmin  (необязательный) TRUE  если извлекаем записи для админа, то есть не учитывая их состояние.
         *                                              FALSE если извлекаем только активные записи.
         * @return  array              Массив массивов, индексированных именами колонок извлечённых записей.
         *
         * -----------------------------------------------------------------
         */

        public function selectBy ( $code, $pagenum = 1, $pagesize = 10, $hasAdmin = FALSE ) {
            $offset = ( $pagenum - 1 ) * $pagesize;
            $where  = $hasAdmin ? ''
                                : 'AND `active` = 1 ';
            $rows   = $this->owner->queryRows ( 'SELECT * '
                                              . 'FROM `vacancies` '
                                              . 'WHERE `district` = ' . $this->owner->bind ( $code ) . ' '
                                              .        $where
                                              . 'LIMIT '  . $this->owner->bind ( $pagesize + 1 ) . ' '
                                              . 'OFFSET ' . $this->owner->bind ( $offset       ) );
            if ( $rows ) {
                if ( count ( $rows ) > $pagesize ) {
                    array_pop ( $rows );
                    $rows[ ] = FALSE;
                }
            }
            return $rows;
        }

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Получить список URL-ов активных вакансий.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @return  array  Массив массивов, индексированных именами колонок "url_root" и "id" только.
         *
         * -----------------------------------------------------------------
         */

        public function getSitemap ( ) {
            return $this->owner->queryRows ( 'SELECT `url_root`, '
                                                 . ' `vacancy_id` AS `id` '
                                           . 'FROM `vacancies` '
                                           . 'WHERE `active` = 1' );
        }

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Удалить запись вакансии по её ИД.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @param   int   $id  Идентификатор удаляемой записи.
         * @return  void
         *
         * -----------------------------------------------------------------
         */

        public function remove ( $id ) {
            $this->owner->removeFor ( 'vacancies', $id );
        }

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Удалить записи вакансий с истёкшим сроком давности.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @return  void
         *
         * -----------------------------------------------------------------
         */

        public function removeOld ( ) {
            $this->owner->exec ( 'DELETE '
                               . 'FROM `vacancies` '
                               . 'WHERE `killtime` < "' . date ( 'Y-m-d H:i:s' ) . '"' );
        }

        /**
         * -----------------------------------------------------------------
         *
         * Метод: Обновить запись вакансии по её ИД.
         *
         * -----------------------------------------------------------------
         *
         * @public
         * @param   int    $id   Идентификатор обновляемой записи.
         * @param   array  $row  Массив, индексированный именами колонок сохраняемой записи.
         * @return  void
         *
         * -----------------------------------------------------------------
         */

        public function update ( $id, $row ) {
            $this->owner->updateFor ( 'vacancies', $id, $row );
        }
    }
