Account and Contact Hooks

1 views Markdown

The eight hooks over the profile picture, addresses, linked providers and preferences.

Overview

Everything a customer uses to describe themselves lives here: the profile picture, billing addresses, linked social accounts and notification preferences.

The address side carries four hooks: the save gate and its event, the default changing, and deletion. The default address is the one printed on invoices.

Reference

Following a profile picture change

actionuser.avatar_changed
AccountUsers two branches

Runs when a customer uploads or removes a profile picture.

Parameters 3
$uidintThe account owner.
$actionstringWhat happened: set uploaded, removed taken away.
$picturestringThe path of the new picture. On the removal branch it is always empty: check the action before using the path.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:user.avatar_changed', 10, function ($uid, $action, $picture) {
    // On the removal branch the path arrives EMPTY.
    if ($action === 'removed') { Acme::dropAvatar($uid); return; }

    Acme::mirrorAvatar($uid, $picture);
});

Stopping a contact being saved

gateuser.contact_save
AccountUsers zero means new

Runs before a customer saves a contact address.

Parameters 3
$uidintThe account the address will belong to.
$dataarrayThe address data: name, email, phone, country and tax details.
$idintThe id being edited; a zero means a new one is being added.
Return 1
string|nullA non-empty text blocks the operation and is shown as the error. An empty return lets it carry on.
Listener
Hook::add('gate:user.contact_save', 10, function ($uid, $data, $id) {
    // A zero id means a new record.
    if (!Acme::taxNumberValid($data)) return 'The tax details did not verify.';

    return null;
});

Following a contact being saved

actionuser.contact_saved
AccountUsers create and edit

Runs after a contact address is saved.

Parameters 4
$uidintThe owner of the address.
$saved_idintThe id of the saved address.
$dataarrayThe address data.
$is_newboolTrue when it was newly added.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:user.contact_saved', 10, function ($uid, $saved_id, $data, $is_new) {
    Acme::syncContact($uid, $saved_id, $data);
});

Following the default contact changing

actionuser.contact_default_changed
AccountUsers default changed

Runs when an address becomes the default. The default is the address printed on invoices.

Parameters 2
$uidintThe owner of the address.
$idintThe address made default.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:user.contact_default_changed', 10, function ($uid, $id) {
    // The default address is the one printed on invoices.
    Acme::syncBillingAddress($uid, $id);
});

Following a contact being deleted

actionuser.contact_deleted
AccountUsers after deletion

Runs after a contact address is deleted.

Parameters 3
$uidintThe owner of the address.
$idintThe id deleted; the row is gone.
$existingarrayThe full address as read immediately before deletion.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:user.contact_deleted', 10, function ($uid, $id, $existing) {
    Acme::dropContact($uid, $id);
});

Following a social account being linked

actionuser.connected_provider
SocialAuth account linking

Runs when an account is linked to an outside provider.

Parameters 2
$userobjectThe matched user record.
$mod_namestringThe name of the provider linked.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:user.connected_provider', 10, function ($user, $mod_name) {
    Acme::noteLink((int) ($user->id ?? 0), $mod_name);
});

Following notification preferences changing

actionuser.notification_preferences_changed
AccountUsers per channel

Runs when a customer changes which notifications they receive.

Parameters 2
$uidintThe account whose preferences changed.
$prefsarrayWhat was written, per channel. The keys are deliberately the database column names, so which layer changed is never in doubt.
Return 1
voidThe return is ignored. The preferences are already written.
Listener
Hook::add('action:user.notification_preferences_changed', 10, function ($uid, $prefs) {
    Acme::syncPreferences($uid, $prefs);
});

Producing the text of an activity record

filteruser.action_text
User::addAction only when empty

Runs while the visible text of an account activity is resolved. Produce the text for your own activity keys here.

Parameters 2
$textstringThe text the core found. The hook runs only when no text was found, so this is always empty. It is passed for signature consistency.
$ctxarrayContext: the activity key, the language asked for and the placeholder values.
Return 1
string|nullReturn the text you produced. Return nothing for keys that are not yours.
Listener
Hook::add('filter:user.action_text', 10, function ($text, $ctx) {
    // The hook runs only when no text was found.
    if (!str_starts_with($ctx['key'] ?? '', 'acme.')) return null;

    return Acme::actionText($ctx['key'], $ctx['lang'] ?? '', $ctx['variables'] ?? []);
});

Pitfalls

The picture path is empty on the removal branch

The profile picture hook fires for uploads and removals alike, and on removal the path field always arrives empty. A listener using the path directly then works with nothing on that branch.

The default address is the one on the invoice

Changing the default is more than a preference: the invoices that follow carry that address. If you keep an accounting side in step, listen on this hook too.

Was this helpful?

Thanks for your feedback!

Still Need Help?

Our support team is here around the clock for anything you can't find above.