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.
I don't think Propel gives you a direct easy way to do this via an option you pass in to the method. You can, however, override *Base
methods in your classes.
public function toJSON() {
$fields = array_change_key_case(parent::toJSON());
return $fields;
}
Still using the function above but with more detail: array_change_key_case
changes the case of all keys in an array. You can read about it in the PHP official docs.
array_change_key_case ( array $array [, int $case = CASE_LOWER ] )
Returns an array with all keys from array lowercased or uppercased. Numbered indices are left as is.
Parameters
array The array to work on
case Either CASE_UPPER or CASE_LOWER (default)
Return Values
Returns an array with its keys lower or uppercased, or FALSE if array is not an array.
Works in (PHP 4 >= 4.2.0, PHP 5, PHP 7)
Example
<?php
$input_array = array("FirSt" => 1, "SecOnd" => 4);
print_r(array_change_key_case($input_array, CASE_UPPER));
?>
Outputs
Array
(
[FIRST] => 1
[SECOND] => 4
)
For you...
In your example, you could simply type $lower_foo = array_change_key_case($foo);
on the next line since lowercase is the default.
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.