How to use relations using joinWith in yii2 search model - yii2

I have two models with related data fbl_leagues and fbl_country tables. fbl_leagues have a column country_id which is related to fbl_country, now in yii2 gridView i was able to do something like [
'attribute' => 'country_id',
'value' => 'country.name',
], which gives the name of the country rather than the countries id, then i also want to enable searching by country name rather than country_id but i get the below error
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'country.name' in 'where clause'
The SQL being executed was: SELECT COUNT(*) FROM `fbl_leagues` LEFT JOIN `fbl_country` ON `fbl_leagues`.`country_id` = `fbl_country`.`id` WHERE `country`.`name` LIKE '%s%'
Below is my LeagueSearch model
class LeagueSearch extends League
{
/**
* {#inheritdoc}
*/
public function rules()
{
return [
[['id', 'created_at', 'updated_at'], 'integer'],
[['name','country_id'], '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 = League::find();
$query->joinWith('country');
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$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([
'id' => $this->id,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'country.name',$this->country_id]);
return $dataProvider;
}
}
and my league model
{
return [
[['country_id', 'created_at', 'updated_at'], 'integer'],
[['name','country_id'], 'required'],
[['name'], 'string', 'max' => 100],
[['country_id'], 'exist', 'skipOnError' => true, 'targetClass' => Country::className(), 'targetAttribute' => ['country_id' => 'id']],
];
}
/**
* {#inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'country_id' => Yii::t('app', 'Country Name'),
'name' => Yii::t('app', 'Name'),
'created_at' => Yii::t('app', 'Created At'),
'updated_at' => Yii::t('app', 'Updated At'),
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getCountry()
{
return $this->hasOne(Country::className(), ['id' => 'country_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getPerfectSelections()
{
return $this->hasMany(PerfectSelection::className(), ['league_id' => 'id']);
}
Now i noticed that when i comment out $query->joinWith('country'); then there will be no error but searching not working as expected

Hmm silly me i guess i later figure out the problem the table name is fbl_country so i suppose write fbl_country.name but wrote country.name in my search model, it's being a long day though, thanks for all

Related

yii2 custom validation addError message doesn't show

when i use custom validations on yii2 dynamic forms it doesn't show any error messages below the input field.Below I have posted my model.
It doesn't show any error messges when qty field gets validated
namespace frontend\models;
use Yii;
class OrderD extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'order_d';
}
public function rules()
{
return [
[['item_id', 'qty', 'price', 'value'], 'required'],
[['item_id'], 'integer'],
[['price', 'value'], 'number'],
[['order_code'], 'string', 'max' => 10],
[['item_id'], 'exist', 'skipOnError' => true, 'targetClass' => Item::className(), 'targetAttribute' => ['item_id' => 'id']],
[['order_code'], 'exist', 'skipOnError' => true, 'targetClass' => OrderH::className(), 'targetAttribute' => ['order_code' => 'code']],
['qty', 'validateQty']
];
}
public function validateQty($attribute)
{
$qty = $this->$attribute;
if ($qty >= 5)
{
$this->addError('qty', "qty validation successful");
}
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'item_id' => 'Item ID',
'order_code' => 'Order Code',
'qty' => 'Qty',
'price' => 'Price',
'value' => 'Value',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getItem()
{
return $this->hasOne(Item::className(), ['id' => 'item_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getOrderCode()
{
return $this->hasOne(OrderH::className(), ['code' => 'order_code']);
}
}
Be sure to know custom validations are php functions and not convert as javascript to validate in runtime .. those will work after the page has submit and send to controller ..
here is simple sample you want :
['qty','custom_function_validation'],
];
}
public function custom_function_validation($attribute, $params){
if($this->$attribute>5){
$this->addError($attribute,'it\'s more than 5');
}
}
To create a validator that supports client-side validation, you should implement the yii\validators\Validator::clientValidateAttribute() method which returns a piece of JavaScript code that performs the validation on the client-side.
public function clientValidateAttribute($model, $attribute, $view)
{
return <<<JS
// your validation
JS;
}
See the documentation here: http://www.yiiframework.com/doc-2.0/guide-input-validation.html#implementing-client-side-validation
Use method addError() in a controller and then just render your view file
Example
if ($promo_code) {
if ($promo_code->status == '1') {
$message = 'This combination is incorrect.';
$model->addError('promo_code', $message);
return $this->render('index', compact('model'));
}
$promo_code->status = '1';
$promo_code->save();
$model->save();
}

yii2 add search widget grid view

**how can i add girdView search to my relation table column ?
i have all girlview search box except my relation table
(developersActivity.developer_point) and ('developersActivity.project_done')
i have thier value but widget search box not apear above them**
my controller
$searchModel = new DeveloperSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('dashboard', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel
]);
my search model
class DeveloperSearch extends Developers
{
public function rules()
{
return parent::rules();
}
public function scenarios()
{
return Model::scenarios();
}
public function search($param)
{
$query = Developers::find();
$dataProvider = new ActiveDataProvider([
'query' => $query
]);
$this->load($param);
$query->joinWith('developersActivity');
$dataProvider->setSort([
'attributes'=> [
'name',
'developersActivity.developer_point'=>[
'asc'=>['developer_point'=>SORT_ASC],
'desc'=>['developer_point'=>SORT_DESC],
]
]
]);
$query->andFilterWhere([
'developer_id' => $this->developer_id,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'phone', $this->phone])
->andFilterWhere(['like', 'email', $this->email]);
return $dataProvider;
}
}
the model
class Developers extends ActiveRecord
{
public static function tableName()
{
return 'developers';
}
public function rules()
{
return [
[['name',
'family',
'phone',
'email',
'address',
'brithday',
'age',
'ability',
'role',
'join_date',
], 'required'],
[['developer_id'], 'integer'],
['email','email'],
[['phone'],'integer', 'min' => 10],
[['address'], 'string', 'max' => 100],
[['name'], 'string', 'min' => 3],
];
}
public function getDevelopersActivity(){
return $this->hasOne(DevelopersActivity::className(),['developer_activity_id' => 'developer_id']);
}
}
and developersActivity model class
class DevelopersActivity extends ActiveRecord
{
public function rules()
{
return [
[['developer_activity_id',
'developer_point',
'project_done',
'free_rate',
'address',
'estimate_for_next_project',
'date_project_done',
'number_of_project',
'activity_rate',
], 'safe'],
];
}
}
here is the view
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
'developer_id',
'name',
'email',
'phone',
'email',
'developersActivity.developer_point',
'developersActivity.project_done'
// 'value'=>'developersActivity.point',
//'contentOptions'=>['style'=>'width: 120px;']
],
]);
?>
in your model add a getter for the field (assuming the field is named actvityname)
/* Getter for deleveloer activity name */
public function getDevelopersActivityName() {
return $this->developersActivity->activityname;
}
in your searchModel
add a public var for your related field and declare as safe in rules
/* your calculated attribute */
public $activityName;
/* setup rules */
public function rules() {
return [
/* your other rules */
[['activityName'], 'safe']
];
}
and in the filter you can add the filter for your field
// filter by developer activity
$query->joinWith(['developersActivity' => function ($q) {
$q->where('yor_develeoper_activity_table.your_developer_activity_column LIKE "%' . $this->activityName. '%"');
}]);
return $dataProvider;
in gridview you can refer directly using
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
'fullName',
'activityName',
['class' => 'yii\grid\ActionColumn'],
]
you can take a look ad this tutorial for some suggestions http://www.yiiframework.com/wiki/621/filter-sort-by-calculated-related-fields-in-gridview-yii-2-0/

Getting unknown property error in Yii2

I know there are some answers related to this, but I couldn't find any useful ones.
I am getting this error: Getting unknown property: app\models\Employees::holidays and I've no idea what I'm doing wrong.
Could someone help me out to solve this?
This is my model code:
<?php
namespace app\models;
use Yii;
use yii\data\ActiveDataProvider;
use yii\db\ActiveRecord;
/**
* This is the model class for table "employee".
*
* #property integer $id
* #property string $name
* #property string $surname
* #property string $employment_date
*/
class Employee extends ActiveRecord
{
/** #const SCENARIO_CREATE scenario - create */
const SCENARIO_CREATE = 'create';
/** #const SCENARIO_EDIT scenario - edit */
const SCENARIO_EDIT = 'edit';
/** #const SCENARIO_SEARCH scenario - search */
const SCENARIO_SEARCH = 'search';
/** #const HOLIDAYS_PER_WORK_DAY Earned holidays per one working day. */
const HOLIDAYS_PER_WORK_DAY = 0.4;
/** #var integer $holidays calculated holidays field used in list. */
public $holidays;
/**
* #inheritdoc
*/
public static function tableName()
{
return 'employee';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
// id
['id', 'required', 'except' => self::SCENARIO_SEARCH],
['id', 'integer'],
// name
['name', 'required'],
['name', 'string', 'max' => 255],
// surname
['surname', 'required'],
['surname', 'string', 'max' => 255],
// employment date
['employment_date', 'required'],
['employment_date', 'match',
'pattern' => '/^([1][9]|[2][0])[0-9]{2}[-](0[1-9]|1[0-2])[-](0[1-9]|[1-2][0-9]|3[0-1])$/',
'message' => Yii::t('app',
'Neteisingas datos formatas (Formatas: YYYY-MM-DD)'),
],
['employment_date', 'date',
'format' => 'php:Y-m-d',
'max' => time(),
'tooBig' => Yii::t('app', 'Blogai įvesta data. Vėliausia galima data: ' . date('Y-m-d'))
],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'name' => Yii::t('app', 'Darbuotojo vardas'),
'surname' => Yii::t('app', 'Darbuotojo pavardė'),
'employment_date' => Yii::t('app', 'Įdarbinimo data'),
];
}
/**
* #inheritdoc
*/
public function scenarios()
{
return [
self::SCENARIO_CREATE => [
'name',
'surname',
'employment_date',
],
self::SCENARIO_EDIT => [
'id',
'name',
'surname',
'employment_date',
],
self::SCENARIO_SEARCH => [
'id',
'name',
'surname',
'employment_date',
'holidays',
],
];
}
/**
* Creates data provider instance with search query applied
*
* #param array $params
*
* #return ActiveDataProvider
*/
public function search($params)
{
$holidays = 'floor(datediff(curdate(), employment_date) * ' .
Employee::HOLIDAYS_PER_WORK_DAY . ')';
$query = Employees::find()->select([
$this->tableName() . '.id',
$this->tableName() . '.name',
$this->tableName() . '.surname',
$this->tableName() . '.employment_date',
$holidays . ' as holidays',
]);
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'defaultOrder' => ['name' => SORT_ASC],
],
]);
$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([
'id' => $this->id,
'employment_date' => $this->employment_date,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'surname', $this->surname]);
return $dataProvider;
}
}
My index.php file:
<?php
use app\models\EmployeeSearch;
use yii\data\ActiveDataProvider;
use yii\grid\GridView;
use yii\helpers\Html;
use yii\web\View;
use yii\widgets\Pjax;
/* #var $this View */
/* #var $searchModel EmployeeSearch */
/* #var $dataProvider ActiveDataProvider */
$this->title = Yii::t('app', 'Employees');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="employee-index">
<h1><?= Html::encode($this->title) ?></h1>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
<p>
<?= Html::a(Yii::t('app', 'Create Employee'), ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?php Pjax::begin(); ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
'name',
'surname',
'employment_date',
'holidays',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
<?php Pjax::end(); ?></div>
I know it's a quite long, but I don't know how much of the code I should include.
I think this line holidays in index php file is causing the error.
Add this on your rule .
['holidays', 'integer']
And use this $this->holidays during search.
There is something strange with your code:
My assumption is that you have 2 different models:
"Employee" and "Employees" in your app/models directory.
You are using "Employees" model in the search() of "Employee". For this to work you need to define:
public $holidays;
in your "Employees" model as your error suggests.
But my guess is that you wanted to use maybe "Employee" model for the search(). If this is the case, change the "Employees" to "Employee" in that function:
$query = Employee::find()...
you must comment all your index.php file and run your code. if you have not any error it means error is in index.php page.
then you must uncomment lines one by one and after each uncomment run your project.when you have error it means that line is one of your errors. then keep comment that line and uncomment other one by one and repeat that task to end of file.
in this way you can find your error.
Add the property for holidays in the model before class started:
#property string $holidays
Then it will work.

Yii2 Select2 - selected value on update - junction table (many to many)

I am successfully inserting new rows in my junction table (userlocations) on action create and I successfully update them on action update but the problem is on ation update the location_id field is always empty. It should retrieve the location_id's from userlocations table and populate the field on update but it doesnt.
Database: http://i.stack.imgur.com/JFjdz.png
UserController:
<?php
namespace backend\controllers;
use Yii;
use backend\models\User;
use backend\models\UserSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\helpers\ArrayHelper;
use backend\models\Locations;
use backend\models\Userlocations;
class UserController extends Controller
{
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* Lists all User models.
* #return mixed
*/
public function actionIndex()
{
$searchModel = new UserSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single User model.
* #param integer $id
* #return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new User model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$model = new User();
$locations = ArrayHelper::map(Locations::find()->all(), 'id', 'name');
$userlocations = new Userlocations();
if ($model->load(Yii::$app->request->post()) ) {
$model->setPassword($model->password);
$model->generateAuthKey();
$userlocations->load(Yii::$app->request->post());
if ($model->save() && !empty($userlocations->location_id)){
foreach ($userlocations->location_id as $location_id) {
$userlocations = new Userlocations();
$userlocations->setAttributes([
'location_id' => $location_id,
'user_id' => $model->id,
]);
$userlocations->save();
}
}
return $this->redirect(['user/index']);
} else {
return $this->render('create', [
'model' => $model,
'locations' => $locations,
'userlocations' => $userlocations,
]);
}
}
/**
* Updates an existing User model.
* If update is successful, the browser will be redirected to the 'view' page.
* #param integer $id
* #return mixed
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
$locations = ArrayHelper::map(Locations::find()->all(), 'id', 'name');
$userlocations = new Userlocations();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
Userlocations::deleteAll(['user_id' => $id]);
$userlocations->load(Yii::$app->request->post());
if (!empty($userlocations->location_id)){
foreach ($userlocations->location_id as $location_id) {
$userlocations = new Userlocations();
$userlocations->setAttributes([
'location_id' => $location_id,
'user_id' => $model->id,
]);
$userlocations->save();
}
}
return $this->redirect(['user/index']);
} else {
return $this->render('update', [
'model' => $model,
'locations' => $locations,
'userlocations' => $userlocations,
]);
}
}
/**
* Deletes an existing User model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* #param integer $id
* #return mixed
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the User model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* #param integer $id
* #return User the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = User::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
User model:
class User extends \common\models\User
{
public $password;
/**
* #inheritdoc
*/
public static function tableName()
{
return 'User';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['username', 'password'], 'required'],
[['status', 'created_at', 'updated_at'], 'integer'],
[['username', 'password_hash', 'password_reset_token', 'email'], 'string', 'max' => 255],
[['auth_key'], 'string', 'max' => 32],
[['username'], 'unique', 'message' => 'Username already taken!'],
[['email'], 'unique'],
[['password_reset_token'], 'unique'],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'username' => 'Username',
'auth_key' => 'Auth Key',
'password_hash' => 'Password Hash',
'password_reset_token' => 'Password Reset Token',
'email' => 'Email',
'status' => 'Status',
'created_at' => 'Created At',
'updated_at' => 'Updated At',
];
}
}
My form:
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\helpers\ArrayHelper;
use kartik\select2\Select2;
use backend\models\Locations;
?>
<div class="user-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'username')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'password')->passwordInput(['maxlength' => true]) ?>
<?= $form->field($model, 'email')->textInput(['maxlength' => true]) ?>
<?= $form->field($userlocations, 'location_id')->widget(Select2::classname(), [
'data' => ArrayHelper::map(Locations::find()->all(), 'id', 'name'),
'size' => Select2::MEDIUM,
'options' => ['placeholder' => 'Select a location ...', 'multiple' => true],
'pluginOptions' => [
'allowClear' => true,
],
]); ?>
<?= $form->field($model, 'status')->dropDownList(['10' => 'Active', '0' => 'Inactive']) ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
For multiple location select, use the plugin code as given below.
<?= Select2::widget([
'name' => 'Userlocations[location_id]',
'value' => $location_ids, // initial value
'data' => ArrayHelper::map(Locations::find()->all(), 'id', 'name'),
'options' => ['placeholder' => 'Select your locations...', 'multiple' => true],
'pluginOptions' => [
'tags' => true,
'maximumInputLength' => 10
],
]); ?>
$location_ids will be the location array you have previously selected during creation time.Also remember when you are making changes for more than one table,make sure you do it in a transaction.

