How can we convert the keys of a toJson() returned object to lowercase or camelCase? Consider the following example:
Query:
$foo = FooQuery::create()
->filterByBar($bar)
->findOne()
->toJson();
Result:
{"Id": 1, "Bar":"StackOverflow"}
It seems to be PascalCase by default. How can I get lowercase properties on the json result?
The function I'm referring to can be found here and is applied to an ObjectCollection.
Update: I want to avoid using arrays as: array_change_key_case() does not work for multidimensional arrays when dealing with complex objects.
I know this can be achieved through some modifications but I want to know if there's a better approach, preferably without casting to an array first for performance purposes.
There is a way to configure your generated classes to use camelCase keys. In your propel.json (or .yaml, .php .ini .xml) configuration file add the objectModel as follows:
"generator": {
"defaultConnection": "bookstore",
"connections": [ "bookstore" ],
"objectModel": {
"defaultKeyType": "camelName"
}
}
This will make all your keys camelCased but it turns out that this only works with the toArray()
method. When you call toJSON()
you're actually using the exportTo('JSON')
method. If you look at the exportTo
method you can see that it is calling:
$this->toArray(TableMap::TYPE_PHPNAME, $includeLazyLoadColumns, array(), true)
This is forcing exportTo('JSON')
and toJSON()
to use TableMap::TYPE_PHPNAME
as the key type. If you look at the toArray
method definition it uses your "defaultKeyType"
as the default $keyType
. If you call toArray()
without any parameters and you have "defaultKeyType": "camelName"
then it will use TableMap::TYPE_CAMELNAME
and therefore return all the keys as camelCase.
The root of the problem is in Propel's generator classes. The base classes are generated in
propel/src/Propel/Generator/Builder/Om/ObjectBuilder.php
If we look at how it generates the toArray
method we find:
public function toArray(\$keyType = TableMap::$defaultKeyType, \$includeLazyLoadColumns = true, \$alreadyDumpedObjects = array()" . ($hasFks ? ", \$includeForeignObjects = false" : '') . ")
The important point here is that it is using TableMap::$defaultKeyType
. Now if we look at exportTo
method generation we have to look in templates/baseObjectMethods.php
and the exportTo method definition is as follows:
public function exportTo($parser, $includeLazyLoadColumns = true)
{
if (!$parser instanceof AbstractParser) {
$parser = AbstractParser::getParser($parser);
}
return $parser->fromArray($this->toArray(TableMap::TYPE_PHPNAME, $includeLazyLoadColumns, array(), true));
}
The important point here is that it uses the hardcoded value TableMap::TYPE_PHPNAME
. If you change that hardcoded value to TableMap::TYPE_CAMELNAME
and regenerate your classes then toJSON()
will give all keys as camelCase.
So unfortunately you cannot make toJSON
use camelCase without modifying the source. I would think that the exportTo
method should use the defaultKeyType
so we can use the configuration to modify this behavior. That being said there may be a perfectly good reason to have the hardcoded value instead of a configurable value.
Update:
It looks like this only works with a single instance of each of the generated model classes. With the ObjectCollection
and Collection
classes the toArray
and exportTo
methods use hardcoded values of TableMap::TYPE_PHPNAME
Propel/Runtime/Collection/Collection.php
public function exportTo($parser, $usePrefix = true, $includeLazyLoadColumns = true)
{
if (!$parser instanceof AbstractParser) {
$parser = AbstractParser::getParser($parser);
}
$array = $this->toArray(null, $usePrefix, TableMap::TYPE_PHPNAME, $includeLazyLoadColumns);
return $parser->listFromArray($array, lcfirst($this->getPluralModelName()));
}
Propel/Runtime/Collection/ObjectCollection.php
public function toArray($keyColumn = null, $usePrefix = false, $keyType = TableMap::TYPE_CAMELNAME, $includeLazyLoadColumns = true, $alreadyDumpedObjects = [])
{
$ret = [];
$keyGetterMethod = 'get' . $keyColumn;
/** @var $obj ActiveRecordInterface */
foreach ($this->data as $key => $obj) {
$key = null === $keyColumn ? $key : $obj->$keyGetterMethod();
$key = $usePrefix ? ($this->getModel() . '_' . $key) : $key;
$ret[$key] = $obj->toArray($keyType, $includeLazyLoadColumns, $alreadyDumpedObjects, true);
}
return $ret;
}
So once again it would be nice if we could use the configuration file to set these to TableMap::CAMELNAME
but unfortunately that won't work.