{"record":{"id":"632d062c49626893","repo":"yiisoft/yii2","slug":"must-have-a-primary-key","errorCode":null,"errorMessage":"\"{}\" must have a primary key.","messagePattern":"\"(.+?)\" must have a primary key\\.","errorType":"exception","errorClass":"InvalidConfigException","httpStatus":null,"severity":"error","filePath":"framework/db/ActiveRecord.php","lineNumber":187,"sourceCode":"     * @throws InvalidConfigException if there is no primary key defined.\n     * @internal\n     */\n    protected static function findByCondition($condition)\n    {\n        $query = static::find();\n\n        if (!ArrayHelper::isAssociative($condition) && !$condition instanceof ExpressionInterface) {\n            // query by primary key\n            $primaryKey = static::primaryKey();\n            if (isset($primaryKey[0])) {\n                $pk = $primaryKey[0];\n                if (!empty($query->join) || !empty($query->joinWith)) {\n                    $pk = static::tableName() . '.' . $pk;\n                }\n                // if condition is scalar, search for a single primary key, if it is array, search for multiple primary key values\n                $condition = [$pk => is_array($condition) ? array_values($condition) : $condition];\n            } else {\n                throw new InvalidConfigException('\"' . get_called_class() . '\" must have a primary key.');\n            }\n        } elseif (is_array($condition)) {\n            $aliases = static::filterValidAliases($query);\n            $condition = static::filterCondition($condition, $aliases);\n        }\n\n        return $query->andWhere($condition);\n    }\n\n    /**\n     * Returns table aliases which are not the same as the name of the tables.\n     *\n     * @param Query $query\n     * @return array\n     * @throws InvalidConfigException\n     * @since 2.0.17\n     * @internal\n     */","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/yiisoft/yii2/blob/66f00d18a29b520f85e8e8f1e32d1e7e7b556cac/framework/db/ActiveRecord.php#L169-L205","documentation":"ActiveRecord::findByCondition() powers findOne()/findAll(). When the condition is neither an associative array nor an ExpressionInterface, it is treated as a primary key value and gets wrapped as [pk => value]. If static::primaryKey() returns an empty array, the wrapping cannot happen and InvalidConfigException is thrown stating the class must have a primary key.","triggerScenarios":"Calling Model::findOne(1) or Model::findAll([1, 2, 3]) on an AR whose table has no primary key and whose primaryKey() method is not overridden.","commonSituations":"Database views modeled as AR; tables without a PRIMARY KEY constraint; following tutorial examples that use findOne($id) on a model that was generated for a keyless table; schema cache stale after dropping the key.","solutions":["Define a primary key on the underlying table","Override primaryKey() in the AR class to return identifying column(s)","Query by an associative condition instead of a bare scalar: Model::findOne(['slug' => $slug])","Use find()->andWhere(['column' => $value]) directly, which never assumes a primary key"],"exampleFix":"// before\n$user = UserView::findOne(5); // throws: no primary key\n\n// after\n$user = UserView::find()->where(['email' => $email])->one();","handlingStrategy":"validation","validationCode":"// before findOne/findAll with a scalar\nif (!is_array($condition) || !ArrayHelper::isAssociative($condition)) {\n    if (empty($modelClass::primaryKey())) {\n        throw new InvalidArgumentException(\"{$modelClass} has no primary key; query by column instead.\");\n    }\n}\n$model = $modelClass::findOne($condition);","typeGuard":null,"tryCatchPattern":"try {\n    $model = Model::findOne($id);\n} catch (yii\\base\\InvalidConfigException $e) {\n    // model lacks a PK: degrade to a safe column lookup\n    $model = Model::find()->where(['external_id' => $id])->one();\n}","preventionTips":["Static-analyze models: flag findOne(<scalar>) on classes with empty primaryKey()","Prefer associative conditions in code that serves keyless tables","Cover findOne-by-id paths with unit tests per model class"],"tags":["primary-key","find","active-record","yii2"],"backgroundTag":"missing-primary-key","analyzedSha":"66f00d18a29b520f85e8e8f1e32d1e7e7b556cac","analyzedAt":"2026-08-17T05:17:23.470Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}