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 ..
Related
I am working with yii2 Framework. I want only few records in gridview where status = 1 in databaseenter image description here
This is the screen shot of gridview I want only highlighted records whose status is 1
This Code of view page is -:
<p>All Issued Devices are Highlighted </p>
<?php
use yii\helpers\Html;
use yii\grid\GridView;
/* #var $this yii\web\View */
/* #var $searchModel frontend\models\DeviceTrackerSearch */
/* #var $dataProvider yii\data\ActiveDataProvider */
//$this->title = 'Tracked Devices';
//$this->params['breadcrumbs'][] = $this->title;
?>
<b style="font-size: 30px;">ALL ISSUED DEVICES ARE HIGHLIGHTED </b>
<div class="device-tracker-index">
<h1><?= Html::encode($this->title) ?></h1>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'rowOptions'=>function($model)
{
if($model->status == '1')
{
return ['class'=>'info'];
}
else
{
return ['class'=>'default'];
}
},
'columns' => [
['class' => 'yii\grid\SerialColumn'],
[
'attribute'=>'devices_device_id',
'value'=>'devicesDevice.device_name'
],
//'id',
//'devices_device_id',
'user_first_name',
'user_last_name',
'email_address:email',
// 'user_department',
'assign_date',
'return_date',
//'status',
//'Issued_by',
[
'attribute'=>'Issued_by' ,
'value'=>'issuedBy.first_name'
],
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
</div>
Please Tell me how to solve the problem?
Controller action is-
public function actionReturn()
{
// echo "welcome";die;
if(isset(Yii::$app->user->identity->user_type))
{
$status = Yii::$app->user->identity->user_type;
if($status == 1)
{
$this->layout = 'headerLayoutSuperadmin';
$searchModel = new DeviceTrackerSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index1', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
else
{
$this->layout = 'headerLayout';
$searchModel = new DeviceTrackerSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index1', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
}
else
{
return $this->redirect(array('site/login'));
}
}
sql search models-:
<?php
namespace frontend\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use frontend\models\DeviceTracker;
/**
* DeviceTrackerSearch represents the model behind the search form about `frontend\models\DeviceTracker`.
*/
class DeviceTrackerSearch extends DeviceTracker
{
/**
* #inheritdoc
*/
public function rules()
{
return [
[['id', 'status', 'Issued_by'], 'integer'],
[['user_first_name', 'devices_device_id', 'user_last_name', 'email_address', 'user_department', 'assign_date', 'return_date'], '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 = DeviceTracker::find();
//$query = DeviceTracker::findAll('status=>1');
// print_r($query);die;
//echo $query;die;
// 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;
}
$query->joinWith('devicesDevice');
// grid filtering conditions
$query->andFilterWhere([
'id' => $this->id,
//'devices_device_id' => $this->devices_device_id,
'assign_date' => $this->assign_date,
'return_date' => $this->return_date,
'status' => $this->status,
'Issued_by' => $this->Issued_by,
]);
$query->andFilterWhere(['like', 'user_first_name', $this->user_first_name])
->andFilterWhere(['like', 'user_last_name', $this->user_last_name])
->andFilterWhere(['like', 'email_address', $this->email_address])
->andFilterWhere(['like', 'user_department', $this->user_department])
->andFilterWhere(['like', 'devices.device_name', $this->devices_device_id]);
return $dataProvider;
}
}
in your search model, update the query to suit the output you want to pass to view.
something like this for the current condition i.e. you want to display only those rows whose status is 1
so,
$query->andFilterWhere([
'id' => $this->id,
//'devices_device_id' => $this->devices_device_id,
'assign_date' => $this->assign_date,
'return_date' => $this->return_date,
'status' => 1, // **this is where you are filtering the data with status == 1**
'Issued_by' => $this->Issued_by,
]);
and brief explanation of flow is
your controller which calls the dataProvider, gives you data that you pass to view and then eventually to gridView
the data is stored in mysql tables, so whatever data you want to send to view you need to query it in the searchModel in dataProvider's query
return dataProvide to controller > view > gridWidget
I have a table rawmaterial. The fields are - rmname, usedate, useqty, unitcost, productname, chargenumber. I've added a gridview (which comes from rmtemplate table) with a checkboxcolumn in the form. The gridview contains columns productname, rmname, qty, unitcost. How can I insert the checked rows along with usedate, chargenumber(which come from respective textboxes) in the table rawmaterial.
I've checked ActiveRecord batch insert (yii2) but not getting how to use it with checkbocolumn.
Checked How I can process a checkbox column from Yii2 gridview? - not quite sure with it.
Checked Yii2 How to properly create checkbox column in gridview for bulk actions? - I think it's not using activeform.
form.php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use kartik\grid\GridView;
use dosamigos\datepicker\DatePicker;
use kartik\select2\Select2;
use yii\helpers\ArrayHelper;
use frontend\models\Rmtemplate;
/* #var $this yii\web\View */
/* #var $model frontend\models\Rawmaterial */
/* #var $form yii\widgets\ActiveForm */
?>
<div class="rawmaterial-form">
<?php $form = ActiveForm::begin(); ?>
<div class="form-group">
<div class="col-xs-12 col-sm-12 col-lg-12">
<?= $form->field($model, 'usedate')->widget(
DatePicker::className(), [
// inline too, not bad
'inline' => false,
// modify template for custom rendering
//'template' => '<div class="well well-sm" style="background-color: #fff; width:250px">{input}</div>',
'clientOptions' => [
'autoclose' => true,
'todayHighlight' => true,
'format' => 'yyyy-mm-dd'
]
]);?>
</div>
<div class="col-xs-12 col-sm-12 col-lg-12">
<?= GridView::widget([
'dataProvider' => $dataProvider2,
'filterModel' => $searchModel2,
'columns' => [
['class' => 'kartik\grid\CheckboxColumn'],
//'id',
//'productname',
[
'attribute'=>'productname',
'filterType'=>GridView::FILTER_SELECT2,
'filter'=>ArrayHelper::map(Rmtemplate::find()->orderBy(['productname' => SORT_ASC])->asArray()->all(), 'productname', 'productname'),
'filterWidgetOptions'=>[
'pluginOptions'=>['allowClear'=>true],
],
'filterInputOptions'=>['placeholder'=>'Charge Name'],
],
'rmname',
'qty',
[
'attribute' => 'unitcost',
'value' => 'unitcost.unitcost',
],
//['class' => 'yii\grid\ActionColumn'],
],
]); ?>
</div>
</div>
<?= $form->field($model, 'chargenumber')->textInput()->hiddenInput()->label(false) ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary','name' => 'submit', 'value' => 'create_update']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
<?php
/* start getting the chargeno */
$script = <<<EOD
$(window).load(function(){
$.get('index.php?r=rmprod/rawmaterial/get-for-chargeno',{ orderid : 1 }, function(data){
//alert(data);
var data = $.parseJSON(data);
$('#rawmaterial-chargenumber').attr('value',data.chargeno);
}
);
});
EOD;
$this->registerJs($script);
/*end getting the chargeno */
?>
And it looks like below.
CreateAction looks like -
public function actionCreate()
{
$model = new Rawmaterial();
$searchModel2 = new RmtemplateSearch();
$dataProvider2 = $searchModel2->search(Yii::$app->request->queryParams);
if (isset($_POST['submit'])) {
if ($_POST('submit') == 'create_update' ) {
// then perform the insert
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
'searchModel2' => $searchModel2,
'dataProvider2' => $dataProvider2,
]);
}
}
} else {
// no insert but render for filter ..
return $this->render('create', [
'model' => $model,
'searchModel2' => $searchModel2,
'dataProvider2' => $dataProvider2,
]);
}
}
Update
RawMaterialForm.php
<?php
namespace frontend\modules\rmprod\models;
use Yii;
/**
* This is the model class for table "rawmaterial".
*
* #property integer $id
* #property string $vname
* #property string $challan
* #property string $purchasedate
* #property string $purchaseqty
* #property string $rate
* #property string $rmname
* #property string $usedate
* #property string $useqty
* #property string $unitcost
* #property string $productname
* #property integer $chargenumber
*/
class RawMaterialForm extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'rawmaterial';
}
/**
* #inheritdoc
*/
// public function rules()
// {
// return [
// [['purchasedate', 'usedate'], 'safe'],
// [['chargenumber'], 'integer'],
// [['vname', 'productname'], 'string', 'max' => 40],
// [['challan'], 'string', 'max' => 20],
// [['purchaseqty', 'rmname', 'useqty'], 'string', 'max' => 50],
// [['rate', 'unitcost'], 'string', 'max' => 10],
// ];
// }
public function rules()
{
return [
[['usedate'], 'safe'],
[['chargenumber'], 'integer'],
[['productname'], 'string', 'max' => 40],
[['rmname', 'useqty'], 'string', 'max' => 50],
[['unitcost'], 'string', 'max' => 10],
[['rmtemplate_ids'], 'safe'],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'vname' => 'Vname',
'challan' => 'Challan',
'purchasedate' => 'Purchasedate',
'purchaseqty' => 'Purchaseqty',
'rate' => 'Rate',
'rmname' => 'Rmname',
'usedate' => 'Usedate',
'useqty' => 'Useqty',
'unitcost' => 'Unitcost',
'productname' => 'Productname',
'chargenumber' => 'Chargenumber',
];
}
}
RawmaterialController
<?php
namespace frontend\modules\rmprod\controllers;
use Yii;
use frontend\models\Rawmaterial;
use frontend\modules\rmprod\models\RawmaterialSearch;
use frontend\modules\rmprod\models\RmtemplateSearch;
use frontend\modules\rmprod\models\RawMaterialForm;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\helpers\Json;
/**
* RawmaterialController implements the CRUD actions for Rawmaterial model.
*/
class RawmaterialController extends Controller
{
/**
* #inheritdoc
*/
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* Lists all Rawmaterial models.
* #return mixed
*/
public function actionIndex()
{
$searchModel = new RawmaterialSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$searchModel2 = new RmtemplateSearch();
$dataProvider2 = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'searchModel2' => $searchModel2,
'dataProvider2' => $dataProvider2,
]);
}
/**
* Displays a single Rawmaterial model.
* #param integer $id
* #return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Rawmaterial model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$model = new RawMaterialForm();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(
['create']
// redirect to where you want
);
}
$searchModel2 = new RmtemplateSearch();
$dataProvider2 = $searchModel2->search(Yii::$app->request->queryParams);
return $this->render('create', [
'model' => $model,
'searchModel2' => $searchModel2,
'dataProvider2' => $dataProvider2,
]);
}
/**
* Updates an existing Rawmaterial 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->id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
/**
* Deletes an existing Rawmaterial 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']);
}
public function actionGetForChargeno($orderid)
{
$rates = Rawmaterial::find()->select('(max(chargenumber) + 1) as chargeno')->asArray()->one();
echo Json::encode($rates);
}
/**
* Finds the Rawmaterial model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* #param integer $id
* #return Rawmaterial the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Rawmaterial::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
public function save()
{
try {
if ($this->validate()) {
// assuming Rmtemplate is the model used in RmtemplateSearch
$selectedRmtemplate = Rmtemplate::find()->where(['id' => $this->rmtemplate_ids]);
foreach ($selectedRmtemplate->each() as $rm) {
$rawMaterial = new Rawmaterial();
$rawMaterial->rmname = $rm->rmname;
$rawMaterial->usedate = $this->usedate;
$rawMaterial->useqty = $rm->qty;
$rawMaterial->unitcost = $rm->unitcost;
$rawMaterial->productname = $rm->productname;
$rawMaterial->chargenumber = $this->chargenumber;
if (!$rawMaterial->save()) {
throw new \Exception('Error while saving rawMaterial!');
}
}
return true;
}
} catch (\Exception $exc) {
\Yii::error($exc->getMessage());
}
return false;
}
}
Error
According to my understanding, you have to do two things.
Firstly, you have to grab all the checked rows data of the grid view as an array or object. You can see how to do that from Get Grid Data .
Secondly you have to change your create action for handling the data you fetch from that grid. You can take help from Batch Insert
Hope it helps...
Prepare additional model like RawMaterialForm with properties that will be taken from ActiveForm usedate, chargenumber and rmtemplate_ids. The last one is the array of GridView IDs. Remember to add rules() in the RawMaterialForm for the properties.
The view - just the GridView needs some tweaks. Extend the configuration for Checkbox column.
[
'class' => 'kartik\grid\CheckboxColumn',
'name' => 'RawMaterialForm[rmtemplate_ids]',
'checkboxOptions' => function ($model, $key, $index, $column) {
return ['value' => $model->id];
}
],
The action:
public function actionCreate()
{
$model = new RawMaterialForm();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(
// redirect to where you want
);
}
$searchModel2 = new RmtemplateSearch();
$dataProvider2 = $searchModel2->search(Yii::$app->request->queryParams);
return $this->render('create', [
'model' => $model,
'searchModel2' => $searchModel2,
'dataProvider2' => $dataProvider2,
]);
}
RawMaterialForm's save() method:
public function save()
{
try {
if ($this->validate()) {
// assuming Rmtemplate is the model used in RmtemplateSearch
$selectedRmtemplate = Rmtemplate::find()->where(['id' => $this->rmtemplate_ids]);
foreach ($selectedRmtemplate->each() as $rm) {
$rawMaterial = new Rawmaterial();
$rawMaterial->rmname = $rm->rmname;
$rawMaterial->usedate = $this->usedate;
$rawMaterial->useqty = $rm->qty;
$rawMaterial->unitcost = $rm->unitcost;
$rawMaterial->productname = $rm->productname;
$rawMaterial->chargenumber = $this->chargenumber;
if (!$rawMaterial->save()) {
throw new \Exception('Error while saving rawMaterial!');
}
}
return true;
}
} catch (\Exception $exc) {
\Yii::error($exc->getMessage());
}
return false;
}
This will copy every selected row into a new Rawmaterial row with additional inputs from ActiveForm.
In case of errors in $rawMaterial saving check $rawMaterial->errors property.
Fair warning - depending on the system performance this can be slow (or even fatal) in case of selecting many rows at once.
I am making a cost accounting application.
I want to find the average price over 3 months.
The summary produced in Kartik GridView shows initial share price not the total.
this is my controller
<?php
namespace backend\controllers;
use Yii;
use backend\models\Triwulan;
use backend\models\TriwulanSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* TriwulanController implements the CRUD actions for Triwulan model.
*/
class TriwulanController extends Controller
{
/**
* #inheritdoc
*/
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* Lists all Triwulan models.
* #return mixed
*/
public function actionIndex()
{
$searchModel = new TriwulanSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Triwulan model.
* #param string $id
* #return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Triwulan model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$model = new Triwulan();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->rm_code]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
/**
* Updates an existing Triwulan model.
* If update is successful, the browser will be redirected to the 'view' page.
* #param string $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->rm_code]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
/**
* Deletes an existing Triwulan model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* #param string $id
* #return mixed
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the Triwulan model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* #param string $id
* #return Triwulan the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Triwulan::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
this is my view
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
use kartik\grid\GridView;
/* #var $this yii\web\View */
/* #var $model backend\models\Triwulan */
$this->title = $model->rm_code;
$this->params['breadcrumbs'][] = ['label' => 'Triwulans', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="triwulan-view">
<h1><?= Html::encode($this->title) ?></h1>
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'rm_code',
'deskripsi_barang',
],
]) ?>
</div>
<?= GridView::widget([
'dataProvider'=>new yii\data\ActiveDataProvider([
'pagination'=>false,
'query'=>$model->getPenerimaans(),
]),
'columns'=>[
['class' => 'kartik\grid\SerialColumn'],
'rm_code',
'bulan',
// 'price',
[
'label' => 'Price',
//'attribute' => 'idDhs.idMatakuliah.jam',
'pageSummary' => true,
// 'pageSummary' => 'Total',
'value' => function ($model) {
if ($model)
return $model->price / 3;
}
],
// ['class' => 'kartik\grid\ActionColumn'],
// 'product',
// 'qty'
],
'showPageSummary' => true,
]) ?>
</div>
Correcting some syntax errors from scaisEdge's answer
public function actionIndex()
{
$searchModel = new TriwulanSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
// calc the average
$myAverage = 0;
$myTot =0;
$myCnt = 0;
foreach ($dataProvider->models as $key => $value) {
$myTot += $value['price'];
$cnt++; // This should be $myCnt
}
if ($cnt>0){ // inside if use $myCnt
$myAverage = myTot/$myCont // here use $myTot/$myCnt
}
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
// render the avegare too
'myAverage' => $myAverage,
]);
}
I'm trying to filter gridviews by select2 widget. But the select2 widget should not be within the gridview. It would be like in the screenshot -
And when I select the select2 widget the data is filtered.
My index.php code is -
<?php
use yii\helpers\Html;
use yii\grid\GridView;
use kartik\select2\Select2;
use yii\helpers\ArrayHelper;
use frontend\modules\productstockbook\models\Productnames;
use yii\helpers\Json;
/* #var $this yii\web\View */
/* #var $searchModel frontend\modules\productstockbook\models\ProductionSearch */
/* #var $dataProvider yii\data\ActiveDataProvider */
$this->title = 'Product Stock Book';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="production-index">
<h1><?= Html::encode($this->title) ?></h1>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
<?php
echo Select2::widget([
'model' => $model,
'attribute' => 'productnames_productname',
'data' => ArrayHelper::map(Productnames::find()->all(),'productnames_productname','productnames_productname'),
'options' => ['placeholder' => 'Select Product', 'id' => 'catid'],
'pluginOptions' => [
'allowClear' => true
],
]);
?>
<div class="row-fluid">
<div class="form-group">
<div class="col-xs-3 col-sm-3 col-lg-3" >
<input type="text" class="form-control" id="production" readonly placeholder ="Production">
</div>
<div class="col-xs-3 col-sm-3 col-lg-3" >
<input type="text" class="form-control" id="sell" readonly placeholder ="Sell">
</div>
<div class="col-xs-3 col-sm-3 col-lg-3" >
<input type="text" class="form-control" id="stock" readonly placeholder ="Stock">
</div>
<div class="col-xs-3 col-sm-3 col-lg-3" >
<button type="button" id="search" class="btn btn-primary"><span class="glyphicon glyphicon-search" aria-hidden="true"></span></button>
</div>
</div>
</div>
<div class= 'col-md-6'>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
//'productionid',
'productiondate',
//'itemid',
'productname',
//'batchno',
'prodqty',
//['class' => 'yii\grid\ActionColumn'],
],
]); ?>
</div>
<div class='col-md-6'>
<?php
echo GridView::widget([
'dataProvider' => $dataProvider2,
'filterModel' => $searchModel2,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'billdate',
'productsales_partyname',
'productname',
'total',
],
]);
?>
</div>
</div>
<?php
/* start getting the textboxes */
$script = <<< JS
$(function(){
//$(document).ready(function(e) { getTotalproduction(); });
$('#catid').change(function(){
getIndexpage();
//getTotalproduction();
//getTotalsell();
//getTotalstock();
});
var catid = $(this).val();
var getIndexpage = function(){
var catid = String($('#catid').val());
window.onbeforeunload = function(e) {return getTotalproduction();};
window.location.href = 'index.php?r=productstockbook/production/index&catid='+catid;
} ;
var getTotalproduction = function(){
var catid = String($('#catid').val());
$.get('index.php?r=productstockbook/production/get-for-production',{ catid : catid }, function(data){
//alert(data);
var data = $.parseJSON(data);
$('#production').attr('value',data.totalproduction);
});
} ;
var getTotalsell = function(){
var catid = String($('#catid').val());
$.get('index.php?r=productstockbook/production/get-for-sales',{ catid : catid }, function(data){
//alert(data);
var data = $.parseJSON(data);
$('#sell').attr('value',data.totalsell);
});
};
var getTotalstock = function(){
var totalproduction = parseInt($('#production').val());
var totalsell = parseInt($('#sell').val());
var totalstock = Math.round(totalproduction - totalsell)
//alert(totalstock);
if (isNaN(totalstock) || totalstock < -10000000 || totalstock > 1000000) {
totalstock = '';
}
$('#stock').val(totalstock);
};
// var getTotalstock = function(){
// var catid = String($('#catid').val());
// $.get('index.php?r=productstockbook/production/get-for-stock',{ catid : catid }, function(data){
// alert(data);
// var data = $.parseJSON(data);
// $('#stock').attr('value',data.stock);
// });
// };
});
JS;
$this->registerJs($script);
/* end getting the textboxes */
?>
My Controller code is -
<?php
namespace frontend\modules\productstockbook\controllers;
use Yii;
use frontend\modules\productstockbook\models\Production;
use frontend\modules\productstockbook\models\ProductionSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use frontend\modules\productstockbook\models\ProductsalesSearch;
use yii\helpers\Html;
use frontend\modules\productstockbook\models\Productnames;
use frontend\modules\productstockbook\models\Productsales;
use yii\helpers\Json;
use yii\db\Query;
use yii\db\Command;
/**
* ProductionController implements the CRUD actions for Production model.
*/
class ProductionController extends Controller
{
/**
* #inheritdoc
*/
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* Lists all Production models.
* #return mixed
*/
public function actionIndex()
{
$catid = yii::$app->request->get('catid');
$searchModel = new ProductionSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams, $catid);
$searchModel2 = new ProductsalesSearch();
$dataProvider2 = $searchModel2->search(Yii::$app->request->queryParams, $catid);
$model = new Productnames();
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'searchModel2' => $searchModel2,
'dataProvider2' => $dataProvider2,
'model' => $model,
]);
}
/**
* Displays a single Production model.
* #param integer $id
* #return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Production model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$model = new Production();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->productionid]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
/**
* Updates an existing Production 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->productionid]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
/**
* Deletes an existing Production 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 Production model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* #param integer $id
* #return Production the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Production::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
public function actionGetForProduction($catid)
{
$production = Production::find()->select('sum(prodqty) as totalproduction')->where(['productname'=>$catid])->asArray()->one();
echo Json::encode($production);
}
public function actionGetForSales($catid)
{
$sell = Productsales::find()->select('sum(total) as totalsell')->where(['productname'=>$catid])->asArray()->one();
echo Json::encode($sell);
}
// public function actionGetForStock($catid)
// {
// //$stock = Productsales::find()->joinWith('Production')->select('sum(production.prodqty) - sum(productsales.total) as stock')->where(['productname'=>$catid])->asArray()->one();
// //echo Json::encode($stock);
// //$subQuery1 = (new Query())->select(['productname,sum(prodqty) as totalproduction'])->from('production')->where(['productname'=>$catid]);
// $subQuery2 = (new Query())->select(['productname,sum(total) as totalsell'])->from('productsales')->where(['productname'=>$catid]);
// $subQuery3 = (new Query())->select(['productname,(sum(prodqty) - sell.totalsell) as totalstock'])->from('production')->leftJoin(['sell' => $subQuery2],'sell.productname = productname')->where(['productname'=>$catid]);
// echo Json::encode($subQuery3);
// }
}
ProductionSearch Model code -
<?php
namespace frontend\modules\productstockbook\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use frontend\modules\productstockbook\models\Production;
/**
* ProductionSearch represents the model behind the search form about `frontend\modules\productstockbook\models\Production`.
*/
class ProductionSearch extends Production
{
/**
* #inheritdoc
*/
public function rules()
{
return [
[['productionid', 'itemid', 'prodqty'], 'integer'],
[['productiondate', 'productname', 'batchno'], '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,$catid)
{
$query = Production::find()
//->select(['productionid', 'productiondate', 'itemid', 'productname', 'batchno', 'prodqty'])
->orDerBy([
'productiondate'=>SORT_DESC,
])
->andWhere(['productname' => $catid]);
// 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([
'productionid' => $this->productionid,
'productiondate' => $this->productiondate,
'itemid' => $this->itemid,
'prodqty' => $this->prodqty,
]);
$query->andFilterWhere(['like', 'productname', $this->productname])
->andFilterWhere(['like', 'batchno', $this->batchno]);
return $dataProvider;
}
}
ProductsalesSearch Model Code -
<?php
namespace frontend\modules\productstockbook\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use frontend\modules\productstockbook\models\Productsales;
/**
* ProductsalesSearch represents the model behind the search form about `frontend\modules\productstockbook\models\Productsales`.
*/
class ProductsalesSearch extends Productsales
{
/**
* #inheritdoc
*/
public function rules()
{
return [
[['id', 'productsales_ebillid', 'discount'], 'integer'],
[['year', 'console', 'billno', 'billdate', 'productsales_partyname', 'itemid', 'productname', 'batchno', 'expdate', 'productiondate', 'prodqty', 'qty', 'free', 'total'], 'safe'],
[['mrp', 'rate'], 'number'],
];
}
/**
* #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,$catid)
{
$query = Productsales::find()
->orDerBy([
'billdate'=>SORT_DESC,
])
->andWhere(['productname' => $catid]);
// 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,
'productsales_ebillid' => $this->productsales_ebillid,
'billdate' => $this->billdate,
'expdate' => $this->expdate,
'mrp' => $this->mrp,
'rate' => $this->rate,
'productiondate' => $this->productiondate,
'discount' => $this->discount,
]);
$query->andFilterWhere(['like', 'year', $this->year])
->andFilterWhere(['like', 'console', $this->console])
->andFilterWhere(['like', 'billno', $this->billno])
->andFilterWhere(['like', 'productsales_partyname', $this->productsales_partyname])
->andFilterWhere(['like', 'itemid', $this->itemid])
->andFilterWhere(['like', 'productname', $this->productname])
->andFilterWhere(['like', 'batchno', $this->batchno])
->andFilterWhere(['like', 'prodqty', $this->prodqty])
->andFilterWhere(['like', 'qty', $this->qty])
->andFilterWhere(['like', 'free', $this->free])
->andFilterWhere(['like', 'total', $this->total]);
return $dataProvider;
}
// public function gettotalProduction()
// {
// return $this->c_name.' - '.$this->c_address.' - '.$this->c_mobileno;
// }
}
My point is that in the javascript in index.php file the following code is being used -
window.location.href = 'index.php?r=productstockbook/production/index&catid='+catid;
I don't want the page to be relocated. Because if it relocates I cannot get sum(prodqty) and sum(total) in the textboxes as you can see in the page beaceuse the page is being relocated. How can I achieve this without relocating the page? This is same question as Filter data with kartik Select2 widget in gridview
Update
Currently after working on Edvin's solution - I'm facing the following error -
Since I see it already filters data (but with page reload), I moved to part 2 where you asked for sum of specific column.
I couldn't think of better solution at the moment but this should work for now. Note that it might be only temporary solution because this code is "fragile" (adding an additional column, changing tables' ID, etc.) will break this and you will forced to modify this code. Also please note it takes the sum of all printed rows (meaning only the visible (1st by default) is counted).
I have used something similar before and since IDs are somewhat static, it rarely breaks for me.
$(document).on('ready', function(e) {
var row0 = $('#w0')[0]['childNodes'][2]['childNodes'][2]['children'];
var row1 = $('#w1')[0]['childNodes'][2]['childNodes'][2]['children'];
var total0 = 0;
var total1 = 0;
for (var i = 0; i < row0.length; i++) {
total0 += parseInt(row0[i]['children'][3]['textContent'], 10);
}
for (var i = 0; i < row1.length; i++) {
total1 += parseInt(row1[i]['children'][4]['textContent'], 10);
}
// ^ This number takes (n+1)th column
console.log('First table total value: ' + total0);
console.log('Second table total value: ' + total1);
console.log('Difference between 1st and 2nd total values: ' + (total0 - total1));
})
This will work, assuming your tables' IDs are "w0" (first table) and "w1" (second table) and there are exactly that amount of columns in your represented picture.
In case there are a lot of records, you can increase the size of how many rows to print in table by adding additional lines in Model:
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 30,
]
]);
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