yii sort by a custom attribute - sorting

In my Vacation model Vac I have this function
public function getVacCount(){
this function returns how many days there are in one vacation.
and I want to add a custom column to the cgridview like this:
<?php
$this->widget('zii.widgets.grid.CGridView', array(
...
array(
'name' => 'count',
'value' => '$data->getVacPeriod()'
),
...
),
));
?>
it works fine.
but I don't know how can I sort upon this custom attribute.
I tried to use CSort but it does not work. any idea?

To use CSort for sorting, you'll need to convert your vacation function into a SQL query and then stash the results in a public variable in your model.
CSort only works with SQL statements/functions, as underneath it's using ORDER BY to do all the sorting.
More info (and demo code) available here
Here's a sample of how I'm doing it on a site of mine:
$criteria->select = array(
"*",
new CDbExpression("IF(survey.RequestDate, survey.RequestDate, SurveyCompleteDate) AS SurveyDate")
);
This then allows me to do this type of filter:
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
'sort'=>array(
'attributes'=>array(
'SurveyDate' => array(
'asc' => 'SurveyDate',
'desc' => 'SurveyDate DESC',
),
'*',
),
),
);
Note: you'll also need a public variable defined in your model to hold the results of the CDbExpression that you're doing. Mine is called SurveyDate.

Related

Create custom validation for form entity

I am using the form builder to create an choice-field form an entity looking like this:
$form->add(
'existing_items','entity', array(
'label' => 'Artikel aus',
'class' => 'ProjectShoppinglistBundle:Item',
'empty_value' => 'Bitte einen Artikel auswählen',
'property' => 'name',
'query_builder' => function(EntityRepository $er) use ($options) {
return $er->createQueryBuilder('item')
->leftJoin('item.userItems', 'userItem')
->where('userItem.user = ' . $options['attr']['id'])
->orderBy('item.name', 'ASC');
},
'attr' => array(
'class' => 'form-control',
),
));
but I am using jquery to change the content of the dropdown, so I need to change the validation for the field, how can I achieve that the values in the form are valid for all elements in the items-table and not just the one's which are linked to my the userId used in the query?
This is necessary for my approach because I have a second dropdown where the user can define if he wants to see the items on his own list, of other lists or all items
I have already taken a look at this but I still don't really get it how I can use the eventListener to get the desired result.
If someone could give me a useful hint, I would appreciate this very much.

CakePHP paginator isn't sorting

In CakePHP, I would like to sort my index list (created by the paginator component) on sequence ASC, but it won't work. If I see the query setup in the CakePHP docs (http://book.cakephp.org/2.0/en/core-libraries/components/pagination.html#query-setup), my paginator settings should look like this:
public function index()
{
$this->Paginator->settings = array(
'Attraction' => array(
'conditions' => array(
'Attraction.deleted' => null
),
'order' => array(
'Attraction.sequence ASC',
'Attraction.id ASC'
),
'limit' => 15
)
);
$attractions = $this->Paginator->paginate('Attraction');
$this->set('attractions', $attractions);
}
But every time I load my index file, the list is sorted on ID and ignores the "order" setting. Can anybody tell me if there's anything wrong with my "order" item in my paginator settings? ;)
Thx!
try
'order' => array(
'Attraction.sequence' => 'asc',
'Attraction.id' => 'asc'
),
edit:
The sorting direction must be in the array value, while the field name should be the index. The examles I found in the documentations are always showing that
I don't think you should have that Attraction index in your options array - in other words, I think your options array should look like this:
$this->Paginator->settings = array(
'conditions' => array(
'Attraction.deleted' => null
),
'order' => array(
'Attraction.sequence ASC',
'Attraction.id ASC'
),
'limit' => 15
);

filter and CActiveDataProvider in CGridView

I have these code in index action of controller:
public function actionIndex()
{
$cid = #$_GET['cid'];
$country = Country::model()->findByPk($cid);
if($cid)
$dataProvider=new CActiveDataProvider('City', array(
'criteria'=>array(
'condition'=>'ci_co_id ='.$cid,
),
));
else
$dataProvider=new CActiveDataProvider('City');
$this->render('index',array(
'dataProvider'=>$dataProvider,
'country' => $country
));
}
and these in view/index.php file:
<?php
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'city-grid',
'dataProvider'=>$dataProvider,
'filter' => $dataProvider,
'columns'=>array(
array(
'name' => ' ',
'value' => '$row + 1',
),
'ci_name',
'ci_pcode',
array(
'class'=>'CButtonColumn',
),
)
));
?>
but Yii gives me this error:
CActiveDataProvider and its behaviors do not have a method or closure named "getValidators".
What is the problem?
The filter has to be a class that extends CModel. However, you don't seem to be doing any actual filtering, so you can just comment the filter line of your CGridView out.
As a side note, you have major security hole in your criteria. You are leaving yourself wide open to an SQL injection attack.
Specify your criteria as follows to let the database handler properly escape the input:
'criteria'=>array(
'condition'=>'ci_co_id =:cid',
'params'=>array(':cid'=>$cid),
),

