filamentphp / filamentphp/filament

Tenancy: creating a record crashes when the ownership relationship is a `HasOneThrough` (`HasOneThrough::save()` is undefined)

Closed Beginner friendly
#20,505 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
PHP
Stars
32.1k
Forks
4.2k
Avg merge
1d 17h
Merged PRs (30d)
61

Description

### Package

filament/filament

### Package Version

v5.8.1

### Laravel Version

v13.31.0

### Livewire Version

v4.4.5

### PHP Version

PHP 8.5.10

### Problem description

When a resource's tenant ownership relationship is a Laravel `HasOneThrough` (for example `post -> author -> team`), creating a record inside the tenant panel throws:

```
BadMethodCallException: Call to undefined method Illuminate\Database\Eloquent\Relations\HasOneThrough::save()
```

The `created` observer registered by `BelongsToTenant::observeTenancyModelCreation()` returns early only for `BelongsTo` and Znck `BelongsToThrough`, syncs for `BelongsToMany`, and otherwise calls `$relationship->save($tenant)`:

https://github.com/filamentphp/filament/blob/4.x/packages/panels/src/Resources/Resource/Concerns/BelongsToTenant.php#L199-L211

`HasOneThrough` / `HasManyThrough` are read-only relations and have no `save()` method, so the create always crashes. Query scoping works fine for this relationship because `scopeEloquentQueryToTenant()` falls back to `whereHas()`; only creation is broken.

This is the Filament 3 issue #15264 / PR #15265 again, which replaced `HasManyThrough` checks with the `HasOneOrManyThrough` base class elsewhere. The tenancy observer never received the same treatment. The same code exists on the `4.x`, `5.x` and `6.x` branches.

### Expected behavior

Creating a record whose ownership relationship is a `HasOneThrough` succeeds. The tenant is already implied by the intermediate model, so the observer should return early for `HasOneOrManyThrough` the same way it does for `BelongsTo` and `BelongsToThrough`.

### Steps to reproduce

1. A `Team` tenant, a `User` with `team_id`, and a `Post` with `author_id` and:

```php
public function team(): HasOneThrough
{
return $this->hasOneThrough(Team::class, User::class, 'id', 'id', 'author_id', 'team_id');
}
```

2. A `PostResource` with `protected static ?string $tenantOwnershipRelationshipName = 'team';` in a panel using `->tenant(Team::class)`.
3. Create a post from the resource's create page (or run the feature test in the reproduction repository):

```bash
composer setup
php artisan test tests/Feature/HasOneThroughTenancyTest.php
```

### Reproduction repository (issue will be closed if this is not valid)

https://github.com/sawirricardo/filament-tenancy-has-one-through-repro

### Relevant log output

```shell
BadMethodCallException: Call to undefined method Illuminate\Database\Eloquent\Relations\HasOneThrough::save()
vendor/laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php:67
vendor/filament/filament/src/Resources/Resource/Concerns/BelongsToTenant.php:210
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in packages/panels/src/Resources/Resource/Concerns/BelongsToTenant.php around the tenancy creation observer and review the existing relation handling. Reproduce the failure with tests/Feature/HasOneThroughTenancyTest.php from the reproduction repository, then verify that creating a record with a HasOneThrough ownership relationship succeeds without calling an unsupported save operation.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.