Yii Model with combined tables using relations in Active Record - yii

I've got the model class Contactindiv with relations and search as follows.
public function relations()
{
return array(
'contactlogs' => array(self::HAS_MANY, 'Contactlog', 'ContactIndivID'),
);
}
public function search()
{
$criteria=new CDbCriteria;
$criteria->compare('ContactIndivID',$this->ContactIndivID);
$criteria->compare('PersonalTitle',$this->PersonalTitle,true);
$criteria->compare('NameLast',$this->NameLast,true);
$criteria->compare('NameMiddle',$this->NameMiddle,true);
$criteria->compare('NameFirst',$this->NameFirst,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
The current page shows the data in a searchable CGridView format.
My goal is to combine the 'contactlogs' from relations into the Model in order to have it show up on the page in a searchable fashion in the GridView. Basically add a searchable GridView column for each contact showing their contact log.
Thanks ahead of time for your help!

For your first goal (show contactlogs in model) you can write a getter in your main model. It depends, what you want to show in your gridview column but you could use something like:
public function getContacts()
{
$names = array();
foreach($this->contactlogs as $log)
$names[] = $log->name;
return implode(', ', $names);
}
Now you can use contacts as if it where a regular attribute of your "Contactindiv" model.
For your second goal you could add a public property which will contain the filter value, and which you can use in your search() method:
public $contactFilter;
public function search()
{
// ...
if(!empty($this->contactFilter)) {
$criteria->with = array(
'contactlogs' => array(
'joinType' => 'INNER JOIN',
'on' => 'contactlogs.name = :name',
),
);
$criteria->params[':name'] = $this->contactFilter;
}
// ..
}
Now you only need to add all the above in your gridview's columns configuration:
array(
'name' => 'contacts',
'filter' => CHtml::activeTextField($model, 'contactFilter'),
)
Please note, that i'm writing most of this from the top of my head and couldn't fully test it. But it should hopefully make the basic concept clear to you. Please let me know if it works.

Related

YII1 clistview data is empty

I have a custom dropdown pagination in my project and using CListView to display the items, but it's not working correctly. Whatever the number that I select in the dropdownlist, or the page number, the content of the list is empty. I can see that the summary number is changed and correctly shown, but the list is not shown. What I am missing?
Below is the model - candidateSearch() code:
public function candidateSearch()
{
$criteria = new CDbCriteria;
$criteria->compare('can_id',$this->can_id);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'sort'=>array(
'defaultOrder'=>'name ASC',
),
'pagination'=>array(
'pageSize'=> Yii::app()->user->getState('pageSize', Yii::app()->params['defaultPageSize']),
),
));
}
Below is the controller code:
public function actionIndex()
{
if (isset($_GET['pageSize']))
{
Yii::app()->user->setState('pageSize',(int)$_GET['pageSize']);
unset($_GET['pageSize']);
}
$model = new JbMeetGreet('candidateSearch');
$model->unsetAttributes(); // clear any default values
$model->can_id = Yii::app()->user->id;
$model->created_by = Yii::app()->user->id;
$model->created_by_lvl = Yii::app()->user->user_level;
//search function
if(isset($_POST['JbMeetGreet']))
{
$model->attributes = $_POST['JbMeetGreet'];
}
$this->render('index', array('model'=>$model));
}
Below is the index page code:
$pageSize = Yii::app()->user->getState('pageSize',Yii::app()->params['defaultPageSize']);
echo CHtml::dropDownList('pageSize',$pageSize,array(5=>5,10=>10,15=>15,20=>20,25=>25,30=>30),
array('onchange'=>"$.fn.yiiListView.update('meet-greet-grid',{ data:{pageSize: $(this).val() }})",
'empty'=>'-- Select Page Range --','style'=>'width:100%;'));
$this->widget('zii.widgets.CListView',array(
'id'=>'meet-greet-grid',
'dataProvider'=>$model->candidateSearch(),
'itemView'=>'_profile_list',
'template'=>'{summary}{pager}{items}',
'summaryText'=>'Result {start} - {end} of {count} results',
});
You must add your dropDownList into your grid (to make ajax call to work):
// into your view
$pageSize = Yii::app()->user->getState('pageSize', Yii::app()->params['defaultPageSize']);
$perPagina = CHtml::dropDownList('pageSize', $pageSize, array(5=>5,10=>10,15=>15,20=>20,25=>25,30=>30),
array('onchange' => "$.fn.yiiGridView.update('meet-greet-grid',{ data:{pageSize: $(this).val() }})")
);
$this->widget('zii.widgets.CListView',array(
'id'=>'meet-greet-grid',
// add this
'header' => $perPagina,
'dataProvider'=>$model->candidateSearch(),
'itemView'=>'_profile_list',
'template'=>'{summary}{pager}{items}',
'summaryText'=>'Result {start} - {end} of {count} results',
});
I managed to solve it. I have animation in my item view page. Deleted the animation code, and data in the subsequent pages are displayed. Thank you.

