{"record":{"id":"feffdc6888e65073","repo":"yiisoft/yii2","slug":"the-formname-method-should-be-explicitly-defin","errorCode":null,"errorMessage":"The \"formName()\" method should be explicitly defined for anonymous models","messagePattern":"The \"formName\\(\\)\" method should be explicitly defined for anonymous models","errorType":"exception","errorClass":"InvalidConfigException","httpStatus":null,"severity":"error","filePath":"framework/base/Model.php","lineNumber":271,"sourceCode":"     * an empty string, then the input name would be \"b\".\n     *\n     * The purpose of the above naming schema is that for forms which contain multiple different models,\n     * the attributes of each model are grouped in sub-arrays of the POST-data and it is easier to\n     * differentiate between them.\n     *\n     * By default, this method returns the model class name (without the namespace part)\n     * as the form name. You may override it when the model is used in different forms.\n     *\n     * @return string the form name of this model class.\n     * @see load()\n     * @throws InvalidConfigException when form is defined with anonymous class and `formName()` method is\n     * not overridden.\n     */\n    public function formName()\n    {\n        $reflector = new ReflectionClass($this);\n        if (PHP_VERSION_ID >= 70000 && $reflector->isAnonymous()) {\n            throw new InvalidConfigException('The \"formName()\" method should be explicitly defined for anonymous models');\n        }\n        return $reflector->getShortName();\n    }\n\n    /**\n     * Returns the list of attribute names.\n     *\n     * By default, this method returns all public non-static properties of the class.\n     * You may override this method to change the default behavior.\n     *\n     * @return string[] list of attribute names.\n     */\n    public function attributes()\n    {\n        $class = new ReflectionClass($this);\n        $names = [];\n        foreach ($class->getProperties(\\ReflectionProperty::IS_PUBLIC) as $property) {\n            if (!$property->isStatic()) {","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/base/Model.php#L253-L289","documentation":"Model::formName() returns the class short name and is the prefix load(), ActiveForm and Html::active* helpers use for input names (POST[formName][attribute]). PHP anonymous classes have no usable short name, so when ReflectionClass::isAnonymous() is true (PHP >= 7) formName() throws InvalidConfigException unless the anonymous class overrides it. The throw usually surfaces indirectly: $model->load($post) calls formName() to match POST keys.","triggerScenarios":"Creating an inline model `new class extends \\yii\\base\\Model { ... }` and calling load(), rendering it with ActiveForm/ActiveField, or using Html::activeTextInput() on it; calling formName() directly for cache keys or logging on an anonymous model instance.","commonSituations":"One-off validation models for API payloads written inline to avoid creating a file; anonymous subclasses of Model used in tests and fixtures.","solutions":["Override formName() inside the anonymous class: public function formName() { return 'QuickForm'; }","Or skip form-based loading: set attributes directly with setAttributes($data, false) and then call validate()","Or promote the class to a named class file - named classes never hit this guard"],"exampleFix":"// before\n$model = new class extends \\yii\\base\\Model {\n    public $email;\n    public function rules() { return [[['email'], 'email']]; }\n};\n$model->load(Yii::$app->request->post()); // throws\n\n// after\n$model = new class extends \\yii\\base\\Model {\n    public $email;\n    public function rules() { return [[['email'], 'email']]; }\n    public function formName() { return 'QuickForm'; }\n};\n$model->load(Yii::$app->request->post()); // matches POST['QuickForm']['email']","handlingStrategy":"validation","validationCode":"$reflector = new \\ReflectionClass($model);\nif ($reflector->isAnonymous()\n    && (new \\ReflectionMethod($model, 'formName'))->getDeclaringClass()->getName() === \\yii\\base\\Model::class) {\n    // formName() will throw; override it or bypass load()\n    $model->setAttributes($data, false);\n    $ok = $model->validate();\n} else {\n    $ok = $model->load($data);\n}","typeGuard":null,"tryCatchPattern":"try {\n    $model->load(Yii::$app->request->post());\n} catch (\\yii\\base\\InvalidConfigException $e) {\n    // anonymous model without formName() override\n    Yii::error($e->getMessage());\n    $model->setAttributes(Yii::$app->request->post(), false);\n}","preventionTips":["Always override formName() in anonymous models used with forms","For pure server-side validation use setAttributes() plus validate() instead of load()","Review 'new class extends' near Model in code review as a lint habit"],"tags":["model","forms","anonymous-class","php7"],"backgroundTag":"anonymous-class-limitation","analyzedSha":"66f00d18a29b520f85e8e8f1e32d1e7e7b556cac","analyzedAt":"2026-08-17T05:17:23.470Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}