Yii2 type cast column as integer - php

In Yii2, I have a model, for example Product. What I want to do is select an extra column from the database as int
This is an example of what I'm doing:
Product::find()->select(['id', new Expression('20 as type')])
->where(['client' => $clientId])
->andWhere(['<>', 'hidden', 0]);
The problem is, I'm getting the result as "20". In other words, 20 is returned as string. How can I make sure that the selected is Integer?
I tried the following also but its not working:
Product::find()->select(['id', new Expression('CAST(20 AS UNSIGNED) as type')])
->where(['client' => $clientId])
->andWhere(['<>', 'hidden', 0]);

You can manually typecast in Product's afterFind() function or use AttributeTypecastBehavior.
But above all, you'll have to define a custom attribute for alias you use in the query. For example $selling_price in your Product model if you use selling _price as an alias.
public $selling_price;
After that, you can use any of the following approaches.
1) afterFind
Example Below
public function afterFind() {
parent::afterFind();
$this->selling_price = (int) $this->selling_price;
}
2) AttributeTypecastBehavior
Example below
public function behaviors()
{
return [
'typecast' => [
'class' => \yii\behaviors\AttributeTypecastBehavior::className(),
'attributeTypes' => [
'selling_price' => \yii\behaviors\AttributeTypecastBehavior::TYPE_INTEGER,
],
'typecastAfterValidate' => false,
'typecastBeforeSave' => false,
'typecastAfterFind' => true,
],
];
}

Related

Yii2 GridView Filters behaving not working properly

Yii2 Gridiview filter not working properly. Selecting one filter have impact on other filters. Changing one filter (dropdown) auto-select the values of other filters (dropdowns). This problem also exists in URL as well, changing one filter appends the other filters in URL as well and result shown as combined. but in reality only one filter should be applied which is being changed.
// Search Model, adding dummy table names
public function search($params)
{
$query = Model::find()->with('model_b');
if (empty($params['sort'])) {
$query->orderBy("group, " . Model::getSortByType() . ', "title"');
}
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'attributes' => [
'code',
'title',
'updated_at'
]
]
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'type' => $this->type,
'price_type' => $this->price_type,
'status' => $this->status,
'terms_related' => $this->terms_related,
'required' => $this->required,
'group' => $this->group,
]);
$query->andFilterWhere(['ilike', 'title', $this->title]);
$query->andFilterWhere(['is_qr' => $this->is_qr]);
return $dataProvider;
}
//Controller
public function actionIndex()
{
$searchModel = new ModelSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render("index", [
"searchModel" => $searchModel,
"dataProvider" => $dataProvider,
]);
}
// In View, the filter I change
[
'attribute' => 'is_qr',
'format' => 'boolean',
'filter' => [1 => 'TRUE', 0 => 'FALSE'],
'content' => function ($service) { return ((int) $service->is_qr === 1) ? 'TRUE' : 'FALSE'; }
],
// the filter being changed with above filter
[
'attribute' => 'terms_related',
'filter' => array(0 => 'FALSE', 1 => 'TRUE'),
'content' => function ($service) { return ((int) $service->terms_related === 1) ? 'TRUE' : 'FALSE'; }
]
Observations:
Consider I have 5 filters in a GridView.
Action 1: I changed a filter, only that filter is applied first time but after page reload, other filters are being populated with values with "0". Because on selecting one filter, all filters are being pushed in URL with empty values other than selected one. And filters with empty values are being applied to rest of the filters with "0" value
Problem
The problem is, once I select a filter, gridview sends all possible filters in URL. The filters I did not select, have empty values.
Yii::$app->request->queryParams
This has all filters and the filters other than I selected have empty values, and
$this->load($params);
in search() deals empty values as 0. So, filters that I have not touched are being populated with "0" value.
I have found the solution, it is a custom solution but works for me.
I created a Trait
trait ParamsTrimable
{
public function trimParams($params, $modelClass)
{
$modelClass = basename(str_replace('\\', '/', $modelClass));
if ($params[$modelClass]) {
$params[$modelClass] = array_filter($params[$modelClass], function ($value) {
return ($value !== '');
});
}
return $params;
}
}
And before
$this->load($params);
I called trait's function i.e.
$params = $this->trimParams($params, static::class);
$this->load($params);
Reason behind the trait solution is, this problem may occur in other listings as well. To fix, we only need to use trait and call the function to remove empty values from params.

Kartik DateRangePicker with two attributes cause invalidDateFormat and NaN on range date selection

