In my controller I have my pagination set to order by 2 fields.
public $paginate = [
'limit' => 50,
'order' => ['first_name', 'last_name']
];
$contacts = $this->paginate($this->Contacts);
This works fine on the first page, but since I left out the default direction => 'ASC' the Paginator links don't work at all:
/contacts?page=2&sort=0&direction=first_name
When I add in the direction, it works, but of course only sorts by the first field, messing up the sort order.
/contacts?page=2&sort=Contacts.first_name&direction=ASC
Should the default direction be explicitly required?
Is there a method to maintain both fields for sorting during pagination?
Sorting by virtual fields (e.g. full_name => first_name . ' ' . last_name) doesn't work as it did in 2.x
Solved both issues with the following:
Set the default sort order to be the same as the virtual field:
public $paginate = [
'order' => ['first_name', 'last_name']
];
Then just add the following to the View to prevent the paginator from overriding the default order unless specified by the user:
if (empty($_GET['direction'])) { $this->Paginator->options(['url' => ['direction' => null, 'sort' => null]]); }
Related
Using CakePHP 2.3, I'm retrieving data using a paginator. So, say my models are Countries having many Cities, and in my CountryController I have...
$this->Paginator->settings = [
'fields' => [
'id'
'country_name'
],
'contain' => [
'City' => [
'id'
'city_name',
'conditions' => [
'population >' => 1000000;
]
]
]
];
...which gets me a list of all counties with each row containing a list of any populous cities.
In the view I am obviously able to iterate foreach ($cities as $city) and echo $country['country_name'] etc. and also if I wish I can show a count of the contained cities by echoing count($country['City']).
Using the paginator I can sort the country results by passing back a field name in the query string, e.g. sort=country_name, but how can I get the results to sort by the count of the contained cities?
It is unfortunately not possible to sort by the count of the hasMany Table using the custom Cakephp Pagination. Your best bet is to use counterCache as described in the Docs.
You will need to have a field in country table, named as city_count. This field will be updated in the Country Table automatically by Cakephp whenever there is a save operation on city table.
Since you only want to count the cities with population > 100K. You can specify the condition in counterScope which will only update the column when condition is met.
This can be defined in your City Model as below:
class City extends AppModel {
public $belongsTo = array(
'Country' => array(
'counterCache' => true,
'counterScope' => array(
'City.population > ' => 1000000
)
)
);
}
I have JobsTable:
This is relation definition:
$this->hasMany( 'JobContracts', [
'foreignKey' => 'job_id'
] );
Saving code:
$entity = $this->patchEntity( $entity, $toSave, [
'fieldList' => ['notes],
'associated' => [
'JobContracts' => ['fieldList' => ['id', 'checked']]
]
] );
And now:
if I put this notes in fieldList then JobContracts are NOT saved properly.
If I remove fieldList, then I am able to save it properly.
Question is Why? I need to control base model fields also. Any suggestions?
Ive already checked: http://book.cakephp.org/3.0/en/orm/saving-data.html#avoiding-property-mass-assignment-attacks
You need to allow assigning the association property too, not only notes. If you don't, then the associated data is never going to be set on the resulting entity, and consequently is not going to be saved.
Check the docs that you've linked again, the tags example shows exactly that:
// Only allow changing the title and tags
// and the tag name is the only column that can be set
$entity = $this->patchEntity($entity, $data, [
'fieldList' => ['title', 'tags'],
'associated' => ['Tags' => ['fieldList' => ['name']]]
]);
$this->save($entity);
http://book.cakephp.org/3.0/en/orm/saving-data.html#avoiding-property-mass-assignment-attacks
So, add job_contracts to the field list, and you should be good.
I can use the cakephp set() to add a value in an object at the top level but I also need to set a value for an object inside the object and I can seem to access it. Is this possible?
I need to add business_id inside the employee object.
I though I might use $user->set->employee('business_id', '1'); but I get an error on the employee part.
object(App\Model\Entity\User) {
'email' => 'dfgdfg#sdfsdf.com',
'new_password' => 'ttt',
'confirm_password' => 'ttt',
'employee' => object(App\Model\Entity\Employee) {
'name' => 'dsfsfsdfsfd',
'email' => 'sdfsdfsdf#sdfsdf.com',
'surname' => 'sdfsdfsdfsdf',
'employee_num' => 'sdfsdfsdfsd',
'[new]' => true,
I tried it a couple of different ways and I got it work by $user->employee->set('business_id', '1');
I'm using paginator component of paramType is querystring, and specified multiple order in action before taking results from $this->paginate() as like this
$this->paginate['order'] = array(
"field1" => "Asc",
"field2" => "Desc",
"field3" => "Asc",
);
$this->set('results', $this->paginate('ModelName'));
When i click on page 2 link it's generating a url like ?page=2&sort=field1&direction=Asc I want to remove the sort key from query strings and need to fetch the results by applying order i've specified.
Could anybody help me on this ?
Thanks
As seen on documentation, you can override the paginate method:
/**
* Overridden paginate method - group by week, away_team_id and home_team_id
*/
public function paginate($conditions, $fields, $order, $limit, $page = 1,
$recursive = null, $extra = array()) {
$recursive = -1;
$group = $fields = array('week', 'away_team_id', 'home_team_id');
return $this->find(
'all',
compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group')
);
}
Or this, taken from documentation:
Control which fields used for ordering
By default sorting can be done with any column on a model. This is sometimes undesirable as it can allow users to sort on un-indexed columns, or virtual fields that can be expensive to calculate. You can use the 3rd parameter of PaginatorComponent::paginate() to restrict the columns that sorting will be done on:
$this->Paginator->paginate('Post', array(), array('title', 'slug'));
This would allow sorting on the title and slug columns only. A user that sets sort to any other value will be ignored.
http://book.cakephp.org/2.0/en/core-libraries/components/pagination.html
I want to require all checkboxes in the set
My code looks like this:
$this->widgetSchema['consent_confirmation'] = new sfWidgetFormSelectCheckbox(
array(
'choices' => Doctrine_Core::getTable('MyTable')->getOptions(),
)
);
UPDATE:
My validation looks like this:
$this->validatorSchema['consent_confirmation'] = new sfValidatorChoice(array(
'choices' => array(Doctrine_Core::getTable('MyTable')->getOptions()),
'multiple' => true,
'required' => true
));
How can I make it return 'Required' if they're not all checked, and be valid if they're all checked?
My symfony 1.* memory is very hazy at this point but I think what you need to do here is add a rule to the validatorSchema to handle validation of this widget.
According to the Validation Appendix the validator you need is sfValidatorChoice.
This widget has a number of options, including:
multiple
min
max
Assuming that you have two options as above, and you want to enforce selecting both, I'm guessing that you might need to add the following to your form's configure() method:
public function configure()
{
$this->widgetSchema['consent_confirmation'] = new sfWidgetFormSelectCheckbox(array(
'choices' => array(
'1' => 'Yes I agree to #1',
'2' => 'Yes I agree to #2',
)),
);
$this->validatorSchema['consent_confirmation'] = new sfValidatorChoice(array(
'multiple' => true,
'min' => 2,
'max' => 2,
));
}
Something like that - I'm not sure about the assignment to the validatorSchema to be honest, there might be something like addValidator() or setValidator()methods instead. EDIT: I think there were some helper methods added, but some of these might be 1.4 specific. The above assignment should work either way...
Hope this helps :)