Nitish Kumar
Nitish Kumar

Reputation: 6276

Extending accessor and mutators in Laravel

I'm trying to develop a small application in Laravel 5.5 where I've made an abstract model which is extending Laravel eloquent model something like this:

<?php

use Illuminate\Database\Eloquent\Model;

class AbstractModel extends Model
{


}

Now just for general convention I'm setting a database connection for this model:

<?php

use Illuminate\Database\Eloquent\Model;

class AbstractModel extends Model
{
    /**
     *  Defining connection for database
     *
     * @var string
     **/
    protected $connection='mysql';

}

And I created my own model which extends this abstract class:

<?php

use Illuminate\Database\Eloquent\SoftDeletes;

class Posts extends AbstractModel
{
    use SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'unique_id', 'title', 'contents'
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'id'
    ];

}

By defining this by default we come to know that it will have a database connection of mysql, similarly fillable are defined for mass assignment. In similar way I want to make one encrypt variable which will encrypt and decrypt the data from the database so for this I added accessor and mutator to my encrypt variable inside my model:

<?php

use Illuminate\Database\Eloquent\SoftDeletes;

class Posts extends AbstractModel
{
    use SoftDeletes;

    /**
     * The attributes that will have encryption.
     *
     * @var array
     */
    protected $encryption = ['unique_id', 'title', 'contents'];

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'unique_id', 'title', 'contents'
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'id'
    ];

}

and configured it back in my abstract model:

<?php

use Illuminate\Database\Eloquent\Model;

class AbstractModel extends Model
{
    /**
     *  Defining connection for database
     *
     * @var string
     **/
    protected $connection='mysql';

    /**
     *  Defining encryption
     *
     * @var array
     **/
    protected $encryption = [];

    public function setAttributes()
    {
        foreach ($this->encryption as $attributes)
        {
            $this->attributes[$attributes] = Crypt::encryptString($this->attributes[$attributes]);
        }
    }

    public function getAttributes()
    {
        foreach ($this->encryption as $attributes)
        {
            $this->attributes[$attributes] = Crypt::decryptString($this->attributes[$attributes]);
        }
    }
}

I think my approach is wrong as there is no encryption or decryption done on these attributes, I can see the data sent to create rows is stored in database without any encryption and in same way retrieved also.

More over I would like to have a use case where I'm encrypting these fields also their attribute accessor or mutator is called inside their own model for example:

<?php

use Illuminate\Database\Eloquent\SoftDeletes;

class Posts extends AbstractModel
{
    use SoftDeletes;

    /**
     * The attributes that will have encryption.
     *
     * @var array
     */
    protected $encryption = ['unique_id', 'title', 'contents'];

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'unique_id', 'title', 'contents'
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'id'
    ];

    public function setTitleAttributes($value)
    {
        return ucfirst($value);
    }

    public function getTitleAttributes($value)
    {
        return ucfirst($value)
    }

}

Any feedback comments are welcome. Thanks.

Upvotes: 0

Views: 1764

Answers (1)

Risan Bagja Pradana
Risan Bagja Pradana

Reputation: 4674

First, the Eloquent model has setAttribute() & getAttribute() methods not setAttributes() and getAttributes().

Secondly, for this kind of situation. I'd rather use the Eloquent's event. Because the setAttribute() or getAttribute() methods will only be fired when you access the attribute dynamically like: $post->title.

You can encrypt the attributes on saving event. And decrypt it back using retrieved event.

<?php

namespace App;

use Illuminate\Support\Facades\Crypt;
use Illuminate\Database\Eloquent\Model;

abstract class AbstractModel extends Model
{
    protected $connection = 'mysql';

    protected $encryption = [];

    protected static function boot()
    {
        parent::boot();

        // When being retrieved, decrypt the attributes.
        static::retrieved(function ($instance) {
            foreach ($instance->encryption as $encryptedKey) {
                $instance->attributes[$encryptedKey] = Crypt::decryptString($instance->attributes[$encryptedKey]);
            }
        });

        // When saving (could be create or update) the modal, encrypt the attributes.
        static::saving(function ($instance) {
            foreach ($instance->encryption as $encryptedKey) {
                $instance->attributes[$encryptedKey] = Crypt::encryptString($instance->attributes[$encryptedKey]);
            }
        });

        // Once it's saved, decrypt it back so it's still readble.
        static::saved(function ($instance) {
            foreach ($instance->encryption as $encryptedKey) {
                $instance->attributes[$encryptedKey] = Crypt::decryptString($instance->attributes[$encryptedKey]);
            }
        });
    }
}

When you decided to use this approach, don't override both the setAttribute() & getAttribute() methods. Since it will most likely to double encrypt/decrypt the attributes.

Also, it supposed to be setTitleAttribute() and getTitleAttribute()—in singular form. And on the setTitleAttribute() you should mutate the title attribute; not returning the intended value. I don't see the purpose of capitalizing the title either since it will be encrypted anyway.

<?php

namespace App;

class Post extends Model
{
    protected $encryption = ['unique_id', 'title', 'contents'];

    protected $fillable = ['unique_id', 'title', 'contents'];

    // Expected to mutate the title attribute.
    public function setTitleAttribute($value)
    {
        $this->attributes['title'] = ucfirst($value);
    }

    public function getTitleAttribute($value)
    {
        return ucfirst($value);
    }
}

Hope this gives you some ideas.

Upvotes: 2

Related Questions