2.5.1. Работа с классом kDBItem

Data Source Eng Data Source

Класс kDBItem - один из базовых классов. Он предоставляет методы для работы с данными, относящимися к одной записи. Для связи объектов класса kDBItem с прочими частями системы широко используются события. События происходящие во время выполнения методов класса kDBItem можно перехватывать и выполнять необходимые действия. И наоборот - в системе имеются события, при наступлении которых выполняются методы класса kDBItem.

Загрузка из базы данных

Для загрузки из базы данных используется метод Load. Метод возвращает значение true в случае успешной загрузки данных и false в прочих случаях. Если требуется проверить факт успешной загрузки из базы данных у отдельно взятого объекта, то можно использовать метод isLoaded, который возвращает статус последней загрузки объекта. Условия загрузки данных задаются параметрами метода. Они могут быть простыми и сложными.

Простое условие загрузки получается если первым параметром передаётся значение одного из полей записи. Обычно это Integer значение первичного ключа и тогда второй параметр не нужен. Если первым параметром передают значение другого поля, то вторым параметром должно быть название соответствующего поля.

// получаем ссылку на объект. Третий параметр предотвращает автоматическую загрузку данных в объект
$user =& $this->Application->recallObject('u', null, Array('skip_autoload' => true));
/* @var $user kDBItem */

// загружаем данные объекта с ID = 23
$user->Load(23);

// Тут могут быть всякие действия с объектом

// Загружаем в объект данные пользователя по его адресу электронной почты
$user->Load('erik@intechnic.com', 'Email');

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

$load_keys = Array(
    'FirstName' => 'Erik',
    'LastName' => 'Snarski'
);

$user->Load($load_keys);

В итоге для загрузки данных будет использоваться SQL-запрос с условием следующего вида:

WHERE FirstName = 'Erik' AND LastName = 'Snarski'

Изменение данных

Для изменения данных одного поля используются методы SetField и SetDBField. SetDBField записывает в поле значение параметра $value в точности таким как оно передано. SetField перед тем как записать значение, проверяет не назначен ли для поля форматер, и если назначен, то трансформирует значение поля методом Parse форматера.

// например в случае с форматированием даты
$user->SetField('dob', '12/22/1971');
// результат операции будет эквивалентен
$user->SetDBField('dob', 62197200);

Часто бывает удобно поменять данные сразу в нескольких полях. Например, переписать значения полей, которые пришли в результате submit-а формы. Для этого имеются функции SetFieldsFromHash и SetDBFieldsFromHash. Они принимают в качестве параметра массив, содержащий имена полей и значения для них. SetFieldsFromHash, как вышеупомянутый SetField, вызывает метод Parse форматера для трансформации значений.

$field_values = Array(
    'Email' => 'erik@intechnic.com',
    'FirstName' => 'Erik',
    'LastName' => 'Snarski',
    'dob' => '12/22/1971'
);
// обычно подобный массив получают из запроса конструкцией вида
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
list ($id, $field_values) = each($items_info);

// и, наконец - запись данных в объект
$user->SetFieldsFromHash($field_values);

Иногда, например при импорте данных, приходится один и тот же объект класса kDBItem использовать многократно с разными наборами данных. В таких случаях может оказаться полезным метод Clear, выставляющий всем полям значения по умолчанию и таким образом гарантирующий, что в каждом наборе будут только его собственные данные.

Определение изменившихся полей

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

Проверка

Для проверки данных объекта применяется метод Validate. Если в ходе выполнения метода обнаруживается несоответствие данных предъявляемым к ним требованиям, то в свойство FieldErrors объекта, представляющее собой массив с именами полей в качестве ключей верхнего уровня, дописываются элементы второго уровня с ключами 'pseudo'. Это коды ошибок, коротко описывающие сущность проблемы. Проверить есть ли в объекте такие коды можно методом HasErrors.

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

[Name] = Array
        (
            [pseudo] = required
        )

Иногда бывает необходимо не только считывать информацию об ошибках в полях, но и записывать её. Например, при написании функций проверки данных, специфических для конкретной задачи. Тогда, для обозначения ошибок в поле следует использовать предоставляемый классом kDBItem метод SetError.

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

/**
 *
 * @param kEvent $event
 */
function OnBeforeItemUpdate(&$event)
{
    $object =& $event->getObject();
    /* @var $object kDBItem */

    $category_id = $object->getDBField('CategoryId');
    if (!$category_id) {
        // значение не задано - проверка не нужна
        return;
    }

    if ($category_id == $object->GetOriginalField('CategoryId')) {
        // значение не изменялось - проверка не нужна
        return;
    }

    $sql = 'SELECT COUNT(*)
            FROM ' . $this->Application->getUnitOption('c', 'TableName') . '
            WHERE ' . $this->Application->getUnitOption('c', 'IDField') . ' = ' . $this->Conn->qstr($category_id);
    if (!$this->Conn->GetOne($sql)) {
        $object->SetError('CategoryId', 'invalid_category', 'la_error_InvalidCategory');
    }
}

