Save contact form data to database
Stripping this back, your basic problem is saving data to the database from an unreliable source. This is a fundamentally insecure operation—we've got no idea if this user is genuine or attempting something malicious. That's why the underlying Yii framework is trying to protect your install of Craft, and which is the reason you're seeing this ‘buggy’ behaviour with setAttributes().

The Yii framework implements a ‘safe’ validator. A key point made in this helpful article is this: 


  Massive Assignment will only be made for fields which have passed
  some explicit validation rule.


This means that any attributes you define without an explicit validation rule will be considered unsafe. This will be the reason that only your email field was saving—it has an explicit validation rule (i.e. an email pattern).

How can you resolve this, then?

Set attributes explicitly

Firstly, you could explicitly set each attribute like you have been doing. Since this is not a massive assignment, the data is treated as safe and will save OK. Both $record-&gt;setAttribute() and $record-&gt;attribute() will work for this.

Define attributes with explicit validation

Another solution is to define explicit validation rules, and will allow massive assignment on these attributes, but explicit rules only really apply for some data types. What if you want a simple string?

Make existing attributes safe

A solution could be to add the safe rule to your attribute definition. If you follow the definition of attributes down the rabbit hole, you'll find that attribute configs are normalised by ModelHelper::normalizeAttributeConfig() (and subsequently ModelHelper::getRules()) which does not cater for this. Now where does that leave us?

It's possible to override (and extend) the inherited rules() method. Assuming you wanted to make all attributes safe and valid for massive assignment, I reckon something like this would work.

public function rules()
{
    // Are there any existing rules?
    $rules = parent::rules();

    // Make all attributes safe
    $rules[] = array(implode(',', array_keys($this-&gt;getAttributes())), 'safe');

    // Send it on outta here
    return $rules;
}


You should then be able to massively set attributes with $record-&gt;setAttributes().