Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Universal accessors and mutators in Laravel 4

Tags:

php

laravel

I know it's possible to define accessors and mutators for individual fields, like so:

public function setSomeAttribute($value) {
    // set the attribute
}
public function getSomeAttribute() {
    // return the attribute}
}

But is it possible to define a fallback method that will be used for all attribute gets and sets?

The reason being that I want to convert any empty values to null values on the fly, to keep my database clean and allow nullable fields to be null instead of an empty string (if there's a better way to do this let me know!)

I'm looking for something like

public function setAttribute($property,$value) {
    $this->$property = empty($value) ? null : $value;
}

UPDATE:

Thanks to Chris Goosey I've found a solution that works for me. I extended the Eloquent model method setAttribute, and I set the value to the column default if it's empty. That's usually null, zero or an empty string in my databases so works for me!

public function setAttribute($key, $value)
{
    // Convert empty values to their default values (e.g. null, zero)
    if(empty($value) && $this->getSchema()->hasColumn($key)) {
        $value = $this->getSchema()->getColumn($key)->getDefault();
    }
    parent::setAttribute($key,$value);
}
like image 921
tprsn Avatar asked Mar 18 '14 14:03

tprsn


People also ask

What are mutators and accessors in Laravel?

Accessors and mutators allow you to format Eloquent attributes when retrieving them from a model or setting their value. For example, you may want to use the Laravel encrypter to encrypt a value while it is stored in the database, and then automatically decrypt the attribute when you access it on an Eloquent model.

What are accessors and mutators explain with code snippets in Laravel?

Laravel accessors and mutators are custom, user defined methods that allow you to format Eloquent attributes. Accessors are used to format attributes when you retrieve them from the database, while mutators format the attributes before saving them to the database.

What is the use of $casts in Laravel?

Casting a value means changing it to (or ensuring it is already) a particular type. Some types you might be familiar with are Integer or Boolean . Simply put, type casting is a method of changing an entity from one data type to another.

How do you define a mutator in Laravel?

Defining A Mutator The mutator closure will receive the value that is being set on the attribute, allowing you to manipulate the value and return the manipulated value. To use our mutator, we only need to set the first_name attribute on an Eloquent model: use App\Models\User; $user = User::find(1);


1 Answers

Best way is probably to extend the Eloquent class overwriting the setAttribute and getAttribute methods.

For all your models to inherit these overwritten methods you would want to create a class that extends eloquent e.g.

<?php 

class BaseModel extends eloquent {

    public function setAttribute($property,$value) {
        $this->$property = empty($value) ? null : $value;
    }

    public function getAttribute($key) {
        // Do Stuff
    }
}

and then all your models should extend from this new class, e.g.

<?php

class User extends BaseModel {
    protected $table = 'users';
}

It is also worth mentioning your new methods should have all the functionality of the old method plus your new functionality, this is what the getAttribute() looks like (Illuminate\Database\Eloquent line 2212):

/**
 * Get an attribute from the model.
 *
 * @param  string  $key
 * @return mixed
 */
public function getAttribute($key)
{
    $inAttributes = array_key_exists($key, $this->attributes);

    // If the key references an attribute, we can just go ahead and return the
    // plain attribute value from the model. This allows every attribute to
    // be dynamically accessed through the _get method without accessors.
    if ($inAttributes || $this->hasGetMutator($key))
    {
        return $this->getAttributeValue($key);
    }

    // If the key already exists in the relationships array, it just means the
    // relationship has already been loaded, so we'll just return it out of
    // here because there is no need to query within the relations twice.
    if (array_key_exists($key, $this->relations))
    {
        return $this->relations[$key];
    }

    // If the "attribute" exists as a method on the model, we will just assume
    // it is a relationship and will load and return results from the query
    // and hydrate the relationship's value on the "relationships" array.
    $camelKey = camel_case($key);

    if (method_exists($this, $camelKey))
    {
        return $this->getRelationshipFromMethod($key, $camelKey);
    }
}

and the setAttribute in the same file looks like this (line 2338):

/**
 * Set a given attribute on the model.
 *
 * @param  string  $key
 * @param  mixed   $value
 * @return void
 */
public function setAttribute($key, $value)
{
    // First we will check for the presence of a mutator for the set operation
    // which simply lets the developers tweak the attribute as it is set on
    // the model, such as "json_encoding" an listing of data for storage.
    if ($this->hasSetMutator($key))
    {
        $method = 'set'.studly_case($key).'Attribute';

        return $this->{$method}($value);
    }

    // If an attribute is listed as a "date", we'll convert it from a DateTime
    // instance into a form proper for storage on the database tables using
    // the connection grammar's date format. We will auto set the values.
    elseif (in_array($key, $this->getDates()))
    {
        if ($value)
        {
            $value = $this->fromDateTime($value);
        }
    }

    $this->attributes[$key] = $value;
}

Hope this helps!

like image 187
Chris Goosey Avatar answered Oct 07 '22 17:10

Chris Goosey