{"record":{"id":"39861b2b28dc9c41","repo":"laravel/framework","slug":"please-implement-the-prunable-method-on-your-model","errorCode":null,"errorMessage":"Please implement the prunable method on your model.","messagePattern":"Please implement the prunable method on your model\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/Illuminate/Database/Eloquent/MassPrunable.php","lineNumber":50,"sourceCode":"\n            if ($count > 0) {\n                event(new ModelsPruned(static::class, $total));\n            }\n        } while ($count > 0);\n\n        return $total;\n    }\n\n    /**\n     * Get the prunable model query.\n     *\n     * @return \\Illuminate\\Database\\Eloquent\\Builder<static>\n     *\n     * @throws \\LogicException\n     */\n    public function prunable()\n    {\n        throw new LogicException('Please implement the prunable method on your model.');\n    }\n}\n","sourceCodeStart":32,"sourceCodeEnd":53,"githubUrl":"https://github.com/laravel/framework/blob/e0f6eb3518ac29fbbca8529e97d0df7fc9f24481/src/Illuminate/Database/Eloquent/MassPrunable.php#L32-L53","documentation":"The MassPrunable trait ships a default prunable() that throws, signaling the developer must override it on their model to declare which records are prunable. MassPrunable.pruneAll() calls prunable() to obtain the base query; without an override there is nothing to prune and the trait refuses to run. This is the trait's contract enforcement mechanism.","triggerScenarios":"Adding 'use MassPrunable;' to a model and calling Model::pruneAll() without defining a prunable() method that returns a Builder.","commonSituations":"Setting up the prunable:console command for the first time; forgetting to implement prunable() after adopting the trait; copying a model skeleton that omits the method.","solutions":["Implement prunable() on the model returning an Eloquent Builder, e.g. public function prunable() { return static::where('created_at', '<=', now()->subYear()); }.","Confirm you used MassPrunable (bulk delete) intentionally; the plain Prunable trait also requires this method and deletes row by row.","Re-run php artisan model:prune after adding the method."],"exampleFix":"// before\nclass LogEntry extends Model\n{\n    use MassPrunable;\n}\n\n// after\nclass LogEntry extends Model\n{\n    use MassPrunable;\n\n    public function prunable()\n    {\n        return static::where('created_at', '<=', now()->subMonth());\n    }\n}","handlingStrategy":"validation","validationCode":"// Before scheduling model:prune, verify the trait contract is satisfied\n$reflection = new \\ReflectionMethod($modelClass, 'prunable');\nif ($reflection->getDeclaringClass()->getName() === \\Illuminate\\Database\\Eloquent\\MassPrunable::class) {\n    throw new \\RuntimeException(\"{$modelClass} uses MassPrunable without overriding prunable()\");\n}","typeGuard":"function overridesPrunable(string $modelClass): bool {\n    $r = new \\ReflectionMethod($modelClass, 'prunable');\n    return $r->getDeclaringClass()->getName() !== \\Illuminate\\Database\\Eloquent\\MassPrunable::class;\n}","tryCatchPattern":null,"preventionTips":["Always add a prunable() method whenever you add 'use MassPrunable;'.","Add a CI/static-analysis rule or a boot-time assertion that any model using the trait overrides prunable().","Run 'php artisan model:prune --pretend' to exercise the code path before scheduling it."],"tags":["eloquent","prunable","trait","mass-pruning","contracts"],"backgroundTag":null,"analyzedSha":"e0f6eb3518ac29fbbca8529e97d0df7fc9f24481","analyzedAt":"2026-08-11T20:52:37.562Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}