Caching ActiveDataProvider with redis - yii2

How i cache data of active data provider query. If query run its get data from cache
return new ActiveDataProvider([
'query' => Aircraft::find()->andWhere(['owner_id' => 6, 'owner_type' => 'organization']),
]);

Using cache() should be enough (as long as you have configured cache component):
return new ActiveDataProvider([
'query' => Aircraft::find()
->andWhere(['owner_id' => 6, 'owner_type' => 'organization'])
->cache($cacheDuration),
]);

Related

How can I display in view multiple GridViews with one DataProvider group it by some field?

A have a table 'operations' with finance operations for all users, including 'id', 'user_id', 'sum', 'name', 'date_picked' etc.
I have created one ActiveDataProvider that gets all operations for concrete User. Now I would like to display in view data from this dataprovider - not in one GridView, but in several GridViews which data grouped by 'date_picked' value.
I know that I can make a new dataprovider for the each 'date_picked' but there will be a large amount of requests to database.
Is there a way to display grouped data in several GridViews based on one dataprovider's data?
Controller:
/** #var OperationComponent $comp */
$comp = Yii::$app->operation;
$userId = Yii::$app->user->id;
$filterModel = $comp->getOperationSearch();
$operations = $comp->getSearchProvider($userId, Yii::$app->request->get());
OperationComponent:
public function getOperationSearch()
{
return new OperationSearch();
}
public function getSearchProvider($user_id, $params)
{
$model = new OperationSearch();
return $model->search($user_id, $params);
}
OperationSearch:
public function search($user_id, $params)
{
$query = Operation::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
// сортировка по умолчанию
'sort' => [
'defaultOrder' => [
'date_picked' => SORT_DESC
]
]
]);
return $dataProvider;
View:
....
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $filterModel,
.....
]); ?>
....
There is an Yii2 extension for complex GridViews: kartik-v/yii2-grid
It contains collapsible rows for showing details for a row.
You can view a demo here.
What you are looking for is the Expand Row Column Widget. It allows to expand a row by just loading the required data for this row details.
This extension has a good documentation is quite stable and tested and can be used straight forward.

Yii2. How to set scenario in dataProvider?

I want to return different fields depends on scenario. How can I set it in dataProvider?
$query = User::find();
$activeData = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
]);
Fields in User model:
public function fields()
{
if ($this->scenario == 'statistics') {
return [
'id',
'email',
'count'
];
}
return [
'id',
'name'
];
}
What about using the $select property?
$query = User::find()->select(['id','email','count']);
$activeData = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
]);
Or even better, create an ActiveQuery class for them:
class UserQuery extends ActiveQuery
{
public function statistics()
{
return $this->select(['id','email','count']);
}
/* add as many filtering functions as you need here */
}
Then override the find() method in the User class to use it:
public static function find()
{
return new \app\models\UserQuery(get_called_class());
}
Then do:
$activeData = new ActiveDataProvider([
'query' => User::find()->statistics(),
'pagination' => [
'pageSize' => 10,
],
]);
Note: In default implementation of Yii2 RESTful API you can also select the required fields within url by doing: GET /users?fields=id,email,count
Simply get all the models using getModels() method, set scenario for all of them in the loop and then return data provider. Your example will change into following code:
$query = User::find();
$activeData = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
]);
foreach($activeData->getModels() as $model) {
$model->scenario = 'statistics';
}
return $activeData;

Yii2 show active() records on a new index page of the same controller

