{"action":"reopened","issue":{"url":"https://api.github.com/repos/cakephp/cakephp/issues/6467","labels_url":"https://api.github.com/repos/cakephp/cakephp/issues/6467/labels{/name}","comments_url":"https://api.github.com/repos/cakephp/cakephp/issues/6467/comments","events_url":"https://api.github.com/repos/cakephp/cakephp/issues/6467/events","html_url":"https://github.com/cakephp/cakephp/issues/6467","id":72640038,"number":6467,"title":"Entity methods behavior inconsistency?","user":{"login":"jhawkwind","id":5790556,"avatar_url":"https://avatars.githubusercontent.com/u/5790556?v=3","gravatar_id":"","url":"https://api.github.com/users/jhawkwind","html_url":"https://github.com/jhawkwind","followers_url":"https://api.github.com/users/jhawkwind/followers","following_url":"https://api.github.com/users/jhawkwind/following{/other_user}","gists_url":"https://api.github.com/users/jhawkwind/gists{/gist_id}","starred_url":"https://api.github.com/users/jhawkwind/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jhawkwind/subscriptions","organizations_url":"https://api.github.com/users/jhawkwind/orgs","repos_url":"https://api.github.com/users/jhawkwind/repos","events_url":"https://api.github.com/users/jhawkwind/events{/privacy}","received_events_url":"https://api.github.com/users/jhawkwind/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/cakephp/cakephp/labels/Defect","name":"Defect","color":"f7c6c7"},{"url":"https://api.github.com/repos/cakephp/cakephp/labels/ORM","name":"ORM","color":"ffffff"}],"state":"open","locked":false,"assignee":{"login":"markstory","id":24086,"avatar_url":"https://avatars.githubusercontent.com/u/24086?v=3","gravatar_id":"","url":"https://api.github.com/users/markstory","html_url":"https://github.com/markstory","followers_url":"https://api.github.com/users/markstory/followers","following_url":"https://api.github.com/users/markstory/following{/other_user}","gists_url":"https://api.github.com/users/markstory/gists{/gist_id}","starred_url":"https://api.github.com/users/markstory/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/markstory/subscriptions","organizations_url":"https://api.github.com/users/markstory/orgs","repos_url":"https://api.github.com/users/markstory/repos","events_url":"https://api.github.com/users/markstory/events{/privacy}","received_events_url":"https://api.github.com/users/markstory/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/cakephp/cakephp/milestones/38","html_url":"https://github.com/cakephp/cakephp/milestones/3.0.3","labels_url":"https://api.github.com/repos/cakephp/cakephp/milestones/38/labels","id":1074414,"number":38,"title":"3.0.3","description":"Bug fixes and optimizations for 3.0","creator":{"login":"markstory","id":24086,"avatar_url":"https://avatars.githubusercontent.com/u/24086?v=3","gravatar_id":"","url":"https://api.github.com/users/markstory","html_url":"https://github.com/markstory","followers_url":"https://api.github.com/users/markstory/followers","following_url":"https://api.github.com/users/markstory/following{/other_user}","gists_url":"https://api.github.com/users/markstory/gists{/gist_id}","starred_url":"https://api.github.com/users/markstory/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/markstory/subscriptions","organizations_url":"https://api.github.com/users/markstory/orgs","repos_url":"https://api.github.com/users/markstory/repos","events_url":"https://api.github.com/users/markstory/events{/privacy}","received_events_url":"https://api.github.com/users/markstory/received_events","type":"User","site_admin":false},"open_issues":15,"closed_issues":84,"state":"open","created_at":"2015-04-20T01:44:45Z","updated_at":"2015-05-03T13:12:51Z","due_on":"2015-05-03T04:00:00Z","closed_at":null},"comments":2,"created_at":"2015-05-02T12:31:30Z","updated_at":"2015-05-03T13:12:51Z","closed_at":null,"body":"This involves four methods in `\\Cake\\Datasource\\EntityTrait` and how each works, their naming seem to have an unexpected behavior.\r\n\r\n`get()` function returns the value of a single property, if a property has been changed the new values will be presented. Unchanged values are also presented.\r\n```php\r\n    /**\r\n     * Returns the value of a property by name\r\n     *\r\n     * @param string $property the name of the property to retrieve\r\n     * @return mixed\r\n     * @throws \\InvalidArgumentException if an empty property name is passed\r\n     */\r\n    public function &get($property)\r\n```\r\n\r\n`extract()` function returns an array of properties from the requested array of properties. Allows you to get multiple properties all at once, but same as `get()` where changed properties get the new values, and unchanged values are also presented.\r\n```php\r\n    /**\r\n     * Returns an array with the requested properties\r\n     * stored in this entity, indexed by property name\r\n     *\r\n     * @param array $properties list of properties to be returned\r\n     * @param bool $onlyDirty Return the requested property only if it is dirty\r\n     * @return array\r\n     */\r\n    public function extract(array $properties, $onlyDirty = false)\r\n```\r\n\r\n`getOriginal()` function returns the original value of a single property if changed, **and the current value if was not changed.** Behaves like `get()` in that it is a single value, but ensures only the original data is presented.\r\n```php\r\n    /**\r\n     * Returns the value of an original property by name\r\n     *\r\n     * @param string $property the name of the property for which original value is retrieved.\r\n     * @return mixed\r\n     * @throws \\InvalidArgumentException if an empty property name is passed.\r\n     */\r\n    public function getOriginal($property)\r\n    {\r\n        if (!strlen((string)$property)) {\r\n            throw new \\InvalidArgumentException('Cannot get an empty property');\r\n        }\r\n        if (isset($this->_original[$property])) {\r\n            return $this->_original[$property];\r\n        }\r\n        return $this->get($property);\r\n    }\r\n```\r\n\r\nThen it is expected that `extractOriginal()` would be an extension of `getOriginal()` but with the `extract()` output. Meaning, I would expect `extractOriginal()` to return an array of properties with their original values if they were changed, otherwise present me with the current value, from the requested array list of properties.\r\n\r\nHowever, `extractOriginal()` only gives the original of the changed values, **and null/unset for where the values remained the same**, not as you would expect it to behave like `getOriginal()`.\r\n\r\n```php\r\n    /**\r\n     * Returns an array with the requested original properties\r\n     * stored in this entity, indexed by property name\r\n     *\r\n     * @param array $properties List of properties to be returned\r\n     * @return array\r\n     */\r\n    public function extractOriginal(array $properties)\r\n    {\r\n        $result = [];\r\n        foreach ($properties as $property) {\r\n            $original = $this->getOriginal($property);\r\n            if ($original !== null && $original !== $this->get($property)) {\r\n                $result[$property] = $original;\r\n            }\r\n        }\r\n        return $result;\r\n    }\r\n```\r\n\r\nWhile I like the ability of `getOriginal()` to be able to return a value regardless of its changed state, the inconsistency and the lack of mention in some areas of the code documentation is a bit confusing. Perhaps `getOriginal()` and `extractOriginal()` can be uniformed in its behavior to be one way (provide only the original values of changed properties), and then an optional parameter flag would flip its operation the other way (provide all requested values, and the original one where it was changed)?"}}