filamentphp / filamentphp/filament
Tenancy: creating a record crashes when the ownership relationship is a `HasOneThrough` (`HasOneThrough::save()` is undefined)
- 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