I don't even know how to define my question, so I couldn't really search for an answer, so it would be appreciated if you could point me to the right direction.
I have a model a view and a controller. Normally if I'm calling index, I'm getting all records. That works fine. Now I would like to query from the DB only the active() records. So I would like to see on a new index page only the active records. Where do I have to define this? Of course, the function itself is already defined in ModelQuery. But in Controller, I have to create a new function, in order to be able to call a new view.
public function actionIndex2() {
$searchModel = new ModelSearch;
$dataProvider = $searchModel->search($_GET);
...
return $this->render('index2', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
]);
}
I have tried to add ->active() to all possible places here but no luck. I can't mess with ModelSearch, because then I would ruin basic index site functionality also. It's clear that I can use it when I'm doing so: Order::find()->active(), but now it's not the case. Do I have to create a new ModelSearch also? I hope it's not necessary. Many thanks!
Assuming you want filter by $id eg in your url /index2?id=10) you could try this way
public function actionIndex2($id) {
$searchModel = new ModelSearch;
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider->query->
andWhere(['id' => $id]);
...
return $this->render('index2', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
]);
}
So officially you have to add your ActiveQueries to the search method of your ModelSearch.
public function search($params) {
$query = Model::find()->activeQuery1()->activeQuery2();
$dataProvider = new ActiveDataProvider([
'query' => $query,
...
or
public function search($params) {
$query = Model::find();
$dataProvider = new ActiveDataProvider([
'query' => $query->activeQuery1()->activeQuery2(),
...
This means you have to create a new ModelSearch or a new search method in ModelSearch as many times as many combinations of ActiveQueries you wish to use.

Error 504 gateway time-out

I have a problem with download excel file from site. I use kartik\export\ExportMenu; I add into view in script this :
<?php
ini_set('max_execution_time', 300); //300 seconds = 5 minutes
$gridColumns = [
'order_number',
[
But it doesnt help. How could I fixed it?
public function actionIndex()
{
$searchModel = new OrderSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
There are three configurations that can help in case of timeouts but it's not guaranteed.
Options to set in ExportMenu widget configuration:
Large Files Linking
'stream' => false
Large Files Streaming
'stream' => false, // this will automatically save the file to a folder on web server
'streamAfterSave' => true, // this will stream the file to browser after its saved on the web folder
'deleteAfterSave' => true, // this will delete the saved web file after it is streamed to browser,
'target' => '_blank',
Batch Loading
'batchSize' => 10,
'target' => '_blank',
try increase the max_execution_time
<?php
ini_set('max_execution_time', 3600); //3600 seconds = 60 minutes
$gridColumns = [
'order_number',
[

LIMIT is not working in ActiveDataProvider

I am using following code and the limit doesnt work. But if I see the command than it shows limit in that, but when I pass the query to ActiveDataProvider it fetch all the records:
$data= User::find()->where(['category_id'=> 5])->orderBy(['rand()' => SORT_DESC])->limit(4);
$command = $data->createCommand();
$data2 = $command->queryAll();// This works fine and fetch only 4 data
$dataProvider = new ActiveDataProvider([
'query' => $data,
]); // But this displays all data without limit
What is wrong I am doing here?
Here is what happens when preparing models in yii\data\ActiveDataProvider:
/**
* #inheritdoc
*/
protected function prepareModels()
{
if (!$this->query instanceof QueryInterface) {
throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
}
$query = clone $this->query;
if (($pagination = $this->getPagination()) !== false) {
$pagination->totalCount = $this->getTotalCount();
$query->limit($pagination->getLimit())->offset($pagination->getOffset());
}
if (($sort = $this->getSort()) !== false) {
$query->addOrderBy($sort->getOrders());
}
return $query->all($this->db);
}
We're interested in this part:
if (($pagination = $this->getPagination()) !== false) {
$pagination->totalCount = $this->getTotalCount();
$query->limit($pagination->getLimit())->offset($pagination->getOffset());
}
So as you can see if pagination is not false, limit is managed automatically.
You can just set pagination to false and then manual setting of limit will work:
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => false,
]);
You can use Limit in the ActiveDataProvider and keep Pagination like so:-
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 50,
],
]);
$dataProvider->setTotalCount(1000);
This will make $this->getTotalCount() = 1000 and Keep the pagination.
Go here and here for more reference.