Am trying to display a grid view in the yii2 view action of a controller but I get an error of Call to a member function getTotalCount() on a non-object
I have two related tables that is table Cases which is related to table Accusers as below
Case Model coe:
public function getAccusers()
{
return $this->hasMany(Accuser::className(), ['case_ref_no' => 'ref_no']);
}
Accusers model relation
public function getCaseRefNo()
{
return $this->hasOne(Cases::className(), ['ref_no' => 'case_ref_no']);
}
In the case controller the action index has a grid view in which I select the view action
In the view action I have passed grid view which basically I would wish that it displays the records of Accusers related to a certain case (That is the case ref no just as in the relationship above)
The case controller code: Action view
public function actionView($id)
{
$dataProvider = Accuser::find()
->where(['case_ref_no'=>$id])
->all();
return $this->render('view', [
'dataProvider' => $dataProvider,
'model' => $this->findModel($id),
]);
}
The view page (code)
use kartik\grid\GridView;
...........
<?= GridView::widget([
'dataProvider' => $dataProvider,
'showPageSummary' =>false,
//'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
I used 'showPageSummary' =>false, but I still get that error of Call to a member function getTotalCount() on a non-object
Full accuser model
class Accuser extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'accuser';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['fname', 'secname', 'date_of_birth', 'case_ref_no', 'idno'], 'required'],
[['date_of_birth'], 'safe'],
[['fname', 'secname', 'case_ref_no', 'idno'], 'string', 'max' => 100]
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'fname' => 'Fname',
'secname' => 'Secname',
'date_of_birth' => 'Date Of Birth',
'case_ref_no' => 'Case Ref No',
'idno' => 'Idno',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getCaseRefNo()
{
return $this->hasOne(Cases::className(), ['ref_no' => 'case_ref_no']);
}
}
The var_dump($dataProvider); in the view I got(after changing the find()->all() to find() only
object(yii\db\ActiveQuery)[60]
public 'sql' => null
public 'on' => null
public 'joinWith' => null
public 'select' => null
public 'selectOption' => null
public 'distinct' => null
public 'from' => null
public 'groupBy' => null
public 'join' => null
public 'having' => null
public 'union' => null
public 'params' =>
array (size=0)
empty
private '_events' (yii\base\Component) =>
array (size=0)
empty
private '_behaviors' (yii\base\Component) =>
array (size=0)
empty
public 'where' =>
array (size=1)
'case_ref_no' => string '#RERADRADAR' (length=11)
public 'limit' => null
public 'offset' => null
public 'orderBy' => null
public 'indexBy' => null
public 'modelClass' => string 'frontend\models\Accuser' (length=23)
public 'with' => null
public 'asArray' => null
public 'multiple' => null
public 'primaryModel' => null
public 'link' => null
public 'via' => null
public 'inverseOf' => null
For the dataProvider you should create with the proper class (ActiveDataprovider, ArrayDataProvider or SqlDataProvider )
this way and not directly form the query like you have done
$query = Accuser::find()
->where(['case_ref_no'=>$id]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
If you think the problem is related to the summary too you should avoid this using
'summary'=>"",
this way
<?= GridView::widget([
'dataProvider' => $dataProvider,
'summary'=>"",
change your action to this
public function actionView($id)
{
$dataProvider = Accuser::find()
->where(['case_ref_no'=>$id]);
return $this->render('view', [
'dataProvider' => $dataProvider,
'model' => $this->findModel($id),
]);
}
gridview work with dataprovider and find result is your data provider . if you use all() after find your $dataprovider variable is an array of your Accuser object!
Related
I was trying to make a search model for a mini Geo location system. Whenever I tried to get the data sorted by directly calling the GeoData model it works unless we try to sort.
But when we try to use the CustomMade SearchModel it sends :
Invalid argument supplied for foreach()
Here is the SearchModel:
<?php namespace app\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\GeoData;
/**
* This is the ActiveQuery class for [[GeoData]].
*
* #see GeoData
*/
class GeoDataSearch extends GeoData
{
const TODOS = 1;
const FECHA = 2;
const ENVIADO_POR = 3;
public function rules()
{
return [
[['latitude', 'longitude'], 'required'],
[['latitude', 'longitude', 'accuracy', 'speed', 'betterlocation'], 'number'],
[['device_id', 'active', 'sended', 'mobildate', 'created_at', 'updated_at', 'created_by'], 'integer'],
[['created_by'], 'exist', 'skipOnError' => true, 'targetClass' => User::class, 'targetAttribute' => ['created_by' => 'id']],
];
}
/*public function active()
{
return $this->andWhere('[[status]]=1');
}*/
public function scenarios()
{
return Model::scenarios();
}
public function search($params)
{
$query = GeoData::find()->where(['created_by' => $params])->all();
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'defaultOrder' => [
'created_at' => SORT_DESC,
]
]
]);
$dataProvider->sort->attributes['created_at'] = [
'asc' => ['created_at' => SORT_ASC],
'desc' => ['created_at' => SORT_DESC]
];
$dataProvider->sort->attributes['created_by'] = [
'asc' => ['created_by' => SORT_ASC],
'desc' => ['created_by' => SORT_DESC]
];
$dataProvider->sort->attributes['geo_id'] = [
'asc' => ['geo_id' => SORT_ASC],
'desc' => ['geo_id' => SORT_DESC]
];
$this->load($params);
$query->andFilterWhere([
'geo_id' => $this->geo_id,
'latitude' => $this->latitude,
'longitude' => $this->longitude,
'accuracy' => $this->accuracy,
'speed' => $this->speed,
'device_id' => $this->device_id,
'betterlocation' => $this->betterlocation,
'active' => $this->active,
'mobiledate' => $this->mobildate,
'sended' => $this->sended,
'updated_at' => $this->updated_at,
'created_at' => $this->created_at,
'created_by' => $this->created_by,
]);
return $dataProvider;
}
}
And the controller (The code that is commented works but it won't allow me to use the GridView nor filter or Sort:
public function actionView($id)
{
$title = "Ver Historial";
$id = $_GET['id'];
$searchModel = new GeoDataSearch;
$dataProvider = $searchModel->search($id);
return $this->render(
'view',
[
'title' => $title,
'searchModel' => $searchModel,
'dataProvider' => $dataProvider
]);
/*
if (Yii::$app->user->can(AuthItem::ROLE_ADMINISTRATOR))
{
$id = $_GET['id'];
$title = "Ver Historial";
$model = new GeoData;
$dataProvider = $model::findAll(['created_by'=> $id]);
return $this->render(
'view',
[
'title' => $title,
'dataProvider' => $dataProvider,
]
);
} else {
throw new ForbiddenHttpException('Access denied for user '.Yii::$app->user->identity->id);
}
*/
}
Any Suggestion will be really appreciated!
First your GeoDataSearch search() function should look like this:
public function search($params)
{
$query = GeoData::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'defaultOrder' => [
'created_at' => SORT_DESC,
]
]
]);
$this->load($params);
if (!$this->validate())
return $dataProvider;
$query->andFilterWhere([
'geo_id' => $this->geo_id,
'latitude' => $this->latitude,
'longitude' => $this->longitude,
'accuracy' => $this->accuracy,
'speed' => $this->speed,
'device_id' => $this->device_id,
'betterlocation' => $this->betterlocation,
'active' => $this->active,
'mobiledate' => $this->mobildate,
'sended' => $this->sended,
'updated_at' => $this->updated_at,
'created_at' => $this->created_at,
'created_by' => $this->created_by,
]);
return $dataProvider;
}
Add a second function to GeoDataSearch model:
/**
* #param int $creator_id ID of the creator
* #param mixed $params
* #return ActiveDataProvider
*/
public function searchByCreator($creator_id, $params)
{
// This will verify if the $params['GeoDataSearch'] index exists
if ( isset( $params['GeoDataSearch'] ) ) {
$params['GeoDataSearch']['created_by'] = $creator_id;
} else {
// Create the index if it doesn't exist
$params['GeoDataSearch'] = ['created_by' => $creator_id];
}
return $this->search($params);
}
Update your actionView:
public function actionView($id)
{
$title = "Ver Historial";
$searchModel = new GeoDataSearch;
// The $id param is already receiving the $_GET['id']
$dataProvider = $searchModel->searchByCreator($id, Yii::$app->request->queryParams);
return $this->render(
'view',
[
'title' => $title,
'searchModel' => $searchModel,
'dataProvider' => $dataProvider
]);
}
Don't setup user validation in the actionView, use AccessControl instead.
Explaining
DataProvider are for easy data pagination and sorting (View docs).
Querys filters the data for the DataProvider manipulation.
The search method is a convention for attaching query filters to a DataProvider in a single place for better reuse. So you should be looking to have a generic search method that can be reused on multiple scenarios.
In this example, the GeoDataSearch->search() method is used for searching multiple GeoData models by passing any parameter you want, so you will pass an array to it, most likely with the data from front-end.
If you are using Yii2 ActiveForm inputs (with GET method set) in your front-end, on GeoDataSearch fields. For example:
<?= $form->field($searchModel, 'speed')->textInput(); ?>
This will allow to filter the results by the speed attribute. When a user submit this form, your back-end will get the following content from Yii::$app->request->queryParams:
[
'GeoDataSearch' => [
'speed' => '10.00'
]
]
You should use this on the search method, that will filter all GeoData by their speed.
Important to notice that the speed attribute will be a index on GeoDataSearch. This is the way Yii2 builds params with their core form methods and affects the models load() method, and the reason that the implementation on searchByCreator seems a little convoluted.
Back to your case,
You need to filter by the creator. Since this needs to always be attached to the search query params, I recommend you to create a new method (searchByCreator), which consumes the already existing search method, force the created_by attribute, all while keeping the other filters alive.
I have a database having relationship of three levels. cheque->account->customer. Now I am trying to retrieve data from all three table at same time using the following method.
$query = Cheque::find();
$query->joinWith(['account.customer']);
$query->orderBy('sr desc');
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
Cheque Model:
class Cheque extends \common\components\db\ActiveRecord {
/**
* #inheritdoc
*/
public static function tableName() {
return 'cheque';
}
/**
* #inheritdoc
*/
public function rules() {
return [
[['sr'], 'integer'],
[['ID', 'account_ID'], 'required'],
[['ID', 'account_ID', 'created_by', 'branch_ID', 'application_ID'], 'string'],
[['serial_start', 'serial_end', 'status'], 'number'],
[['created_on'], 'safe']
];
}
/**
* #inheritdoc
*/
public function attributeLabels() {
return [
'ID' => 'ID',
'account_ID' => 'Account ID',
'serial_start' => 'Serial Start',
'serial_end' => 'Serial End',
'created_on' => 'Created On',
'created_by' => 'Created By',
'branch_ID' => 'Branch ID',
'application_ID' => 'Application ID',
'status' => 'Status',
'sr' => 'ID'
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getAccount() {
return $this->hasOne(Account::className(), ['ID' => 'account_ID']);
}
public static function getActiveChequeBook($account_ID) {
return Cheque::findAll(['account_ID' => $account_ID, 'status' => array_search('Active', \common\models\Lookup::$cheque_status)]);
}
}
But executing this I get the following error:
pre>Exception 'yii\base\InvalidCallException' with message 'Setting read-only property: common\models\Account::customer'
Property customer in your common\models\Account model has no setter (only getCustomer method exists). Check you model and add appropriate property to class.
I have to implementing export excel using moonland PHP excel extension. When I implemented it, it throws the error:
"Call to a member function getAttributeLabel() on null".
Code
$dataProvider = new \app\models\Details;
\moonland\phpexcel\Excel::widget([
'models' => $dataProvider,
'mode' => 'export', //default value as 'export'
'columns' => ['id','name','address'], //without header working, because the header will be get label from attribute label.
//'header' => ['id' => 'Header Column 1','name' => 'Header Column 2', 'address' => 'Header Column 3'],
]);
My model
class Details extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'details';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['id'], 'required'],
[['id'], 'integer'],
[['name', 'address'], 'string', 'max' => 50],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
/* return [
'id' => Yii::t('app', 'ID'),
'name' => Yii::t('app', 'Name'),
'address' => Yii::t('app', 'Address'),
];*/
return [
'id' => 'ID',
'name' => 'Name',
'address' =>'Address',
];
}
}
Table
May i know what is the problem in that, thanks in advance for your idea
This is my model Riders:
<?php
namespace backend\models;
use Yii;
class Riders extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'riders';
}
public function rules()
{
return [
[['cagories_category_id', 'rider_firstname', 'rider_no_tlpn', 'rider_ucinumber', 'countries_id', 'rider_province', 'rider_city', 'rider_dateofbirth', 'rider_gender'], 'required'],
[['user_id', 'countries_id'], 'integer'],
[['rider_dateofbirth', 'cagories_category_id'], 'safe'],
[['rider_gender', 'rider_status'], 'string'],
[['rider_firstname', 'rider_lastname', 'rider_nickname', 'rider_province', 'rider_city'], 'string', 'max' => 45],
[['rider_email', 'rider_sponsor', 'rider_birthcertificate_url', 'rider_parental_consent_url'], 'string', 'max' => 100],
[['rider_no_tlpn'], 'string', 'max' => 15],
[['rider_ucinumber'], 'string', 'max' => 11]
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'rider_id' => 'rider_id',
'cagories_category_id' => 'Category Name',
'user_id' => 'User Team',
'rider_firstname' => 'Rider Firstname',
'rider_lastname' => 'Rider Lastname',
'rider_nickname' => 'Rider Nickname',
'rider_email' => 'Rider Email',
'rider_no_tlpn' => 'Rider No Tlpn',
'rider_ucinumber' => 'Rider Ucinumber',
'countries_id' => 'Country Name',
'rider_province' => 'Rider Province',
'rider_city' => 'Rider City',
'rider_sponsor' => 'Rider Sponsor',
'rider_dateofbirth' => 'Rider Dateofbirth',
'rider_gender' => 'Rider Gender',
'rider_birthcertificate_url' => 'Rider Birthcertificate Url',
'rider_parental_consent_url' => 'Rider Parental Consent Url',
'rider_status' => 'Rider Status',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getRegistrations()
{
return $this->hasMany(Registrations::className(), ['riders_rider_id' => 'rider_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getCagoriesCategory()
{
return $this->hasOne(Categories::className(), ['category_id' => 'cagories_category_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'user_id']) -> from(user::tableName() . 'ud');
}
/**
* #return \yii\db\ActiveQuery
*/
public function getUserDesc()
{
return $this->hasOne(UserDesc::className(), ['desc_id' => 'user_id']) -> from(['ud' => userDesc::tableName()]);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getCountries()
{
return $this->hasOne(Countries::className(), ['id' => 'countries_id']);
}
}
This my Controller actionIndex:
$searchModel = new RidersSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$totalCount = Yii::$app->db->createCommand('SELECT COUNT(*) FROM riders WHERE user_id = :user_id',
[':user_id' => Yii::$app->user->identity->id])->queryScalar();
$dataProvider = new SqlDataProvider([
'sql' => 'SELECT * FROM riders WHERE user_id = :user_id',
'params' => [':user_id' => Yii::$app->user->identity->id],
'totalCount' => $totalCount,
'key' => 'rider_id',
'pagination' => [
'pageSize' => 10,
],
'sort' => [
'attributes' => [
'cagories_category_id',
'rider_id',
'rider_firstname',
'rider_email:email',
'rider_no_tlpn',
]
]
]);
$models = $dataProvider->getModels();
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
This is my view index:
<?= GridView::widget([
'dataProvider' => $dataProvider,
// 'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
[
'label' => 'Category Name',
'attribute'=>'cagories_category_id',
'value' => 'cagoriesCategory.category_name', <---Can't work again
],
[
'label' => 'BIB',
'attribute'=>'rider_id',
],
'rider_firstname',
'rider_email:email',
'rider_no_tlpn',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
Before I use sqldataprovider, it can call from model function have relation, after use sqldataprovider can't work. How to get relation table value???
then before use it, i can to merge rider_firstname and rider_lastname with return $model->rider_firstname . " " . rider_lastname; after use sqldataprovider can't work too??
SqlDataProvider returns data as an array so You can't able to access related object with $dataProvider->models()
either you have to use ActiveDataProvider or change your Sql of SqlDataProvider with join condition
sql='sql' => 'SELECT * FROM riders inner join '. Categories::tableName() .' as c on c.category_id=riders.cagories_category_id WHERE user_id = :user_id'
I have Post model and two related models, User and Comments.
Relation with User and Comment model defined in Post model:
public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
public function getUserName()
{
return $this->user->name;
}
public function getComments(){
return $this->hasMany(Comment::className(), ['comment_id' => 'id']);;
}
public function countCommentPoints() {
return $this->hasMany(Comment::className(), ['comment_id' => 'id'])->sum('point');
}
My PostSearch model looks like following. I omitted the code andFilterWhere() because it is out of the scope:
class PostSearch extends Post {
public $userName;
public function rules()
{
return ['userName', 'safe'];
}
public function search($params) {
$query = Post::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$dataProvider->setSort([
'attributes' => [
'userName' => [
'asc' => ['user.name' => SORT_ASC],
'desc' => ['user.name' => SORT_DESC],
],
],
]);
if (!($this->load($params) && $this->validate())) {
$query->joinWith(['user']);
return $dataProvider;
}
}
return $dataProvider;
}
View code:
echo GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'id',
'title',
[
'attribute' => 'userName',
'value' => 'userName',
'label' => 'User Name'
],
[
'attribute' => 'comment',
'value' => function($model){
return $model->countCommentPoints();
},
'label' => 'Points',
],
],
]);
With all the code above I could sort the records by id and title and by userName which is an attribute of the related User model. But I could not sort by the column Points since it is not an attribute of the Comment model but a sum value returned from the getLeadEarningPerWebmaster() method. I have tried to read Yii 2.0 documentation here http://www.yiiframework.com/doc-2.0/guide-output-data-widgets.html but the section Sorting Data is still under development. How can I make GridView sort by the Points column?
You can sort GridView columns by related columns that are not necessarily inside the original ActiveRecord.
echo GridView::widget([
'dataProvider' => new ActiveDataProvider([
'query' => $model->joinWith([
'comment' => function ($query) {
$query->select('SUM(point) AS sum')->from(['comment' => '{{%comment}}']);
}
]),
'sort' => [
'attributes' => [
'comment.sum' => [
'asc' => ['comment.sum' => SORT_ASC],
'desc' => ['comment.sum' => SORT_DESC],
]
],
'sortParam' => 'post-sort',
'defaultOrder' => ['comment.sum' => SORT_ASC]
],
]),
That should get you started.