In Laravel, when would you register Route::singleton instead of Route::resource, and what do creatable() and apiSingleton change?
answer
- one instance, no identifier
- show, edit, update only
- creatable(): create, store, destroy
- destroyable(): just destroy
- apiSingleton drops create and edit
basics
~20 sRoute::singleton is for a resource that exists at most once per parent or user, such as a book's cover: it registers show, edit and update with no id. creatable() adds create, store and destroy; apiSingleton drops the create and edit forms.
solid answer
~30 sA **singleton resource** has at most one instance, so its URIs carry no identifier: `Route::singleton('books.cover', BookCoverController::class)` registers only `GET /books/{book}/cover` (`show`), `GET /books/{book}/cover/edit` (`edit`) and `PUT|PATCH /books/{book}/cover` (`update`), named `books.cover.show` and so on. It suits things like a member's profile or reading preferences (identified by the logged-in user) or a book's cover. `->creatable()` adds `create`, `store` and `destroy`; `->destroyable()` adds only `destroy`. `Route::apiSingleton` leaves out the form pages, so it registers `show` and `update`, plus `store` and `destroy` when creatable. `make:controller --singleton` stubs `create`, `store` and `destroy` as `abort(404)` unless you also pass `--creatable`.
code
php · 11 lines<?php
use App\Http\Controllers\BookCoverController;
use App\Http\Controllers\ReadingPreferencesController;
use Illuminate\Support\Facades\Route;
Route::singleton('books.cover', BookCoverController::class)
->creatable();
Route::singleton('preferences', ReadingPreferencesController::class)
->middleware('auth');go deeper
Know that singleton resources model one-of-a-kind records and have no id in the URL.
List the default singleton routes and what creatable(), destroyable() and apiSingleton add or remove.
Decide when a record is truly a singleton, identify it safely from the authenticated user, and keep creation routes off when not needed.
Weigh singleton URLs against future cardinality changes so public URLs stay stable as the domain evolves.
## What makes a resource a singleton A normal **resource** is a collection: many books, each addressed by an id. A **singleton resource** is something that exists **at most once** in its context, so there is nothing to list and no id to put in the URL: - the logged-in member's **profile** or **reading preferences**; - a book's **cover** image; - a library branch's **opening hours**. Using `Route::resource` for these produces nonsense routes (`index` of a single thing, `/cover/{cover}`), which is why Laravel has `Route::singleton`. ## Routes it registers ```php Route::singleton('books.cover', BookCoverController::class); ``` | Verb | URI | Action | Name | |---|---|---|---| | GET | `/books/{book}/cover` | `show` | `books.cover.show` | | GET | `/books/{book}/cover/edit` | `edit` | `books.cover.edit` | | PUT/PATCH | `/books/{book}/cover` | `update` | `books.cover.update` | A top-level singleton such as `Route::singleton('preferences', PreferencesController::class)` gives `/preferences` and `/preferences/edit`; the controller finds the record from the authenticated user rather than from the URL. ## Adding creation and deletion By default a singleton is assumed to always exist, so there is no way to create or delete it. Two methods change that: - **`->creatable()`** adds `create` (`GET .../cover/create`), `store` (`POST .../cover`) and `destroy` (`DELETE .../cover`). - **`->destroyable()`** adds only `destroy`, for something that can be removed but is created elsewhere. ## API singletons `Route::apiSingleton()` is to `singleton` what `apiResource` is to `resource`: it leaves out the HTML form pages. | Registration | Actions | |---|---| | `Route::singleton(...)` | `show`, `edit`, `update` | | `Route::singleton(...)->creatable()` | `create`, `store`, `show`, `edit`, `update`, `destroy` | | `Route::singleton(...)->destroyable()` | `show`, `edit`, `update`, `destroy` | | `Route::apiSingleton(...)` | `show`, `update` | | `Route::apiSingleton(...)->creatable()` | `store`, `show`, `update`, `destroy` | `singletons([...])` and `apiSingletons([...])` register several at once, and the pending registration supports `only()`, `except()`, `names()`, `parameters()` and `middleware()` like a normal resource. ## Generating the controller ```bash php artisan make:controller BookCoverController --singleton php artisan make:controller BookCoverController --singleton --creatable ``` With `--singleton` alone, the stub still contains `create`, `store` and `destroy`, each typed `never` and calling `abort(404)`, so they fail loudly if something routes to them; `--creatable` replaces those bodies with empty placeholders. `--api` switches to the API variant, and `--parent=Book` generates a nested singleton controller that type-hints the parent. ## Judgement calls 1. **Is there really only one?** If a book might later have several images, a nested resource (`books.images`) avoids a migration of your URL design. 2. **Who identifies it?** For user-owned singletons, derive the record from the authenticated user so one member cannot address another's preferences. 3. **Should it be creatable?** If the record is created automatically (a profile row at registration), leave `creatable()` off and keep the surface smaller. 4. **Does deletion make sense?** Use `destroyable()` when users may remove it but not create it through this controller. ## Common mistakes - **Using `Route::resource` with `only(['show', 'edit', 'update'])`** for a one-of-a-kind record, which still puts an id in the URL that clients must invent or leak. - **Trusting an id in a hand-written singleton URL** such as `/members/{member}/preferences`, which lets one member address another's record unless a policy checks it. - **Forgetting `creatable()`** and then wondering why `POST /books/{book}/cover` returns 405: the default singleton has no store route. - **Implementing the stubbed `create`, `store` and `destroy`** without registering them; the methods exist in the generated class but no route reaches them until the registration opts in.
- How does a controller for Route::singleton('preferences', ...) know whose preferences to show?The URI has no identifier, so the controller derives the record from context, typically the authenticated user: `$request->user()->preferences`. Applying `auth` middleware to the singleton makes that user available and stops anonymous access.
- What is the difference between creatable() and destroyable() on a singleton?`creatable()` adds `create`, `store` and `destroy`, for a singleton users can create through this controller. `destroyable()` adds only `destroy`, for one created elsewhere, such as at registration, that users may still remove.
saying these in an interview costs you the question
- A singleton resource registers index and show without an id.
- Route::singleton registers create and store by default.
- apiSingleton registers the same routes as singleton but returns JSON.
- Singleton resources cannot be nested under a normal resource.
- destroyable() adds create, store and destroy.