-
Notifications
You must be signed in to change notification settings - Fork 587
Description
Description
When using a browser field with a BelongsTo relationship (e.g., max(1)), the preview functionality breaks because hydrateBrowser() in HandleRevisions trait always sets the relationship as a Collection, even for BelongsTo relationships that expect a single model.
Steps to Reproduce
- Create a module with a
BelongsTorelationship (e.g.,Blogbelongs toCategory) - Configure the browser in the repository:
protected $browsers = ['category'];
- Define the relationship in the model:
public function category(): BelongsTo { return $this->belongsTo(Category::class); }
- In the frontend view, access the relationship:
$category = $item->category; echo $category->title; // Works normally, breaks in preview
- Try to preview the item in Twill admin
Expected Behavior
Preview should work the same as the normal frontend view - $item->category should return a single Category model (or null).
Actual Behavior
Preview throws an error like:
Property [title] does not exist on this collection instance.
Because $item->category returns a Collection instead of a single model during preview hydration.
Root Cause
In HandleRevisions::hydrateBrowser(), the method unconditionally calls hydrateOrderedBelongsToMany(), which always sets the relationship as a Collection:
public function hydrateBrowser(...): void {
$this->hydrateOrderedBelongsToMany($object, $fields, $relationship, $positionAttribute, $model);
}However, in HandleBrowsers::updateBrowser() (used for saving), there's a proper check for BelongsTo:
if ($object->$relationship() instanceof BelongsTo) {
// Handles single item correctly
$foreignKey = $object->$relationship()->getForeignKeyName();
$id = Arr::get($relatedElements, '0.id');
$object->$foreignKey = $id;
// ...
}This check is missing in hydrateBrowser().
Proposed Fix
Add the same BelongsTo check to hydrateBrowser():
public function hydrateBrowser(
TwillModelContract $object,
array $fields,
string $relationship,
string $positionAttribute = 'position',
null|TwillModelContract|string $model = null
): void {
$fieldsHasElements = isset($fields['browsers'][$relationship]) && !empty($fields['browsers'][$relationship]);
$relatedElements = $fieldsHasElements ? $fields['browsers'][$relationship] : [];
$relationRepository = $this->getModelRepository($relationship, $model);
// Handle BelongsTo relationships - set single model instead of collection
if (method_exists($object, $relationship) && $object->$relationship() instanceof BelongsTo) {
$relatedItem = !empty($relatedElements) ? $relationRepository->getById($relatedElements[0]['id']) : null;
$object->setRelation($relationship, $relatedItem);
return;
}
// Existing logic for BelongsToMany relationships
$this->hydrateOrderedBelongsToMany($object, $fields, $relationship, $positionAttribute, $model);
}Environment
- Twill version: 3.5.2
- Laravel version: 11.x
- PHP version: 8.2
Related
- BelongsTo form browser not working with revisions #1082 - Similar issue that was partially fixed by fix: Support revisions preview and restore with belongsTo browsers #1085, but only addressed the
getTable() on nullerror, not the Collection vs single model issue