arraypress/wp-register-user-fields

Lightweight library for registering custom fields on WordPress user profile screens.

Installs: 8

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/arraypress/wp-register-user-fields

dev-main 2025-12-03 21:24 UTC

This package is auto-updated.

Last update: 2025-12-04 21:02:54 UTC


README

A lightweight library for registering custom fields on WordPress user profile and Add New User screens. This library provides a clean, simple API for adding common field types to user profiles without JavaScript dependencies or complex configuration.

Features

  • Simple API: Register custom user fields with minimal code
  • Multiple Field Types: Text, textarea, number, select, checkbox, URL, and email
  • Automatic Saving: Fields are automatically saved to user meta
  • Smart Sanitization: Each field type has appropriate default sanitization
  • Dynamic Options: Select fields support callable options for dynamic data
  • Section Grouping: Group related fields under custom section headings
  • Add New User Support: Fields appear on the Add New User screen
  • Permission Control: Control field visibility based on user capabilities
  • No JavaScript: Pure PHP implementation with WordPress-native markup
  • Lightweight: Single class, minimal footprint

Requirements

  • PHP 7.4 or higher
  • WordPress 5.0 or higher

Installation

Install via Composer:

composer require arraypress/wp-register-user-fields

Basic Usage

Simple Text Fields

register_user_fields( [
    'company' => [
        'label'       => __( 'Company', 'textdomain' ),
        'type'        => 'text',
        'description' => __( 'Your company or organization name.', 'textdomain' ),
    ],
    'job_title' => [
        'label'       => __( 'Job Title', 'textdomain' ),
        'type'        => 'text',
        'placeholder' => __( 'e.g., Senior Developer', 'textdomain' ),
    ],
] );

Custom Section Title

register_user_fields( [
    'department' => [
        'label'   => __( 'Department', 'textdomain' ),
        'type'    => 'select',
        'options' => [
            ''          => __( '— Select —', 'textdomain' ),
            'sales'     => __( 'Sales', 'textdomain' ),
            'support'   => __( 'Support', 'textdomain' ),
            'dev'       => __( 'Development', 'textdomain' ),
            'marketing' => __( 'Marketing', 'textdomain' ),
        ],
    ],
    'employee_id' => [
        'label' => __( 'Employee ID', 'textdomain' ),
        'type'  => 'text',
    ],
], __( 'Employee Information', 'textdomain' ) );

Multiple Field Groups

Register multiple groups with different section titles:

// Contact information
register_user_fields( [
    'phone' => [
        'label'       => __( 'Phone', 'textdomain' ),
        'type'        => 'text',
        'placeholder' => '+1 (555) 000-0000',
    ],
    'extension' => [
        'label' => __( 'Extension', 'textdomain' ),
        'type'  => 'number',
        'min'   => 100,
        'max'   => 9999,
    ],
], __( 'Contact Information', 'textdomain' ) );

// Social profiles
register_user_fields( [
    'linkedin' => [
        'label'       => __( 'LinkedIn URL', 'textdomain' ),
        'type'        => 'url',
        'placeholder' => 'https://linkedin.com/in/username',
    ],
    'twitter' => [
        'label'       => __( 'Twitter/X Handle', 'textdomain' ),
        'type'        => 'text',
        'placeholder' => '@username',
    ],
], __( 'Social Profiles', 'textdomain' ) );

Select with Dynamic Options

register_user_fields( [
    'manager_id' => [
        'label'       => __( 'Reports To', 'textdomain' ),
        'type'        => 'select',
        'description' => __( 'Select your direct manager.', 'textdomain' ),
        'options'     => function() {
            $managers = get_users( [
                'role__in' => [ 'administrator', 'editor' ],
                'orderby'  => 'display_name',
            ] );
            
            $options = [ '' => __( '— Select Manager —', 'textdomain' ) ];
            
            foreach ( $managers as $manager ) {
                $options[ $manager->ID ] = $manager->display_name;
            }
            
            return $options;
        },
    ],
] );

Field Types

Text

Standard single-line text input.

