Developers
Users & profiles
The User facade resolves
Hubzero\User\Manager,
which hands out
Hubzero\User\User
objects. Hubzero\User\User is a
Relational model over #__users, so everything the
ORM offers — get(), set(), save(), relationships, query scopes — is
available on it.
Three separate things are called "the user" on a hub, and reaching for the wrong one is the mistake this page exists to prevent:
| You want | Ask for | Lives in |
|---|---|---|
| The account — id, name, username, email, blocked | Hubzero\User\User, via the User facade |
#__users |
| The profile — bio, organisation, phone, ORCID | Components\Members\Models\Member |
#__user_profiles |
| Community group membership | ->groups() on either |
#__xgroups and its maps |
The first two look identical from the outside — Member extends User, and
both answer get(). They are not identical, and asking the wrong one for a
profile field gets you a default rather than an error. That is the section
on extended profile fields below.
The current user
Any call on the facade that is not getInstance() is forwarded to the
current user, because the manager's __call() passes it on:
use User;
$id = User::get('id');
$name = User::get('name');
$email = User::get('email');
The current user comes from the session, and is a User with no id when
nobody is logged in. User::get('id') returns 0 rather than null in that
case, which is why so much code can compare against it without a guard.
That 0 is also the trap. Writing User::get('id') into a created_by
column without checking isGuest() first records a row owned by nobody, and
every later getInstance(0) on it returns an empty User whose name is
blank. The row is not wrong enough to notice: the booking exists, the list
renders, and the owner column is simply empty. Check first.
Other users
$author = User::getInstance($booking->get('created_by'));
getInstance($id = null) takes a numeric id, a username, or an email
address. With no argument it returns the current user. Resolved users are
cached for the request, so asking twice costs one query. An id that matches
nothing gives you an empty User — check get('id') before using it.
Two things follow from how it resolves:
- Anything
is_numeric()is treated as an id, never as a username. A hub that allows all-digit usernames cannot look one up this way. - When the id you pass is the current user's, you get back the object in
the session, not a copy.
set()on it changes the logged-in user for the rest of the request. Load a fresh model with the ORM if you mean to modify somebody.
The ORM's own finders are there for anything more selective:
$user = Hubzero\User\User::oneByUsername('janedoe');
$user = Hubzero\User\User::oneByEmail('jane@example.org');
$user = Hubzero\User\User::oneOrFail($id); // throws if absent
The columns
#__users holds the account, and nothing more:
| Column | Notes |
|---|---|
id |
The numeric user id. Reference this from your own tables |
name |
Display name — "Jane Doe" |
username |
Login name |
email |
Email address |
password |
Hashed. Never read it; see Hubzero\User\Password |
block |
1 when the account is blocked |
activation |
Non-zero once the email address is confirmed |
registerDate |
Set automatically on create |
registerIP |
Set automatically on create |
lastvisitDate |
Updated by setLastVisit() |
access |
The account's view access level |
params |
Per-member preferences, as a Registry |
Two keys are not columns and are handled in get(): guest returns
isGuest(), and uidNumber is an alias for id.
Preferences in params are read with getParam(), not get():
$tz = User::getParam('timezone', Config::get('offset'));
setParam($key, $value) and defParam($key, $value) write and default one.
Logged in or not
if (User::isGuest())
{
// logged OUT
App::abort(403, Lang::txt('Login required'));
}
User::get('guest') is the same test. Note that isGuest() does more than
read a flag on a cloud-hosted hub: it will accept a signed JWT cookie and
populate the user from it. Always call it rather than inspecting the
property yourself.
Extended profile fields
Bio, gender, organisation, disability and the rest are not on
Hubzero\User\User. They live in #__user_profiles as key/value rows and
are reached through
Components\Members\Models\Member,
which extends Hubzero\User\User and loads them on first miss:
use Components\Members\Models\Member;
$member = Member::oneOrFail($id);
$bio = $member->get('bio');
$gender = $member->get('gender');
A key that occurs several times for one member — a multi-value field such as
disability — comes back as an array; a key that occurs once comes back as
a string.
$member->picture($anonymous = 0, $thumbnail = true, $serveFile = true)
returns a URL for the member's picture, falling back to a generated
placeholder, and $member->link($type = '') builds the URL of their
profile.
Access groups
Access groups are the ACL groups an administrator assigns, not community groups:
foreach (User::getInstance($id)->accessgroups() as $map)
{
echo $map->get('group_id');
}
accessgroups() is a one-to-many relationship to Hubzero\Access\Map, so
it returns a query you can constrain, and iterating it runs it.
getAuthorisedGroups() returns the group ids as a flat array, including
inherited parents, and getAuthorisedViewLevels() the view levels those
groups grant.
Permissions are checked with authorise($action, $assetname = null)
(authorize() is a spelling alias):
if (!User::authorise('core.edit', 'com_blog'))
{
App::abort(403, Lang::txt('JERROR_ALERTNOAUTHOR'));
}
Community group membership
Community groups — the #__xgroups ones — hang off the user model:
$managed = User::getInstance($id)->groups('managers');
groups($role = 'all') accepts all, members, managers, applicants
or invitees, and returns an array of row objects. It is computed once per
request.
The underlying query is
Hubzero\User\Helper::getGroups($uid, $type = 'all', $cat = null), which
returns an array — empty when there are none, never false — of objects
carrying:
| Field | Meaning |
|---|---|
gidNumber |
The group's numeric id |
cn |
The group alias, as it appears in a URL |
description |
The group's title |
published |
0 or 1 |
approved |
0 or 1 |
join_policy |
How the group admits members |
registered |
1 if the member applied; 0 if they were invited |
regconfirmed |
1 once the application was accepted |
manager |
1 if the member manages the group |
Helper::getCommonGroups($uid, $pid) returns the groups two members share,
which is what profile pages use to decide how much to show.
Rewritten and checked against 2.4-main @ 348f0057c2 on 2026-09-10.