Возможно, всё что я напишу ниже – очевидно, и все этим пользуются давно, но я вот недавно только это понял и придумал, так что, может, кому и пригодится.
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