{"record":{"id":"ac6c4a764864c934","repo":"flarum/framework","slug":"relation-aggregates-can-only-be-used-with-number-attributes","errorCode":null,"errorMessage":"Relation aggregates can only be used with number attributes","messagePattern":"Relation aggregates can only be used with number attributes","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"framework/core/src/Api/Schema/Concerns/GetsRelationAggregates.php","lineNumber":25,"sourceCode":" * LICENSE file that was distributed with this source code.\n */\n\nnamespace Flarum\\Api\\Schema\\Concerns;\n\nuse Closure;\nuse Tobyz\\JsonApiServer\\Schema\\Type\\Number;\n\ntrait GetsRelationAggregates\n{\n    /**\n     * @var array{name: string, relation: string, column: string, function: string, constrain: Closure}|null\n     */\n    public ?array $relationAggregate = null;\n\n    public function relationAggregate(string $relation, string $column, string $function, ?Closure $constrain = null): static\n    {\n        if (! $this->type instanceof Number) {\n            throw new \\InvalidArgumentException('Relation aggregates can only be used with number attributes');\n        }\n\n        $name = $this->name;\n\n        $this->relationAggregate = compact('name', 'relation', 'column', 'function', 'constrain');\n\n        return $this;\n    }\n\n    public function countRelation(string $relation, ?Closure $constrain = null): static\n    {\n        return $this->relationAggregate($relation, '*', 'count', $constrain);\n    }\n\n    public function sumRelation(string $relation, string $column, ?Closure $constrain = null): static\n    {\n        return $this->relationAggregate($relation, $column, 'sum', $constrain);\n    }","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/flarum/framework/blob/4b939f685389bfe8a380e9e28ddf305a1c66950c/framework/core/src/Api/Schema/Concerns/GetsRelationAggregates.php#L7-L43","documentation":"GetsRelationAggregates::relationAggregate() records an aggregate (count/sum/avg/min/max) over a relation to be computed for the attribute, but only when the attribute's type is a Number. Calling it on a non-numeric attribute throws this InvalidArgumentException at schema-definition time, because aggregates like SUM/AVG are meaningless on strings, dates-as-strings, JSON, etc.","triggerScenarios":"Calling countRelation/sumRelation/avgRelation/minRelation/maxRelation on an attribute whose $this->type is not an instance of Number — e.g. adding a sumRelation over a string column or a count aggregate on a boolean/date-typed attribute.","commonSituations":"Defining API serializers/attributes where the column was declared as string or date but developers assume counts are always allowed; copy-pasting an aggregate registration from a numeric field to a text field; a model column type changed from int to string without updating the schema extension.","solutions":["Change the attribute's type to a Number type (or register the aggregate on a numeric attribute instead).","Use countRelation only on attributes typed as Number; for non-numeric relations compute the aggregate manually in the serializer/model.","If the column is conceptually numeric, fix the column cast/migration (e.g. integer cast) so the schema type resolves to Number."],"exampleFix":"// before\n$attribute->type('string')\n    ->sumRelation('posts', 'views');\n// after\n$attribute->type(Number::class) // or use the numeric attribute definition\n    ->sumRelation('posts', 'views');","handlingStrategy":"type-guard","validationCode":"// before registering an aggregate, confirm the attribute type is numeric\nif (! ($attribute->type ?? null) instanceof Number) {\n    throw new \\LogicException('Aggregate requires a Number-typed attribute');\n}","typeGuard":"function attributeIsNumeric($attribute): bool {\n    return $attribute instanceof SomeAttribute && $attribute->type instanceof Number;\n}","tryCatchPattern":"try {\n    $attribute->sumRelation('posts', 'views');\n} catch (\\InvalidArgumentException $e) {\n    // fall back to manual aggregate computation in the serializer\n}","preventionTips":["Only call count/sum/avg/min/maxRelation on integer/decimal-typed attributes.","Check the column's migration cast before wiring aggregates.","Compute non-numeric aggregates manually instead of via relationAggregate.","Keep column types in sync when refactoring model fields."],"tags":["api-schema","aggregates","type-mismatch","invalid-argument"],"backgroundTag":"type-mismatch","analyzedSha":"4b939f685389bfe8a380e9e28ddf305a1c66950c","analyzedAt":"2026-09-15T18:09:20.879Z","contentChangedAt":"2026-09-15T18:09:20.879Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}