I discovered an issue with casts when upgrading an app from 5 to 7 that caused quite a few tests to fail. Quick 7 will return null values either returned from the database or when instantiating a new entity as an empty string, whereas Quick 5 and 6 will return the casted value.
I tested with ACF 2021 and the BooleanCast@quick cast.
I created the following issue:
I feel like the behavior in 5 and 6 is the right way to handle things, but that’s just my opinion.
This was intentional, but was actually fixed in 7.0.1 as a bug fix. The new behavior is the desired behavior since a NULL value does not necessarily mean false. It could mean not set yet. If you want a default value, you should be able to set default="false" on the property.
Hi. I do have related issues.
I have this : property name="show" column="FotosGrandes" type="boolean" casts="S-NCast";
In the DB its stored as “Y or N” but I want to be a pure boolean on CFML side.
It works ONLY when it’s not null. If its NULL it will be an empty string, and no cast is called. Also, default won’t get respected so default="true" won’t do anything. Only works the default if I add persistent="false"
Any ideas how can I get a pure boolean of a DB null (The cast its made that if I get null, It’s equal to false)??
@elpete is the expert for all things Quick, but I bet he’s going to ask to see a copy of your S-NCast code to see if the problem lies there. Would you mind sharing that?
No problem. But the cast is not called in that case:
component implements="quick.models.Casts.CastsAttribute" {
/**
* Devuelve true si el valor en la base de datos es 'S', de lo contrario false.
*/
public any function get(
required entity,
required string key,
any value
){
if ( isNull( arguments.value ) || trim( arguments.value ) == "" ) {
return false;
}
var val = uCase( trim( arguments.value ) );
return val == "S";
}
/**
* Guarda 'S' en la base de datos si el valor es true, y 'N' si es false.
*/
public any function set(
required entity,
required string key,
any value
){
if ( isNull( arguments.value ) ) {
return "N";
}
if ( isBoolean( arguments.value ) ) {
return arguments.value ? "S" : "N";
}
var val = uCase( trim( arguments.value ) );
return ( val == "S" ) ? "S" : "N";
}
}
If it is null it never gets called the cast, which in my opinion is the cast who should be able to decide what null means, no empty string in all cases, which then I have to null check every variable with the cast, but the cast can make this work.
Also in castValueForGetter you will check again for null
In my opinion you should return null if its NULL AND no Cast is set. In the case of Null and cast, the cast should be responsible for the handling the null.
Also, the default don’t get applied, because the var has a value (NULL, but a value anyway)
@rodrigoEscrol sorry for the delay in getting back to you.
I may be reaching the limits of my own understanding of Quick internals here, but I think you bring up an excellent point.
From what you are describing, the issue is not really that your custom cast is broken. It sounds like Quick is not calling the custom cast at all when the database value is NULL. So your cast never gets the chance to decide that NULL should become false on the CFML side.
I can also understand why Quick may behave this way intentionally. NULL and false are not always the same thing. NULL can mean “not set”, while false means “explicitly no”. So Quick may be trying to preserve that difference before the cast runs.
However, I agree with your general point that if a custom cast is defined, there is a good argument that the cast should be allowed to decide how NULL is handled. In your case, you want the database value to be S/N/NULL, but the CFML side to always behave like a pure boolean.
My practical recommendation would be:
If NULL should always mean false for this column, the cleanest solution is probably to fix that at the database level. Update existing NULL values to “N” and add a database default of “N” so the column does not return NULL anymore.
If you cannot change the database, you could create your own custom getter for the column, (e.g. getFotosGrandes()) that will check for null and return false, like this:
function getFotosGrandes() {
var value = retrieveAttribute( "FotosGrandes" );
// if value is null or doesn't have a length, return false.
if ( isNull( value || !len( value ) {
return false;
}
return value;
}
Since this original thread is older and Quick has changed quite a bit since then, I think this topic probably deserves a new thread. The core question would be something like: “Should custom casts in Quick be called for NULL database values?” That would let @elpete clarify whether the current behavior is intentional, configurable, or something they would consider changing.
I think your question is valid. I am just not 100% sure whether Quick’s current behavior is by design or whether there is a better built-in way to handle this.
Side note: I noticed in your cast you transform true into “S” instead of “Y”. I know in Spanish, “yes” is “si”. One recommendation I have is to take spoken language differences out of boolean data types. Instead of using a string value there, I recommend using a boolean datatype like “bit” if you’re using SQL Server. That way, the value becomes universally understood.
I try to avoid the custos getter/setter because this in my db all places, but i know its a solution, but for me not practical.
Agree on the bit field, but its a 50 gb database whit years of data and so much external processes that uses the S,N as boolean. I am triying to make the change but its hard.
Still i agree on the suggested aproach, if i dont add any cast , make quick behave like now, but if i add a cast i thik it should be respected.