Should I add the "fetch_group" method to my user class or should I create a separate class for group
Logical inconsistencies

You create a User object expecting a user_id to be passed to setup the object, id object to this design but im sure you've got it covered, but then later on you ask for a user_id to run a query? 

I would leave it in the constructor and use type hiniting (php 7.0 +) to make sure it passed on creation

public function __construct(int $userId) {
    $this-&gt;userId = $userId;
    $this-&gt;get_user_by_id();
}    


But the type hint should make sure an exception is thrown if it isn't provided correctly!

PSR2

The PSR2 is a great coding style guide for php it says you should use camelCase for variable names &amp; function names you should look into it.

Returning early

You should return early where you can this makes your code easier to read and less indentation levels

private function get_user_by_id($id) 
{
    if ($id) {
        return false;
    }

    $data   = $this-&gt;db-&gt;row("SELECT user_id, realm, user_name, display_name, created, last_seen, deleted, ignored, blacklisted, whitelisted, blocked, filtered FROM users WHERE user_id = :id", array('id' =&gt; $id));

    if ( !$data ) {
        return false;
    }

    $this-&gt;data($data);

    return $this;    
}


Adding more functions to setup a user

I would create a Service class for your user as your current class is doing both Model work (connecting to the database) and being a "Object". 

&lt;?php

namespace SomeNamespace\Services;

use SomeNamespace\Models\Users\GetUserDetails;
use SomeNamespace\Models\Users\GetUserGroupDetails;

class User
{
    // Properties list

    public function __construct(
        GetUserDetails $getUserDetails,
        GetUserGroupDetails $getUserGroupDetails
    ) {
        $this-&gt;getUserDetails = $getUserDetails;
        $this-&gt;getUserGroupDetails = $getUserGroupDetails;
    }

    public function populateData(int $userId)
    {
        $userDetails = $this-&gt;getUserDetails-&gt;get($userId);

        if(empty($userDetails)){
            throw new \Exception("Missing user for id $userId", 1);    
        }

        $groupDetails = $this-&gt;getUserGroupDetails-&gt;get($userId);

        $this-&gt;setupUserDetails($userDetails);
        $this-&gt;setupUserGroupDetails($groupDetails);

        return $this;
    }

    private function setupUserDetails($userDetails)
    {
        $this-&gt;someUserDetails = $userDetails["something"];
    }

    private function setupUserGroupDetails($groupDetails)
    {
        $this-&gt;someGroupDetail = $groupDetails["something"];
    }
}

// GetUserDetails.php

namespace SomeNamespace\Models\Users;

class GetUserDetails
{
    public function __construct()
    {
        $this-&gt;db = some::db::factory();
    }

    // Lots of sql queries
}

// GetGroupDetails.php

namespace SomeNamespace\Models\Users\Groups;

class GetUserGroupDetails
{
    public function __construct()
    {
        $this-&gt;db = some::db::factory();
    }

    // Lots of sql queries
}


I may be wrong as you seem to be using factories to construct objects instead of dependency injection, but im sure you see where im going with it!