Сохранение в базу данных

Для сохранения в базу данных используются методы Create и Update. Метод Create предназначен для создания новой записи. Метод Update - для изменения существующей записи. Оба эти метода по умолчанию, перед тем как записывать данные в базу, делают проверку методом Validate, и производят запись только в случае если проверка пройдена успешно. В приведённом ниже примере делается импорт данных, правда, если данные создаваемого пользователя не пройдут проверку, то запись просто не будет создана.

// считываем данные из внешнего источника данных в массив
$sql = 'SELECT username, password, email
        FROM user';
$users = $application->Conn->Query($sql);

// создаём объект класса kDBItem
$user =& $application->recallObject('u.-item', null, Array('skip-autoload' => true));
/* @var $user kDBItem*/

foreach ($users AS $user_data) {
    // выставляем значения по умолчанию вызовом метода Clear
    $user->Clear();

    // выставляем значения полей импортируемой записи
    $user->SetDBField('Login', $user_data['username']);
    $user->SetDBField('Email', $user_data['email']);
    $user->SetDBField('Password', $user_data['password']);
    $user->SetDBField('VerifyPassword', $user_data['password']);

    // вызываем метод Create для создания записи в нашей системе
    $user->Create();
}

Использование событий

Четыре основных метода класса kDBItem, способные непосредственно обращаться к базе данных - это методы:

  • Create;

  • Load;

  • Update;

  • Delete.

Каждый из них в определённых ситуациях вызывает события, которые можно использовать для связи объекта класса kDBItem с остальными частями системы. Все эти четыре метода могут в ходе своего выполнения вызывать события OnBeforeItem**** и OnAfterItem****, где **** - имя метода. Событие OnBeforeItem**** вызывается до обращения к базе данных. Событие OnAfterItem**** вызывается только после успешного обращения к базе данных.

Методы Create и Update могут вызывать ещё и событие OnAfterItemValidate. Это происходит в случае, если производилась проверка данных объекта и эта проверка прошла успешно. Событие OnAfterItemValidate вызывается до того как произойдёт обращение к базе данных. Ниже приведён пример того, как можно использовать событие OnAfterItemDelete для выполнения дополнительных действий после успешного удаления записи.

function OnAfterItemDelete(&$event)
{
    $object =& $event->getObject();
    /* @var $object kDBItem */

    // несмотря на то что запись в базе данных уже удалена, в памяти хранятся все её данные и
    // в зависимости от значения полей удалённой записи можно выполнять разные действия
    $topic_id = $object->GetDBField('TopicId');
    if (!$topic_id) {
        // deleting non-existing post
        return ;
    }

    $post_helper =& $this->Application->recallObject('PostHelper');
    /* @var $post_helper PostHelper */

    // update posts count in topic
    $post_helper->updatePostCount($topic_id, -1);
}

Основные события, использующие этот класс

Основные события, которые используют класс kDBItem находятся в классе kDBEventHandler.

Основные События

Аналоги, используемые при работе с временными таблицами

Используемые методы kDBItem

OnNew

OnPreCreate

setID(0)

OnCreate

OnPreSaveCreated

SetFieldsFromHash, Create, IsTempTable, setTempID, setID

OnUpdate

OnPreSave

Load, SetFieldsFromHash, Update, Load

Эти события как правило инициируются непосредственно с web-страницы, то есть, их имена передаются в запросе к серверу.

Событие OnNew происходит при открытии формы ввода данных для создания новой записи.

  • OnCreate - при отсылке данных на сервер для создания новой записи.

  • OnUpdate - при отсылке данных на сервер для модификации существующей записи.

function OnUpdate(&$event)
{
    // Получаем ссылку на объект класса kDBItem.
    $object =& $event->getObject( Array('skip_autoload' => true) );

    // Получаем данные из запроса.
    $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
    if ($items_info) {
        foreach ($items_info as $id => $field_values) {
            // Загружаем данные из базы по идентификатору записи
            $object->Load($id);

            // Вставляем данные из запроса в объект.
            $object->SetFieldsFromHash($field_values);

            $this->customProcessing($event, 'before');

            // Вызываем метод класса kDBItem.
            if ( $object->Update($id) ) {
                $this->customProcessing($event, 'after');
                $event->status=erSUCCESS;
            }
            else {
                $event->status=erFAIL;
                $event->redirect=false;
                break;
            }
        }
    }
    $event->setRedirectParam('opener', 'u');
}

См. также