<?php

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

namespace App\Models;

/**
 * @property Group $group
 * @property int $group_id
 * @property int $group_leader
 * @property User $user
 * @property int $user_id
 * @property int $user_pending
 * @property array|null $playmodes
 */
class UserGroup extends Model
{
    protected $table = 'phpbb_user_group';
    public $timestamps = false;
    protected $primaryKeys = ['user_id', 'group_id'];
    protected $casts = [
        'playmodes' => 'array',
        'user_pending' => 'boolean',
    ];

    public function group()
    {
        return $this->belongsTo(Group::class, 'group_id');
    }

    public function user()
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    public function getGroupAttribute(): Group
    {
        // Assumes that `group_id` refers to an existing group that is present
        // in `app('groups')` cache
        return app('groups')->byId($this->group_id);
    }

    public function getPlaymodesAttribute(?string $value): ?array
    {
        if ($this->group->has_playmodes) {
            return json_decode($value) ?? [];
        }

        return null;
    }
}

<?php

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

namespace App\Models;

/**
 * @property Group $group
 * @property int $group_id
 * @property int $group_leader
 * @property User $user
 * @property int $user_id
 * @property int $user_pending
 * @property array|null $playmodes
 */
class UserGroup extends Model
{
    protected $table = 'phpbb_user_group';
    public $timestamps = false;
    protected $primaryKeys = ['user_id', 'group_id'];
    protected $casts = [
        'playmodes' => 'array',
        'user_pending' => 'boolean',
    ];

    public function group()
    {
        return $this->belongsTo(Group::class, 'group_id');
    }

    public function user()
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    public function getGroupAttribute(): Group
    {
        // `group_id` attribute should always be valid or otherwise this will throw error (as it tries to return null).
        return app('groups')->byId($this->group_id);
    }

    public function getPlaymodesAttribute(?string $value): ?array
    {
        if ($this->group->has_playmodes) {
            return json_decode($value) ?? [];
        }

        return null;
    }
}

Update note that the `group_id` validity is not an assumption but a requirement
Update note that the `group_id` validity is not an assumption but a requirement
