0

I don't know how to implement AJAX callback in hook_form_alter().

The field is plain text by default and it is populated by NID (number) of the client company. The module name is "my_car". I didn't use node reference in this field.

Here is the code:

function my_car_form_alter(&$form, &$form_state, $form_id) {

    if ($form_id == 'car_node_form') {

        $clientCompanies = companyTitle(12); // TID 12

        // Replace Client company NID with title
        $form['field_car_company']['und'][0]['value'] = array(
            '#title' => 'Client company',
            '#type' => 'select',
            '#default_value' => $form_state['node']->field_car_company['und'][0]['value'],
            '#options' => $clientCompanies,
            '#required' => TRUE,
            '#empty_option' => t('- Select -')
        );
    }
}

Here I use companyTitle() function to return all companies (nodes) that have TID 12 and replace field type ("textfield" with "select") and it's content - a number with text (NID with title).

function companyTitle($tid) {

    $results = db_query("
        SELECT n.nid, n.title
        FROM {node} AS n
        LEFT JOIN {field_data_field_company_type} AS ct ON ct.entity_id = n.nid
        WHERE n.type = 'company' AND ct.field_company_type_tid = :tid
        ORDER BY n.title ASC",
        array(':tid' => $tid))->fetchAllKeyed();

    return $results;
}

I have written the function that returns NID and title of client company based on TID and title:

function ajaxCompanyTitle($tid, $companyName) {

    $query = db_select('node', 'company')
        ->fields('company', array('nid', 'title'))
        ->condition('company.type', 'company', '=')
        ->condition('type.field_company_type_tid', $tid, '=')
        ->condition('company.title', '%'.db_like($companyName).'%', 'LIKE')
        ->orderBy('company.title', 'ASC')
        ->range(0, 10);

    $query->leftJoin('field_data_field_company_type', 'type', 'type.entity_id = company.nid');

    $results = $query->execute()->fetchAll();

    return $results;
}

Now is the question of how to implement AJAX with the last function. I need text field (it does not to be a select list) with AJAX search where NID will be rendered as title (NID remaining only in the database in the field_car_company_value column).

Apparently, I don't know how to implement a callback function.

function my_car_form_alter(&$form, &$form_state, $form_id) {

    if ($form_id == 'car_node_form') {

        // Replace Client company NID with title
        $form['field_car_company']['und'][0]['value'] = array(
            '#title' => 'Client company',
            '#type' => 'textfield',
            '#required' => TRUE,
            '#ajax' => array(
                'callback' => 'ajax_callback',
                'wrapper' => 'edit-field-car-company-und-0-value',
            )
        );
    }
}

function ajax_callback($string = '') {

    $matches = array();

    if ($string) {

        $clientCompanies = ajaxCompanyTitle(12, $string);

        foreach ($clientCompanies as $clientCompany) {
            $matches[$clientCompany->title] = $clientCompany->title;
        }
    }

    drupal_json_output($matches);
}
0

There are two types of ajax in Drupal. Traditional AJAX, and the Drupal Form API #ajax api. You are mixing the two, by using #ajax in your form, but returning and AJAX response, rather than an #ajax callback response.

To understand #ajax, you need to understand the process by which the Form API works. This is how a standard non-Ajax Form API form works in Drupal:

  1. Some function is called which builds a render array representing the form.
  2. Form alter hooks are called, allowing other modules/theme to alter the form.
  3. Drupal caches the render array in the database, to be used for comparison on submission, as a security measure to ensure that the submitted data matches the type of data expected to be submitted.
  4. The form is rendered into HTML and sent to the browser.
  5. The user submits the form.
  6. Drupal retrieves the form from the database, confirms that the submitted data is allowed, and sanitizes it.
  7. Drupal validates the form, runs submission hooks, and redirects the user to whatever page they are supposed to be on after form submission.

