Skip to content

Accessors and Mutators

Accessors and mutators allow you to transform data when retrieving from or storing to the database. They provide a clean way to format data consistently across your application.

Accessors

Accessors transform data when retrieving from the database. They are called automatically when you access a model attribute.

Basic Accessors

php
class User extends Model
{
    // Get first name from full name
    public function getFirstNameAttribute()
    {
        return explode(' ', $this->name)[0];
    }

    // Format email as lowercase
    public function getEmailAttribute($value)
    {
        return strtolower($value);
    }

    // Get full URL for avatar
    public function getAvatarUrlAttribute()
    {
        if ($this->avatar) {
            return wp_upload_dir()['baseurl'] . '/avatars/' . $this->avatar;
        }
        return get_avatar_url($this->email);
    }

    // WordPress integration
    public function getGravatarAttribute()
    {
        return get_avatar_url($this->email, 80);
    }
}

Using Accessors

php
$user = User::find(1);

echo $user->first_name; // Calls getFirstNameAttribute()
echo $user->avatar_url; // Calls getAvatarUrlAttribute()
echo $user->gravatar; // WordPress gravatar URL

// Original database value
echo $user->getRawOriginal('name'); // Original name without transformation

Modern Accessor Syntax

Using the Attribute class for more complex transformations:

php
use AvelPress\Database\Eloquent\Casts\Attribute;

class User extends Model
{
    protected function firstName(): Attribute
    {
        return Attribute::make(
            get: fn ($value, $attributes) => explode(' ', $attributes['name'])[0],
        );
    }

    protected function fullName(): Attribute
    {
        return Attribute::make(
            get: fn ($value, $attributes) => $attributes['first_name'] . ' ' . $attributes['last_name'],
        );
    }

    protected function avatarUrl(): Attribute
    {
        return Attribute::make(
            get: function ($value, $attributes) {
                if ($attributes['avatar']) {
                    return wp_upload_dir()['baseurl'] . '/avatars/' . $attributes['avatar'];
                }
                return get_avatar_url($attributes['email']);
            }
        );
    }
}

Mutators

Mutators transform data when saving to the database. They are called automatically when you set a model attribute.

Basic Mutators

php
class User extends Model
{
    // Hash password automatically
    public function setPasswordAttribute($value)
    {
        $this->attributes['password'] = wp_hash_password($value);
    }

    // Store email as lowercase
    public function setEmailAttribute($value)
    {
        $this->attributes['email'] = strtolower(trim($value));
    }

    // Auto-generate slug from title
    public function setTitleAttribute($value)
    {
        $this->attributes['title'] = $value;
        $this->attributes['slug'] = sanitize_title($value);
    }

    // WordPress meta integration
    public function setBioAttribute($value)
    {
        $this->attributes['bio'] = wp_kses_post($value);
    }
}

Using Mutators

php
$user = new User;
$user->password = 'plaintext'; // Automatically hashed
$user->email = 'JOHN@EXAMPLE.COM'; // Stored as lowercase
$user->title = 'My Blog Post'; // Generates slug automatically
$user->save();

Modern Mutator Syntax

php
use AvelPress\Database\Eloquent\Casts\Attribute;

class User extends Model
{
    protected function password(): Attribute
    {
        return Attribute::make(
            set: fn ($value) => wp_hash_password($value),
        );
    }

    protected function email(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => strtolower($value),
            set: fn ($value) => strtolower(trim($value)),
        );
    }

    protected function slug(): Attribute
    {
        return Attribute::make(
            set: fn ($value) => sanitize_title($value),
        );
    }
}

Combined Accessors and Mutators

Handle both getting and setting in a single method:

php
class Product extends Model
{
    protected function price(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => $value / 100, // Store in cents, return in dollars
            set: fn ($value) => $value * 100, // Convert dollars to cents for storage
        );
    }

    protected function description(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => ucfirst($value),
            set: fn ($value) => strtolower(trim($value)),
        );
    }
}

Appending Accessors to JSON

Make accessors available when converting models to arrays or JSON:

