In Eloquent, what does associate() do on a belongsTo relation such as $photo->album(), and why must you still call save()?
answer
- sets the foreign key, in memory
- also sets the loaded relation
- returns the child model
- dissociate() writes null
- parent-side save() does both
basics
~20 sassociate() sets the child's foreign key (photos.album_id) to the parent's key and puts the parent in the child's loaded relations, all in memory. It runs no SQL, so the change reaches the database only when save() is called on the child.
solid answer
~40 s`$photo->album()->associate($album)` copies the album's owner key into the photo's `album_id` attribute and calls `setRelation('album', $album)`, so `$photo->album` returns that album without a query. It returns the photo and issues no SQL; the photo is merely dirty until `$photo->save()`. You may pass a key instead of a model, in which case the foreign key is set and the cached relation is cleared. `dissociate()` sets the foreign key and the relation to `null`, which needs a nullable column, and also needs `save()`. From the parent side, `$album->photos()->save($photo)` sets the foreign key and saves in one call. On a `morphTo` relation, `associate()` also sets the type column.
code
php · 15 lines<?php
use App\Models\Album;
use App\Models\Photo;
$photo = Photo::findOrFail($photoId);
$album = Album::findOrFail($albumId);
$photo->album()->associate($album)->save(); // one UPDATE
// no query: the relation was set in memory by associate()
echo $photo->album->title;
$photo->album()->dissociate(); // album_id = null, in memory only
$photo->save(); // UPDATE photos SET album_id = NULL ...go deeper
Recall that associate() lives on the belongsTo side, sets the foreign key in memory, and needs save() to persist.
Explain how associate() also sets the loaded relation, what changes when you pass a key, and how parent-side save() compares.
Spot missing save() calls, nullable-column needs for dissociate(), and unsaved parents in code review and bug reports.
Agree team conventions for writing relations from the owning side, so persistence stays explicit and reviewable across services.
## The belongs-to side owns the column In a photo-sharing site each `Photo` sits in one `Album`. The `photos` table holds the foreign key `album_id`, so the photo model declares `album()` as a `belongsTo` relation. Changing which album a photo is in means changing **the photo's own column**. `associate()` is the relation-aware way to do that. ## What associate() does, step by step Reading `BelongsTo::associate()` in the framework source: 1. It reads the owner key from the model you pass (`$album->id` by default, or the relation's custom owner key). 2. It sets that value on the child's foreign-key attribute: `$photo->album_id = $album->id`. 3. If you passed a model, it stores it as the loaded relation with `setRelation('album', $album)`. If you passed a bare key, it **unsets** the loaded relation so a stale album is not returned. 4. It returns the **child model** (the photo), so you can chain `->save()`. Nothing in that list talks to the database. The photo is now **dirty**: `album_id` differs from its original value. Eloquent is an Active Record ORM with no unit of work that flushes pending changes later, so the change is written only when you call `save()` on the photo. ```php $photo->album()->associate($album); $photo->save(); // UPDATE photos SET album_id = ? WHERE id = ? ``` ## dissociate() `dissociate()` (with the alias `disassociate()`) is the reverse: it sets `album_id` to `null` and sets the loaded relation to `null`. It also needs `save()`, and the column must be nullable or the `UPDATE` fails. ## Associate versus assigning the column | Approach | Sets `album_id` | Sets the loaded `album` relation | Runs SQL | |---|---|---|---| | `$photo->album_id = $album->id` | yes | no — a previously loaded album stays stale | no | | `$photo->album()->associate($album)` | yes | yes | no | | `$album->photos()->save($photo)` | yes | — | yes, saves the photo | | `$album->photos()->create([...])` | yes | — | yes, inserts a new photo | Assigning the column works for persistence, but `associate()` keeps the in-memory object graph consistent and does not need you to know the foreign-key name. The parent-side `save()` and `create()` on a `hasMany` relation set the key **and** persist in one call; those are the calls to use when you start from the album. ## Edge cases worth knowing - **Unsaved parent.** Associating an album that has not been saved yet copies a `null` key; save the parent first. - **Polymorphic relations.** On a `morphTo` relation (say, a comment that belongs to a photo or a video), `associate()` sets both the ID column and the type column from the model's morph class. - **Many-to-many is different.** `associate()` exists only on `belongsTo` and `morphTo`. Linking photos to tags writes pivot rows through `attach()` or `sync()`, which run SQL immediately. - **Events.** Because the write happens in `save()`, the photo's normal saving and updating model events fire then, not during `associate()`. ## A worked sequence 1. `Photo::findOrFail($id)` loads the photo; `album_id` is 3 and nothing is dirty. 2. `$photo->album()->associate($album)` with album 7 sets `album_id` to 7 in memory. `$photo->isDirty('album_id')` is now `true`, and `$photo->album` returns album 7 with no query. 3. If the request ends here, the database still says 3: nothing wrote the change. 4. `$photo->save()` runs one `UPDATE` for the dirty column and fires the photo's saving and updating events. ## Why interviewers ask it The question checks whether a candidate understands that Eloquent writes **only on save**, and which model owns a one-to-many link. A candidate who calls `$album->photos()->associate($photo)` has the direction wrong: `hasMany` has no `associate()`; the child's `belongsTo` does.
- Why does $album->photos()->associate($photo) fail?`associate()` is defined on `BelongsTo`, the side whose table holds the foreign key. `$album->photos()` is a `HasMany` relation, which has no such method. From the album side, call `$album->photos()->save($photo)`, which sets `album_id` on the photo and saves it in one step.
- What happens if you pass associate() an ID instead of a model?It sets the foreign key to that value and unsets the loaded relation, because it has no model to cache. The next read of `$photo->album` runs a query for the new album. The source handles both forms: a `Model` is cached with `setRelation()`, a scalar key clears it with `unsetRelation()`.
saying these in an interview costs you the question
- Saying associate() immediately runs an UPDATE on the child table
- Calling associate() on the hasMany side, such as $album->photos()
- Believing dissociate() deletes the child row
- Thinking Eloquent flushes associate() changes at the end of the request
- Associating an unsaved parent and expecting a valid foreign key