From b25cda24a97811b834fa6d02d5a8ea8346edc28a Mon Sep 17 00:00:00 2001 From: Skydev0h Date: Wed, 18 May 2016 07:59:14 +0300 Subject: [PATCH 1/3] ORM: Case-insensitive column map option Added a global ORM setting that allows to try to find a key in column map case-insensitively if it was not found by an exact match at first time. Can be a real fix for many Oracle bugs, because Phalcon does not expect that column names are converted to uppercase by Oracle in the result. --- CHANGELOG.md | 1 + config.json | 6 +++++- phalcon/mvc/model.zep | 45 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab3dbccd274..e591566ab94 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ - Phalcon\Tag::getTitle() shows a title depending on prependTitle and appendTitle - Using a settable variable for the Mongo Connection Service name instead of a hard coded string [#11725](https://github.com/phalcon/cphalcon/issues/11725) - Added new getter `Phalcon\Mvc\Model\Query\Builder::getJoins()` - to get join parts from query builder +- Added global setting `orm.try_ci_column_map` to attempt to find value in the column map case-insensitively if it is not found, what is a real fix for many possible bugs with Oracle column capitalization. Can be also enabled by setting `tryCIColumnMap` key in `\Phalcon\Mvc\Model::setup()`. # [2.0.11](https://github.com/phalcon/cphalcon/releases/tag/phalcon-v2.0.11) (????-??-??) - Fix Model magic set functionality to maintain variable visibility and utilize setter methods.[#11286](https://github.com/phalcon/cphalcon/issues/11286) diff --git a/config.json b/config.json index 5f477eb0b70..00a7e044b47 100644 --- a/config.json +++ b/config.json @@ -99,7 +99,11 @@ "orm.ignore_unknown_columns": { "type": "bool", "default": false - } + }, + "orm.try_ci_column_map": { + "type": "bool", + "default": false + } }, "destructors": { "request": [ diff --git a/phalcon/mvc/model.zep b/phalcon/mvc/model.zep index 383b9c4b51b..6d572ae7538 100644 --- a/phalcon/mvc/model.zep +++ b/phalcon/mvc/model.zep @@ -493,6 +493,20 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface return this; } + /** + * Attempts to find key case-insensitively + */ + private static function columnMapCIResolve(var columnMap, var key) -> string + { + var cmKey; + for cmKey in array_keys(columnMap) { + if strtolower(cmKey) == strtolower(key) { + return cmKey; + } + } + return key; + } + /** * Assigns values to a model from an array returning a new model. * @@ -530,6 +544,11 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface continue; } + // Try to find case-insensitive key variant + if !isset columnMap[key] && globals_get("orm.try_ci_column_map") { + let key = self::columnMapCIResolve(columnMap, key); + } + // Every field must be part of the column map if !fetch attribute, columnMap[key] { if !globals_get("orm.ignore_unknown_columns") { @@ -638,6 +657,11 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface if typeof key == "string" { if typeof columnMap == "array" { + // Try to find case-insensitive key variant + if !isset columnMap[key] && globals_get("orm.try_ci_column_map") { + let key = self::columnMapCIResolve(columnMap, key); + } + /** * Every field must be part of the column map */ @@ -3612,6 +3636,11 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface continue; } + // Try to find case-insensitive key variant + if !isset columnMap[key] && globals_get("orm.try_ci_column_map") { + let key = self::columnMapCIResolve(columnMap, key); + } + /** * Every field must be part of the column map */ @@ -4406,6 +4435,12 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface * Check if the columns must be renamed */ if typeof columnMap == "array" { + + // Try to find case-insensitive attribute variant + if !isset columnMap[attribute] && globals_get("orm.try_ci_column_map") { + let attribute = self::columnMapCIResolve(columnMap, attribute); + } + if !fetch attributeField, columnMap[attribute] { if !globals_get("orm.ignore_unknown_columns") { throw new Exception("Column '" . attribute . "' doesn't make part of the column map"); @@ -4454,7 +4489,8 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface { var disableEvents, columnRenaming, notNullValidations, exceptionOnFailedSave, phqlLiterals, virtualForeignKeys, - lateStateBinding, castOnHydrate, ignoreUnknownColumns; + lateStateBinding, castOnHydrate, ignoreUnknownColumns, + tryCIColumnMap; /** * Enables/Disables globally the internal events @@ -4518,6 +4554,13 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface if fetch ignoreUnknownColumns, options["ignoreUnknownColumns"] { globals_set("orm.ignore_unknown_columns", ignoreUnknownColumns); } + + /** + * Allows to try to find attributes in column map case-insensitively (needed for Oracle) + */ + if fetch tryCIColumnMap, options["tryCIColumnMap"] { + globals_set("orm.try_ci_column_map", tryCIColumnMap); + } } /** From fe35d33ab94a6b60e5c027d449a48c6170b00f7c Mon Sep 17 00:00:00 2001 From: Skydev0h Date: Wed, 18 May 2016 07:59:14 +0300 Subject: [PATCH 2/3] ORM: Case-insensitive column map option Added a global ORM setting that allows to try to find a key in column map case-insensitively if it was not found by an exact match at first time. Can be a real fix for many Oracle bugs, because Phalcon does not expect that column names are converted to uppercase by Oracle in the result. --- CHANGELOG.md | 1 + config.json | 6 +++++- phalcon/mvc/model.zep | 45 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df82ec8bf42..5292bcd98f6 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,7 @@ - Using a settable variable for the Mongo Connection Service name instead of a hard coded string [#11725](https://github.com/phalcon/cphalcon/issues/11725) - Added new getter `Phalcon\Mvc\Model\Query\Builder::getJoins()` - to get join parts from query builder - Fixed `Phalcon\Db\Dialect\Oracle::prepareTable()` to correctly generate SQL for table aliases [#11799](https://github.com/phalcon/cphalcon/issues/11799) +- Added global setting `orm.try_ci_column_map` to attempt to find value in the column map case-insensitively if it is not found, what is a real fix for many possible bugs with Oracle column capitalization. Can be also enabled by setting `tryCIColumnMap` key in `\Phalcon\Mvc\Model::setup()`. # [2.0.11](https://github.com/phalcon/cphalcon/releases/tag/phalcon-v2.0.11) (????-??-??) - Fix Model magic set functionality to maintain variable visibility and utilize setter methods.[#11286](https://github.com/phalcon/cphalcon/issues/11286) diff --git a/config.json b/config.json index 5f477eb0b70..00a7e044b47 100644 --- a/config.json +++ b/config.json @@ -99,7 +99,11 @@ "orm.ignore_unknown_columns": { "type": "bool", "default": false - } + }, + "orm.try_ci_column_map": { + "type": "bool", + "default": false + } }, "destructors": { "request": [ diff --git a/phalcon/mvc/model.zep b/phalcon/mvc/model.zep index 383b9c4b51b..6d572ae7538 100644 --- a/phalcon/mvc/model.zep +++ b/phalcon/mvc/model.zep @@ -493,6 +493,20 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface return this; } + /** + * Attempts to find key case-insensitively + */ + private static function columnMapCIResolve(var columnMap, var key) -> string + { + var cmKey; + for cmKey in array_keys(columnMap) { + if strtolower(cmKey) == strtolower(key) { + return cmKey; + } + } + return key; + } + /** * Assigns values to a model from an array returning a new model. * @@ -530,6 +544,11 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface continue; } + // Try to find case-insensitive key variant + if !isset columnMap[key] && globals_get("orm.try_ci_column_map") { + let key = self::columnMapCIResolve(columnMap, key); + } + // Every field must be part of the column map if !fetch attribute, columnMap[key] { if !globals_get("orm.ignore_unknown_columns") { @@ -638,6 +657,11 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface if typeof key == "string" { if typeof columnMap == "array" { + // Try to find case-insensitive key variant + if !isset columnMap[key] && globals_get("orm.try_ci_column_map") { + let key = self::columnMapCIResolve(columnMap, key); + } + /** * Every field must be part of the column map */ @@ -3612,6 +3636,11 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface continue; } + // Try to find case-insensitive key variant + if !isset columnMap[key] && globals_get("orm.try_ci_column_map") { + let key = self::columnMapCIResolve(columnMap, key); + } + /** * Every field must be part of the column map */ @@ -4406,6 +4435,12 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface * Check if the columns must be renamed */ if typeof columnMap == "array" { + + // Try to find case-insensitive attribute variant + if !isset columnMap[attribute] && globals_get("orm.try_ci_column_map") { + let attribute = self::columnMapCIResolve(columnMap, attribute); + } + if !fetch attributeField, columnMap[attribute] { if !globals_get("orm.ignore_unknown_columns") { throw new Exception("Column '" . attribute . "' doesn't make part of the column map"); @@ -4454,7 +4489,8 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface { var disableEvents, columnRenaming, notNullValidations, exceptionOnFailedSave, phqlLiterals, virtualForeignKeys, - lateStateBinding, castOnHydrate, ignoreUnknownColumns; + lateStateBinding, castOnHydrate, ignoreUnknownColumns, + tryCIColumnMap; /** * Enables/Disables globally the internal events @@ -4518,6 +4554,13 @@ abstract class Model implements EntityInterface, ModelInterface, ResultInterface if fetch ignoreUnknownColumns, options["ignoreUnknownColumns"] { globals_set("orm.ignore_unknown_columns", ignoreUnknownColumns); } + + /** + * Allows to try to find attributes in column map case-insensitively (needed for Oracle) + */ + if fetch tryCIColumnMap, options["tryCIColumnMap"] { + globals_set("orm.try_ci_column_map", tryCIColumnMap); + } } /** From cf47d964116a5d2c96d3aef97ffd222db133740e Mon Sep 17 00:00:00 2001 From: Skydev0h Date: Wed, 18 May 2016 14:56:35 +0300 Subject: [PATCH 3/3] Fix indentation of config.json Tabs -> spaces. Sorry for that. --- config.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config.json b/config.json index 00a7e044b47..4f79fe2acb5 100644 --- a/config.json +++ b/config.json @@ -100,10 +100,10 @@ "type": "bool", "default": false }, - "orm.try_ci_column_map": { - "type": "bool", - "default": false - } + "orm.try_ci_column_map": { + "type": "bool", + "default": false + } }, "destructors": { "request": [