When #ajax is implemented, the process is the same as above, but with some additional steps in the middle.

  1. Some function is called which builds a render array representing the form.
  2. Form alter hooks are called, allowing other modules/theme to alter the form.
  3. Drupal caches the render array in the database, to be used for comparison on submission, as a security measure to ensure that the submitted data matches the type of data expected to be submitted.
  4. The form is rendered into HTML and sent to the browser.

---------- #ajax behavior start --------------

  1. The user does something that triggers #ajax
  2. The browser sends all the data in the form back to Drupal, as if the form were submitted.
  3. Drupal retrieves the form from the database, and confirms that the submitted data is allowed, and sanitizes it.
  4. Validation and submit handlers for the form are called. The form must be set to be rebuilt in the submit handler for the ajax to work.
  5. The form is rebuilt from the original form definition, passing it the #ajax submitted values.
  6. Form alter hooks are called, allowing other modules to alter the form, using the #ajax submitted values.
  7. Drupal caches the new render array in the database, to be used for comparison on submission.
  8. The ajax built form is passed to the #ajax callback handler. The handler then returns the part of the form to be injected back into the DOM on the browser. This can be part or all of the form.
  9. The new content is inserted into the DOM.

---------- #ajax behavior end --------------

  1. The user submits the form.
  2. Drupal retrieves the form from the database, confirms that the submitted data is allowed, and sanitizes it.
  3. Drupal validates the form, runs submission hooks, and redirects the user to whatever page they are supposed to be on after form submission.

Referring back to your code, your ajax callback will receive two arguments, $form and $form_state, and needs to return a section of your form. So it should look something like:

function ajax_callback($form, $form_state) {
  return $form['some_element']['of_the_form'];
}

In your form, this is likely $form['field_car_company']['und'][0]['value']. You will build the options of that form element when the form is initially rendered the way you are now, and if the form has been submitted using ajax, use the submitted values to determine the new options to show.

  • Ok, I understand the difference now. But, the question is how to implement ajaxCompanyTitle() function in callback? – Marko Basta May 26 at 14:59
  • You don't. You build it in hook_form_alter() using the submitted values. Then you return that section from your ajax callback. I suggest doing the #ajax example in the Examples module (dgo.to/examples) to understand how it works. There are also lots of tutorials on the internet. It's better to understand the fundamental concept, before trying to build your own form. – Jaypan May 26 at 21:20
0

@Jaypan

I did the following:

function my_car_menu() {

    $items = array();

    $items['car/autocomplete'] = array(
        'title' => 'Autocomplete for cars',
        'page callback' => 'car_autocomplete',
        'access arguments' => array('list of cars'),
        'type' => MENU_CALLBACK
    );

    return $items;
}

function my_car_form_alter(&$form, &$form_state, $form_id) {

    if ($form_id == 'car_node_form') {

        $form['field_car_company']['und'][0]['value']['#ajax'] = array(
            'callback' => 'ajax_callback'
        );

        $form['field_car_company']['und'][0]['value'] = array(
            '#type' => 'textfield',
            '#default_value' => $form_state['node']->field_car_company['und'][0]['value'],
            '#title' => 'Client company',
            '#description' => 'Zakupac',
            '#required' => TRUE,
            '#autocomplete_path' => 'car/autocomplete'
        );
    }
}

function ajax_callback($form, $form_state) {
    return $form['field_car_company']['und'][0]['value'];
}

function car_autocomplete($string = '') {

    $matches = array();

    if ($string) {

        $clientCompanies = ajaxCompanyTitle(12, $string);

        foreach ($clientCompanies as $clientCompany) {
            $matches[$clientCompany->title] = check_plain($clientCompany->title);
        }
    }

    drupal_json_output($matches);
}

The AJAX callback is now working. I have achieved it using '#autocomplete_path' => 'car/autocomplete'. But in database is now written $clientCompany->title instead of $clientCompany->nid. I still need NID in database.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.