Yii2 - ActiveDataProvider "count" error when using a calculated field - yii2

I am trying to filter by a calculated column "age". I have tried the following:
public $age;
public function search($params)
{
$query = Profile::find();
$query->select = ['*', 'age' => 'TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE())'];
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$this->load($params);
if (!$this->validate()) {
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
]);
$query->andFilterWhere([
'between', 'age', $this->age_min, $this->age_max,
]);
return $dataProvider;
}
But I am getting the following error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'age' in 'where
clause' The SQL being executed was: SELECT COUNT(*) FROM profile
WHERE age BETWEEN '41' AND '62'
So I think what's happening is, it is trying to retrieve a count of matching records (for the ListView widget) but it's not taking in to account my custom "select" statement.
Does anybody know how to do this?

Actually you should use:
$query->andFilterWhere([
'between',
new Expression('TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE())'),
$this->age_min,
$this->age_max,
]);
There is no reason for use HAVING here, and it may create problems if you use it on query without GROUP BY clause.

I found the answer. I need to use andFilterHaving() instead of andFilterWhere() if I want to query a calculated column.

Related

Yii2 sort by with calculations in an ActiveDataProviderRecord

I would like to add a groupby with cacluations in yii2 ActiveDataProvider.
SO currently i have the following fields in my database
tbl_products
id, products, pts_log,pts_chum
SO i would like to group my data with the formula
pts_log * pts_chum / 100
So i have the following in my controller
ActiveDataProvider([
'query' => $query,
'sort' => ['defaultOrder' => ['(pts_log * pts_chum / 100)' => SORT_DESC]],
'pagination' => [
'pageSize' => $this->paginator['perPage']/2,
'page' => $this->paginator['page']
],
]);
But am now getting an error
undefined (pts_log * pts_chum / 100)
This works with one item key like pts_log. What do i add to make sorting work with a formulae.
You have to add an alias to the field the query is to be grouped by and use the alias in sort.
$query = (new \yii\db\Query())
->select ('count(*) as c, ((pts_log * pts_chum / 100) as calc_field')
->from('tbl_products')
->groupBy('calc_field');
$dataProvider = new \yii\data\ActiveDataProvider([
'query' => $query,
'sort' => ['defaultOrder' => ['calc_field' => SORT_DESC]],
]);
Use simple the desired aggregates instead of count(*).
In this case You getting an error undefined (pts_log * pts_chum / 100) because it is not column name and not valid expression.
You need create new variable in ActiveRecord model and and fill it with data from the calculated expression.
Then this variable name use in sort 'sort' => ['defaultOrder' => ['expr_item' => SORT_DESC]]
Look to http://webtips.krajee.com/filter-sort-summary-data-gridview-yii-2-0/ A similar option is considered here.
Or prepare $query like:
$tn = TblProdukts::tableName();
$query = TblProdukts::find()->select([
TblProdukts::tableName().'.*',
"(".$tn.".id*".$tn.".rank/100) AS expr"
]);
In Active Record class create variable $expr;
If needed add it to safe rules:
public function rules()
{
return [
[[
'expr'// expression
],
'safe'],
];
}

YIi2 - comparing two field in searchModel