Sort and search column when I'm querying with findbysql in yii2

I'm searching four tables and joined them and got the output I want. But unable to sort or filter the output. Please tell me how I can search it by district or a sell range or collection range.
PartiesSearch model is -
<?php
namespace frontend\modules\districtreport\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use frontend\modules\districtreport\models\Parties;
use frontend\modules\districtreport\models\Bills;
use frontend\modules\districtreport\models\Payment;
use yii\db\Query;
use yii\db\Command;
$query = \Yii::$app->db;
/**
* PartiesSearch represents the model behind the search form about `frontend\modules\districtreport\models\Parties`.
*/
class PartiesSearch extends Parties
{
/**
* #inheritdoc
*/
public function rules()
{
return [
[['party_id'], 'integer'],
[['parties_partyname', 'address', 'parties_district', 'name_manager', 'transport', 'dlno', 'instruction', 'con', 'district','sale','sell','collection'], '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)
{
$sql = 'select
tsell.district as district,
tsell.totalsale as sell,
coalesce(tcollection.collection,0) as collection
from
(SELECT
district,
coalesce(sell.sale,0) as totalsale
FROM `districts`
left join
(SELECT
parties_district,
billdate,
sum(billamount) as sale
FROM `bills`
left join parties on bills.bills_partyname = parties.parties_partyname
group by parties_district) as sell
on sell.parties_district = districts.district) as tsell
left join
(SELECT
parties_district,
payment_date,
COALESCE(sum(payment_amount),0) as collection
FROM `payment`
left join parties on payment.payment_partyname = parties.parties_partyname
group by parties_district) as tcollection
on tsell.district = tcollection.parties_district';
$query = Parties::findBySql($sql);
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
//'sort'=> ['defaultOrder' => ['district'=>SORT_DESC]]
]);
$dataProvider->setSort([
'attributes' => [
'sell' => [
'asc' => ['sell' => SORT_ASC],
'desc' => ['sell' => SORT_DESC],
'label' => 'Sell'
],
'collection' => [
'asc' => ['collection' => SORT_ASC],
'desc' => ['collection' => SORT_DESC],
'label' => 'Collection'
],
'district' => [
'asc' => ['tsell.district' => SORT_ASC],
'desc' => ['tsell.district' => SORT_DESC],
'label' => 'District'
]
]
]);
$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([
'party_id' => $this->party_id,
]);
$query->andFilterWhere(['like', 'parties_partyname', $this->parties_partyname])
->andFilterWhere(['like', 'address', $this->address])
->andFilterWhere(['like', 'parties_district', $this->parties_district])
->andFilterWhere(['like', 'name_manager', $this->name_manager])
->andFilterWhere(['like', 'transport', $this->transport])
->andFilterWhere(['like', 'dlno', $this->dlno])
->andFilterWhere(['like', 'instruction', $this->instruction])
->andFilterWhere(['like', 'con', $this->con])
->andFilterWhere(['like', 'sell', $this->sell])
->andFilterWhere(['like', 'collection', $this->collection])
->andFilterWhere(['like', 'district', $this->district]);
return $dataProvider;
}
}
Parties Model
<?php
namespace frontend\modules\districtreport\models;
use Yii;
/**
* This is the model class for table "parties".
*
* #property integer $party_id
* #property string $parties_partyname
* #property string $address
* #property string $parties_district
* #property string $name_manager
* #property string $transport
* #property string $dlno
* #property string $instruction
* #property string $con
*/
class Parties extends \yii\db\ActiveRecord
{
public $sale;
public $district;
public $sell;
public $collection;
public $bills;
public $partyname;
public $billdate;
//public $sale;
/**
* #inheritdoc
*/
public static function tableName()
{
return 'parties';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['parties_partyname', 'parties_district', 'name_manager'], 'required'],
[['parties_partyname'], 'string', 'max' => 60],
[['address', 'instruction'], 'string', 'max' => 100],
[['parties_district'], 'string', 'max' => 20],
[['name_manager', 'transport', 'dlno'], 'string', 'max' => 30],
[['con'], 'string', 'max' => 10],
[['parties_partyname'], 'unique'],
[['name_manager'], 'exist', 'skipOnError' => true, 'targetClass' => Managers::className(), 'targetAttribute' => ['name_manager' => 'manager_managername']],
[['con'], 'exist', 'skipOnError' => true, 'targetClass' => Console::className(), 'targetAttribute' => ['con' => 'console']],
[['parties_district'], 'exist', 'skipOnError' => true, 'targetClass' => Districts::className(), 'targetAttribute' => ['parties_district' => 'district']],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'party_id' => 'Party ID',
'parties_partyname' => 'Parties Partyname',
'address' => 'Address',
'parties_district' => 'Parties District',
'name_manager' => 'Name Manager',
'transport' => 'Transport',
'dlno' => 'Dlno',
'instruction' => 'Instruction',
'con' => 'Con',
];
}
public function getDistricts()
{
return $this->hasOne(Districts::className(), ['district' => 'parties_district']);
}
public function getBills()
{
return $this->hasMany(Bills::className(), ['bills_partyname' => 'parties_partyname']);
}
public function getPayment()
{
return $this->hasMany(Payment::className(), ['payment_partyname' => 'parties_partyname']);
}
}
index.php
<?php
use yii\helpers\Html;
use kartik\grid\GridView;
//use kartik\widgets\DatePicker;
use kartik\daterange\DateRangePicker;
use kartik\form\ActiveForm;
use dosamigos\datepicker\DatePicker;
use frontend\modules\districtreport\models\ExpartiesSearch;
/* #var $this yii\web\View */
/* #var $searchModel frontend\modules\districtreport\models\PartiesSearch */
/* #var $dataProvider yii\data\ActiveDataProvider */
$this->title = 'Parties';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="parties-index">
<h1><?= Html::encode($this->title) ?></h1>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
<!-- <p>
<?= Html::a('Create Parties', ['create'], ['class' => 'btn btn-success']) ?>
</p> -->
<!-- <div class="custom-filter">
Date range:
<input name="start" />
<input name="end" />
</div> -->
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'export' => false,
'columns' => [
[
//['class' => 'yii\grid\SerialColumn'],
'class' => 'kartik\grid\ExpandRowColumn',
'value' => function($model, $key, $index, $column){
return GridView::ROW_COLLAPSED;
},
'detail' => function($model, $key, $index, $column){
$searchModel = new ExpartiesSearch();
$searchModel-> district = $model->district;
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return Yii::$app->controller->renderPartial('_exparties', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
},
],
//['class' => 'yii\grid\SerialColumn'],
'district',
// [
// 'attribute' => 'date',
// 'value' => 'tsell.date',
// 'filter' => \yii\jui\DatePicker::widget(['language' => 'ru', 'dateFormat' => 'dd-MM-yyyy']),
// 'format' => 'html',
// ],
'sell',
'collection',
//['class' => 'yii\grid\ActionColumn'],
],
]); ?>
</div>
In this picture we can see that though the sell sort is there but it's not sorting the data actually.
In picture 2 we can see that though the data assam is passed to the next level of kartik expandrow, it's not filtering.
According to yii2's docs you're using an ActiveDataProvider with an ActiveRecord inside. Here's the example docs page for your case.
I think what you want to do is to return $dataProvider->getModels() rather than just return $dataProvider. This will return presumably all rows, since I don't see a pagination mechanism (there could be a default pagination count).
For sorting, you could try sorting on the $query rather than the $dataProvider. Try something like:
$query->orderBy([
'tsell.sell' => SORT_ASC
]);
Setup a grid view with a data provider and filter model.
In your view:
<?= GridView::widget([
'dataProvider'=> $dataProvider,
'filterModel' => $searchModel,
'columns' => [
'parties_partyname',
'address',
'parties_district',
'name_manager',
'transport',
'dlno',
'instruction',
'con',
'district',
'sale',
'sell',
'collection'
]
]); ?>
In your controller (replace index with the name of your view file):
$searchModel = new \frontend\modules\districtreport\models\PartiesSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
Try removing $dataProvider->setSort() from your search model.
Also, in your detail of your gridview you are using the same variable name for both search models and data providers. They are two separate models so the variables should not be the same.
<?php
namespace frontend\modules\districtreport\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use frontend\modules\districtreport\models\Parties;
use frontend\modules\districtreport\models\Bills;
use frontend\modules\districtreport\models\Payment;
use yii\db\Query;
use yii\db\Command;
/**
* PartiesSearch represents the model behind the search form about `frontend\modules\districtreport\models\Parties`.
*/
class PartiesSearch extends Parties
{
/**
* #inheritdoc
*/
public function rules()
{
return [
[['party_id'], 'integer'],
[['parties_partyname', 'address', 'parties_district', 'name_manager', 'transport', 'dlno', 'instruction', 'con', 'district','sale','sell','collection'], '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)
{
$sql = 'select
tsell.district as district,
tsell.totalsale as sell,
coalesce(tcollection.collection,0) as collection
from
(SELECT
district,
coalesce(sell.sale,0) as totalsale
FROM `districts`
left join
(SELECT
parties_district,
billdate,
sum(billamount) as sale
FROM `bills`
left join parties on bills.bills_partyname = parties.parties_partyname
group by parties_district) as sell
on sell.parties_district = districts.district) as tsell
left join
(SELECT
parties_district,
payment_date,
COALESCE(sum(payment_amount),0) as collection
FROM `payment`
left join parties on payment.payment_partyname = parties.parties_partyname
group by parties_district) as tcollection
on tsell.district = tcollection.parties_district';
$query = Parties::findBySql($sql);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$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;
}
$query->andFilterWhere([
'party_id' => $this->party_id,
'parties_partyname' => $this->parties_partyname,
'address' => $this->address,
'parties_district' => $this->parties_district,
'name_manager' => $this->name_manager,
'transport' => $this->transport,
'dlno' => $this->dlno,
'instruction' => $this->instruction,
'con' => $this->con,
'sell' => $this->sell,
'collection' => $this->collection,
'district' => $this->district,
]);
return $dataProvider;
}
}
Simplified Search Model that works for me:
<?php
namespace common\models\event;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use common\models\Model;
class ModelSearch extends Model
{
/**
* #inheritdoc
*/
public function rules()
{
return [
[['id', 'name'], '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 = Model::find()->indexBy('id');
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => false
]);
$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;
}
$query->andFilterWhere([
'id' => $this->id,
'name' => $this->name,
]);
return $dataProvider;
}
}
According to http://www.yiiframework.com/doc-2.0/yii-db-activerecord.html#findBySql%28 code with findbysql cannot be sorted or filtered.
However, it can be sorderd by building query following page - http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html
The query will be like -
$subQuery1 = (new Query())->select(['parties_district','billdate','sum(billamount) as sale'])->from ('parties')->join('LEFT JOIN','bills','bills.bills_partyname = parties.parties_partyname')->groupby('parties_district')->where('billdate != "NULL"');
$subQuery2 = (new Query())->select(['district','coalesce(sell.sale,0) as totalsale'])->from('districts')->leftJoin(['sell' => $subQuery1],'sell.parties_district = districts.district');
$subQuery3 = (new Query())->select(['parties_district','payment_date','COALESCE(sum(payment_amount),0) as collection'])->from('payment')->join('LEFT JOIN','parties','payment.payment_partyname = parties.parties_partyname')->groupby('parties_district');
$query = (new Query())->select(['tsell.district as district','tsell.totalsale as sell','coalesce(tcollection.collection,0) as collection'])->from(['tsell'=> $subQuery2])->leftJoin(['tcollection' => $subQuery3],'tcollection.parties_district = tsell.district');
Should anyone run into this issue all these years later: queries built with the findBySql method will be sortable (and pagination will work too, which doesn't with ActiveDataProvider + findBySql) if you pass them to an ArrayDataProvider instead of an ActiveDataProvider, like this:
$query = MyModel::findBySql("SELECT * FROM ...");
$dataProvider = new ArrayDataProvider([
'allModels' => $query->all(),
'sort' => [
'attributes' => [
'model_id',
'model_name'
],
'defaultOrder' => [
'model_name' => SORT_ASC
]
],
'pagination' => [
'pageSize' => 30
]
]);
The traditional filtering methods won't work. From the findBySql() docs:
Note that because the SQL statement is already specified, calling additional query modification methods (such as where(), order()) on the created yii\db\ActiveQuery instance will have no effect.
I've come up with a workaround, it requires slightly more work than using the interface methods, but for complex queries (which I assume is why you are trying to use findBySql) it works:
public function search($params)
{
$this->load($params);
$filters = [];
if ($this->model_id) $filters[] = ' "table"."model_id" = '.$this->model_id.' ';
if ($this->model_name) $filters[] = ' "table"."model_name" ILIKE \'%'.$this->model_name.'%\' ';
// ugly ifs can be put into a foreach if you don't need to customize each condition too much
$filterCondition = count($filters) > 0 ? ' AND ' . implode(' AND ', $filters) : ' ';
$query = MyModel::findBySql(
// your query here
.$filterCondition.
// group by, order by etc.
);
// assemble the ArrayDataProvider (see above)
return $dataProvider;
}
Yii version: 2.0.34, PHP version: 7.3.18, PostgreSQL version: 12.2