Yii Cgridview filter not working?

I am trying to implement a dropdown filter in Cgridview, The column is from another table.
//model search code
//person table
public function search(){
$criteria=new CDbCriteria;
$criteria->alias='per';
$criteria->compare('LastName',$this->LastName,true);
$criteria->compare('FirstName',$this->FirstName,true);
$criteria->join='right JOIN person_surveys ps ON ps.id = per.P_Id';
}
//relation to person survey
public function relations()
{
return array(
'user' => array(self::BELONGS_TO, 'PersonSurvey', 'P_Id'),
);
}
//person survey table model search
public function search()
{
$criteria=new CDbCriteria;
$criteria->compare('person_id',$this->person_id,true);
$criteria->compare('survey_ids',$this->survey_ids,true);
$criteria->join='left JOIN persons ps ON ps.id = per.P_Id';
}
//controller
public function actiongriddisplay(){
$model2=new Persons('search');
if(isset($_GET['ajax'])){
$model2->attributes =$_GET['Persons'];
$this->render('griddisplay',array('model2'=>$model2));
}
else{
$this->render('griddisplay',array('model2'=>$model2));
}
}
//in view file
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider' => $model2->Search(),
'filter' => $model2,
'ajaxType'=>'GET',
// 'ajaxUpdate'=>'items',
//'ajaxUrl' =>$this->createUrl('userManagement'),
'columns' => array(
array(
'name' => 'FirstName',
'header'=>'Full Name',
),
array(
'name' => 'user.survey_ids',
'header'=>'Survey ids',
'filter'=>CHtml::activeDropDownList($model2 ,'survey_ids', array('224'=>'224','223'=>'223','225'=>'225')),
'value'=>'isset($data->user->survey_ids)?$data->user->survey_ids:null'
),
I have put the search on, in models. why is the dropdown filter not working?
Why this is not working please help?
This link (http://www.yiiframework.com/wiki/281/searching-and-sorting-by-related-model-in-cgridview/) will solve your problem.

Yii2 Active Record: The best way to display related records

I have two tables: record, user and a junction table record_user. I display the records, which are related to the logged user, using the Active Record.
In the model User I have the following functions:
public function getRecordUsers()
{
return $this->hasMany(RecordUser::className(), ['user_id' => 'id']);
}
public function getRecords()
{
return $this->hasMany(Record::className(), ['id' => 'record_id'])
->via('recordUsers');
}
See: http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#junction-table
In the model RecordSearch I define the query for the Active Data Provider:
public function search($params)
{
// The Object with all records, to which has the user access (the id of the user is in the junction table record_user).
$loggedUserRecords = YiiUser::findOne(Yii::$app->user->identity->id)->records;
// From the object is extracted an array with ids.
$loggedUserRecordsIds = yii\helpers\ArrayHelper::getColumn($loggedUserRecords, 'id');
// The ids are used to filter the Record object.
$query = Record::find()->filterWhere(['id' => $loggedUserRecordsIds]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
...
The code works as expected. But I would like to know, if there is a more direct way to display the related records, without the extraction of ids from the object.
I guess RecordSearch has this relations
public function getRecordUsers()
{
return $this->hasMany(RecordUser::className(), ['record_id' => 'id']);
}
public function getUsers()
{
return $this->hasMany(User::className(), ['id' => 'user_id'])
->via('recordUsers');
}
Or
public function getUsers()
{
return $this->hasMany(User::className(), ['id' => 'user_id'])->viaTable('recordUsers', ['record_id' => 'id']);
}
then you should be able to check query something like:
$dataProvider = new ActiveDataProvider([
'query' => Record::find()->joinWith(['users'])->where(['user_id'=> Yii::$app->user->identity->id]),
]);
if i didn't make a mistake.
As suggested by BHoft the fastest and most simple way is probably the following.
Model Record:
public function getRecordUsers()
{
return $this->hasMany(RecordUser::className(), ['record_id' => 'id']);
}
Model RecordSearch:
$dataProvider = new ActiveDataProvider([
'query' => Record::find()->joinWith(['recordUsers'])->where(['user_id'=> Yii::$app->user->identity->id]),
]);

Yii, filtering and ordering column in grid view that has data from custom model function

This is follow up on this question:
Display related has_many data inside once cell in Yii TbExtendedGridView
I got that cell working, but now i have no idea how to make it sortable and filterable (filter field is hidden).
View:
$this->widget('bootstrap.widgets.TbExtendedGridView', array(
'id'=>'user-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
'user_name',
'favorite_color',
array(
'value'=>'$data->getAllDates()',
),
),
));
One user can have many Dates that can be single date or date range, so i have getAllDates function that fetches em all and put em all inside string so they can be represented inside single cell for that user.
Model:
public function relations()
{
return array(
'listofdates' => array(self::HAS_MANY, 'Dates', 'user_id'),
);
}
public function getAllDates()
{
$data = '';
foreach ($this->listofdates as $date) {
$data .= $date->start_date.'-'.$date->end_date;
}
return $data;
}
I have no idea how to enable filtering and search for dates column. There is no even title for it or filter field.
I can enable filter field for that column by adding 'name' => 'whatever', but of course there is no single column in database for that data so i get MYSQL error.
I'm guessing i need to create another custom function for filtering but i have no idea where to start.
you can create an additional field for filtering and sorting $datesFilter and filter like this:
in model:
public $datesFilter;
public function rules()
{
array(
array('datesFilter', 'safe', 'on'=>'search')
);
}
public function search()
{
$criteria=new CDbCriteria;
...
// filtering
if ($this->datesFilter!==null)
$criteria->addCondition('YOUR QUERY CONDITION');
...
// sorting
$sort = new CSort;
$sort->attributes =
array(
...
'datesFilter'=>=>array('asc'=>'YOUR SORT ASC', 'desc'=>'YOUR SORT DESC'),
);
}
in view:
$this->widget('bootstrap.widgets.TbExtendedGridView', array(
'id'=>'user-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
'user_name',
'favorite_color',
array(
'value'=>'$data->getAllDates()',
'name'=>'datesFilter',
),
),
));

passing value from Yii CController class to CForm (Form Builder) config array

I'm new to Yii, and I'm trying to do my initial project the "right" way. I've created a CFormModel class that needs three fields to query for some data, a CForm config to construct the form, and a CController to tie it together (all given below).
The data request needs an account, and this can come from a couple of different places. I think retrieving it should be in the controller. However, I don't know how to get it into the form's hidden "account" field from the controller, so that it makes it to the arguments assigned to the CFormModel after submission. More generally, I know how to pass from CController to view script, but not to CForm. Is the registry (Yii::app()->params[]) my best bet?
I suppose I can just leave it out of the form (and required fields) and wait to populate it in the submit action (actionSummaries). Does that break the intention of CForm? Is there a best practice? Even taking this solution, can someone address the first issue, in case it comes up again?
Any other, gentle critique is welcome.
models/SummariesForm.php
class SummariesForm extends CFormModel
{
public $account;
public $userToken;
public $year;
public function rules () {...}
public function fetchSummary () {...}
static public function getYearOptions () {...}
}
views/account/select.php
<?php
$this->pageTitle=Yii::app()->name;
?>
<div class="form">
<?php echo $form->render(); ?>
</div>
controllers/AccountController.php
class AccountController extends CController
{
public $layout = 'extranet';
public function actionSelect ()
{
$model = new SummariesForm();
// retrieve account
require_once 'AccountCookie.php';
/*
*
* Here, I insert the account directly into the
* model used to build the form, but $model isn't
* available to selectForm.php. So, it doesn't
* become part of the form, and this $model doesn't
* persist to actionSummaries().
*
*/
$model->account = AccountCookie::decrypt();
if ($model->account === false) {
throw new Exception('Unable to retrieve account.');
}
$form = new CForm('application.views.account.selectForm', $model);
$this->render('select', array(
'form' => $form,
'account' => $model->account,
));
}
public function actionSummaries ()
{
$model = new SummariesForm();
if (isset($_POST['SummariesForm'])) {
$model->attributes = $_POST['SummariesForm'];
/*
*
* Should I just omit "account" from the form altogether
* and fetch it here? Does that break the "model"?
*
*/
if ($model->validate() === true) {
try {
$summaries = $model->fetchSummary();
} catch (Exception $e) {
...
CApplication::end();
}
if (count($summaries) === 0) {
$this->render('nodata');
CApplication::end();
}
$this->render('summaries', array('model' => $model, 'summaries' => $summaries));
} else {
throw new Exception('Invalid year.');
}
}
}
}
views/account/selectForm.php
<?php
return array(
'title' => 'Select year',
'action' => Yii::app()->createUrl('Account/Summaries'),
'method' => 'post',
'elements' => array(
'account' => array(
'type' => 'hidden',
'value' => $account,
),
'userToken' => array(
'type' => 'hidden',
'value' => /* get token */,
),
'year' => array(
'type' => 'dropdownlist',
'items' => SummariesForm::getYearOptions(),
),
),
'buttons' => array(
'view' => array(
'type' => 'submit',
'label' => 'View summaries',
),
),
);
The answer is NO to do what you asked. You can see $form variable which acted almost like array when it was passed from controller to view. The solution is you add more property $account into selectForm model and treat it like other elements. I don't think leaving the new field outside the form will be properly way if you want to submit its value also.
Edited: