staudenmeir / eloquent-has-many-deep

Laravel Eloquent HasManyThrough relationships with unlimited levels

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

$post->country relation

yakoffka opened this issue · comments

Hello. Thank you for a wonderful and necessary package.
But I can't figure out how to get the $post->country relation:

Consider this example from the Laravel documentation with an additional level:
Country → has many → User → has many → Post → has many → Comment

class Post extends Model
{
    public function country()
    {
        return $this->hasOneDeepFromReverse((new User())->country());
    }
}

return error:
Expected parameter of type '\Staudenmeir\EloquentHasManyDeep\HasManyDeep', '\Illuminate\Database\Eloquent\Relations\BelongsTo' provided

Can you please tell me how to describe this relationship?

Hi @yakoffka,
Please provide the whole exception with its stacktrace. How are you using the relationship when you get this error?

Strictly speaking, I have a slightly different relations:
User{id} → has many → ChatGroup{uuid, user_id} → has many → Chat{uuid, chat_group_uuid} → has many → Post{uuid, chat_uuid}

I was able to describe all connections except $chat->user(). The error occurs in tinker:

TypeError: App\Models\Chats\Chat::hasOneDeepFromReverse(): Argument #1 ($relation) must be of type Staudenmeir\EloquentHasManyDeep\HasManyDeep, Illuminate\Database\Eloquent\Relations\BelongsTo given, called in /var/www/app/Models/Chats/Chat.php on line 137

on 137 line return $this->hasOneDeepFromReverse((new ChatGroup())->user());

(new ChatGroup())->user() return BelongsTo

But, as far as I understand, the receipt scheme is similar. How to get the relation in your example for $post->country()?
Is the example that I described in the first post working?

  • I don't know how to get stacktrace in tinker

App\Models\Chats\Chat::hasOneDeepFromReverse(): Argument #1 ($relation) must be of type Staudenmeir\EloquentHasManyDeep\HasManyDeep, Illuminate\Database\Eloquent\Relations\BelongsTo given, called in /var/www/app/Models/Chats/Chat.php on line 137 {"exception":"[object] (TypeError(code: 0): App\Models\Chats\Chat::hasOneDeepFromReverse(): Argument #1 ($relation) must be of type Staudenmeir\EloquentHasManyDeep\HasManyDeep, Illuminate\Database\Eloquent\Relations\BelongsTo given, called in /var/www/app/Models/Chats/Chat.php on line 137 at /var/www/vendor/staudenmeir/eloquent-has-many-deep/src/Eloquent/Traits/ReversesRelationships.php:29)
[stacktrace]
#0 /var/www/app/Models/Chats/Chat.php(137): App\Models\Chats\Chat->hasOneDeepFromReverse(Object(Illuminate\Database\Eloquent\Relations\BelongsTo))
#1 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php(576): App\Models\Chats\Chat->user()
#2 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php(528): Illuminate\Database\Eloquent\Model->getRelationshipFromMethod('user')
#3 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php(453): Illuminate\Database\Eloquent\Model->getRelationValue('user')
#4 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(2218): Illuminate\Database\Eloquent\Model->getAttribute('user')
#5 /var/www/vendor/psy/psysh/src/ExecutionLoopClosure.php(55) : eval()'d code(3): Illuminate\Database\Eloquent\Model->__get('user')
#6 /var/www/vendor/psy/psysh/src/ExecutionLoopClosure.php(55): eval()
#7 /var/www/vendor/psy/psysh/src/ExecutionClosure.php(89): Psy\{closure}()
#8 /var/www/vendor/psy/psysh/src/Shell.php(383): Psy\ExecutionClosure->execute()
#9 /var/www/vendor/psy/psysh/src/Shell.php(354): Psy\Shell->doInteractiveRun()
#10 /var/www/vendor/symfony/console/Application.php(171): Psy\Shell->doRun(Object(Symfony\Component\Console\Input\ArrayInput), Object(Psy\Output\ShellOutput))
#11 /var/www/vendor/psy/psysh/src/Shell.php(329): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArrayInput), Object(Psy\Output\ShellOutput))
#12 /var/www/vendor/laravel/tinker/src/Console/TinkerCommand.php(81): Psy\Shell->run()
#13 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36): Laravel\Tinker\Console\TinkerCommand->handle()
#14 /var/www/vendor/laravel/framework/src/Illuminate/Container/Util.php(41): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
#15 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(93): Illuminate\Container\Util::unwrapIfClosure(Object(Closure))
#16 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(37): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
#17 /var/www/vendor/laravel/framework/src/Illuminate/Container/Container.php(651): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
#18 /var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php(178): Illuminate\Container\Container->call(Array)
#19 /var/www/vendor/symfony/console/Command/Command.php(308): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#20 /var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php(148): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#21 /var/www/vendor/symfony/console/Application.php(1014): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#22 /var/www/vendor/symfony/console/Application.php(301): Symfony\Component\Console\Application->doRunCommand(Object(Laravel\Tinker\Console\TinkerCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#23 /var/www/vendor/symfony/console/Application.php(171): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#24 /var/www/vendor/laravel/framework/src/Illuminate/Console/Application.php(102): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#25 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(155): Illuminate\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#26 /var/www/artisan(37): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#27 {main}
"}

/var/www/app/Models/Chats/Chat.php on line 137

What exactly does this line look like? It sounds like you defined a (incorrect) return type ...(): BelongsTo.

BelongsTo defined in ChatGroup model:

class ChatGroup extends Model
{
    ...

    /**
     * @return HasManyDeep
     * см. https://github.com/staudenmeir/eloquent-has-many-deep
     */
    public function posts(): HasManyDeep
    {
        return $this->hasManyDeep(Post::class, [Chat::class]);
    }

    /**
     * @return BelongsTo
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    ...
}

And here is the code from the model Chat:

class Chat extends Model
{
    ...

    /**
     * @return BelongsTo
     */
    public function group(): BelongsTo
    {
        return $this->belongsTo(
            ChatGroup::class,
            'chat_group_uuid',
            'uuid'
        );
    }

    /**
     * @return HasMany
     */
    public function posts(): HasMany
    {
        return $this->hasMany(
            Post::class,
            'chat_uuid',
            'uuid'
        );
    }

    /**
     * User → has many → ChatGroup → has many → Chat → has many → Post
     */
    public function user()
    {
         return $this->hasOneDeepFromReverse((new ChatGroup())->user()); // line 137
    }

    ...
}

in the User model, the inverse relationship is defined

class User extends Model
{
    ...

    /**
     * @return HasMany
     */
    public function chatGroups(): HasMany
    {
        return $this->hasMany(
            ChatGroup::class,
            'user_id',
            'id',
        );
    }

    ...
}

Argument #1 ($chatGroup->user()) for $chat->hasOneDeepFromReverse() and truth has a type BelongsTo, but it must be of this type, since this is the inverse relation of HasMany ($user->chatGroup())

I would just like to see a working example of getting a relation $post->country for your example (Country → has many → User → has many → Post → has many → Comment).

class Post extends Model
{
    public function country()
    {
        return $this->hasOneDeepFromReverse((new User())->country());
    }
}

If you get this connection without any problems, then the error is hiding somewhere in my code.

I think I figured it out: in this case, you need to use the BelongsToThrough relation

Indeed the BelongsToThrough relation helped. I suffered a little with the indication of custom keys, but it all worked.

class Chat extends Model
{

    /**
     * Пользователь, которому принадлежит группа данного чата (Chat -> User)
     *
     * User{id} → has many → ChatGroup{uuid, user_id}
     *      → has many → Chat{uuid, chat_group_uuid}
     *      → has many → Post{uuid, chat_uuid}t
     */
    public function user(): BelongsToThrough
    {
        return $this->belongsToThrough(
            User::class,
            ChatGroup::class,
            'chat_group_uuid', // локальный ключ в таблице chats
            null,
            [
                ChatGroup::class => 'chat_group_uuid', // внешний ключ в таблице chats
                User::class => 'user_id', // внешний ключ в таблице chat_groups
            ]
        );
    }   
 }

Thanks again for the package!