php
class User extends Model
{
    // Append these accessors to array/JSON output
    protected $appends = ['first_name', 'avatar_url', 'gravatar'];

    public function getFirstNameAttribute()
    {
        return explode(' ', $this->name)[0];
    }

    public function getAvatarUrlAttribute()
    {
        if ($this->avatar) {
            return wp_upload_dir()['baseurl'] . '/avatars/' . $this->avatar;
        }
        return null;
    }

    public function getGravatarAttribute()
    {
        return get_avatar_url($this->email, 80);
    }
}
php
$user = User::find(1);
$array = $user->toArray();
// Will include 'first_name', 'avatar_url', and 'gravatar'

$json = $user->toJson();
// JSON will include the appended attributes

WordPress-Specific Examples

User Meta Integration

php
class User extends Model
{
    // WordPress user meta accessor
    public function getWpMetaAttribute()
    {
        if ($this->wp_user_id) {
            return get_user_meta($this->wp_user_id);
        }
        return [];
    }

    // Specific meta fields
    public function getDisplayNameAttribute()
    {
        if ($this->wp_user_id) {
            return get_user_meta($this->wp_user_id, 'display_name', true) ?: $this->name;
        }
        return $this->name;
    }

    // WordPress capabilities
    public function getCapabilitiesAttribute()
    {
        if ($this->wp_user_id) {
            $wp_user = get_user_by('id', $this->wp_user_id);
            return $wp_user ? $wp_user->allcaps : [];
        }
        return [];
    }

    // Set WordPress meta when updating
    public function setDisplayNameAttribute($value)
    {
        $this->attributes['display_name'] = $value;
        
        if ($this->wp_user_id) {
            update_user_meta($this->wp_user_id, 'display_name', $value);
        }
    }
}

Post Meta Integration

php
class Post extends Model
{
    // WordPress post meta
    public function getMetaAttribute()
    {
        if ($this->wp_post_id) {
            return get_post_meta($this->wp_post_id);
        }
        return [];
    }

    // Featured image
    public function getFeaturedImageAttribute()
    {
        if ($this->wp_post_id) {
            $attachment_id = get_post_thumbnail_id($this->wp_post_id);
            return $attachment_id ? wp_get_attachment_image_url($attachment_id, 'full') : null;
        }
        return null;
    }

    // WordPress permalink
    public function getPermalinkAttribute()
    {
        if ($this->wp_post_id) {
            return get_permalink($this->wp_post_id);
        }
        return null;
    }

    // WordPress excerpt
    public function getExcerptAttribute()
    {
        if ($this->wp_post_id) {
            $wp_post = get_post($this->wp_post_id);
            return $wp_post ? wp_trim_excerpt('', $wp_post) : null;
        }
        return null;
    }

    // Set featured image
    public function setFeaturedImageAttribute($attachment_id)
    {
        if ($this->wp_post_id && $attachment_id) {
            set_post_thumbnail($this->wp_post_id, $attachment_id);
        }
    }
}

Content Sanitization

php
class Post extends Model
{
    // Sanitize content for WordPress
    public function setContentAttribute($value)
    {
        $this->attributes['content'] = wp_kses_post($value);
    }

    // Auto-generate excerpt
    public function setContentAttribute($value)
    {
        $this->attributes['content'] = wp_kses_post($value);
        
        // Generate excerpt if not provided
        if (!$this->excerpt) {
            $this->attributes['excerpt'] = wp_trim_excerpt($value);
        }
    }

    // Format title
    public function setTitleAttribute($value)
    {
        $this->attributes['title'] = sanitize_text_field($value);
        
        // Auto-generate slug
        if (!$this->slug) {
            $this->attributes['slug'] = sanitize_title($value);
        }
    }
}

Date and Time Formatting

php
class Post extends Model
{
    // Format published date
    public function getPublishedDateAttribute()
    {
        return $this->published_at ? $this->published_at->format('F j, Y') : null;
    }

    // Time ago format
    public function getTimeAgoAttribute()
    {
        if ($this->created_at) {
            return human_time_diff($this->created_at->timestamp, current_time('timestamp'));
        }
        return null;
    }