In my cakephp controller I keep repeating logic in every function for view code

I have a number of functions in my controller logic and for every one I have some logic that pulls data for a "featured" set of units. These units are on several pages. How do I go about making this one piece of logic available to all 4 of the views that need it?
For reference, here is part of my controller logic:
public function index() {
$this->set('title', 'All accommodations available in and near Gulf Shores, AL');
$this->Unit->Behaviors->attach('Containable');
$this->Unit->contain(
array(
'User'=>array(
'id'),
'Location',
'Complex'=>array('location_id'),
'Image'=>array(
'logo','img1'
)
)
);
$c=$this->Unit->find('all',
array(
'limit'=>3,
'conditions'=>array(
'active'=>1,
'featured'=>1
)
)
);
$this->set('featured', $c);
$this->paginate['Unit']=array(
'limit'=>9,
'order' => 'RAND()',
'contain'=>array(
'User'=>array('email'),
'Complex'=>array('location_id','complex_website'),
'Location',
'Image'
),
'conditions'=>array(
'Unit.active'=>1)
);
$data = $this->paginate('Unit');
$this->set('allaccommodations', $data);
}
public function houses() {
$this->set('title', 'Home rentals available in Gulf Shores');
$this->Unit->Behaviors->attach('Containable');
$this->Unit->contain(
array(
'User'=>array(
'id'),
'Location',
'Complex'=>array('location_id'),
'Image'=>array(
'logo','img1'
)
)
);
$c=$this->Unit->find('all',
array(
'limit'=>3,
'conditions'=>array(
'active'=>1,
'featured'=>1
)
)
);
$this->set('featured', $c);
$this->paginate['Unit']=array(
'limit'=>9,
'order' => 'RAND()',
'contain'=>array(
'User'=>array('email'),
'Location',
'Image'
),
'conditions'=>array(
'Unit.type'=>array('house', 'rentalco'),
'Unit.active'=>1)
);
$data = $this->paginate('Unit');
$this->set('allhouses', $data);
}
...
I have two other functions that set a 'featured' variable that is available to the view. I'm sure there is a much better/more efficient way to do this?
Yes, the idea is to have all the data interaction in your model and have your controller clear and easy to follow. Fat models, skinny controllers. There are custom finds you can use: http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#creating-custom-find-types
but I prefer to just make a model function because they're easier to work with in the IDE with code completion and documentation. So for the featured find you keep using you could do something in your Unit model like:
public function getFeatured($limit) {
$results = $this->find('all', array(
'limit' => $limit,
'conditions' => array(
'active' => 1,
'featured' => 1
)
)
);
return $results;
}
Then in the UnitController:
$this->set('featured', $this->Unit->getFeatured(3));

Cakephp 2.0: How to provide ORDER BY instructions for data retrieved recursively through find()?

I am trying to set up a model with a lot of hasMany/hasOne relationships, and I'd like for the owned models to be presented, alphabetically, in a drop-down menu within the "Add" view. I am not sure how to provide ORER BY instructions to models that are retrieved recursively. This is what I've tried:
$services = $this->service->find('all', array(
'order'=>array(
'Service.start_year DESC',
'Servicecat.title DESC'
),
'recursive'=>0
) );
But of course this yields no change. I have a suspicion it's not very complicated, I just can't find a solution in the cookbook/api. Ideas?
You could do this a few ways. I prefer to use the containable behavior for models and then it allows you to finely control how the models are retrieved. You could also setup order in your model's relationship definition.
Model Way
$hasMany = array(
'Servicecat' => array(
'order' => array(
'Servicecat.title DESC'
)
)
);
Containable Way
In your models, set them to use the containable behavior:
public $actsAs = array('Containable');
Then, when you find from your controller, you can explicitly state which models are linked. You can use multidimensional arrays to determine the depth of recursion.
$services = $this->Service->find(
'all',
array(
'contains' => array(
'Servicecat' => array(
'order' => array('Servicecat.title' => 'DESC')
)
),
'order' => array('Services.start_year' => 'DESC')
)
);

Resources