{"record":{"id":"4b9c97dc8d6fb337","repo":"laravel/framework","slug":"please-implement-the-prunable-method-on-your-model-4b9c97","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/Prunable.php","lineNumber":59,"sourceCode":"                    }\n                });\n\n                event(new ModelsPruned(static::class, $total));\n            });\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    /**\n     * Prune the model in the database.\n     *\n     * @return bool|null\n     */\n    public function prune()\n    {\n        $this->pruning();\n\n        return static::isSoftDeletable()\n            ? $this->forceDelete()\n            : $this->delete();\n    }\n\n    /**\n     * Prepare the model for pruning.","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/laravel/framework/blob/e0f6eb3518ac29fbbca8529e97d0df7fc9f24481/src/Illuminate/Database/Eloquent/Prunable.php#L41-L77","documentation":"The plain Prunable trait (row-by-row pruning) provides a default prunable() that throws, requiring the model to override it with a query that returns the rows to prune. Without it, pruneAll() has no source query and the trait cannot run. This is contract enforcement identical in spirit to MassPrunable.","triggerScenarios":"Adding 'use Prunable;' to a model and calling Model::pruneAll() (or the model:prune command) without overriding prunable() to return a Builder.","commonSituations":"Setting up the scheduled model:prune command for the first time; using the wrong trait (Prunable vs MassPrunable) and forgetting the method; copying a model that omitted prunable().","solutions":["Implement prunable() on the model returning an Eloquent Builder selecting rows to prune.","Choose MassPrunable instead if you want fast bulk deletes (both still need prunable()).","Re-run php artisan model:prune after adding the method."],"exampleFix":"// before\nclass OldSession extends Model\n{\n    use Prunable;\n}\n\n// after\nclass OldSession extends Model\n{\n    use Prunable;\n\n    public function prunable()\n    {\n        return static::where('last_activity', '<', now()->subDay());\n    }\n}","handlingStrategy":"validation","validationCode":"$reflection = new \\ReflectionMethod($modelClass, 'prunable');\nif ($reflection->getDeclaringClass()->getName() === \\Illuminate\\Database\\Eloquent\\Prunable::class) {\n    throw new \\RuntimeException(\"{$modelClass} uses Prunable without overriding prunable()\");\n}","typeGuard":"function overridesPrunableTrait(string $modelClass): bool {\n    $r = new \\ReflectionMethod($modelClass, 'prunable');\n    return $r->getDeclaringClass()->getName() !== \\Illuminate\\Database\\Eloquent\\Prunable::class;\n}","tryCatchPattern":null,"preventionTips":["Whenever you add 'use Prunable;', immediately implement prunable() returning a Builder.","Run 'php artisan model:prune --pretend' to confirm the query is wired.","Distinguish Prunable (row-by-row, runs pruning()) from MassPrunable (bulk) - both need prunable()."],"tags":["eloquent","prunable","trait","pruning","contracts"],"backgroundTag":null,"analyzedSha":"e0f6eb3518ac29fbbca8529e97d0df7fc9f24481","analyzedAt":"2026-08-11T20:52:37.562Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}