I've a question about the Kartik DateRangePicker Widget that i use in my gridview for filter some results.
in my SearchModel i've created two attributes
public $date_start;
public $date_end;
i use these for filter a field in database 'insert_date'.
In my view, as grid configuration, i've set these options
[
'attribute' => 'insert_date',
'options' => ['class' => 'gridview-date-column'],
'filter' => DateRangePicker::widget([
'model' => $searchModel,
'name' => 'insert_date',
'attribute' => 'insert_date',
'startAttribute' => 'date_start',
'endAttribute' => 'date_end',
'convertFormat'=>true,
'pluginOptions' => [
'opens'=>'right',
'locale' => [
'cancelLabel' => 'Clear',
'format' => 'd-m-Y',
],
]
]),
'format' => ['date', Yii::$app->formatter->datetimeFormat],
'contentOptions'=>['style'=>'min-width: 200px;']
],
By default $date_start and $date_end haven't a value, so , when i enter in my view and try to filter this field i get an 'invalidDate' error and a series of NaN on the calendars.
This is fixed if i set a value for these two fields or if remove them from the configuration ( so i can only use insert_date attribute as string with these two ranges for filtering ).
Looking in the plugin repository i've found the same case and as response of the author
This problem occurs because you have an invalid date format for the data that does not match the plugin's format.
But as empty these fields can never have a correct data format.
Someone had the same problem?
Thanks in advance for all the responses.
I've got the solution:
In Kartik plugin there's a Behavior Model that i can include in my searchModel:
use kartik\daterange\DateRangeBehavior;
Then i can instantiate it overriding behaviors() method:
public $date_start;
public $date_end;
public function behaviors() {
return [
[
'class' => DateRangeBehavior::className(),
'attribute' => 'insert_date',
'dateStartAttribute' => 'date_start',
'dateEndAttribute' => 'date_end',
'dateStartFormat' => 'd-m-Y',
'dateEndFormat' => 'd-m-Y',
]
];
}
and in the search() method:
public function search($params)
{
// Some other filters
if($this->date_start && $this->date_end) {
// filtered query
}
}
Hope this can help someone else.

merge on column based relational table in gridview yii2

in index.php :
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'ID_REQUEST',
'NOMOR_SURAT',
[
'label' => 'Nama Depan',
'attribute' => 'ID_KARYAWAN',
'value' => 'iDKARYAWAN.FIRST_NAME'
],
[
'label' => 'Nama Belakang',
'attribute' => 'ID_KARYAWAN',
'value' => 'iDKARYAWAN.LAST_NAME'
],
which is iDKARYAWAN is relation from another table in my model
class Request extends \yii\db\ActiveRecord {
/**
* #inheritdoc
*/
public static function tableName() {
return 'ytms_it.request';
}
public function getIDKARYAWAN() {
return $this->hasOne(Karyawan::className(), ['ID_KARYAWAN' => 'ID_KARYAWAN'])->from(Karyawan::tableName(). ' b');
}
How to merge those two column ?
For the elp, thanks.
Create method called getFullName() in related model and calculate full name using PHP concatenation:
use yii\helpers\Html;
...
/**
* #return string
*/
public function getFullName()
{
return Html::encode($this->name . ' ' . $this->surname);
}
Optionally define a label for it in attributeLabels() method of related model:
`fullName` => 'Label for full name',
Then in GridView it's possible to display full name of related model in one column like so:
1) The shortest form:
'relatedModel.fullName',
2) Overriding the label:
[
'attribute' => 'relatedModel.fullName',
'label' => 'Overrided label',
],
3) Using closure:
[
'attribute' => 'relatedModel.fullName', // The attribute name can be different in this case
'value' => function ($model) {
// You can calculate full name here.
// But it's better to just call according method since view is only for display.
return $model->author->fullName;
},
],
Another way is to calculate full name using SQL and include as a part of query result in separate column.
Use Active Record - Selecting extra fields official docs section as a guide, also see this related issue on Github - JoinWith - assign a column aliases to an attribute of related model.
Add $fullName as public property of related model class. Modify query like so:
use yii\db\Expression;
...
->joinWith(['relatedModel' => function (\yii\db\ActiveQuery $query) {
$query->addSelect('fullName' => new Expression("CONCAT(name, ' ', surname)")]);
}]
Then to display it in GridView column you can use one of the options desribed above, for example:
'relatedModel.fullName'

Yii2 how to anchor the value of the relationship in the GridView

I am beginner. I explain the topic:
there is this relationship in the Ticket model:
public function getTyp()
{
return $this->hasOne(Typology::className(), [ 'id' =>'typ_id']);
}
and in the ticket table there is the typ_id column (it is in relationship with the id of the Typology table).
In the view views/ticket/index.php there is GridView::widgetwith these columns:
[
'attribute' => 'typ_id',
'value' => 'typ.typology'
],
I want to anchor the value of the relationship.
I have tried this:
[
'attribute' => 'typ_id',
'value' => function ($model) {
return Html::a (
'typ.typology',
'/typology/view?id='.$model->typ_id
);
}
]
but it doesn't work
someone can help me?
Html::a() interprets typ.typology as raw string. Use $model in value closure to get necessary property through relation.
Also instead of manually concatenate the url with its parameters, just pass them in array (see Url::to() to understand how link is constructed).
[
'attribute' => 'typ_id',
'value' => function ($model) {
return Html::a($model->typ->typology, ['/typology/view', 'id' => $model->typ_id]);
},
],

