I'm migrating from CFWheels 2.5 to Wheels 3. Havin...
# wheels-dev
a
I'm migrating from CFWheels 2.5 to Wheels 3. Having an issue with model properties being lowercase, so a property of
isHidden
is returned as
ishidden
which breaks the consumer of the API (yes, I read the docs, not recommended to use Wheels for APIs). The only way to fix it that I've found is to create properties on the model via the
property()
function. I've tried using
returnAs="structs"
and
returnAs="struct"
which according to the documentation, should preserve case but it does not. Using
renderText()
does not work since the properties are all lower case coming out of the model. Is there a way to get the casing working like in CFWheels 2.5 via a configuration? I want to avoid modifying as much as the code as possible, thanks!
z
Interesting, Are you returning: β€’ model objects and serializing them? β€’
findAll(returnAs="structs")
results? β€’
renderWith()
/
renderText(serializeJSON(...))
?
and which DB + CF Engine Version either Lucee or ACF?
a
SQL server 2022 Lucee 7.0.3.43 yes, tried using
findAll(returnAs="structs")
and both
renderWith()
/
renderText(serializeJSON(...))
also using the
Copy code
Microsoft SQL Server (Vendor Microsoft)
13.2.1
app/extension for Lucee
z
right, this sounds more like a consequence of how Lucee 7 and SQL Server expose query metadata than a Wheels change.
have u tried?
set(preservePropertyCase=true);
or
set(legacyPropertyCasing=true);
a
have not tried that. i will try that now
z
it will restore the 2.5 behavior for applications that rely on mixed-case JSON property names
and
property(name="isHidden");
a
Copy code
set(preservePropertyCase=true);
	set(legacyPropertyCasing=true);
did not work
tried each one and both, same result
the same code works find in CFWheels 2.5 with same Lucee vesion and DB
z
because In Wheels 3, when properties are inferred from query metadata, SQL Server + Lucee often returns:
ishidden
instead of
isHidden
a
Also tried the
jTDS (MSSQL,Sybase)
extension, same thing
yup, i was hoping there was a setting to make it work like CFWheels 2.5.
z
if you read my above comment, you needs to test with one sample model or single model
property(name="isHidden");
then the property declaration is becoming the source of truth for the field name.
a
yes, that works. I was tyring to avoid having to modify all my models
z
right then you can achieve via creating auto-registers properties from the database schema during startup rather than manually
You can use that code to hook into
AppModel.cfc
Copy code
component extends="Model" {

    /**
     * Auto-register properties from DB metadata.
     *
     * Call from config():
     *
     *   autoProperties();
     */
    public void function autoProperties() {

        var tableName = variables.wheels.class.tableName;
        var ds = application.wheels.dataSourceName;

        var columns = queryExecute(
            "
                SELECT COLUMN_NAME
                FROM INFORMATION_SCHEMA.COLUMNS
                WHERE TABLE_NAME = :tableName
                ORDER BY ORDINAL_POSITION
            ",
            {
                tableName = {
                    value = tableName,
                    cfsqltype = "CF_SQL_VARCHAR"
                }
            },
            {
                datasource = ds
            }
        );

        for (var row in columns) {

            // Don't redefine existing properties.
            if (
                !structKeyExists(
                    variables.wheels.class.properties,
                    lCase(row.COLUMN_NAME)
                )
            ) {

                property(
                    name = row.COLUMN_NAME
                );

            }
        }

    }

}
and hook this into the base model’s initialization and automatically register properties whenever a model loads.
Copy code
public void function afterInitialization() {

    if (
        !structKeyExists(
            variables.wheels.class,
            "$autoPropertiesLoaded"
        )
    ) {

        autoProperties();

        variables.wheels.class.$autoPropertiesLoaded = true;
    }

}
Every model inherits the behavior automatically.
a
That's interesting. I'm gonna have to try this. Thanks for the help!
πŸ‘ 1
z
But I always recommend to update your every model to keep your execution seamless.
p
Hey @angel β€” thanks for the really clear report, this turned out to be a genuine framework regression and your repro made it quick to pin down. πŸ™ What's going on: when a model doesn't explicitly declare its properties, Wheels builds them from your database's column metadata. In 2.x that kept the column's original casing, so
isHidden
stayed
isHidden
. A change in the 3.0 line β€” originally aimed at Oracle, which reports column names in a fixed case β€” started normalizing all auto-derived property names to lowercase. So on SQL Server (and MySQL, etc.)
isHidden
now comes back as
ishidden
. That's why it reproduces no matter what you do at the output layer (
returnAs="structs"
,
renderText(serializeJSON(...))
) β€” the casing is already gone by the time the model loads, before serialization runs. And just so you're not chasing it: there isn't a built-in config flag for this today. What we're doing: changing it back so Wheels preserves your database's column casing by default (matching 2.5 behavior), and scoping the lowercasing to only the engine that actually needs it (Oracle). It's coming with a regression test + a cross-engine run so it stays fixed. This is landing in the 4.0 line. Stopgap for right now: explicitly declaring the mixed-case properties restores the casing without waiting on the release β€”
Copy code
property(name="isHidden", column="isHidden");
You'd only need it for the mixed-case ones, and the fix will remove the need for it entirely. One thing that'll help us prioritize: are you moving to 3.0 as a stepping stone toward 4.0, or is 3.0 your target for now? If you're settling on 3.0 we'll look at backporting the fix to the 3.0 line; if 3.0 is mainly a waypoint to 4.0, we'd rather invest in a smoother 2.x β†’ 4.0 upgrade path. Your answer genuinely shapes what we ship next.
a
hi Peter, thanks for the info. Wheels 3.0 is the target for now (Wheels 4 is too new and moves to fast for us). I implemented a solution based on Zain's suggestion:
Copy code
private void function useDatabaseColumnCase() {
		var columns = "";
		cfdbinfo(
			type = "columns",
			table = tableName(),
			datasource = get("dataSourceName"),
			name = "columns"
		);
		for (var col in columns) {
			property(name = col.column_name, column = col.column_name);
		}
	}
Place this function in the main model and then call it via the config function on the model:
Copy code
public void function config() {
		useDatabaseColumnCase();
	}
This seems to be working. I know it adds overhead on initialization but we are ok with this. I would focus on Wheels 4 and maybe put a note somewhere in the docs in case someone else runs into the issue. This brings up a good question though. How long will Wheels 3 be supported?
πŸ‘ 1
z
@Peter Amiri Could you please provide some guidance on the future support, maintenance, and roadmap for Wheels 3.0 going forward? Thanks.
p
@angel I understand waiting till the dust settles on 4.0. As you already know the framework doesn't stop working so it depends what you mean by support. I'll have to think about this some more and publish a policy so everyone knows what to expect.