    // WordPress date format
    public function getWpDateAttribute()
    {
        if ($this->created_at) {
            return date_i18n(get_option('date_format'), $this->created_at->timestamp);
        }
        return null;
    }

    // WordPress time format
    public function getWpTimeAttribute()
    {
        if ($this->created_at) {
            return date_i18n(get_option('time_format'), $this->created_at->timestamp);
        }
        return null;
    }
}

URL and Path Handling

php
class Media extends Model
{
    // Get full URL
    public function getUrlAttribute()
    {
        if ($this->filename) {
            $upload_dir = wp_upload_dir();
            return $upload_dir['baseurl'] . '/' . $this->filename;
        }
        return null;
    }

    // Get file path
    public function getPathAttribute()
    {
        if ($this->filename) {
            $upload_dir = wp_upload_dir();
            return $upload_dir['basedir'] . '/' . $this->filename;
        }
        return null;
    }

    // File size in human readable format
    public function getFileSizeFormattedAttribute()
    {
        if ($this->file_size) {
            return size_format($this->file_size);
        }
        return null;
    }

    // Set filename with sanitization
    public function setFilenameAttribute($value)
    {
        $this->attributes['filename'] = sanitize_file_name($value);
    }
}

Data Validation and Transformation

php
class Product extends Model
{
    // Validate and format price
    public function setPriceAttribute($value)
    {
        // Convert to float and ensure positive
        $price = max(0, floatval($value));
        $this->attributes['price'] = $price;
    }

    // Format price for display
    public function getFormattedPriceAttribute()
    {
        return '$' . number_format($this->price, 2);
    }

    // Validate and set SKU
    public function setSkuAttribute($value)
    {
        $sku = strtoupper(trim($value));
        
        // Validate SKU format
        if (!preg_match('/^[A-Z0-9-]+$/', $sku)) {
            throw new \InvalidArgumentException('Invalid SKU format');
        }
        
        $this->attributes['sku'] = $sku;
    }

    // Format weight
    public function getWeightFormattedAttribute()
    {
        if ($this->weight) {
            return $this->weight . ' kg';
        }
        return null;
    }
}

Performance Considerations

Caching Expensive Accessors

php
class User extends Model
{
    protected $accessorCache = [];

    public function getExpensiveDataAttribute()
    {
        if (!isset($this->accessorCache['expensive_data'])) {
            // Expensive computation or API call
            $this->accessorCache['expensive_data'] = $this->computeExpensiveData();
        }
        
        return $this->accessorCache['expensive_data'];
    }

    private function computeExpensiveData()
    {
        // Expensive operation
        return wp_remote_get('https://api.example.com/data/' . $this->id);
    }
}

Conditional Accessors

php
class Post extends Model
{
    public function getFeaturedImageAttribute()
    {
        // Only fetch if we actually have a WordPress post ID
        if (!$this->wp_post_id) {
            return null;
        }

        // Cache the result to avoid multiple calls
        if (!isset($this->accessorCache['featured_image'])) {
            $attachment_id = get_post_thumbnail_id($this->wp_post_id);
            $this->accessorCache['featured_image'] = $attachment_id 
                ? wp_get_attachment_image_url($attachment_id, 'full') 
                : null;
        }

        return $this->accessorCache['featured_image'];
    }
}

Testing Accessors and Mutators

php
// In your tests
public function test_email_mutator_converts_to_lowercase()
{
    $user = new User();
    $user->email = 'JOHN@EXAMPLE.COM';
    
    $this->assertEquals('john@example.com', $user->email);
}

public function test_first_name_accessor_returns_first_part()
{
    $user = new User(['name' => 'John Doe Smith']);
    
    $this->assertEquals('John', $user->first_name);
}

public function test_password_mutator_hashes_password()
{
    $user = new User();
    $user->password = 'plaintext';
    
    $this->assertTrue(wp_check_password('plaintext', $user->getAttributes()['password']));
}

Released under the MIT License.