Валидация данных вложенных документов MongoDB в Yii2

в 12:51, , рубрики: mongodb, php, yii, yii2

Возможно, всё что я напишу ниже – очевидно, и все этим пользуются давно, но я вот недавно только это понял и придумал, так что, может, кому и пригодится.

Yii2 и расширение yii2-mongodb к сожалению, не работает с вложенными документами, тем самым оставляя за бортом существенное преимущество документоориентированной БД.
В документации предлагают использовать расширение для вложенных документов, но можно обойтись и без него.
Предположим, у нас есть модель, формирующая PDF-файл для загрузки, и мы хотим следить за количеством его скачиваний, IP-адресами скачавших и, например, временем, когда файл был загружен.

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

Основная модель (часть)

/**
 * @property string $pdf_data       стока с данным, которая потом преобразуется в файл
 * @property array $downloads_data  здесь хранятся сведения о загрузках
 *
 */
Class PdfData extends yiimongodbActiveRecord

/** @inheritdoc */
public static function collectionName()
{
    return [‘database’, ‘pdf’]
}

/** @inheritdoc */
public function attributes()
{
    return [
        ‘pdf_data’,
        ‘downloads_data’
    ];
}

Дополнительная модель – для проверки и присвоения значений элементам массива


use MongoDBBSONUTCDateTime

/**
 * Класс для формирования сведений о факте загрузки файла
 */
class DowmnloadData extends yiibaseModel
{
    /** @var MongoDBBSONUTCDateTime $datetime */
    public $datetime;
    /** @var string $clientIp */
    public $clientIp;
    /** @var string $clientHost */
    public $clientHost;
    /** @var string $clientUserAgent */
    public $clientUserAgent;
    /** @var string $referer */
    public $referer;
    /** @var bool $result */
    public $result = false;

    /** @inheritdoc */
    public function rules()
    {
        return [
            ['datetime', 'default', 'value' => function() 
                   { return new UTCDateTime(strtotime("now") * 1000); }],
            ['clientIp', 'default', 'value' => function() 
                   { return Yii::$app->request->getUserIP(); }],
            ['clientHost', 'default', 'value' => function() 
                   { return Yii::$app->request->getUserHost(); }],
            ['clientUserAgent', 'default', 'value' => function() 
                   { return Yii::$app->request->getUserAgent(); }],
            ['referer', 'default', 'value' => function() 
                   { return Yii::$app->request->getReferrer(); }],
            ['result', 'boolean'],
        ];
    }

Далее, в действии контроллера, которое отдает файл наружу, примерно следующее:

// -- skip --

/**
 * @param string $id идентификатор основной модели
 * @return null
 * @throws yiiwebNotFoundHttpException
 */
public function actionDownload($id)
{
    if(($model = PdfData::findOne($id)) === null)
        throw new yiiwebNotFoundHttpException(Yii::t('app', 'File not found'));

    $downloadData = new DowmnloadData();
    if(!empty($model->pdf_data))
    {
        $downloadData->result = true;
        $downloadData->validate(); // Так мы добиваемся присвоения значений по-умолчанию
        $data = $model->downloads_data; // Забрали существующие сведения о загрузках
        $data[] = $downloadData->attributes; // Добавили к ним новые
        // array_values для гарантироанного сохранения массива (а не объекта) в mongodb
        $model->updateAttributes(['downloads_data' => array_values($data)]); 

        // Отправляем файл
        Yii::$app->response->sendContentAsFile($model->pdf_data, 'we are the champions.pdf', [
            'mimeType' => 'text/xml',
            'inline' => true
        ]);
    }
    return null;
}

Таким образом внутри массива downloads_data основной модели мы имеем все атрибуты, которые придумали в DowmnloadData, и можем их потом как угодно показывать и анализировать, не умножая сверх необходимого при этом ни атрибуты основной модели, ни число коллекций в БД.

Автор: andrew72ru

Источник

Поделиться новостью

* - обязательные к заполнению поля