staudenmeir / eloquent-has-many-deep

Laravel Eloquent HasManyThrough relationships with unlimited levels
MIT License
2.67k stars 157 forks source link

HasOneThrough Relation Not Allowed For hasOneDeepFromRelations #238

Closed MannikJ closed 3 months ago

MannikJ commented 3 months ago

staudenmeir/eloquent-has-many-deep: v1.20.1 laravel/framework: v11.15.0

Since updating from Laravel 10 to 11 my previewFile relation doesn't work anymore.

The exception is quite obvious:

RuntimeException: This relationship is not supported.

/var/www/html/vendor/staudenmeir/eloquent-has-many-deep/src/Eloquent/Traits/ConcatenatesNativeRelationships.php:214 /var/www/html/vendor/staudenmeir/eloquent-has-many-deep/src/Eloquent/Traits/ConcatenatesRelationships.php:134 /var/www/html/vendor/staudenmeir/eloquent-has-many-deep/src/Eloquent/Traits/ConcatenatesRelationships.php:64 /var/www/html/app/Traits/HasTaskFiles.php:81 /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:605 /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:557 /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:482 /var/www/html/vendor/staudenmeir/eloquent-json-relations/src/HasJsonRelationships.php:42 /var/www/html/app/Task.php:80 /var/www/html/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:2242 /var/www/html/tests/Unit/app/TaskTest.php:1935

The error comes from the following function in Staudenmeir\EloquentHasManyDeep\Eloquent\Traits\ConcatenatesNativeRelationships

    protected function hasOneOrManyDeepRelationMethod(Relation $relation)
    {
        $classes = [
            BelongsTo::class,
            HasManyThrough::class,
            MorphOneOrMany::class,
            HasOneOrMany::class,
            MorphToMany::class,
            BelongsToMany::class,
        ];

        foreach ($classes as $class) {
            if ($relation instanceof $class) {
                return 'hasOneOrManyDeepFrom' . class_basename($class);
            }
        }

        throw new RuntimeException('This relationship is not supported.'); // @codeCoverageIgnore
    }

I have not yet fully checked why it has worked before and why it throws an error now, but I guess there might have been a restructuring in the inheritance hierarchy of the relation classes so that HasOneThrough relation is not allowed anymore due to the failing instanceof check.

My example code, although simplified a bit, is still a bit too complicated, but I think the error message speaks for itself anyway:

App\Models\Job.php

    public function taskFiles(): HasManyThrough
    {
        return $this->hasManyThrough(
            TaskFile::class, // Pivot table with task_id and file_id
            Task::class
        );
    }

    public function inputTaskFiles(): HasManyThrough
    {
        return $this->taskFiles()->jobInput();
    }

    public function primaryInputTaskFiles(): HasManyThrough
    {
        return $this->inputTaskFiles()
            ->whereHas('file', function ($query) {
                return $query->whereNull('files.parent_id');
            })
            ->orderBy('task_files.id');
    }

App\Models\Task.php:

    public function job(): BelongsTo
    {
        return $this->belongsTo(Job::class)->withTrashed();
    }

    public function previewFile(): HasOne
    {
        return $this->hasOneDeepFromRelations(
            $this->job(),
            (new Job())->primaryInputTaskFiles()->one()
        );
    }
staudenmeir commented 3 months ago

Hi @MannikJ, Thanks for reporting. This is caused by a breaking change in Laravel 11.15. I'll work on a fix.

staudenmeir commented 3 months ago

I released a new version with the fix. The issue was that HasOneThrough no longer is a subclass of HasManyThrough and so $relation instanceof HasManyThrough isn't true anymore.