Error select SQL using expression DBQuery Yii2 - yii2

iam using this to union 3 unrelated table. in table B and C works fine. but table A is getting error
$tableA = (new \yii\db\Query())
->select([new Expression('sm_wp.no_agenda,"Wajib Pajak" as kategori,mfwp.nama_wp,
user.nama,sm_wp.jenis_surat'),
"sm_wp.nomor_surat, sm_wp.tgl_surat, sm_wp.perihal",])
->from('sm_wp')
->leftjoin('mfwp', 'sm_wp.id_mfwp = mfwp.id')
->leftjoin('user', 'user.id = sm_wp.id_user')
;
error :
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '.perihal FROM sm_wp LEFT JOIN mfwp ON sm_wp.id_mfwp = mfwp.id LEFT JOIN `u' at line 2
if i correct to be like this, its work..
$tableA = (new \yii\db\Query())
->select([new Expression('sm_wp.no_agenda,"Wajib Pajak" as kategori,mfwp.nama_wp,
user.nama,sm_wp.jenis_surat, sm_wp.nomor_surat, sm_wp.tgl_surat, sm_wp.perihal'),])
->from('sm_wp')
->leftjoin('mfwp', 'sm_wp.id_mfwp = mfwp.id')
->leftjoin('user', 'user.id = sm_wp.id_user')
;
its work(data appears in gridview as integer). but when i want to use if statement to define sm_wp.jenis_surat to be a string in gridview , no data appears (value not set).
whats wrong with my code..thanks for advice
update:here is my modelsearch
<?php
namespace app\models;
use yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\Smwp;
use app\models\Mfwp;
use app\models\Pejabat;
use yii\db\Expression;
class RekapsmSearch extends Rekapsm
{
/**
* {#inheritdoc}
*/
public $nama_wp;
public $kategori;
public $nama;
public function rules()
{
return [
[['no_agenda', 'jenis_surat'], 'integer'],
[['perihal', 'nama_wp', 'nama', 'kategori', 'nomor_surat', 'tgl_surat'], '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)
{
$tableA = (new \yii\db\Query())
->select([new Expression('sm_wp.no_agenda,"Wajib Pajak" as kategori,mfwp.nama_wp,
user.nama, sm_wp.jenis_surat,
sm_wp.nomor_surat, sm_wp.tgl_surat, sm_wp.perihal'),])
->from('sm_wp')
->leftjoin('mfwp', 'sm_wp.id_mfwp = mfwp.id')
->leftjoin('user', 'user.id = sm_wp.id_user')
;
$tableB = (new \yii\db\Query())
->select([new Expression('sm_seksi.no_agenda, "Seksi" as kategori,
mfseksi.nama_seksi,user.nama, if(sm_seksi.jenis_surat=1,"Permintaan Laporan",
if(sm_seksi.jenis_surat=2,"Permintaan Data",
if(sm_seksi.jenis_surat=3,"Permintaan Profile",
"Lain - Lain")))
'),"concat('ND-',sm_seksi.nomor_surat,'',mfseksi.kode_surat,
date_format(sm_seksi.tgl_surat,'%Y')),
sm_seksi.tgl_surat,sm_seksi.perihal",])
->from('sm_seksi')
->leftjoin('mfseksi', 'sm_seksi.id_pejabat = mfseksi.id')
->leftjoin('user', 'user.id = sm_seksi.id_user')
;
//echo $tableA->createCommand()->getRawSql();
//echo $tableB->createCommand()->getRawSql();
$tableC = (new \yii\db\Query())
->select([new Expression('sm_kpp.no_agenda,"KPP" as kategori, mfkpp.nama_kantor,
user.nama, if(sm_kpp.jenis_surat=1,"Pemberitahuan",
if(sm_kpp.jenis_surat=2,"Permintaan Data",
if(sm_kpp.jenis_surat=3,"Permintaan Profile",
"Lain - Lain"))),
sm_kpp.nomor_surat, sm_kpp.tgl_surat,sm_kpp.perihal'),])
->from('sm_kpp')
->leftjoin('mfkpp', 'sm_kpp.id_kpp = mfkpp.id')
->leftjoin('user', 'user.id = sm_kpp.id_user')
;
$unionQuery = (new \yii\db\Query())
->from(['dummy_name' => $tableA->union($tableB)->union($tableC)]);
$query = $unionQuery;
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$dataProvider->sort->attributes['nama_wp'] = [
// The tables are the ones our relation are configured to
// in my case they are prefixed with "tbl_"
'asc' => ['nama_wp' => SORT_ASC],
'desc' => ['nama_wp' => SORT_DESC],
];
$dataProvider->sort->attributes['no_agenda'] = [
// The tables are the ones our relation are configured to
// in my case they are prefixed with "tbl_"
'asc' => ['no_agenda' => SORT_ASC],
'desc' => ['no_agenda' => SORT_DESC],
];
$dataProvider->sort->attributes['jenis_surat'] = [
// The tables are the ones our relation are configured to
// in my case they are prefixed with "tbl_"
'asc' => ['jenis_surat' => SORT_ASC],
'desc' => ['jenis_surat' => SORT_DESC],
];
$dataProvider->sort->attributes['nomor_surat'] = [
// The tables are the ones our relation are configured to
// in my case they are prefixed with "tbl_"
'asc' => ['nomor_surat' => SORT_ASC],
'desc' => ['nomor_surat' => SORT_DESC],
];
$dataProvider->sort->attributes['tgl_surat'] = [
// The tables are the ones our relation are configured to
// in my case they are prefixed with "tbl_"
'asc' => ['tgl_surat' => SORT_ASC],
'desc' => ['tgl_surat' => SORT_DESC],
];
$dataProvider->sort->attributes['perihal'] = [
// The tables are the ones our relation are configured to
// in my case they are prefixed with "tbl_"
'asc' => ['perihal' => SORT_ASC],
'desc' => ['perihal' => SORT_DESC],
];
$dataProvider->sort->attributes['kategori'] = [
// The tables are the ones our relation are configured to
// in my case they are prefixed with "tbl_"
'asc' => ['kategori' => SORT_ASC],
'desc' => ['kategori' => SORT_DESC],
];
$dataProvider->sort->attributes['nama'] = [
// The tables are the ones our relation are configured to
// in my case they are prefixed with "tbl_"
'asc' => ['nama' => SORT_ASC],
'desc' => ['nama' => SORT_DESC],
];
$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([
'no_agenda' => $this->no_agenda,
'jenis_surat' => $this->jenis_surat,
'tgl_surat' => $this->tgl_surat,
]);
$query
->andFilterWhere(['like', 'nama_wp', $this->nama_wp])
->andFilterWhere(['like', 'perihal', $this->perihal])
->andFilterWhere(['like', 'nomor_surat', $this->nomor_surat])
->andFilterWhere(['like', 'kategori', $this->kategori])
->andFilterWhere(['like', 'nama', $this->nama])
;
return $dataProvider;
}
}
on jenis surat wont change to be a string(table A), but work fine on table B and C

Related

Yii2 Sorting and filtering from a linked table through a table

Good day everyone. I'm trying to add a filter for the associated phone_digital field but I get the following error
Structure -
1 table - ClientClient - first_name, patronymic, last_name, age.
2 table - ClientPhone - client_id, phone_digital.
ClientClient (model)
class ClientClient extends \yii\db\ActiveRecord
{
public static function tableName(){
return 'client_client';
}
public function rules(){
return [
[['age'], 'integer'],
[['first_name', 'patronymic', 'last_name'], 'string', 'max' => 255],
];
}
public function attributeLabels(){
return [
'id' => 'ID',
'first_name' => 'First Name',
'patronymic' => 'Patronymic',
'last_name' => 'Last Name',
'age' => 'Age',
'phone_digital' => 'Phone Digital',
];
}
public function getclientPhone(){
return $this->hasOne(clientPhone::class, ['client_id' => 'id']);
}
public function getPhone(){
return $this->hasOne(clientPhone::class, ['phone_digital' => 'id']);
}
public function getDigital(){
return $this->hasOne(ClientPhone::className(), ['id' => 'phone_digital']);
}
public function getPhoneDigital(){
return $this->phone->phone_digital;
}
}
ClientSearch (model)
class ClientSearch extends Clientclient
{
public $phonedigital;
public function rules(){
return [
[['id', 'age'], 'integer'],
[['first_name', 'phonedigital', 'patronymic', 'last_name'], 'safe'],
];
}
public function scenarios(){
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
public function search($params){
$query = Clientclient::find()->orderBy('phone_digital');
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$query->joinWith(['phonedigital' => function($query) { $query->from(['phonedigital' => 'ClientPhone']); }]);
$dataProvider->sort->attributes['phone'] = [
'asc' => ['phonedigital.phone_digital' => SORT_ASC],
'desc' => ['phonedigital.phone_digital' => SORT_DESC],
];
$query->andFilterWhere([
'id' => $this->id,
'age' => $this->age,
]);
$query->andFilterWhere(['like', 'first_name', $this->first_name])
->andFilterWhere(['like', 'patronymic', $this->patronymic])
->andFilterWhere(['like', 'last_name', $this->last_name])
->andFilterWhere(['like', 'phonedigital.phone_digital', $this->getAttribute('phonedigital')]);
return $dataProvider;
}
}
What do I need to do to make this work?
I guess you should put your orderBy after your join
check the commented lines:
public function search($params){
// do not put the join of a related table at the beginning because it's not related yet
$query = Clientclient::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$query->joinWith(['phonedigital' => function($query) { $query->from(['phonedigital' => 'ClientPhone']); }]);
// orderBy only after call the relacion
$query->orderBy('phonedigital.phone_digital');
$dataProvider->sort->attributes['phone'] = [
'asc' => ['phonedigital.phone_digital' => SORT_ASC],
'desc' => ['phonedigital.phone_digital' => SORT_DESC],
];
$query->andFilterWhere([
'id' => $this->id,
'age' => $this->age,
]);
$query->andFilterWhere(['like', 'first_name', $this->first_name])
->andFilterWhere(['like', 'patronymic', $this->patronymic])
->andFilterWhere(['like', 'last_name', $this->last_name])
->andFilterWhere(['like', 'phonedigital.phone_digital', $this->getAttribute('phonedigital')]);
return $dataProvider;
}
I hope it helps you, and sorry about my ingles

Yii2- How to restrict a user from viewing others data in index page

I am working on yii2 in my project, I have users and their roles. Each role is given access to a Module and a Sub Menu. There is a sub-menu named SIM List in which all the SIM records can be viewed. There is a field named issued_to which tells us that which SIM has been issued to which user.
Unless a SIM is issued to any user, the issued_to field will remain empty. Once issued the name of the user will appear on the SIM List.
Now I want to manage it in such a way that only a specific user can see the list. For example 5 Sims have been issued to a user named U. Now the user U should only see that SIM records which are issued to him, otherwise the list should be empty.
In my Index controller I am getting issued_to field name which is empty by default.
public function actionIndex()
{
$searchModel = new SimsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
By doing like below I can get issued_to user id
$model = $dataProvider->getModels()[0];
$user_id = $model['issued_to'];
var_dump($user_id);
exit();
Now in this controller, I want to add a check of user_id which gives me only the records which are of that specific user.
Index View
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
//'id',
'imsi',
'sim_number',
'operator_name',
'data_details',
'sms_details',
'monthly_bill',
//'created_by',
[
'label' => 'Created By',
'value' => function ($data) {
if (is_object($data))
return $data->created->name;
return ' - ';
},
//'filter' => Html::activeDropDownList($searchModel, 'created_by', \app\models\User::toArrayList(), ['prompt' => "Created By", 'class' => 'form-control']),
],
'created_at',
// 'updated_at',
'status',
// 'updated_by',
//'sim_stauts',
[
'label'=>'SIM Status',
'value'=>function($a){
return $a->getStatusvalue();
}
],
//'issued_to',
[
'label' => 'Issued To',
'value' => function ($d) {
if(is_object($d->user))
//return $d->user->name;
return $d->issued_to == '' ? '' : $d->user->username;
return ' - ';
// return $d->user->name;
},
'filter' => Html::activeDropDownList($searchModel, 'issued_to', \app\models\User::toArrayList(), ['prompt' => "Users", 'class' => 'form-control']),
],
//'returned_by',
[
'label' => 'Returned By',
'value' => function ($d) {
if(is_object($d->user2))
//return $d->user->name;
return $d->returned_by == '' ? '' : $d->user->username;
return ' - ';
// return $d->user->name;
},
'filter' => Html::activeDropDownList($searchModel, 'returned_by', \app\models\User::toArrayList(), ['prompt' => "Users", 'class' => 'form-control']),
],
'historic',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
Update 1
My search model is below
public function search($params)
{
$query = Sims::find();
// 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_by' => $this->created_by,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'updated_by' => $this->updated_by,
'sim_stauts' => $this->sim_stauts,
'issued_to' => $this->issued_to,
'returned_by' => $this->returned_by,
'historic' => $this->historic,
]);
$query->andFilterWhere(['like', 'imsi', $this->imsi])
->andFilterWhere(['like', 'sim_number', $this->sim_number])
->andFilterWhere(['like', 'operator_name', $this->operator_name])
->andFilterWhere(['like', 'data_details', $this->data_details])
->andFilterWhere(['like', 'sms_details', $this->sms_details])
->andFilterWhere(['like', 'monthly_bill', $this->monthly_bill])
->andFilterWhere(['like', 'status', $this->status]);
return $dataProvider;
}
How can I achieve it? Any help would be highly appreciated.
As per your description, you have the user_id saved inside the issued_to field and you need to only fetch the results that have the current logged in user_id saved in the field issued_to.
I assume that your search in the grid view is visible to only logged-in users.
You should set the issued_to param manually by first getting the queryParams array from the request object Yii::$app->request->queryParams; which has the array in the same format as the POST has i.e ['ModelName']['field_name'] so you need to infact set the issued_to as
$arrayParams['SimSearch']['issued_to']=Yii:$app->user->id;
Your actionIndex should look like below
public function actionIndex()
{
$searchModel = new SimsSearch();
$queryParams=Yii::$app->request->queryParams;
//check if user or one of the managers
$isAdmin=in_array(Yii::$app->user->identity->user_role,[1,6]);
//set params if normal user
if(!$isAdmin){
$queryParams['SimsSearch']['issued_to']=Yii::$app->user->id;
}
$dataProvider = $searchModel->search($queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
Hope this helps you out.

Yii2 Filters with Sqldataprovider

I have following code in my search() function of search model. Data is displaying correctly in gridview but i'm not able to use searching functions of gridview.
public function search($params)
{
$totalCount = Yii::$app->db->createCommand('SELECT COUNT(*) FROM patient_test')->queryScalar();
$query = new Query();
$query->select('patient.id as id,patient_test.receipt_number,patient.name as patient_id, GROUP_CONCAT(test_group.name) as test_group_id, patient.age as patient_test_age')
->from('patient_test');
$query->join = [
['LEFT JOIN', 'patient', 'patient_test.patient_id = patient.id'],
['LEFT JOIN', 'test_group', 'patient_test.test_group_id = test_group.id']];
$query->groupBy(['patient_test.patient_id', 'patient_test.receipt_number', 'patient_test.is_sample_received', 'patient_test.is_ready']);
$dataProvider = new SqlDataProvider([
// 'db' => Yii::$app->db,
'sql' => $query->createCommand()->sql,
'totalCount' => $totalCount,
'sort' => [
'attributes' => [
'patient_id' => [
'desc' => ['patient_id' => SORT_DESC ],
'default' => SORT_DESC,
],
],
], 'pagination' => [
'pageSize' => $pagination,
]
]);
$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,
'is_sample_received' => $this->is_sample_received,
]);
$query->andFilterWhere(['like', 'patient_test.receipt_number', $this->receipt_number])
->andFilterWhere(['like', 'patient.name', $this->patient_id])
->andFilterWhere(['like', 'test_group.name', $this->test_group_id])
->andFilterWhere(['like', 'patient.age', $this->patient_test_age])
->andFilterWhere(['like', 'patient.mobile_no', $this->patient_test_mobile]);
return $dataProvider;
}
from this, my code is working fine but filtration/searching not working

Mysql query in activerecord in yii2

I have following mysql query that I need to use in yii2 -
select tsell.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
My search 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
{
public $bills;
public $district;
public $sale;
public $sell;
public $collection;
/**
* #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,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->sort->attributes['sell'] = [
// The tables are the ones our relation are configured to
// in my case they are prefixed with "tbl_"
'asc' => ['tsell.sell' => SORT_ASC],
'desc' => ['tsell.sell' => SORT_DESC],
];
$dataProvider->sort->attributes['collection'] = [
// The tables are the ones our relation are configured to
// in my case they are prefixed with "tbl_"
'asc' => ['collection' => SORT_ASC],
'desc' => ['collection' => SORT_DESC],
];
$dataProvider->sort->attributes['district'] = [
// The tables are the ones our relation are configured to
// in my case they are prefixed with "tbl_"
'asc' => ['tsell.district' => SORT_ASC],
'desc' => ['tsell.district' => SORT_DESC],
];
$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;
}
}
But the problem is that I am unable to sort or filter the columns. So I want to use activerecord to run the query instead of findbysql. Please help me to build this query in activerecord.
My index.php file looks like -
<?php
use yii\helpers\Html;
use yii\grid\GridView;
//use kartik\widgets\DatePicker;
use kartik\daterange\DateRangePicker;
use kartik\form\ActiveForm;
use dosamigos\datepicker\DatePicker;
/* #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,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'district',
'sell',
'collection',
//['class' => 'yii\grid\ActionColumn'],
],
]); ?>
</div>
Here's my PartiesController.php
<?php
namespace frontend\modules\districtreport\controllers;
use Yii;
use frontend\modules\districtreport\models\Parties;
use frontend\modules\districtreport\models\PartiesSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* PartiesController implements the CRUD actions for Parties model.
*/
class PartiesController extends Controller
{
/**
* #inheritdoc
*/
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* Lists all Parties models.
* #return mixed
*/
public function actionIndex()
{
$searchModel = new PartiesSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Parties model.
* #param integer $id
* #return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Parties model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$model = new Parties();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->party_id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
/**
* Updates an existing Parties 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);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->party_id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
/**
* Deletes an existing Parties 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 Parties model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* #param integer $id
* #return Parties the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Parties::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
Pls have a look
$dataProvider = new SqlDataProvider([
'sql' => 'SELECT Name, Title, COUNT(ArticleTags.ID) AS TagCount ' .
'FROM Authors ' .
'INNER JOIN Articles ON (Authors.ID = Articles.AuthorID) ' .
'INNER JOIN ArticleTags ON (Articles.ID = ArticleTags.ID) ' .
'WHERE Name=:author' .
'GROUP BY ArticleID',
'params' => [':author' => 'Arno Slatius'],
]);
In query you missing a district alias
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
For sort i suggest you this notation
$dataProvider->setSort([
'attributes' => [
'sell' => [
'asc' => ['tsell.sell' => SORT_ASC],
'desc' => ['tsell.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'
]
]
]);
Be sure you have declare the vars $bills, $district, $sale, $sell, $collection only in one model class.
If you use these vars in several extended searchModel then declare only in model
otherwise declare only in the searhModel ..
Never declare in both the same var one override the others and mismatch when used by model or modelSearch function ..

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