I'm relatively new to Drupal so please forgive me if I'm completely off base here, but I recently ran into an issue while using the forms api and the profile api to try and set a hidden profile field using the profile_save_profile() function. I built some custom forms for the user to fill out upon creating an account but I don't want those fields displayed or edited via the profile once they have been set.
I might be missing something (and if I am please correct me), but it seems since the hidden profile field is accessible via modules and themes according to the profile/edit page, ("Hidden profile field, only accessible by administrators, modules and themes."), that you should be able to specify whether or not you want to have profile_save_profile() affect hidden fields. Is this the wrong function to use to do this? If so, please show/give me an example of the correct way to save data to the profile from the Forms API.

I know it's probably considered bad practice to update other people's modules like this, and I could have just written some SQL, but that seemed like more of a hack than this approach.

As a work around I modified the profiles module by adding a 3rd parameter to the _profile_get_fields() function. It basically lets me determine whether or not I want it to retrieve the hidden fields.

function _profile_get_fields($category, $register = FALSE, $hidden = FALSE) {
$args = array();
$sql = 'SELECT * FROM {profile_fields} WHERE ';
$filters = array();
if ($register) {
$filters[] = 'register = 1';
}
else {
// Use LOWER('%s') instead of PHP's strtolower() to avoid UTF-8 conversion issues.
$filters[] = "LOWER(category) = LOWER('%s')";
$args[] = $category;
}
if (!user_access('administer users') and !$hidden) {
$filters[] = 'visibility != %d';
$args[] = PROFILE_HIDDEN;
}
$sql .= implode(' AND ', $filters);
$sql .= ' ORDER BY category, weight';
return db_query($sql, $args);
}

I then update the profile_save_profile() function to include the same parameter, and pass it to _profile_get_fields().

function profile_save_profile(&$edit, &$user, $category, $register = FALSE, $hidden = FALSE) {
$result = _profile_get_fields($category, $register, $hidden);
while ($field = db_fetch_object($result)) {
if (_profile_field_serialize($field->type)) {
$edit[$field->name] = serialize($edit[$field->name]);
}
db_query("DELETE FROM {profile_values} WHERE fid = %d AND uid = %d", $field->fid, $user->uid);
db_query("INSERT INTO {profile_values} (fid, uid, value) VALUES (%d, %d, '%s')", $field->fid, $user->uid, $edit[$field->name]);
// Mark field as handled (prevents saving to user->data).
$edit[$field->name] = NULL;
}
}

Then in my code I can just do:

$profile = array(
'profile_business_name' => $business["company"],
'profile_business_address' => $business["address"],
'profile_business_city' => $business["city"],
'profile_business_state' => $business["state"],
'profile_business_zipcode' => $business["zipcode"],
'profile_business_speciality' => $business_speciality, # Hidden field
'profile_business_poc_first_name' => $business["poc_first_name"],
'profile_business_poc_last_name' => $business["poc_last_name"],
'profile_business_phone' => $business["phone"],
'profile_business_fax' => $business["fax"],
'profile_business_email' => $business["email"],
'profile_business_website' => $business["website"],
'profile_business_index' => $index # Hidden field
);

profile_save_profile($profile, $user, 'Business Information', FALSE, TRUE);

This allowed it to retain all the other functionality, but let's me affect the hidden profile fields at will... How did you tackle this problem?

Thanks in advance!

Comments

onthefritz’s picture

Subscribing

Curious if you found a solution to this. Im trying to achieve the same thing.

TechNikh’s picture

found any solution for this?

Cheers,
TechNikh

In 30 seconds set up Automated Visual testing of your website. Zero coding. https://drupal.org/project/drulenium
Ever dreamed of styling your view, We have a solution for you. https://drupal.org/project/views_stylizer

bshensky’s picture

All in all, it's best to use the Profile API - specifically profile_load_profile() and profile_save_profile(). That's what they're there for.

Anyone who has had to make data changes to a Drupal user's Profile data quickly learns that the full set of profile variables must be reconstituted prior to a profile_save_profile() even if only one or two of those variables are being touched.

I used the following code snippet in PHP 5.3. It seeks out the existing "profile_" vars in the $user object, and uses them to dynamically set the specific hash vars to be saved - and/or re-saved - by profile_save_profile(). This effectively future-proofs any changes to the Profile field set by keeping from enumerating the profile variable names in the code (as the OP did).

It uses some PHP 5.3 stuff like get_object_vars() and an anonymous function, but it does work.

global $user;
profile_load_profile($user);
// tricky way to preserve the existing profile vars
foreach(array_filter(array_keys(get_object_vars($user)), function($var){return preg_match('/^profile_/', $var);}) as $s) {
  $edit[$s] = $user->{$s};
}
$edit['profile_tos_agree_flag'] = TRUE; // The single var I want to set
profile_save_profile($edit, $user, 'Personal Information');

Does this help?

donpwinston’s picture

This is not true and doesn't answer the question anyway.

Simpler is always better.

scotthoff’s picture

This was AWESOME. Thanks!