'field_name' => [
    'label'       => __( 'Field Label', 'textdomain' ),
    'type'        => 'text',
    'placeholder' => __( 'Placeholder text...', 'textdomain' ),
    'default'     => '',
]

Textarea

Multi-line text input.

'bio' => [
    'label'       => __( 'Biography', 'textdomain' ),
    'type'        => 'textarea',
    'rows'        => 5,
    'placeholder' => __( 'Tell us about yourself...', 'textdomain' ),
]

Number

Numeric input with optional constraints.

'years_experience' => [
    'label'   => __( 'Years of Experience', 'textdomain' ),
    'type'    => 'number',
    'min'     => 0,
    'max'     => 50,
    'step'    => 1,
    'default' => 0,
]

For decimal values, set a decimal step:

'hourly_rate' => [
    'label' => __( 'Hourly Rate', 'textdomain' ),
    'type'  => 'number',
    'min'   => 0,
    'step'  => 0.01,
]

Select

Dropdown selection with static or dynamic options.

// Static options
'status' => [
    'label'   => __( 'Status', 'textdomain' ),
    'type'    => 'select',
    'default' => 'active',
    'options' => [
        'active'   => __( 'Active', 'textdomain' ),
        'inactive' => __( 'Inactive', 'textdomain' ),
        'pending'  => __( 'Pending', 'textdomain' ),
    ],
]

// Dynamic options via callback
'location' => [
    'label'   => __( 'Office Location', 'textdomain' ),
    'type'    => 'select',
    'options' => function() {
        // Could query from database, API, etc.
        return [
            ''       => __( '— Select —', 'textdomain' ),
            'nyc'    => __( 'New York', 'textdomain' ),
            'london' => __( 'London', 'textdomain' ),
            'tokyo'  => __( 'Tokyo', 'textdomain' ),
        ];
    },
]

Checkbox

Boolean toggle field.

'subscribe_newsletter' => [
    'label'       => __( 'Subscribe to Newsletter', 'textdomain' ),
    'type'        => 'checkbox',
    'default'     => 0,
    'description' => __( 'Receive weekly updates via email.', 'textdomain' ),
]

URL

Text input with URL validation.

'website' => [
    'label'       => __( 'Personal Website', 'textdomain' ),
    'type'        => 'url',
    'placeholder' => 'https://example.com',
]

Email

Text input with email validation.

'alternate_email' => [
    'label'       => __( 'Alternate Email', 'textdomain' ),
    'type'        => 'email',
    'placeholder' => 'alternate@example.com',
    'description' => __( 'Secondary contact email address.', 'textdomain' ),
]

Field Configuration Options

Each field accepts the following configuration options:

Option Type Default Description
label string '' Field label text
type string 'text' Field type
description string '' Help text displayed below the field
default mixed '' Default value for new users
placeholder string '' Placeholder text for text inputs
options array|callable [] Options for select fields
min int|float null Minimum value for number fields
max int|float null Maximum value for number fields
step int|float null Step increment for number fields
rows int 5 Number of rows for textarea fields
sanitize_callback callable null Custom sanitization function
capability string 'edit_users' Required capability to view/edit the field
show_in_admin bool true Show field when editing other users
show_in_profile bool true Show field on own profile

Retrieving Field Values

Standard Method

Use WordPress's built-in function:

$company = get_user_meta( $user_id, 'company', true );

With Default Fallback

Use the helper function to automatically fall back to registered defaults:

$department = get_user_field_value( $user_id, 'department' );

Get All Registered Fields

$all_fields = get_all_user_fields();

foreach ( $all_fields as $group_id => $fields ) {
    foreach ( $fields as $meta_key => $config ) {
        echo $config['label'] . ': ' . get_user_meta( $user_id, $meta_key, true );
    }
}

Get Field Configuration

$config = get_user_field_config( 'department' );

if ( $config ) {
    echo 'Label: ' . $config['label'];
    echo 'Type: ' . $config['type'];
}

Custom Sanitization

Override the default sanitization for any field:

