As a new Dart Fan, I would like to understand the concept of Map/List.
I tried to do HTTP requests, getting JSON data. And it's ok till I have to assign to the Map.
Let me show you the example of JSON data:
{
"error":"",
"error_number":"",
"response_code":200,
"result":[
{
"id":1,
"name":"Great Deal",
"day_aired":"2015-07-05 11:06:09",
"trend":"Noone",
"trend_details": [{
"name":"Great Deal",
}
]
},
{
"id":2,
....
}
]
}
The code:
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<ApiResponse> fetchData(String command, Map params) async {
final String url =
'https://example.com/api/v2/....';
final response = await http.get(url);
if (response.statusCode == 200) {
return ApiResponse.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
}
final response = await http.get(url);
dynamic data = json.decode(response.body);
List<String> parsed = data['result'] as List<String>;
// List<String> parsedList = new List<String>.from(parsed);
if (response.statusCode == 200) {
//return json.decode(response.body);
List<ApiResponse> list = List<ApiResponse>.from(
parsed.map((i) => ApiResponse.fromJson(i as Map<String, dynamic>)));
}
I do the same as this article. But I read this article too. I'm trying to create Future<ApiResponse> with data from json.decode(response.body) (result entry inside of it).
factory ApiResponse.fromJson(Map<String, dynamic> json) {...}
But as I understand, result is not Map<String, dynamic> but when I try to invoke the code below it says:
Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'List<String>' in type cast and it referred to List<String> parsed = data['result'] as List<String>;.
I'm confused and I know the code is a mess. I read in the second article that I should do an additional cast to trend_details but it did not work as I expected. Obviously data['result'] is an array but how to cast it properly? What are the good practices?
result stores a list of Map<String, dynamic>
final parsed = data['result'] as List<Map<String, dynamic>>;
You can parse json string with the following structure and code
You can see picture display correct result name
code snippet
var payload = payloadFromJson(jsonStr);
print('${payload.result[0].name}');
related class
// To parse this JSON data, do
//
// final payload = payloadFromJson(jsonString);
import 'dart:convert';
Payload payloadFromJson(String str) => Payload.fromJson(json.decode(str));
String payloadToJson(Payload data) => json.encode(data.toJson());
class Payload {
String error;
String errorNumber;
int responseCode;
List<Result> result;
Payload({
this.error,
this.errorNumber,
this.responseCode,
this.result,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
error: json["error"] == null ? null : json["error"],
errorNumber: json["error_number"] == null ? null : json["error_number"],
responseCode: json["response_code"] == null ? null : json["response_code"],
result: json["result"] == null ? null : List<Result>.from(json["result"].map((x) => Result.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"error": error == null ? null : error,
"error_number": errorNumber == null ? null : errorNumber,
"response_code": responseCode == null ? null : responseCode,
"result": result == null ? null : List<dynamic>.from(result.map((x) => x.toJson())),
};
}
class Result {
int id;
String name;
DateTime dayAired;
String trend;
List<TrendDetail> trendDetails;
Result({
this.id,
this.name,
this.dayAired,
this.trend,
this.trendDetails,
});
factory Result.fromJson(Map<String, dynamic> json) => Result(
id: json["id"] == null ? null : json["id"],
name: json["name"] == null ? null : json["name"],
dayAired: json["day_aired"] == null ? null : DateTime.parse(json["day_aired"]),
trend: json["trend"] == null ? null : json["trend"],
trendDetails: json["trend_details"] == null ? null : List<TrendDetail>.from(json["trend_details"].map((x) => TrendDetail.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"id": id == null ? null : id,
"name": name == null ? null : name,
"day_aired": dayAired == null ? null : dayAired.toIso8601String(),
"trend": trend == null ? null : trend,
"trend_details": trendDetails == null ? null : List<dynamic>.from(trendDetails.map((x) => x.toJson())),
};
}
class TrendDetail {
String name;
TrendDetail({
this.name,
});
factory TrendDetail.fromJson(Map<String, dynamic> json) => TrendDetail(
name: json["name"] == null ? null : json["name"],
);
Map<String, dynamic> toJson() => {
"name": name == null ? null : name,
};
}
full code
import 'package:flutter/material.dart';
// To parse this JSON data, do
//
// final payload = payloadFromJson(jsonString);
import 'dart:convert';
Payload payloadFromJson(String str) => Payload.fromJson(json.decode(str));
String payloadToJson(Payload data) => json.encode(data.toJson());
class Payload {
String error;
String errorNumber;
int responseCode;
List<Result> result;
Payload({
this.error,
this.errorNumber,
this.responseCode,
this.result,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
error: json["error"] == null ? null : json["error"],
errorNumber: json["error_number"] == null ? null : json["error_number"],
responseCode: json["response_code"] == null ? null : json["response_code"],
result: json["result"] == null ? null : List<Result>.from(json["result"].map((x) => Result.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"error": error == null ? null : error,
"error_number": errorNumber == null ? null : errorNumber,
"response_code": responseCode == null ? null : responseCode,
"result": result == null ? null : List<dynamic>.from(result.map((x) => x.toJson())),
};
}
class Result {
int id;
String name;
DateTime dayAired;
String trend;
List<TrendDetail> trendDetails;
Result({
this.id,
this.name,
this.dayAired,
this.trend,
this.trendDetails,
});
factory Result.fromJson(Map<String, dynamic> json) => Result(
id: json["id"] == null ? null : json["id"],
name: json["name"] == null ? null : json["name"],
dayAired: json["day_aired"] == null ? null : DateTime.parse(json["day_aired"]),
trend: json["trend"] == null ? null : json["trend"],
trendDetails: json["trend_details"] == null ? null : List<TrendDetail>.from(json["trend_details"].map((x) => TrendDetail.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"id": id == null ? null : id,
"name": name == null ? null : name,
"day_aired": dayAired == null ? null : dayAired.toIso8601String(),
"trend": trend == null ? null : trend,
"trend_details": trendDetails == null ? null : List<dynamic>.from(trendDetails.map((x) => x.toJson())),
};
}
class TrendDetail {
String name;
TrendDetail({
this.name,
});
factory TrendDetail.fromJson(Map<String, dynamic> json) => TrendDetail(
name: json["name"] == null ? null : json["name"],
);
Map<String, dynamic> toJson() => {
"name": name == null ? null : name,
};
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
String jsonStr = '''
{
"error":"",
"error_number":"",
"response_code":200,
"result":[
{
"id":1,
"name":"Great Deal",
"day_aired":"2015-07-05 11:06:09",
"trend":"Noone",
"trend_details": [{
"name":"Great Deal"
}
]
}
]
}
''';
void _incrementCounter() {
var payload = payloadFromJson(jsonStr);
print('${payload.result[0].name}');
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
I am working on yii2. I have a custom view in which I have placed to views DetailView and GridView. Below is my controller code
public function actionViewcreated($id)// passed the id of my model which is created in the previous step
{
$model=$this->findModel($id); // this will find my model/record based on the id
$sub_div = $model->sub_div;
$meter_type = $model->meter_type;
$query = "SELECT DISTINCT m.`id` AS meter_id, ins.`meter_msn` AS Meter_Serial_Number, ins.`meter_type` AS Meter_Type, sd.`sub_div_code` AS Sub_Division_Code,sd.`name` AS Sub_Division_Name
FROM `installations` ins
INNER JOIN `meters` m ON ins.`meter_msn` = m.`meter_msn`
INNER JOIN `meter_acceptance_header` map ON ins.`meter_type` = map.`meter_type`
INNER JOIN `survey` sur ON ins.`ref_no` = sur.`ref_no`
INNER JOIN `survey_hesco_subdivision` sd ON sur.`sub_division` = sd.`sub_div_code`
WHERE ins.`meter_type` = '$meter_type'
AND sd.`sub_div_code` = '$sub_div'
AND map.`id` NOT IN (SELECT DISTINCT md.`meter_id` FROM
`meter_acceptance_details` md WHERE md.`flag` IN (1))";
$session = Yii::$app->session;
$session->set('my_sql', $query);
$count = Yii::$app->db->createCommand("SELECT COUNT(DISTINCT m.`id`)
FROM `installations` ins
INNER JOIN `meters` m ON ins.`meter_msn` = m.`meter_msn`
INNER JOIN `meter_acceptance_header` map ON ins.`meter_type` = map.`meter_type`
INNER JOIN `survey` sur ON ins.`ref_no` = sur.`ref_no`
INNER JOIN `survey_hesco_subdivision` sd ON sur.`sub_division` = sd.`sub_div_code`
WHERE ins.`meter_type` = '$meter_type'
AND sd.`sub_div_code` = '$sub_div'
AND map.`id` NOT IN (SELECT DISTINCT md.`meter_id` FROM `meter_acceptance_details` md WHERE md.`flag` IN (1))")->queryScalar();
$session = Yii::$app->session;
$session->set('total', $count);
if($count <= 0)
{
$this->findModel($id)->delete();
\Yii::$app->getSession()->setFlash('errors', '
<div class="alert alert-error alert-dismissable">
<button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
<strong>There are no meters installed against the selected Sub Division!!!! </strong>Acceptance is not Created</div>');
return $this->redirect(['index', 'id' => $model->id]);
}
else
{
$dataProvider = new SqlDataProvider([
'sql' => $query,
'totalCount' => $count,
'pagination' => [
'pageSize' => 40,
],
]);
return $this->render('viewcreated', [
'dataProvider' => $dataProvider,
'model' => $model,
'id' => $model->id
/*'searchModel' => $searchModel*/
]);
}
}
View
<?php Pjax::begin(); ?>
<?= DetailView::widget([
'model' => $model,
'attributes' => [
[
'label'=>'Serial #',
'value' => function($d)
{
return $d->id;
}
],
[
'label' => 'Meter Type',
'value' => function ($d) {
if(is_object($d))
return $d->meter_type;
return ' - ';
},
],
'sub_div',
[
'label' => 'Sub Division Name',
'value' => function ($d) {
if(is_object($d))
return $d->subDiv->name;
return '-';
},
],
[
'label' => 'Prepared By',
'value' => function ($d) {
if(is_object($d))
return $d->prepared->name;
},
],
'prepared_at',
'status',
],
]) ?>
<br>
<?= GridView::widget([
'dataProvider' => $dataProvider,
/*'filterModel' => $searchModel,*/
'id'=>'gv',
'columns' => [
['class' => 'yii\grid\SerialColumn'],
['class' => 'yii\grid\CheckboxColumn', 'checkboxOptions' => function($d) {
return ['value' => $d['meter_id']];
}],
'Meter_Serial_Number',
'Meter_Type',
'Sub_Division_Code',
'Sub_Division_Name',
],
]); ?>
<?php Pjax::end(); ?>
JS
$("#chk input:checkbox").prop("checked", true);
Output
In the above grid view I want to add search column for Meter Serial Number and dropdown for Sub Division Name.
Update 1
Installation Model
class Installations extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'installations';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['created_at', 'updated_at','imsi_no','old_meter_power','old_meter_reactive_power','new_meter_power','new_meter_reactive_power','ct_ratio','ct_ratio_quantity','cable_length','cable_type','atb_installed'], 'safe'],
[['created_by', 'updated_by', 'status'], 'integer'],
[['ref_no', 'meter_msn', 'billing_msn', 'customer_id','ct_ratio_quantity','cable_length','meter_type'], 'string', 'max' => 50],
[['tarrif', 's_load', 'ct_ratio','cable_type'], 'string', 'max' => 100],
[['latitude', 'longitude'], 'string', 'max' => 11],
[['site_issues', 'istallation_status', 'comm_status'], 'string', 'max' => 200],
[['consumer_name', 'consumer_address'], 'string', 'max' => 255],
[['so_person_name'], 'string', 'max' => 40],
[['so_phone_number', 'so_person_designation','atb_installed'], 'string', 'max' => 20],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'ref_no' => 'Ref No',
'meter_msn' => 'Meter MSN',
'billing_msn' => 'Billing MSN',
'tarrif' => 'Tarrif',
'created_at' => 'Created At',
'updated_at' => 'Updated At',
'created_by' => 'Created By',
'updated_by' => 'Updated By',
'status' => 'Status',
'latitude' => 'Latitude',
'longitude' => 'Longitude',
'site_issues' => 'Site Issues',
'istallation_status' => 'Installation Status',
'comm_status' => 'Comm Status',
'customer_id' => 'Customer ID',
'consumer_name' => 'Consumer Name',
'consumer_address' => 'Consumer Address',
's_load' => 'Sanctioned Load',
'so_person_name' => 'Person Name',
'so_phone_number' => 'Phone Number',
'so_person_designation' => 'Person Designation',
'imsi_no' => 'IMSI #',
'old_meter_power' => 'Old Meter Power (kWh)',
'old_meter_reactive_power' => 'Old Meter Reactive Power (kVArh)',
'new_meter_power' => 'New Meter Power (kWh)',
'new_meter_reactive_power'=>' New Meter Reactive Power (kVArh)',
'ct_ratio'=>'CT Ratio',
'ct_ratio_quantity'=>'CT Ratio Quantity',
'cable_length'=>'Cable Length',
'cable_type'=>'Cable Type',
'atb_installed'=>'ATB Installed',
'meter_type'=>'Meter Type',
];
}
public static function getCommStatus()
{
return [
//'' => 'Please Select',
'Verified' => 'Verified',
'Failed' => 'Failed',
'System Verified' => 'System Verified'
//'Return Communication Failed SIM' => 'Return Communication Failed SIM'
];
}
public static function getInstallStatus()
{
return [
//'' => 'Please Select',
'Installed' => 'Installed',
'Not Installed' => 'Not Installed',
'System Installed'=>'System Installed'
//'Return Communication Failed SIM' => 'Return Communication Failed SIM'
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getInstallationImages()
{
return $this->hasMany(InstallationImages::className(), ['installation_id' => 'id']);
}
public function getImages(){
return InstallationImages::find()->where(['installation_id' => $this->id])->all();
}
public function getSiteImages(){
return InstallationImagesSite::find()->where(['installation_id' => $this->id])->all();
}
public function getUser(){
return User::find()->where(['id'=>$this->created_by]);
}
public function getTeamsuser()
{
return $this->hasMany(User::className(), ['id' => 'created_by']);
}
}
Installation Search Model
class InstallationsSearch extends Installations
{
/**
* #inheritdoc
*/
public function rules()
{
return [
[['id', 'created_by', 'updated_by', 'status'], 'integer'],
[['ref_no','meter_type','install_type', 'meter_msn', 'billing_msn',
'tarrif', 'created_at','created_by', 'updated_at', 'latitude',
'longitude', 'site_issues', 'istallation_status', 'comm_status',
'customer_id', 'consumer_name', 'consumer_address', 's_load',
'so_person_name', 'so_phone_number', 'so_person_designation','imsi_no',
'meter_id' , 'Meter_Serial_Number' ,'Meter_Type','Sub_Division_Code','Sub_Division_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 = Installations::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_at' => $this->created_at,
'updated_at' => $this->updated_at,
'created_by' => $this->created_by,
'updated_by' => $this->updated_by,
'meter_type' => $this->meter_type,
'install_type'=>$this->install_type,
'istallation_status'=>$this->istallation_status,
'status' => $this->status,
]);
$query->andFilterWhere(['like', 'ref_no', $this->ref_no])
->andFilterWhere(['like', 'meter_msn', $this->meter_msn])
->andFilterWhere(['like', 'billing_msn', $this->billing_msn])
->andFilterWhere(['like', 'tarrif', $this->tarrif])
->andFilterWhere(['like', 'latitude', $this->latitude])
->andFilterWhere(['like', 'longitude', $this->longitude])
->andFilterWhere(['like', 'site_issues', $this->site_issues])
->andFilterWhere(['like', 'istallation_status', $this->istallation_status])
->andFilterWhere(['like', 'comm_status', $this->comm_status])
->andFilterWhere(['like', 'customer_id', $this->customer_id])
->andFilterWhere(['like', 'consumer_name', $this->consumer_name])
->andFilterWhere(['like', 'consumer_address', $this->consumer_address])
->andFilterWhere(['like', 's_load', $this->s_load])
->andFilterWhere(['like', 'so_person_name', $this->so_person_name])
->andFilterWhere(['like', 'so_phone_number', $this->so_phone_number])
->andFilterWhere(['like','meter_type',$this->meter_type])
->andFilterWhere(['like','created_by',$this->created_by])
//->andFilterWhere(['like', 'imsi_no', $this->imsi_no])
->andFilterWhere(['like', 'so_person_designation', $this->so_person_designation]);
if (!Yii::$app->user->isGuest && in_array(Yii::$app->user->identity->user_role, [3,4])) {
$query->joinWith('teamsuser', true);
$query->andFilterWhere(['=', 'user.group_id', Yii::$app->user->identity->group_id]);
}
$query->orderBy(['id' => SORT_DESC]);
return $dataProvider;
}
}
How can I add these two options in the grid view?
Any help would be highly appreciated.
Update 2
As per answer given I have updated my code
InstallationsSearch
class InstallationsSearch extends Installations
{
public $meter_id;
public $Meter_Serial_Number;
public $Meter_Type;
public $Sub_Division_Code;
public $Sub_Division_Name;
/**
* #inheritdoc
*/
public function rules()
{
return [
[['id', 'created_by', 'updated_by', 'status'], 'integer'],
[['ref_no','meter_type','install_type', 'meter_msn', 'billing_msn', 'tarrif', 'created_at','created_by', 'updated_at', 'latitude', 'longitude', 'site_issues', 'istallation_status', 'comm_status', 'customer_id', 'consumer_name', 'consumer_address', 's_load', 'so_person_name', 'so_phone_number', 'so_person_designation','imsi_no','meter_id' , 'Meter_Serial_Number' ,'Meter_Type','Sub_Division_Code','Sub_Division_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 = Installations::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_at' => $this->created_at,
'updated_at' => $this->updated_at,
'created_by' => $this->created_by,
'updated_by' => $this->updated_by,
'meter_type' => $this->meter_type,
'install_type'=>$this->install_type,
'istallation_status'=>$this->istallation_status,
'status' => $this->status,
]);
$query->andFilterWhere(['like', 'ref_no', $this->ref_no])
->andFilterWhere(['like', 'meter_msn', $this->meter_msn])
->andFilterWhere(['like', 'billing_msn', $this->billing_msn])
->andFilterWhere(['like', 'tarrif', $this->tarrif])
->andFilterWhere(['like', 'latitude', $this->latitude])
->andFilterWhere(['like', 'longitude', $this->longitude])
->andFilterWhere(['like', 'site_issues', $this->site_issues])
->andFilterWhere(['like', 'istallation_status', $this->istallation_status])
->andFilterWhere(['like', 'comm_status', $this->comm_status])
->andFilterWhere(['like', 'customer_id', $this->customer_id])
->andFilterWhere(['like', 'consumer_name', $this->consumer_name])
->andFilterWhere(['like', 'consumer_address', $this->consumer_address])
->andFilterWhere(['like', 's_load', $this->s_load])
->andFilterWhere(['like', 'so_person_name', $this->so_person_name])
->andFilterWhere(['like', 'so_phone_number', $this->so_phone_number])
->andFilterWhere(['like','meter_type',$this->meter_type])
->andFilterWhere(['like','created_by',$this->created_by])
//->andFilterWhere(['like', 'imsi_no', $this->imsi_no])
->andFilterWhere(['like', 'so_person_designation', $this->so_person_designation]);
if (!Yii::$app->user->isGuest && in_array(Yii::$app->user->identity->user_role, [3,4])) {
$query->joinWith('teamsuser', true);
$query->andFilterWhere(['=', 'user.group_id', Yii::$app->user->identity->group_id]);
}
$query->orderBy(['id' => SORT_DESC]);
return $dataProvider;
}
/**
* #param $params
* #return SqlDataProvider
* #throws \yii\db\Exception
*/
public function searchInstallations($params )
{
$query = /** #lang text */
"SELECT DISTINCT
m.`id` AS meter_id,
ins.`meter_msn` AS Meter_Serial_Number,
ins.`meter_type` AS Meter_Type,
sd.`sub_div_code` AS Sub_Division_Code,
sd.`name` AS Sub_Division_Name
FROM `installations` ins
INNER JOIN `meters` m ON ins.`meter_msn` = m.`meter_msn`
INNER JOIN `meter_acceptance_header` map ON ins.`meter_type` =
map.`meter_type`
INNER JOIN `survey` sur ON ins.`ref_no` = sur.`ref_no`
INNER JOIN `survey_hesco_subdivision` sd ON sur.`sub_division` =
sd.`sub_div_code`
WHERE ins.`meter_type` = :meter_type
AND sd.`sub_div_code` = :sub_div
AND map.`id` NOT IN (SELECT DISTINCT md.`meter_id` FROM
`meter_acceptance_details` md WHERE md.`flag` IN (1))";
$queryParams = [
':meter_type' => $params['meter_type'] ,
':sub_div' => $params['sub_div']
];
$queryCount = /** #lang text */
"SELECT COUNT(DISTINCT m.`id`)
FROM `installations` ins
INNER JOIN `meters` m ON ins.`meter_msn` = m.`meter_msn`
INNER JOIN `meter_acceptance_header` map ON ins.`meter_type` = map.`meter_type`
INNER JOIN `survey` sur ON ins.`ref_no` = sur.`ref_no`
INNER JOIN `survey_hesco_subdivision` sd ON sur.`sub_division` = sd.`sub_div_code`
WHERE ins.`meter_type` = :meter_type
AND sd.`sub_div_code` = :sub_div
AND map.`id` NOT IN (SELECT DISTINCT md.`meter_id` FROM
`meter_acceptance_details` md WHERE md.`flag` IN (1))";
$dataProvider = new SqlDataProvider ( [
'sql' => $query ,
'pagination' => [
'pageSize' => 60 ,
] ,
] );
if ( !($this->load ( $params ) && $this->validate ()) ) {
return $dataProvider;
}
if($this->Meter_Serial_Number!==''){
$dataProvider->sql .= ' AND ins.meter_msn like :meter_serial_number';
$queryParams[':meter_serial_number'] = $this->Meter_Serial_Number . '%';
$queryCount .= ' AND ins.meter_msn like :meter_serial_number';
}
if($this->Sub_Division_Code!==''){
$dataProvider->sql .= ' AND sd.sub_div_code like :sub_div_code';
$queryParams[':sub_div_code'] = $this->Sub_Division_Code . '%';
$queryCount .= ' AND sd.sub_div_code like :sub_div_code';
}
$count = Yii::$app->db->createCommand ( $queryCount , $queryParams )->queryScalar ();
$dataProvider->totalCount = $count;
$dataProvider->params = $queryParams;
//add to session
$session = Yii::$app->session;
$session->set ( 'my_sql' , Yii::$app->db->createCommand ( $query , $queryParams )->rawSql );
$session->set ( 'total' , $count );
//return data provider
return $dataProvider;
}
}
Controler Code
public function actionViewcreated( $id ) {// passed the id of my model which is created in the previous step
$model = $this->findModel ( $id ); // this will find my model/record based on the id
$sub_div = $model->sub_div;
$meter_type = $model->meter_type;
$queryParams = Yii::$app->request->queryParams;
$queryParams['sub_div'] = $sub_div;
$queryParams['meter_type'] = $meter_type;
$searchModel = new InstallationsSearch();
$dataProvider = $searchModel->searchInstallations( $queryParams );
if ( Yii::$app->session->get ( 'total' ) <= 0 ) {
$this->findModel ( $id )->delete ();
\Yii::$app->getSession ()->setFlash ( 'errors' , '
<div class="alert alert-error alert-dismissable">
<button aria-hidden="true" data-dismiss="alert" class="close"
type="button">×</button>
<strong>There are no meters installed against the selected Sub Division!!!! </strong>Acceptance is not Created</div>' );
return $this->redirect ( [ 'index' , 'id' => $model->id ] );
}
return $this->render ( 'viewcreated' , [
'dataProvider' => $dataProvider ,
'model' => $model ,
'id' => $model->id ,
'searchModel' => $searchModel
] );
}
View
<?= GridView::widget([
'dataProvider' => $dataProvider,
//'ajaxUpdate' => true,
//'filterModel' => $searchModel,
'id'=>'gv',
'columns' => [
['class' => 'yii\grid\SerialColumn'],
['class' => 'yii\grid\CheckboxColumn', 'checkboxOptions' => function($d) {
return ['value' => $d['meter_id']];
}],
[
'attribute'=>'Meter_Serial_Number',
'filter'=> \yii\helpers\Html::activeTextInput($searchModel, 'Meter_Serial_Number',['class'=>'form-control']),
],
'Meter_Type',
'Sub_Division_Code',
[
'attribute' => 'Sub_Division_Name',
'filter'=> \yii\helpers\Html::activeTextInput($searchModel,'Sub_Division_Name', ['class'=>'form-control']),
],
],
]); ?>
Update 3
After updating my code I got search filters on both the Meter Serial and Sub Division
But when I try to search a serial number then the page got refreshed and again I see the full view
By echo $dataProvider->sql; I got
SELECT DISTINCT m.idAS meter_id, ins.meter_msnAS Meter_Serial_Number, ins.meter_typeAS Meter_Type, sd.sub_div_codeAS Sub_Division_Code, sd.nameAS Sub_Division_Name FROMinstallationsins INNER JOINmetersm ON ins.meter_msn= m.meter_msnINNER JOINmeter_acceptance_headermap ON ins.meter_type= map.meter_typeINNER JOINsurveysur ON ins.ref_no= sur.ref_noINNER JOINsurvey_hesco_subdivisionsd ON sur.sub_division= sd.sub_div_codeWHERE ins.meter_type= :meter_type AND sd.sub_div_code= :sub_div AND map.idNOT IN (SELECT DISTINCT md.meter_idFROMmeter_acceptance_detailsmd WHERE md.flagIN (1)) AND ins.meter_msn like :meter_serial_number AND sd.sub_div_code like :sub_div_code
The above query remains the same whether I have searched for any serial number or not.
Note: I already know how to add a search option by adding a filter field in a calculated column in my search model. But as I have a custom view so I am confused that how can I do it.
I have'nt used it quite though with the GridView and there could be better solutions than this one if anyone could suggest but this can work the way you want.
You should update your InstallationSearch model to use the filter with the GridView.If you are not using the default InstallationSearch model's search() function anywhere then you can update the existing search method or else create a separate method. I assume you are not using it anywhere else and I will add a search function that will use the SqlDataProvider for displaying and filtering the GridView.
To implement the search you need to identify how many aliases are you using for the columns you have to declare all of them as public attributes of your search model
public $meter_id;
public $Meter_Serial_Number;
public $Meter_Type;
public $Sub_Division_Code;
public $Sub_Division_Name;
then add them to the safe rule
public function rules() {
return [
[ [ 'meter_id' , 'Meter_Serial_Number' ,'Meter_Type','Sub_Division_Code','Sub_Division_Name'] , 'safe' ] ,
];
}
Now add the search() function
public function searchInstallations($params)
{
$query = "SELECT DISTINCT
m.`id` AS meter_id,
ins.`meter_msn` AS Meter_Serial_Number,
ins.`meter_type` AS Meter_Type,
sd.`sub_div_code` AS Sub_Division_Code,
sd.`name` AS Sub_Division_Name
FROM `installations` ins
INNER JOIN `meters` m ON ins.`meter_msn` = m.`meter_msn`
INNER JOIN `meter_acceptance_header` map ON ins.`meter_type` = map.`meter_type`
INNER JOIN `survey` sur ON ins.`ref_no` = sur.`ref_no`
INNER JOIN `survey_hesco_subdivision` sd ON sur.`sub_division` = sd.`sub_div_code`
WHERE ins.`meter_type` = :meter_type
AND sd.`sub_div_code` = :sub_div
AND map.`id` NOT IN (SELECT DISTINCT md.`meter_id` FROM
`meter_acceptance_details` md WHERE md.`flag` IN (1))";
$queryParams = [
':meter_type' => $params['meter_type'],
':sub_div' => $params['sub_div']
];
$queryCount = "SELECT COUNT(DISTINCT m.`id`)
FROM `installations` ins
INNER JOIN `meters` m ON ins.`meter_msn` = m.`meter_msn`
INNER JOIN `meter_acceptance_header` map ON ins.`meter_type` = map.`meter_type`
INNER JOIN `survey` sur ON ins.`ref_no` = sur.`ref_no`
INNER JOIN `survey_hesco_subdivision` sd ON sur.`sub_division` = sd.`sub_div_code`
WHERE ins.`meter_type` = :meter_type
AND sd.`sub_div_code` = :sub_div
AND map.`id` NOT IN (SELECT DISTINCT md.`meter_id` FROM `meter_acceptance_details` md WHERE md.`flag` IN (1))";
$dataProvider = new SqlDataProvider(
[
'sql' => $query,
'pagination' => [
'pageSize' => 40
]
]
);
if (!($this->load($params) && $this->validate())) {
$dataProvider->params = $queryParams;
$count = Yii::$app->db->createCommand($queryCount, $queryParams)->queryScalar();
$dataProvider->totalCount = $count;
return $dataProvider;
}
if ($this->Meter_Serial_Number !== '') {
$dataProvider->sql .= ' AND ins.meter_msn like :meter_serial_number';
$queryParams[':meter_serial_number'] = $this->Meter_Serial_Number . '%';
$queryCount .= ' AND ins.meter_msn like :meter_serial_number';
}
if ($this->Sub_Division_Code !== '') {
$dataProvider->sql .= ' AND sd.sub_div_code like :sub_div_code';
$queryParams[':sub_div_code'] = $this->Sub_Division_Code . '%';
$queryCount .= ' AND sd.sub_div_code like :sub_div_code';
}
$count = Yii::$app->db->createCommand($queryCount, $queryParams)->queryScalar();
$dataProvider->totalCount = $count;
$dataProvider->params = $queryParams;
//add to session
$session = Yii::$app->session;
$session->set('my_sql', Yii::$app->db->createCommand($query, $queryParams)->rawSql);
$session->set('total', $count);
//return data provider
return $dataProvider;
}
Update your actionViewcreated to the following
public function actionViewcreated($id)
{
// passed the id of my model which is created in the previous step
$model = $this->findModel($id); // this will find my model/record based on the id
$sub_div = $model->sub_div;
$meter_type = $model->meter_type;
$queryParams = Yii::$app->request->queryParams;
$queryParams['sub_div'] = $sub_div;
$queryParams['meter_type'] = $meter_type;
$searchModel = new InstallationSearch();
$dataProvider = $searchModel->searchInstallation($queryParams);
if (Yii::$app->session->get('total') <= 0) {
$this->findModel($id)->delete();
\Yii::$app->getSession()->setFlash(
'errors',
'<div class="alert alert-error alert-dismissable"><button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
<strong>There are no meters installed against the selected Sub Division!!!! </strong>Acceptance is not Created</div>'
);
return $this->redirect(['index', 'id' => $model->id]);
}
return $this->render(
'viewcreated', [
'dataProvider' => $dataProvider,
'model' => $model,
'id' => $model->id,
'searchModel' => $searchModel
]
);
}
and enable the filter for the grid view like below
[
'attribute'=>'Meter_Serial_Number',
'filter'=> \yii\helpers\Html::activeTextInput($searchModel, 'Meter_Serial_Number',['class'=>'form-control']),
],
[
'attribute' => 'Sub_Division_Name',
'filter'=> \yii\helpers\Html::activeTextInput($searchModel,'Sub_Division_Name', ['class'=>'form-control']),
],
EDIT
For making your dropdown work either change the function toArrayList() to the following if it is not used anywhere else or create a separate function as you need to have the sub_div_name as the value to be searched in the table so change the sub_div_code to the field which has the sub_div_name i assume it is the name field in the SurveyHescoSubdivision .
/**
* #return mixed
*/
public static function toArrayList()
{
return ArrayHelper::map(
self::find()->all(), 'name', function ($model, $defaultValue) {
return $model['sub_div_code'] . ' - ' . $model['name'];
}
);
}