I'm migrating to postgres from mysql and having an issue with "magic find" and case sensitivity in Postgres.
Model->findByFirstname inflects to "Model"."firstname" and Postgres can't find that column, since it's named "Firstname" in my db.
Any ideas?
You can override the magic handling of FindBy<Fieldname> by adding the PHP handler __call to your AppModel base class. This will be called for each magic find method.
The first parameter is the function name, and the second parameter are the function arguments.
public function __call($method, $params)
{
if (strpos($method, 'findBy') === 0 || strpos($method, 'findAllBy') === 0)
{
// customer handler here
return ....
}
return parent::__call($method,$params);
}
For your custom handler. You can do something like return $this->find(...). Where you apply conditions that are for findBy or findAllBy. Except use the case sensitive field name for the condition. That field name can be extracted from $method.
Related
I have a scenario where I need to check in opportunity update if particular field values of opportunity which are mentioned in metadata are changed or not, if these values are changed meaning that that record would be considered for update. I have done this with field sets but not sure how we can achieve the same using custom metadata. Attaching the code used for field sets here
Public static boolean isValuesChanged(List<Opportunity> newOpportunitiesList, Map<id,Opportunity> oldOpportunityMap)
{
for(Opportunity oppRecord : newOpportunitiesList)
{
for(Schema.FieldSetMember fieldSetMemberObj : SObjectType.Opportunity.FieldSets.Opportunity_Comparision_FieldSet.getFields())
{
if(oppRecord.get(fieldSetMemberObj.getFieldPath()) != oldOpportunityMap.get(oppRecord.id).get(fieldSetMemberObj.getFieldPath()) && oppRecord.Amount > 0)
{
return true;
}
}
}
return false;
}
This is what I have done when I used field sets. The same I want to do using custom metadata.How can I check changes in Apex ?
Thanks in Advance
Cut it into 2 problems.
Given a set of strings with field api names - how to detect changes
Set<String> fields = new Set<String>{'Name', 'CloseDate', 'StageName'};
Set<Id> oppsToProcess = new Set<Id>();
for(Opportunity opp: newList){
Opportunity old = oldMap.get(opp.Id);
for(String field : fields){
if(opp.get(field) != old.get(field)){
oppsToProcess.add(opp.Id);
break;
}
}
}
Given a custom metadata with field names - how do I make a set of strings out of it.
Solution for this one depends what exactly you have in custom meta:
list with multiple records, each with single field's name?
1 record with field names saved in columns like Field1__c, Field2__c, Field3__c
1 record with list of fields stored comma-separated, like Fields__c = 'Name,CloseDate,StageName'
You'll have to do it yourself, query and loop through first one or call String.split(','); on the last one or...
I have multiple domain objects with differently names start and end dates. I would like to create a shared custom validator that accepts two dates and validates them.
All the documentation seems to state the second argument in the validator closure is the current object. Is there a way to do the following? If so how would the constraint closure in the domain object look?
grails.gorm.default.constraints = {
'*'(nullable: true)
endDateValidator(
validator: { endDateValue, startDateValue ->
if(!(endDateValue?.after(startDateValue))){
return 'end.date.before.start.date.error'
}
else{
return true
}
}
)
}
I guess you still need to create a validator for each domain class since each of them has differently named properties. However, you can share the validation business logic among these validators.
Example:
The shared validation logic would go to a static boolean validateDateOrdering(Date first, Date second) method and validators might look like:
fooValidator(
validator: { value, domainInst ->
validateDateOrdering(value, domainInst['bar'])
}
)
barValidator(
validator: { value, domainInst ->
validateDateOrdering(domainInst['foo'], value)
}
)
Use case
My use case is that I need to validate a Table Tennis score.
Form
<input name="data[MatchesPlayer][1][score]" type="number" id="MatchesPlayer1Score">
<input name="data[MatchesPlayer][2][score]" type="number" id="MatchesPlayer2Score">
Constraints
One score must be bigger than 11.
One score must be 2 points or greater than the other if the score is higher than 11.
Problem
When cake validates multiple rows from the same model, the model data is set to that record. This means that it's not possible to compare the two values as they aren't both available in $this->data. As I am using saveAll() each record is set to the model and then validated before it's saved.
Question
I'd like to know if there is a good way to validate this pair of data without resorting to saving it into the session or similar before I can validate it.
What I normally do here is I create a wrapper for the save method. This allows me to perform custom manipulation that would otherwise not be possible with model callbacks, or even use custom transactions etc.
In your case, it would be something like:
class MatchesPlayer extends Model {
protected $_saveData = null;
public function updateScore($data) {
$this->_saveData = $data;
try {
// You can use saveAll to validate
// only, and not actually save
$saved = $this->saveAll($data, array('validate' => 'only'));
} catch (Exception $e) {
// Catch exceptions here in case the
// saveAll is instead something that throws Exceptions
// Or your database uses exceptions
$saved = false;
}
$this->_saveData = null;
return $saved
}
}
You could then use $this->_saveData across the model. If you want to be clever with this, you could detect all sub-models that are being saved in the $data and then set the $this->_saveData on those as well - I would make this an AppModel method of course.
Note that you may want to throw exceptions from this updateScore() method when validation fails. Throwing an exception if validation fails - vs save - would allow you to set a custom flash message for the user as well, or even have an api that responds with a different status code.
Use custom validation rules in MatchesPlayer model, please check
http://book.cakephp.org/2.0/en/models/data-validation.html#adding-your-own-validation-methods
I can't believe I don't remember how to do this, but how do I specify in my model the default fields that are returned with the find() methods? I can't find on google how to do this, or at least I don't know the wording to search for.
What you will most likely need to do, is to check if the fields key exists in a beforeFind() method in your model.
If the fields key is not set, you can set it to $this->fields in your native models, and create the beforeFind() in your AppModel, then you can instruct that method to use the $this->fields array from your models.
UPDATE
// AppModel.php
parent function beforeFind($queryData = array()) {
if (empty($queryData['fields']) && !empty($this->fields)) {
$queryData['fields'] = $this->fields;
}
return $queryData;
}
// And in your Model:
public $fields = array(
'Alert.id'
);
This will check for existence of a fields array, and will then check for existence of a $this->fields property. If it does exist, it will apply it to the query data and return that modified query data to the beforeFind() - this will change your find.
Adjust it to fit your needs, and good luck!
I have made a custom afterFind function in a model, but I just want it to execute it if NOT in admin mode.
public function afterFind($results) {
if(Configure::read('Routing.admin')){
return $results;
}else{
return $this->locale(&$results);
}
}
But it doesn't seems to work. I'm thinking this might not be possible. Any idea?
checking on the core Configure settings doesnt make sense to me.
besides the fact that that 'Routing.admin' is deprecated - its Prefix.admin.
it only stores the prefixes that cake uses.
If you really want to you can store the information in configure::read() in beforeFilter() of your AppController and read it from your model again.
But it would need to something that does not conflict with your settings.
So if you use Prefix you probably could use Routing again:
//beforeFilter - prior to any model find calls!
$isAdmin = !empty($this->params['admin']);
Configure::write('Routing.admin', $isAdmin);
the other option you always have is to pass the information on to the model.
Router::getParam('prefix', true) gives you current request prefix value.
public function afterFind($results, $primary = false) {
return Router::getParam('prefix', true) == 'admin' ? $results : $this->locale(&$results);
}
Tested with Cake 2.4.