register_user_fields( [
    'custom_html' => [
        'label'             => __( 'Custom Bio', 'textdomain' ),
        'type'              => 'textarea',
        'sanitize_callback' => function( $value ) {
            return wp_kses( $value, [
                'p'      => [],
                'br'     => [],
                'strong' => [],
                'em'     => [],
                'a'      => [ 'href' => [], 'title' => [] ],
            ] );
        },
    ],
] );

Permission Control

Control field visibility based on user capabilities:

register_user_fields( [
    'salary' => [
        'label'       => __( 'Salary', 'textdomain' ),
        'type'        => 'number',
        'capability'  => 'manage_options', // Only administrators
        'description' => __( 'Annual salary (admin only).', 'textdomain' ),
    ],
    'notes' => [
        'label'      => __( 'HR Notes', 'textdomain' ),
        'type'       => 'textarea',
        'capability' => 'edit_users',
        'show_in_profile' => false, // Don't show to user on their own profile
    ],
] );

Complete Example

// Employee information
register_user_fields( [
    'employee_id' => [
        'label'       => __( 'Employee ID', 'textdomain' ),
        'type'        => 'text',
        'placeholder' => 'EMP-0000',
    ],
    'department' => [
        'label'   => __( 'Department', 'textdomain' ),
        'type'    => 'select',
        'options' => [
            ''          => __( '— Select —', 'textdomain' ),
            'sales'     => __( 'Sales', 'textdomain' ),
            'support'   => __( 'Support', 'textdomain' ),
            'dev'       => __( 'Development', 'textdomain' ),
            'hr'        => __( 'Human Resources', 'textdomain' ),
        ],
    ],
    'start_date' => [
        'label'       => __( 'Start Date', 'textdomain' ),
        'type'        => 'text',
        'placeholder' => 'YYYY-MM-DD',
    ],
    'manager_id' => [
        'label'   => __( 'Manager', 'textdomain' ),
        'type'    => 'select',
        'options' => function() {
            $users   = get_users( [ 'role' => 'administrator' ] );
            $options = [ '' => __( '— None —', 'textdomain' ) ];
            foreach ( $users as $user ) {
                $options[ $user->ID ] = $user->display_name;
            }
            return $options;
        },
    ],
    'remote_worker' => [
        'label'       => __( 'Remote Worker', 'textdomain' ),
        'type'        => 'checkbox',
        'description' => __( 'This employee works remotely.', 'textdomain' ),
    ],
], __( 'Employee Information', 'textdomain' ) );

// Contact details
register_user_fields( [
    'phone' => [
        'label'       => __( 'Phone', 'textdomain' ),
        'type'        => 'text',
        'placeholder' => '+1 (555) 000-0000',
    ],
    'extension' => [
        'label' => __( 'Extension', 'textdomain' ),
        'type'  => 'number',
        'min'   => 100,
        'max'   => 9999,
    ],
    'slack_handle' => [
        'label'       => __( 'Slack Handle', 'textdomain' ),
        'type'        => 'text',
        'placeholder' => '@username',
    ],
], __( 'Contact Information', 'textdomain' ) );

// Usage in templates
$user_id     = get_current_user_id();
$department  = get_user_field_value( $user_id, 'department' );
$is_remote   = get_user_meta( $user_id, 'remote_worker', true );
$employee_id = get_user_meta( $user_id, 'employee_id', true );

Integration with Register Columns

This library pairs well with wp-register-columns to display user field values in the admin users list:

// Register the field
register_user_fields( [
    'department' => [
        'label'   => __( 'Department', 'textdomain' ),
        'type'    => 'select',
        'options' => [
            'sales'   => 'Sales',
            'support' => 'Support',
            'dev'     => 'Development',
        ],
    ],
] );

// Display in users list table
register_user_columns( [
    'department' => [
        'label'            => __( 'Department', 'textdomain' ),
        'meta_key'         => 'department',
        'sortable'         => true,
        'display_callback' => function( $value, $user_id ) {
            return ucfirst( $value ) ?: '';
        },
    ],
] );

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

GPL-2.0-or-later

Author

David Sherlock - ArrayPress

Support