Filter setup for related model in GridView

I am trying to setup the filter for related model in Yii2's GridView widget, but I am keep getting the error like the filter value must be an integer.
I have followed this question. Now, I have a two models Services.php and ServiceCharge.php.
In ServiceCharge.php the relation is setup like:
public function getServiceName()
{
return $this->hasOne(Services::className(),['id'=>'service_name']);
}
In the ServiceChargeSearch.php the code is like this:
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\ServiceCharges;
/**
* ServiceChargesSearch represents the model behind the search form about `app\models\ServiceCharges`.
*/
class ServiceChargesSearch extends ServiceCharges
{
/**
* #inheritdoc
*/
public function attributes()
{
// add related fields to searchable attributes
return array_merge(parent::attributes(), ['serviceName.services']);
}
public function rules()
{
return [
[['id'], 'integer'],
[['charges_cash', 'charges_cashless'], 'number'],
[['id', 'serviceName.services', 'room_category'], 'safe'],
];
}
/**
* #inheritdoc
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* #param array $params
*
* #return ActiveDataProvider
*/
public function search($params)
{
$query = ServiceCharges::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$dataProvider->sort->attributes['serviceName.services'] = [
'asc' => ['serviceName.services' => SORT_ASC],
'desc' => ['serviceName.services' => SORT_DESC],
];
$query->joinWith(['serviceName']);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
// 'service_name' => $this->service_name,
'room_category' => $this->room_category,
'charges_cash' => $this->charges_cash,
'charges_cashless' => $this->charges_cashless,
])
->andFilterWhere(['LIKE', 'serviceName.services', $this->getAttribute('serviceName.services')]);
return $dataProvider;
}
}
and in my Gridview it is setup like this:
[
'attribute'=>'service_name',
'value'=>'serviceName.services',
],
Which is showing the services name from the related model correctly.
I am not able to see what I am doing wrong, but the filter field for the attribute for service is not showing at all.
Actually it is much simpler than it seems.
add the column_name to safe attribute.
Note: this should be relation Name
add the join with query - like - $query->joinWith(['serviceName','roomCategory']);
add the filter condition like:
->andFilterWhere(['like', 'services.services', $this->service_name])
->andFilterWhere(['like', 'room_category.room_category', $this->room_category]);
if like to add sorting add the code like:
$dataProvider->sort->attributes['service_name'] = [
'asc' => ['services.services' => SORT_ASC],
'desc' => ['services.services' => SORT_DESC],
];
$dataProvider->sort->attributes['room_category'] = [
'asc' => ['room_category.room_category' => SORT_ASC],
'desc' => ['room_category.room_category' => SORT_DESC],
];
5 you should also set the relation name say public $roomCategory
That's it. Both sorting and filtering for related table works perfectly.
Note: Remove default validation like integer for related column and default filtering generated by gii otherwise it will generate an error.
Update on Latest version:
Adding Public $attribute is not needed.
Adding safe attribute for relation is also not needed.
but the attribute in your current model, which you want filter is
to added to safe attribute that is a must.
and most importantly in your gridview, the related attribute has to
be in closure format.
that is example
[
'attribute=>'attribute_name',
'value=function($data){
return $data->relationname->related_table_attribute_name
}
],
remember it you are using relation_name.related_table_attribute_name filter somehow doesn't work for me.
There is a fairly comprehensive set of instructions on the Yii Framework website. The only thing to note is that the search model complains about the following lines, but everything appears to work as intended without them:
$this->addCondition(...);
For a model, PaymentEvent (table: subs_payment_event), which has a currency_id field linked to model Currency, this is the complete set of additional code (using the Basic template):
In the main model, PaymentEvent.php:
public function getCurrencyName()
{
return $this->currency->name;
}
In the search model, PaymentEventSearch.php:
public $currencyName;
In its rules:
[['currencyName'], 'safe'],
In the attributes of its setSort statement, include:
'currencyName' => [
'asc' => ['subs_currency.name' => SORT_ASC],
'desc' => ['subs_currency.name' => SORT_DESC],
'label' => 'Currency'
],
Before the grid filtering conditions:
$query->joinWith(['currency' => function ($q) {
$q->where('subs_currency.name LIKE "%' . $this->currencyName . '%"');
}]);
Finally, in the GridView columns array in the view (including my usual link across to the related model records):
[
'attribute' => 'currencyName',
'label' => 'Currency',
'format' => 'raw',
'value' => function ($data) {
return Html::a($data->currency->name, ['/currency/' . $data->currency_id]);
},
],

Categories