Drupal 8: Attaching Ajax to field reference using hook_form_alter - drupal

I have three Types of Content:
Promoteur (fr)
Fiche Plan Action ((fr)
Fiche Action (fr)
The "Fiche Plan Action" has a reference field to the "Promoteur" (field_nom_du_promoteur_en_questi).
The "Fiche Action" has a reference field to the "Fiche Plan d'action" (field_plan_d_action).
The creation of content types as follows: Promoteur, Fiche Plan Action and finaly Fiche Action.
I want to attach an Ajax callback to the filed (field_plan_d_action), so when I change this autocomplete field, a field of a type text was filled by a link to the "Promoteur" content automatically.
I had tried to get the object in the "Fiche Action" then from that object I get, the Object of the "Promoteur" then I update the field by a link to this "Promoteur".
Note: I tried to attach Ajax to an autocomplete (reference) and a select list. When I set a static ID to get the "Promoteur" it works, but I want to get the Object of "Fiche Plan Action" and "Promoteur "dynamically from the current form (using form_alter()).
I found just one example on the Drupal docs using select list type. for the other filed types as Date (datepicker) its not clear for me.
function nom_promoteur_form_alter(&$form, FormStateInterface $form_state, $form_id){
if($form_id == 'node_fiche_d_action_form' || $form_id == 'node_fiche_d_action_edit_form'){
$form['field_plan_d_action']['widget'][0]['target_id']['#ajax'] = [
'callback' => 'myAjaxCallback',
'disable-refocus' => FALSE,
'event' => 'change',
'wrapper'=>'edit-field-nom-du-promoteur-0-value',
];
$form['field_example_select']['widget']['#ajax'] = [
'callback' => 'myAjaxCallback',
'disable-refocus' => FALSE,
'event' => 'change',
'wrapper'=>'edit-field-nom-du-promoteur-0-value',
];
}
}
function myAjaxCallback(array &$form, FormStateInterface $form_state) {
//$values = $form_state->cleanValues()->getValues();
/* fiche plan d'action (Object)*/
$nodeFPA = $form['field_plan_d_action']['widget'][0]['target_id']['#default_value'];
//$nidFPA = (int) $nodeFPA->nid->value;
/* Get Promoteur Dynamically (Object)*/
//$promoteur = $nodeFPA->field_nom_du_promoteur_en_questi->entity;
/* Promoteur with Static ID (Object)*/
$promoteur = \Drupal\node\Entity\Node::load(2);
$nid = $promoteur->id();
$nLabel = $promoteur->label();
/* Url Alias */
$url_alias = \Drupal::service('path_alias.manager')->getAliasByPath('/node/'. $nid);
/* Update field */
$form['field_nom_du_promoteur']['widget'][0]['value']['#value'] = "<a href='$url_alias'>$nLabel </a>";
/* Just For Test */
//$form['field_nom_du_promoteur']['widget'][0]['value']['#value'] = " Test ";
return $form['field_nom_du_promoteur'];
}

Related

Automatically generate tags based on Post Object (ACF) titles in WordPress

I'm creating a custom post type for our projects page. I've also made a custom posts type for our employees.
With ACF I've made a Relationship field which where you can add the team members to a project and it's displayed on the website.
Based in the selected team members posts in the relationship field I would like the generate a tag for each title (Employee name) that is loaded in the relationship field.
This is were I'm stuck now.
The name in the Post Object is called teamleden. I've tried adding code to my customs posts type file but it doesn't work.
<?php
// save post action
add_action('acf/save_post', 'set_employee_tags_on_save_update', 20);
/**
* #param $post_id int|string
*/
function set_employee_tags_on_save_update($post_id) {
// get our current post object
$post = get_post($post_id);
// if post is object
if(is_object($post)) {
// check we are on the projects custom type and post statuses are either publish or draft
// change 'projects' post type to your post type name which has the relationship field
if($post->post_type === 'projects' && ($post->post_status === 'publish' || $post->post_status === 'draft')) {
// get relationship field employees
// this example uses Post Object as the Return Format and is a multiple value
// change get field param 'employees' to your relationship field name
$employees = get_field('employees');
// team member tags to set empty array
$team_member_tags_to_set = [];
// if employees has value or values
if($employees) {
// get all of our current team member tags
// change 'team_members' taxonomy value to your members taxonomy name
$team_member_tags = get_terms([
'taxonomy' => 'team_members',
'orderby' => 'name',
'order' => 'ASC'
]);
// empty array for existing team member tags
$existing_team_member_tags = [];
// if we have existing team member tags
if(!empty($team_member_tags)) {
// foreach team member tags as team member tag
foreach($team_member_tags as $team_member_tag) {
// add existing team member to our existing team member tags array by tag ID => tag name
// this is so we can use this later to check if a team member tag already exists so we dont create duplicates
$existing_team_member_tags[$team_member_tag->ID] = $team_member_tag->name;
}
}
// foreach employees as employee
foreach($employees as $employee) {
// get the title for current employee
$title = get_the_title($employee->ID);
// search existing team members array and return the tag id via key
$existing_team_member_tag_id = array_search($title, $existing_team_member_tags);
// if we have an existing team member tag id
if($existing_team_member_tag_id) {
// add the existing team member tag id as integer to array
$team_member_tags_to_set[] = (int)$existing_team_member_tag_id;
} else {
// else create a new tag for team member by adding title (name) as string to array
$team_member_tags_to_set[] = (string)$title;
}
}
}
// remove the action
remove_action('acf/save_post', 'acf_save_post');
// set post tags for this post id, removing any unused team member tags if relationship field team members are changed
wp_set_object_terms($post_id, $team_member_tags_to_set, $taxonomy = 'team_members', false);
// re add the action
add_action('acf/save_post', 'acf_save_post');
}
}
// finally return
return;
}
This is not tested but should get you on the right track.
You will need to add this in your function.php.
Reference to things you will need to change in my example code...
projects post_type is the post type name which this code fires when post is saved or updated.
employees is the name of the acf relationship field in the projects post type.
team_members is the custom tag taxonomy name which you should have working on your projects post type.
Basically what this code does, is when a projects post is saved, published or updated using the acf/save_post action. It gets the acf relationship field member data for this post. If there are members in the field, it then gets all existing team member tags and creates a simple array of existing member tags by ID => Title(name).
It then loops through all members in the relationship field, and checks if tag already exists for member, if it does it adds the existing member tag (int) ID to the $team_member_tags_to_set array. If no existing member tag is found, we just add the member title (name) as a (string) to the $team_member_tags_to_set array.
After all this is done, we simply use wp_set_post_tags() passing our $team_member_tags_to_set array to update team_members taxonomy terms for the current projects post.
I've also set append to false in wp_set_post_tags() which removes all previous tags and creates a new set of tags. This will help if members get updated in the acf relationship field.
https://developer.wordpress.org/reference/functions/wp_set_post_tags/
See code below, and read my comments so you know whats happening.
<?php
// save post action
add_action('acf/save_post', 'set_employee_tags_on_save_update', 20);
/**
* #param $post_id int|string
*/
function set_employee_tags_on_save_update($post_id) {
// get our current post object
$post = get_post($post_id);
// if post is object
if(is_object($post)) {
// check we are on the projects custom type and post statuses are either publish or draft
// change 'projects' post type to your post type name which has the relationship field
if($post->post_type === 'projects' && ($post->post_status === 'publish' || $post->post_status === 'draft')) {
// get relationship field employees
// this example uses Post Object as the Return Format and is a multiple value
// change get field param 'employees' to your relationship field name
$employees = get_field('employees');
// team member tags to set empty array
$team_member_tags_to_set = [];
// if employees has value or values
if($employees) {
// get all of our current team member tags
// change 'team_members' taxonomy value to your members taxonomy name
$team_member_tags = get_terms([
'taxonomy' => 'team_members',
'orderby' => 'name',
'order' => 'ASC'
]);
// empty array for existing team member tags
$existing_team_member_tags = [];
// if we have existing team member tags
if(!empty($team_member_tags)) {
// foreach team member tags as team member tag
foreach($team_member_tags as $team_member_tag) {
// add existing team member to our existing team member tags array by tag ID => tag name
// this is so we can use this later to check if a team member tag already exists so we dont create duplicates
$existing_team_member_tags[$team_member_tag->ID] = $team_member_tag->name;
}
}
// foreach employees as employee
foreach($employees as $employee) {
// get the title for current employee
$title = get_the_title($employee->ID);
// search existing team members array and return the tag id via key
$existing_team_member_tag_id = array_search($title, $existing_team_member_tags);
// if we have an existing team member tag id
if($existing_team_member_tag_id) {
// add the existing team member tag id as integer to array
$team_member_tags_to_set[] = (int)$existing_team_member_tag_id;
} else {
// else create a new tag for team member by adding title (name) as string to array
$team_member_tags_to_set[] = (string)$title;
}
}
}
// remove the action
remove_action('acf/save_post', 'acf_save_post');
// set post tags for this post id, removing any unused team member tags if relationship field team members are changed
wp_set_post_tags($post_id, $team_member_tags_to_set, false);
// re add the action
add_action('acf/save_post', 'acf_save_post');
}
}
// finally return
return;
}

Filter ModelAdmin by many_many relation

I'm managing the DataObject class 'trainer' with ModelAdmin. A trainer has a many_many relation to my other class 'language'.
On my 'trainer' class I'm manipulating the 'searchableFields' function to display a ListboxField in the filters area.
public function searchableFields() {
$languagesField = ListboxField::create(
'Languages',
'Sprachen',
Language::get()->map()->toArray()
)->setMultiple(true);
return array (
'Languages' => array (
'filter' => 'ExactMatchFilter',
'title' => 'Sprachen',
'field' => $languagesField
)
);
}
That works like expected and shows me the wanted ListboxField. The Problem is, after selecting 1 or 2 or whatever languages and submitting the form, I'm receiving
[Warning] trim() expects parameter 1 to be string, array given
Is it possible here to filter with an many_many relation? And if so, how? Would be great if someone could point me in the right direction.
Update:
Full Error Message: http://www.sspaste.com/paste/show/56589337eea35
Trainer Class: http://www.sspaste.com/paste/show/56589441428d0
You need to define that logic within a $searchable_fields parameter instead of the searchableFields() which actually constructs the searchable fields and logic.
PHP would be likely to throw an error if you go doing fancy form stuff within the array itself, so farm that form field off to a separate method in the same DataObject and simply call upon it.
See my example, I hope it helps.
/* Define this DataObjects searchable Fields */
private static $searchable_fields = array(
'Languages' => array (
'filter' => 'ExactMatchFilter',
'title' => 'Sprachen',
'field' => self::languagesField()
)
);
/* Return the searchable field for Languages */
public function languagesField() {
return ListboxField::create(
'Languages',
'Sprachen',
Language::get()->map()->toArray()
)->setMultiple(true);
}
Yes, it's possible. You just need to override two methods - one in Trainer data object and one in TrainerModelAdmin. First one will make a field, second one will do filtering.
Trainer Data Object:
public function scaffoldSearchFields($_params = null)
{
$fields = parent::scaffoldSearchFields($_params);
// get values from query, if set
$query = Controller::curr()->request->getVar('q');
$value = !empty($query['Languages']) && !empty($query['Languages']) ? $query['Languages'] : array();
// create a field with options and values
$lang = ListboxField::create("Languages", "Sprachen", Language::get()->map()->toArray(), $value, null, true);
// push it to field list
$fields->push($lang);
return $fields;
}
Trainer Model Admin
public function getList()
{
$list = parent::getList();
// check if managed model is right and is query set
$query = $this->request->getVar('q');
if ($this->modelClass === "Trainer" && !empty($query['Languages']) && !empty($query['Languages']))
{
// cast all values to integer, just to be sure
$ids = array();
foreach ($query['Languages'] as $lang)
{
$ids[] = (int)$lang;
}
// make a condition for query
$langs = join(",", $ids);
// run the query and take only trainer IDs
$trainers = DB::query("SELECT * FROM Trainer_Languages WHERE LanguageID IN ({$langs})")->column("TrainerID");
// filter query on those IDs and return it
return $list->filter("ID", $trainers);
}
return $list;
}

How to add / update a field collection without updating parent node/entity drupal 7

I needed to to add / update field collections to node entities without updating the node entities. I tried two ways listed in https://www.drupal.org/node/1842304 and http://alexrayu.com/blog/saveupdate-field-collection-without-nodesave but none of them seems to be working exactly the way I want.
I tried as follows:
$node = node_load($nid);
$field_collection_item = entity_create('field_collection_item', array('field_name' => 'field_college_rating_data'));
$field_collection_item->setHostEntity('node', $node);
$field_collection_item->field_text1['und'][0]['tid'] = $form_state['input']['field_text1']['und'];
$field_collection_item->field_text2['und'][0]['value'] = $form_state['input']['field_text2']['und'];
$field_collection_item->save();
It added a field collection but it updates the node.
I also tried to alter the field collection form submit and used custom submit handler as follows:
function own_custom_field_collection_submit($form,$form_state) {
$field_collection_item = field_collection_item_form_submit_build_field_collection($form, $form_state);
$field_collection_item->save(TRUE);
drupal_set_message(t('The changes have been saved.'));
$form_state['redirect'] = $field_collection_item->path();
}
I have copied this code from core field collection module to change the default argument to "TRUE" in the save function. It added the field collection but didn't associated with the parent node.
I need to save the field collection separately since my node entity form is very large with 50 to 60 fields and field collections and I don't want to update it as many times as I add / update any field collections to the node.
Any help will be much appreciated.
Thanks
You have to pragmatically relate your field collection to the host entity by the following code.
$field_collection_item = field_collection_item_form_submit_build_field_collection($form, $form_state);
$field_collection_item->save(TRUE);
$host_entity = $field_collection_item->hostEntity();
$lang = 'und';
if (isset($host_entity->nid) && isset($host_entity->vid) && isset($lang) && isset($field_collection_item->item_id) && isset($field_collection_item->revision_id)) {
$query = db_select('field_data_field_text1', 'fd');
$query->addExpression('max(fd.delta)', 'total_row');
$query->condition('fd.entity_id', $host_entity->nid);
$max_delta = $query->execute()->fetchField();
if (isset($max_delta)) {
$max_delta = $max_delta + 1;
} else {
$max_delta = 0;
}
db_insert('field_data_{field_collection}')->fields(array(
'entity_type' => 'node',
'bundle' => '{node_type}',
'entity_id' => $host_entity->nid,
'revision_id' => $host_entity->vid,
'language' => $lang,
'delta' => $max_delta,
'field_text' => $field_collection_item->item_id,
'field_text_revision_id' => $field_collection_item->revision_id
))
->execute();
}
Replace your content type and field type in { } and then you just have your field collection relate to your host entity.
Thanks

Drupal custom user registration form

I have built a custom registration form using module_form_alter hook. I have also added the required new fields to the database with db_add_field. Now I'm able to add the values to the table in user registration/ user profile edit and the values are also getting stored in the database.. But what I'm not able to do is get the values that are stored in the database in the user profile edit form is displayed. Is there a hook to load the values from database to form on form load? Or is there any other way?
function customUser_schema_alter(&$schema) {
// Add field to existing schema.
$schema['users']['fields']['detail'] = array(
'type' => 'varchar',
'length' => 100,
);
}
function customUser_install() {
$schema = drupal_get_schema('users');
db_add_field('users', 'detail', $schema['fields']['detail']);
}
function customUser_form_alter(&$form, &$form_state, $form_id) {
// check to see if the form is the user registration or user profile form
// if not then return and don’t do anything
if (!($form_id == 'user_register_form' || $form_id == 'user_profile_form')) {
return;
}
$form['account']['detail'] = array(
'#type' => 'textfield',
'#title' => t('Additional Detail'),
);
}
A proper answer needs more details. I can only assume what you did.
You added fields to the {users} table. You didn't update the database schema which made drupal_write_record not be aware of the new fields, that being the reason they are not populated.
You created a new table {my_table} with the fields.
In both cases you need hook_user_insert()
/**
* Implements hook_user_insert().
*/
function mymodule_user_insert(&$edit, $account, $category) {
// Here you add the code to update the entry in {users} table,
// or int your custom table.
// $edit has the values from the form, $account->uid has the
// uid of the newly created user.
}
Note: If my first assumption is true that's not the drupal way to do it. You should have done the 2nd way instead. And even in that case use the hook_schema to create your table in mymodule.install instead of doing db_add_field().
For drupal 7 you could have uses the profile module (core) or profile2 to achieve that.
Based on that code
Try to change to this inside the form alter.
$account = $form['#user'];
$form['account']['detail'] = array(
'#type' => 'textfield',
'#title' => t('Additional Detail'),
'#default_value' => $account->detail,
);

Drupal: Modifying a User at Registration

I needed to create a custom select list for the user registration page that pulls a SQL query from nodes I need to link to users. I have successfully accomplished this task.. :)
However, when I submit the value, I can't seem to control where the value is stored. In fact, I can't store the value at all. I have created a custom field for my value, but only the new field name is stored, and it is serialized and stored in the Data column of the user table.
Below is my code, I've commented my issues in it. Any help would be appreciated!
<?php
// $Id$
//create the additional user form field, a select list named "account_name"
//then calls the next function to populate it.
function accountselect_user($op, &$edit, &$account, $category = NULL) {
if ($op == 'register' || 'edit')
$fields['Information']['account_name'] = array(
'#type' => 'select',
'#title' => 'Account',
'#description' => t('Select the account to which the contact belongs'),
'#options' => accountselect_getclubs() ,
);
return $fields;
}
//contains query to pull results to select list...this part is working
function accountselect_getclubs() {
$return = array();
$sql = 'SELECT DISTINCT `title` FROM node WHERE type = \'accounts\' ';
$result = db_query($sql);
while ($row = db_fetch_array($result)) {
$return[] = $row['title'];
}
return $return;
}
//CAN'T GET THIS PART TO WORK - query to update the row - the uid = 29 is for
//testing puposes. Once I get the test value to work
//the test value I will worry about updating the correct user.
function accountselect_submitaccount() {
$sql = "UPDATE users SET account = \'value\' WHERE uid = \'29\'";
db_query($sql);
drupal_set_message(t('The field has been updated.'));
}
//I SUSPECT THE PROBLEM IS HERE...call the submitaccount function.
//I have tried hook_form_alter as well...
function accountselect_submit(&$form, &$form_state) {
if($form_id == 'user-register')
drupal_execute('accountselect_submitaccount');
}
Have you checked Drupal's logs? It should be throwing errors, as this is not a valid query.
$sql = "UPDATE users SET account = \'value\' WHERE uid = \'29\'";
Should be:
$sql = "UPDATE users SET account = 'value' WHERE uid = '29'";
Additionally, in:
function accountselect_submit(&$form, &$form_state) {
if($form_id == 'user-register')
drupal_execute('accountselect_submitaccount');
}
$form_id is never defined.
You say you've created the field in the database, but it must match the name of the Drupal field to be automatically handled. You've got two different names for it - account_name in the Drupal field, but account in the database. Make them consistent and it should be automatically handled, no submit functions required.

Resources