I have a table called TPurchasing which contain data about a store's purchasing record.
TPurchasing
id | serial
transaction_time | timestamp without time zone
supplier | character varying(200)
total | double precision
paid | double precision
By using gii feature in yii2, I manage to generate a model called TPurchasingSearch from "CRUD Generator" menu. In the TPurchasingSearch, there is a function called search() which is used by the GridView in the view file.
public function search($params)
{
$query = TPurchasing::find();
$dataProvider = new ActiveDataProvider([
'pagination' => ['pageSize' => 20],
'query' => $query,
'sort' => ['defaultOrder' => ['transaction_time' => SORT_DESC]]
]);
$this->load($params);
if (!$this->validate()) {
$query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere(['is_removed' => 0]);
$query->andFilterWhere(['like', 'supplier_name', $this->supplier_name])
->andFilterWhere(['like', 'payment_type', $this->payment_type])
->andFilterWhere(['>', 'total', 'paid']);
return $dataProvider;
}
Right now I'm trying to display records that is still not totally paid off, which means the "paid" column is smaller than "total" column. How can I do this comparison in the search function? The above code gave me an error:
invalid input syntax for type double precision: "paid"
on this part
->andFilterWhere(['>', 'total', 'paid']);
since it tried to compare total column with string "paid". I might missed it, but I can't seem to find the related answer in Yii2 documentation.
No need to use andFilterWhere() here, you could simply try :
$query->andWhere('total > paid');

how to use relation table when using sqldataprovider

please help I dont know how to get relation table when using sqldataprovider. Anyone understand how to use relation model?
$model = new Finalresult();
$searchModel = new FinalresultSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider = new SqlDataProvider([
'sql' => 'SELECT finalresult.bib,
finalresult.series_id,
finalresult.category_id,
GROUP_CONCAT(DISTINCT finalresult.point ORDER BY series.serie_seri_no DESC) AS seriPoint
FROM finalresult, series GROUP BY finalresult.bib',
'key' => 'bib',
]);
I'm trying to get relation table:
'attribute'=>'category_id',
'width'=>'300px',
'value'=>function ($model, $key, $index, $widget) {
return $model->category->category_name;
},
then getting trying to non-object
You can't use relations with SqlDataProvider, because each single result will be presented as array, for example:
[
'id' => 1,
'name' => 'Some name',
'category_id' => 1,
],
For example, you can access category_id as `$model['category_id'].
SqlDataProvider is for very very complex queries, your query can easily be written as ActiveQuery and you can use ActiveDataProvider and get all advantages of that (relations, etc.).
You can find category by id, but it will be lazily loaded that means amount of queries is multiplied by number of rows.
With ActiveDataProvider and relations you can use eager loading and reduce amount of queries. Read more in official docs.
Grid Columns example in documentation
try to change "value" to
'value'=> function($data) {
return $data['category']['category_name'];
}

CackePHP 3 formatResults() ORDER by field created dynamically

I wish to sort on a field that I dynamically creates in formatResults()
$users = $this->Users
->find('all')
->formatResults(function ($users) use ($lat, $lng) {
return $users->map(function ($user) use ($lat, $lng) {
$user->distance = getDist($lat, $lng);
return $user;
});
})
->order([
'distance' => 'ASC'
]);
[...]
$this->set('users', $this->paginate($users));
The field $user->distance is not in the database.
$user->distance contains a float (getDist($lat, $lng);) that is variable depending on the position of the user at the time of his request.
->order([
'distance' => 'ASC'
]);
return an error : Column not found: 1054 Unknown column 'Users.distance' in 'order clause'
My question is this: Can I sort a field I have dynamically created when receiving information from the user?
You can sort by a field calculated in php, but it will not work for pagination, since for doing the sorting you need all the records from the database. You need to create an equivalent logic that runs in SQL so you can sort.
Check this plugin that can help you calculate the distances and sort by them https://github.com/dereuromark/cakephp-geo/
The is the find('distance') custom finder that you can dynamically add to your table when using the provided behavior. https://github.com/dereuromark/cakephp-geo/blob/master/src/Model/Behavior/GeocoderBehavior.php#L223

Yii 2 Gridview - Search Query generates ambiguous fields

I am generating related records search query for Gridview use
I get this error :
SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'dbowner' in where clause is ambiguous
The SQL being executed was: SELECT COUNT(*) FROM tbl_iolcalculation LEFT JOIN tbl_iolcalculation patient ON tbl_iolcalculation.patient_id = patient.id WHERE (dbowner=1) AND (dbowner=1)
I have two related models 1) iolcalculation and patient - each iolcalculation has one patient (iolcalculation.patient_id -> patient.id)
The relevant code in my model IolCalculationSearch is :
public function search($params)
{
$query = IolCalculation::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$dataProvider->sort->attributes['patient.lastname'] = [
'asc' => ['patient.lastname' => SORT_ASC],
'desc' => ['patient.lastname' => SORT_DESC],
];
$query->joinWith(['patient'=> function($query) { $query->from(['patient'=>'tbl_iolcalculation']); } ]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
'patient_id' => $this->patient_id,
'preop_id' => $this->preop_id,
'calculation_date' => $this->calculation_date,
'iol_calculated' => $this->iol_calculated,
The reason this error is generated is that each model has an override to the default Where clause as follows, the reason being that multiple users data needs to be segregated from other users, by the field dbowner:
public static function defaultWhere($query) {
parent::init();
$session = new Session();
$session->open();
$query->andWhere(['t.dbowner' => $session['dbowner']]);
}
this is defined in a base model extending ActiveRecord, and then all working models extend this base model
How Can I resolve this ambiguous reference in the MySQL code?
Thanks in advance
$query->andFilterWhere([
// previous filters
self::tableName() . '.structure_id' => $this->structure_id,
// next filters
]);
I think, that you are searching for table aliases.
(https://github.com/yiisoft/yii2/issues/2377)
Like this, of course you have to change the rest of your code:
$query->joinWith(['patient'=> function($query) { $query->from(['patient p2'=>'tbl_iolcalculation']); } ]);
The only way I can get this to work is to override the default scope find I had set up for most models, so that it includes the actual table name as follows - in my model definition:
public static function find() {
$session = new Session();
$session->open();
return parent::find()->where(['tbl_iolcalculation.dbowner'=> $session['dbowner']]);
}
There may be a more elegant way using aliases, so any advice would be appreciated - would be nice to add aliases to where clauses, and I saw that they are working on this....