i work on Symfony3.4
I have a dynamic form country->region->city (3 distincts entities).
I can change fields based on user selection without any problem.
But i can't persist in database because of a constraint violation on the field city (and only this field)
And I realy don't know why because I call them (region and city) the same way...
Any help would be welcome.
Here is the Form :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$em = $options['entityManager'];
$builder
->add('rue', TextType::class, array(
'label' => 'votre rue',
'required' => true,
))
->add('pays', EntityType::class, array(
'class' => 'AppBundle\Entity\Pays',
'placeholder' => '--choisir--',
'choice_label' => 'nom',
'required' => true
))
;
$addRegion = function (FormInterface $form, Pays $pays = null) {
$regions = null === $pays ? array() : $pays->getRegions();
$form->add('region', EntityType::class, array(
'class' => 'AppBundle\Entity\Region',
'placeholder' => '--choisir une région--',
'choices' => $regions,
'choice_label' => 'nom',
'required' => true
));
};
$addVille = function (FormInterface $form, Region $region = null) {
$villes = null === $region ? array() : $region->getVilles();
$form->add('ville', EntityType::class, array(
'class' => 'AppBundle\Entity\Ville',
'placeholder' => '--choisir une ville--',
'choices' => $villes,
'choice_label' => 'nom',
'required' => true
));
$form->add('submit', SubmitType::class
);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($addRegion, $addVille){
$form = $event->getForm();
$addVille($form, null);
$addRegion($form, null);
}
);
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($addRegion, $addVille, $em) {
$form = $event->getForm();
$data = $event->getData();
if(isset($data['pays'])){
$paysId = $data['pays'];
$repo = $em->getRepository('AppBundle\Entity\Pays');
$pays = $repo->find($paysId);
$addRegion($form, $pays);
$addVille($form, null);
}
else if(isset($data['region'])){
$regionId = $data['region'];
$repo = $em->getRepository('AppBundle\Entity\Region');
$region = $repo->find($regionId);
$addVille($form, $region);
}
}
);
}
And here is the Template :
{% block body %}
{% form_theme form 'bootstrap_4_layout.html.twig' %}
{% for message in app.flashes('notice') %}
<div style="color:green;">
{{ message }}
</div>
{% endfor %}
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_end(form) }}
{% endblock %}
{% block javascripts %}
<script>
var $pays = $('#adresse_pays');
var $region = $('#adresse_region');
var $ville = $('#adresse_ville');
$pays.change(function() {
var $form = $(this).closest('form');
var data = {};
data[$pays.attr('name')] = $pays.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
$ville.empty()
var select = $(html).find('#adresse_ville > option')
$ville.append(select)
$region.empty()
var select = $(html).find('#adresse_region > option')
$region.append(select)
$region.val($("#adresse_region option:first").val());
console.log('regions')
console.log(select)
}
});
});
$region.change(function() {
var $form = $(this).closest('form');
var data = {};
data[$region.attr('name')] = $region.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
$ville.empty()
var select = $(html).find('#adresse_ville > option')
$ville.append(select)
$ville.val($("#adresse_ville option:first").val());
console.log('villes')
console.log(select)
}
});
});
</script>
{% endblock %}
This is the alert on the form (next to city field) :
Error This value is not valid
I went further thanks to the debug bar, and it seems that this field does not send an object but an integer.
The difference i found with another field is that in the city field :
Normalized Format submitted is null
while in another field such as region :
Normalized Format
Region {#6035 ▼
-id: 3
-nom: "Catalogne"
-villes: PersistentCollection {#6039 …}
-pays: Pays {#5802 ▶}
-adresses: PersistentCollection {#6041 …}
}
It works !
i split the function
$builder->addEventListener(
FormEvents::PRE_SUBMIT, ... in half => i remove the else if and made another similar function to listen when "region" is changed.
What bothers me is I don't understand why it works this way but not the other way...
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($addRegion, $addVille, $em) {
$form = $event->getForm();
$data = $event->getData();
if(isset($data['pays'])){
$paysId = $data['pays'];
$repo = $em->getRepository('AppBundle\Entity\Pays');
$pays = $repo->find($paysId);
$addRegion($form, $pays);
$addVille($form, null);
}
}
);
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function(FormEvent $event) use ($addVille, $em) {
$form = $event->getForm();
$data = $event->getData();
if(isset($data['region'])){
$regionId = $data['region'];
$repo = $em->getRepository('AppBundle\Entity\Region');
$region = $repo->find($regionId);
$addVille($form, $region);
}
}
);
Related
I am trying to get the current product ID (if is a product page) in this AJAX call. I have attempted to use global $product but couldn't make it work. Is there any way to access the current product (post) in an AJAX call?
add_action('wp_ajax_get_product_list', 'get_product_list');
add_action('wp_ajax_nopriv_get_product_list', 'get_product_list');
function get_product_list()
{
$args = array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => -1,
'orderby' => 'name',
'order' => 'ASC'
);
$loop = new WP_Query($args);
$allProducts = '';
//if the product page get the current product ID
// Do something here with the current product ID
foreach ($loop->posts as $product) {
$allProducts = $allProducts . '<option value="' . $product->ID . '">' . $product->post_title . '</option>';
}
wp_reset_query();
echo $allProducts;
wp_die();
}
AJAX call
jQuery(document).ready(function($) {
var data = {
'action': 'get_product_list',
};
jQuery.post("/wp-admin/admin-ajax.php", data, function(response) {
console.log(response);
});
});
I'm having difficulty getting this to create a category on the front-end with Ajax. It's 99% working.
Here is my form:
<form id="new_idea" name="new_idea" method="POST">
<ul>
<li>
<label>Idea name</label>
<input type="text" name="idea_name" required />
</li>
<li class="full">
<label>Description</label>
<input type="text" name="idea_description" />
</li>
</ul>
</form>
Here is my function (in functions.php):
add_action( 'wp_ajax_add_new_idea', 'add_new_idea' );
add_action( 'wp_ajax_nopriv_add_new_idea', 'add_new_idea' );
function ajax_scripts() {
$parameters = array(
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('inputs')
);
wp_enqueue_script('my-ajax', get_template_directory_uri().'/js/ajax.js', array('jquery'), null, true);
wp_localize_script('my-ajax', 'inputs', $parameters );
}
add_action('wp_enqueue_scripts', 'ajax_scripts');
function ajaxStatus($status, $message, $data = NULL) {
$response = array (
'status' => $status,
'message' => $message,
'data' => $data
);
$output = json_encode($response);
exit($output);
}
// New Idea
function add_new_idea() {
if(isset($_POST["new_idea_form"])) {
ajaxStatus('error', 'No need to update anything.');
} else {
$nonce = $_POST['nonce'];
if(wp_verify_nonce($nonce, 'inputs') !== false) {
require_once(ABSPATH . 'wp-admin/includes/taxonomy.php');
$idea_name = $_POST['idea_name'];
$idea_description = $_POST['idea_description'];
$idea_slug = sanitize_title_with_dashes($idea_name);
$idea = array(
'cat_name' => $idea_name,
'category_parent' => '',
'category_nicename' => $idea_slug,
'category_description' => $idea_description,
'taxonomy' => 'ideas'
);
wp_insert_category( $idea );
//print_r($idea);
//die;
// Success message
ajaxStatus('success', 'Added new idea');
} else {
// No nonce!
ajaxStatus('error', 'Nonce failed!');
}
}
}
...and this is my ajax.js:
$('#new_idea').on('submit', function(e) {
e.preventDefault();
$.post( inputs.ajaxurl, {
action : 'add_new_idea',
nonce : inputs.nonce,
post : $(this).serialize()
},
function(response) {
console.log(response);
ResponseSuccess(response);
});
return false;
});
As for troubleshooting, if I hardcode values into the $idea array like this and submit the form...
$idea = array(
'cat_name' => 'cool idea',
'category_parent' => '',
'category_nicename' => 'cool-dea',
'category_description' => 'a description of my cool idea',
'taxonomy' => 'ideas'
);
...it actually works and my category gets created.
So from what I can tell, the real problem is that it is not getting the $_POST[] values that were submitted, although I can't see why.
Any help would be awesome.
Try this code.
function add_new_idea() {
$params = array();
parse_str($_POST["post"], $params);
if(isset($_POST["post"])) {
ajaxStatus('error', 'No need to update anything.');
} else {
$nonce = $_POST['nonce'];
if(wp_verify_nonce($nonce, 'inputs') !== false) {
require_once(ABSPATH . 'wp-admin/includes/taxonomy.php');
$idea_name = $params['idea_name'];
$idea_description = $params['idea_description'];
$idea_slug = sanitize_title_with_dashes($idea_name);
$idea = array(
'cat_name' => $idea_name,
'category_parent' => '',
'category_nicename' => $idea_slug,
'category_description' => $idea_description,
'taxonomy' => 'ideas'
);
wp_insert_category( $idea );
//print_r($idea);
//die;
// Success message
ajaxStatus('success', 'Added new idea');
} else {
// No nonce!
ajaxStatus('error', 'Nonce failed!');
}
}
}
Read This
you need to use parse_str for serlize object.
Retrieving serialize data in a PHP file called using AJAX
I have created a custom html form that is having 2 dropdowns State and City. When selects a State i want to populate the city dropdown. But the feature is not working. I already followed many urls but unable to solve the issue.
See the code below i have tried. Can anybody help me.
function zenuineform($form, &$form_state) {
$value_dropdown_first = isset($form_state['values']['comp_state']) ? $form_state['values']['comp_state'] : 1;
$form = array();
$form['comp_state']=array(
'#type'=>'select',
'#title'=>t('State : '),
'#options'=> $state_arr,
'#default_value' => $state,
'#ajax' => array(
// When 'event' occurs, Drupal will perform an ajax request in the
// background. Usually the default value is sufficient (eg. change for
// select elements), but valid values include any jQuery event,
// most notably 'mousedown', 'blur', and 'submit'.
'event' => 'change',
'callback' => 'zenuineform_ajax_callback',
'wrapper' => 'filtered_city',
),
);
$form['comp_city']=array(
'#type'=>'select',
'#title'=>t('City : '),
'#default_value' => $city,
'#prefix' => '<div id="filtered_city">',
'#suffix' => '</div>',
'#options' => zenuineform_second_dropdown_options($value_dropdown_first),
);
return $form;
}
function zenuineform_ajax_callback($form, $form_state) {
return $form['comp_city'];
}
function zenuineform_second_dropdown_options($state_id = '') {
if($state_id !='' ) {
$result = db_query('SELECT * FROM zen_city WHERE state_id = '.$state_id);
if ($result) {
$options = '<option value="">--Select--</option>';
while ($row = $result->fetchAssoc()) {
$rows[$row['id']] = $row['city_name'];
}
return $rows;
}
}
else {
return array('--Select--');
}
}
I am trying to populate the second select box with the all date fields of the first selected content type name in select box. I am using ajax_callback to fetch the selected value by $form_state. I am getting error, which I can't determine why. Can anybody help?
This is the my custom module code.
function mymodule_settings($form, &$form_state){
$content_type_options = array();
$result = db_query("SELECT * FROM {node_type}");
foreach($result as $record){
$content_type_options[$record->type] = $record->name;
}
$form = array();
$form['content_type'] = array(
'#title' => t('Content Types'),
'#description' => t('Select a content type.'),
'#type' => 'select',
'#options' => $content_type_options,
'#ajax' => array(
'event' => 'change',
'wrapper' => 'reg-start-date',
'callback' => 'mymodule_datefields_ajax_callback',
'method' => 'replace',
),
);
$form['checkboxes_fieldset'] = array(
'#title' => t("Start Date"),
'#prefix' => '<div id="reg-start-date">',
'#suffix' => '</div>',
'#type' => 'select',
'#description' => t('Select the date field'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
function mymodule_datefields_ajax_callback($form, $form_state) {
$fieldname = $form_state['triggering_element']['#value'];
$field_query = db_query("SELECT fc.field_name FROM {field_config} fc, {field_config_instance} fci
WHERE fc.field_name = fci.field_name
AND fc.type = 'datetime'
AND fci.bundle = '".$fieldname."'");
$datefield_options = array();
foreach($field_query as $record){
$datefield_options = $record;
}
return $datefield_options;
//dpm($form_state, 'AJAX $form_state');
}
Here is the error, which I am getting in popup -
An AJAX HTTP error occurred. HTTP Result Code: 200 Debugging
information follows. Path: /module_dev/?q=system/ajax StatusText: OK
ResponseText: Fatal error: Cannot use object of type stdClass as array
in /var/www/module_dev/includes/common.inc on line 5786
I went through the /var/www/module_dev/includes/common.inc on line 5786, and this is the code I find there.
function drupal_render(&$elements) {
// Early-return nothing if user does not have access.
if (empty($elements) || (isset($elements['#access']) && !$elements['#access'])) {
return;
}
// Do not print elements twice.
if (!empty($elements['#printed'])) {
return;
}
// Try to fetch the element's markup from cache and return.
if (isset($elements['#cache'])) {
$cached_output = drupal_render_cache_get($elements);
if ($cached_output !== FALSE) {
return $cached_output;
}
}
// If #markup is set, ensure #type is set. This allows to specify just #markup
// on an element without setting #type.
if (isset($elements['#markup']) && !isset($elements['#type'])) {
$elements['#type'] = 'markup';
}
// If the default values for this element have not been loaded yet, populate
// them.
if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) {
$elements += element_info($elements['#type']);
}
// Make any final changes to the element before it is rendered. This means
// that the $element or the children can be altered or corrected before the
// element is rendered into the final text.
if (isset($elements['#pre_render'])) {
foreach ($elements['#pre_render'] as $function) {
if (function_exists($function)) {
$elements = $function($elements);
}
}
}
// Allow #pre_render to abort rendering.
if (!empty($elements['#printed'])) {
return;
}
// Get the children of the element, sorted by weight.
$children = element_children($elements, TRUE);
// Initialize this element's #children, unless a #pre_render callback already
// preset #children.
if (!isset($elements['#children'])) {
$elements['#children'] = '';
}
// Call the element's #theme function if it is set. Then any children of the
// element have to be rendered there.
if (isset($elements['#theme'])) {
$elements['#children'] = theme($elements['#theme'], $elements);
}
// If #theme was not set and the element has children, render them now.
// This is the same process as drupal_render_children() but is inlined
// for speed.
if ($elements['#children'] == '') {
foreach ($children as $key) {
$elements['#children'] .= drupal_render($elements[$key]);
}
}
// Let the theme functions in #theme_wrappers add markup around the rendered
// children.
if (isset($elements['#theme_wrappers'])) {
foreach ($elements['#theme_wrappers'] as $theme_wrapper) {
$elements['#children'] = theme($theme_wrapper, $elements);
}
}
// Filter the outputted content and make any last changes before the
// content is sent to the browser. The changes are made on $content
// which allows the output'ed text to be filtered.
if (isset($elements['#post_render'])) {
foreach ($elements['#post_render'] as $function) {
if (function_exists($function)) {
$elements['#children'] = $function($elements['#children'], $elements);
}
}
}
// Add any JavaScript state information associated with the element.
if (!empty($elements['#states'])) {
drupal_process_states($elements);
}
// Add additional libraries, CSS, JavaScript an other custom
// attached data associated with this element.
if (!empty($elements['#attached'])) {
drupal_process_attached($elements);
}
$prefix = isset($elements['#prefix']) ? $elements['#prefix'] : '';
$suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
$output = $prefix . $elements['#children'] . $suffix;
// Cache the processed element if #cache is set.
if (isset($elements['#cache'])) {
drupal_render_cache_set($output, $elements);
}
$elements['#printed'] = TRUE;
return $output;
}
What your AJAX callback should return is the rendering array for a form element that replaces the content of the <div> tag whose CSS ID is set as #ajax[wrapper]. What your AJAX callback is returning is an array of objects, which is not what the render API is expecting. (The render API uses arrays.) That is why you get an error saying that a object cannot be used as array.
See ajax_example_simplest() as example of form builder using AJAX; in particular, see its AJAX callback, ajax_example_simplest_callback().
In short, the code of mymodule_datefields_ajax_callback() should be the following one.
function mymodule_datefields_ajax_callback($form, $form_state) {
return $form['checkboxes_fieldset'];
}
The form builder should use the following code.
function mymodule_settings($form, &$form_state){
$content_type_options = array();
$result = db_query("SELECT * FROM {node_type}");
foreach ($result as $record) {
$content_type_options[$record->type] = $record->name;
}
// $form is already passed as argument; you don't need to initialize it to an empty array.
// $form = array();
$form['content_type'] = array(
'#title' => t('Content Types'),
'#description' => t('Select a content type.'),
'#type' => 'select',
'#options' => $content_type_options,
'#ajax' => array(
'event' => 'change',
'wrapper' => 'reg-start-date',
'callback' => 'mymodule_datefields_ajax_callback',
'method' => 'replace',
),
);
// An AJAX request call to the form builder function has been done.
if (!empty($form_state['values']['content_type'])) {
// Use $form_state['values']['content_type'] to get the option list.
// Set the value of $date_options with that list.
$field_query = db_query("query to execute");
$date_options = array();
foreach ($field_query as $record) {
$date_options[$record->field_name] = $record->field_name;
}
}
else {
$date_options = array();
}
$form['checkboxes_fieldset'] = array(
'#title' => t("Start Date"),
'#prefix' => '<div id="reg-start-date">',
'#suffix' => '</div>',
'#type' => 'select',
'#options' => $date_options,
'#description' => t('Select the date'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
$date_options is an array with the format value => string_to_show; it's is the same format you used for $content_type_options.
I'm working on Drupal 7, I have a form constructed like this
function qt_debate_response_form($form, &$form_state, $node_id){
$form['node_id'] = array(
'#type' => 'value',
'#value' => $node_id,
);
$form['response_body'] = array(
'#type' => 'textarea',
'#required' => TRUE,
'#row' => 4,
'#default_value' => '',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Post'),
'#ajax' => array(
'callback' => 'qt_debate_response_form_js',
'wrapper' => 'response-message-' . $node_id,
'method' => 'append',
'effect' => 'fade',
),
);
return $form;
}
And an ajax callback function to add new comment
function qt_debate_response_form_js($form, $form_state) {
global $user;
$body_text = $form_state['values']['response_body'];
$node_id = $form_state['values']['node_id'];
$message_js = '
<script language="javascript" type="text/javascript">
qt_debate_response_load_new_item(' . $node_id . ',' . $user->uid . ');
jQuery(".response-form-wrapper textarea").val("");
</script>';
$comment = new stdClass();
$comment->nid = $form_state['values']['node_id']; // Node Id the comment will attached to
$comment->cid = 0;
$comment->pid = 0;
$comment->uid = $user->uid;
$comment->is_anonymous = 0;
$comment->homepage = '';
$comment->status = COMMENT_PUBLISHED;
$comment->language = LANGUAGE_NONE;
$comment->subject = text_summary($body_text, null, 60);
$comment->comment_body[$comment->language][0]['value'] = $body_text;
$comment->comment_body[$comment->language][0]['format'] = 'filtered_html';
comment_submit($comment);
comment_save($comment);
$output = $message_js;
return $output;
}
here are my Javascript that load new created comment into Div (ajax)
function qt_debate_user_post_load_new_items(debate_id) {
// get the latest comment id in context
$top_comment = jQuery(".view-debate-user-posts .views-row").first();
$top_comment_id = jQuery(".nid-field-hidden", $top_comment).html();
jQuery.ajax({
type: "GET",
url: "/qt_debate/ajax/load_new_items/" + debate_id + "/" + $top_comment_id,
data: "",
success: function(html){
$new_items = jQuery(".view-content", html);
jQuery("form", $new_items).attr("action","/debate/199");
jQuery(".form-submit", $new_items).attr("id","edit-submit--5");
if ($new_items.html() != null) {
html = '<div class="new_items_wrapper" style="display: none">' + $new_items.html() + '</div>';
if (jQuery(".view-debate-user-posts .view-content").length == 0) {
jQuery(".view-debate-user-posts .view-empty").remove();
jQuery(".view-debate-user-posts").append('<div class="view-content"></div>');
}
jQuery(".view-debate-user-posts .view-content").prepend(html);
jQuery(".view-debate-user-posts .view-content .new_items_wrapper").fadeIn(500, function() {
jQuery(".views-row", this).unwrap();
Drupal.attachBehaviors();
});
}
},
});
var t = setTimeout("qt_debate_user_post_load_new_items(" + debate_id + ")", 30000)
}
The hook_menu which is return the views content to jQuery call back
function qt_debate_ajax_load_new_items() {
$debate_id = arg(3);
print views_embed_view('debate_user_posts_load_new_items_', 'default', array($debate_id));
exit(0);
}
View template file, i also return a new form inside
print drupal_render(drupal_get_form('qt_debate_response_form', $row->nid));
The return view content rendered good, with Drupal.attachBehaviors in Javascript, all others effect in returned view content also work well. Except the form submit ajax.
Can any one help please ? The attachBehaviors not work with return ajax form.
Thanks so much!
Drupal.attachBehaviors(context);
basically re-runs any functions defined by
Drupal.behaviors.yourFunctionName = function(context) {
$('div.someSelectorclass:not(.already-processed-class)', context).addClass('already-processed-class').bind(someMethod);
}
and these methods must add a selector [already-processed-class] to test for whether the bind(); [ or click(function(e){}); or each(function(){}); or whatever ] has already been added. The "context" is to pass less-than 'document' - say, if your new content is known to be inside a smaller context that will still be found by original behaviors function: in this example I could pass the parent container selector of my new 'div.someSelectorclass'
Drupal.attachBehaviors('div.parentContainerClass');
instead of
Drupal.attachBehaviors(document);