{"record":{"id":"00f823ec1fe54b1c","repo":"yiisoft/yii2","slug":"relation-names-are-case-sensitive-class-has-a-r","errorCode":null,"errorMessage":"Relation names are case sensitive. {class} has a relation named \"{realName}\" instead of \"{name}\".","messagePattern":"Relation names are case sensitive\\. (.+?) has a relation named \"(.+?)\" instead of \"(.+?)\"\\.","errorType":"exception","errorClass":"yii\\base\\InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"framework/db/ActiveRelationTrait.php","lineNumber":184,"sourceCode":"        $this->inverseOf = $relationName;\n        return $this;\n    }\n\n    /**\n     * Finds the related records for the specified primary record.\n     * This method is invoked when a relation of an ActiveRecord is being accessed lazily.\n     * @param string $name the relation name\n     * @param ActiveRecordInterface|BaseActiveRecord $model the primary model\n     * @return mixed the related record(s)\n     * @throws InvalidArgumentException if the relation is invalid\n     */\n    public function findFor($name, $model)\n    {\n        if (method_exists($model, 'get' . $name)) {\n            $method = new \\ReflectionMethod($model, 'get' . $name);\n            $realName = lcfirst(substr($method->getName(), 3));\n            if ($realName !== $name) {\n                throw new InvalidArgumentException('Relation names are case sensitive. ' . get_class($model) . \" has a relation named \\\"$realName\\\" instead of \\\"$name\\\".\");\n            }\n        }\n\n        return $this->multiple ? $this->all() : $this->one();\n    }\n\n    /**\n     * If applicable, populate the query's primary model into the related records' inverse relationship.\n     * @param array $result the array of related records as generated by [[populate()]]\n     * @since 2.0.9\n     */\n    private function addInverseRelations(&$result)\n    {\n        if ($this->inverseOf === null) {\n            return;\n        }\n\n        foreach ($result as $i => $relatedModel) {","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/db/ActiveRelationTrait.php#L166-L202","documentation":"Lazy relation access goes through ActiveRelationTrait::findFor($name, $model): it looks up the getter method with method_exists() (which is case-insensitive in PHP), then compares lcfirst of the real method name after 'get' against the property name actually used. Because the comparison is strict, $model->Items still finds getItems() but then throws InvalidArgumentException explaining relation names are case sensitive.","triggerScenarios":"Accessing $order->Items when the getter is getItems(); $model->Author vs getAuthor(); dynamic access $model->{$name} where $name's casing came from user input or generated code.","commonSituations":"IDE autocompletion guessing wrong casing; refactors that rename getters without grepping all usages; template or API layer building property names dynamically; copy-pasting property access between codebases with different conventions.","solutions":["Use the exact case matching the getter minus 'get': getItems() is accessed as ->items","Rename the getter to the casing you want and update all references","For dynamic access, normalize the name first: $model->{lcfirst($name)}","Grep for the wrongly-cased property in templates and API mappers after renaming relations"],"exampleFix":"// before\n$items = $order->Items; // getter is getItems() -> throws\n\n// after\n$items = $order->items;","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function hasExactCaseRelation(\\yii\\db\\BaseActiveRecord $model, string $name): bool\n{\n    if (!method_exists($model, 'get' . $name)) {\n        return false;\n    }\n    $method = new ReflectionMethod($model, 'get' . $name);\n    return lcfirst(substr($method->getName(), 3)) === $name;\n}","tryCatchPattern":"try {\n    $items = $order->items;\n} catch (yii\\base\\InvalidArgumentException $e) {\n    if (strpos($e->getMessage(), 'Relation names are case sensitive') === 0) {\n        $items = $order->{lcfirst($relationName)}; // corrected casing\n    } else {\n        throw $e;\n    }\n}","preventionTips":["Normalize dynamically built property names with lcfirst() before relation access","When refactoring getters, grep templates and API mappers for all casings","Add relation-name smoke tests that access every relation once"],"tags":["relations","naming","case-sensitivity","magic-getter"],"backgroundTag":"case-sensitive-name-mismatch","analyzedSha":"66f00d18a29b520f85e8e8f1e32d1e7e7b556cac","analyzedAt":"2026-08-17T05:17:23.470Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}