Using AJAX to load states

  • Posts: 42
  • Thank you received: 2
9 years 3 months ago #210609

-- HikaShop version -- : 2.5.0
-- Joomla version -- : 3.4.1
-- PHP version -- : 5.4.43

Hi Hika Team!

We've had to repurpose the states field (when someone creates or edits their address) to load a list of around 6000 shipping zones for our primary country and to work with our own courier. It is working, but we're wanting to reduce the load on our database and are wanting to know which view to edit so that we can load this list using AJAX and Jquery.

Regards,
Bruce Atkinson

Last edit: 9 years 3 months ago by GSW00d.

Please Log in or Create an account to join the conversation.

  • Posts: 82863
  • Thank you received: 13372
  • MODERATOR
9 years 3 months ago #210612

Hi,

The loading of the data and display of the fields is not done in a view. It's done in the file administrator/components/com_hikashop/classes/field.php
So one solution is to do your modifications directly there.
But a cleaner solution is to implement a new type of custom field (that you can have extending from the state type custom field class) via the creation of a HikaShop plugin.
You can see an example of that with the plugin plugins/hikashop/datepickerfield/ and you can see documentation on that in the Fields API section of our developer documentation:
www.hikashop.com/support/support/documen...r-documentation.html

The following user(s) said Thank You: GSW00d

Please Log in or Create an account to join the conversation.

  • Posts: 42
  • Thank you received: 2
9 years 2 months ago #213712

Hi!

I've written Joomla friendly ajax code using the select2 library, is there an easy way of bundling this up in a hika plugin so that it will display as a field? I have looked at datepickerfield.php and datepickerfield_class.php and have made custom Joomla plugins in the past.

We are using a custom version of the "states" field in out Hika Database. You can see what we're wanting to integrate via the examples below.

select.php

<?php
 // Get Joomla! framework define( '_JEXEC', 1 ); 
define( '_JEXEC', 1 );
define('JPATH_BASE', '/path/to/joomla');
require_once ( JPATH_BASE .'/includes/defines.php' );
require_once ( JPATH_BASE .'/includes/framework.php' );
?>
<html>
<head>
  <script src="/t3-assets/js/js-9610a.js?t=123" type="text/javascript"></script>
  <link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet" />
  <script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
  <script type="text/javascript">
  jQuery(document).ready(function() {
    jQuery('select').select2();
  });
  function refresh_area(){
    jQuery('#areaBox').html("<option>Select an area</option").select2({
      ajax: {
        url: 'fetch_data.php',
        dataType: 'json',
        delay: 250,
        data: function (params) {
          return {
                q: params.term, // search term
                country: get_country // call to get_country function
              };
            },
            processResults: function (data) {
            // parse the results into the format expected by Select2.
            // since we are using custom formatting functions we do not need to
            // alter the remote JSON data
            return {
              results: data
            };
          },
          cache: true
        }
      });
  }
  function get_country(){
    var country = jQuery('#country').val();
    return country;
  }
  </script>
</head>
<body>
  <center>
    <div>
      <select id="country" onChange="refresh_area();" style="width: 25%">
       <option>
        Select a country
      </option>
      <?php
      // open db
      $app = &JFactory::getApplication('site');
      $db =& JFactory::getDbo(); 
      // prepare query
      $query = $db->getQuery(true); 
      $query  = "SELECT * ";
      $query .= "FROM `#__hikashop_zone` ";
      $query .= "WHERE zone_type = 'country' ";
      $query .= "AND zone_published = 1 ";
      $query .= "ORDER BY `zone_name_english` ASC";
      // fetch the results
      $db->setQuery($query);
      $results = $db-> loadAssocList();
      // validate to make sure we have a result and return
      if(count($results) > 0){
        foreach ($results as $result) {
         echo '<option value="'.$result['zone_namekey'].'">'.$result['zone_name_english']."</option>";      
       } 
     } else {
       echo '<option>No Country Found</option>';
     }
      // close database connection
      $db->freeResult();
     ?>
   </select>
 </div>
 <br />
 <div>
  <select id="areaBox" style="width: 25%">
    <option>
      Select an area
    </option>
  </select>
</div>     
</center>
</body>
</html>

fetch_data.php
<?php
 // Get Joomla! framework define( '_JEXEC', 1 ); 
define( '_JEXEC', 1 );
define('JPATH_BASE', '/path/to/joomla');
require_once ( JPATH_BASE .'/includes/defines.php' );
require_once ( JPATH_BASE .'/includes/framework.php' );
// open db
$app = &JFactory::getApplication('site');
$db =& JFactory::getDbo(); 
// prepare search and country strings
$search = strip_tags(trim($_GET['q'])); 
$country = strip_tags(trim($_GET['country']));
// prepare query
$query  = $db->getQuery(true); 
$query  =  "SELECT #__hikashop_zone_link.zone_parent_namekey, #__hikashop_zone_link.zone_child_namekey, #__hikashop_zone.zone_namekey, #__hikashop_zone.zone_name_english ";
$query .=  "FROM  `#__hikashop_zone_link` ";
$query .=  "INNER JOIN  `#__hikashop_zone` ON #__hikashop_zone_link.zone_child_namekey = #__hikashop_zone.zone_namekey ";
$query .=  "AND #__hikashop_zone_link.zone_parent_namekey = '".$db->escape($country)."' ";
$query .=  "AND #__hikashop_zone.zone_published = 1 ";
$query .=  "AND #__hikashop_zone.zone_name_english LIKE '%".$db->escape($search)."%' ";
$query .=  "ORDER BY #__hikashop_zone.zone_name_english ASC ";
$query .=  "LIMIT 40";
// fetch the results
$db->setQuery($query);
$results = $db-> loadAssocList();
// validate to make sure we have a result
if(count($results) > 0){
	foreach ($results as $key => $value) {
		$data[] = array('id' => $value['zone_namekey'], 'text' => $value['zone_name_english']);			 	
	} 
} else {
	$data[] = array('id' => '0', 'text' => 'No Area Found');
}
// close database connection
$db->freeResult();
// return the result in json
echo json_encode($data);
?>

So what we're looking for is contents for a new plugin named "customzoneajax". Would we install it at './plugins/hikashop/customzoneajax' and what would the contents of files 'customzoneajax.php' and 'customzoneajax_class.php' be?

Last edit: 9 years 2 months ago by GSW00d.

Please Log in or Create an account to join the conversation.

  • Posts: 26158
  • Thank you received: 4028
  • MODERATOR
9 years 2 months ago #213745

Hi,

We split the plugin custom fields into two files in order to have something light ; so when the field is not required (which is the case in most pages), the class is not loaded.
Like Nicolas told you, the datepicker is today the best example for a plugin custom field ; and when you create your plugin, you can add other PHP files if you want an entry point for the AJAX calls, you can also create a system plugin to be able to catch some Joomla URLs.

Using the plugin system, you can create new kind of custom fields and in these fields, the plugin can display whatever it wants as long as the data can be read in the POST to store a value in the database.

Regards,


Jerome - Obsidev.com
HikaMarket & HikaSerial developer / HikaShop core dev team.

Also helping the HikaShop support team when having some time or couldn't sleep.
By the way, do not send me private message, use the "contact us" form instead.

Please Log in or Create an account to join the conversation.

  • Posts: 42
  • Thank you received: 2
9 years 2 months ago #213805

Hi Jerome!

That didn't really help me much. Fortunately I discovered a workaround that achieves my needs by doing the following. For those who want to do something similar, this is what I did.

Create 'ajax' folder in joomla root. Saved select2.min.css and select2.min.js to this folder. Disabled chosen library in hikashop configuration. Created custom /ajax/fetch_data.php file as follows:

<?php
 // Get Joomla! framework define( '_JEXEC', 1 ); 
define( '_JEXEC', 1 );
define('JPATH_BASE', '/path/to/joomla');
require_once ( JPATH_BASE .'/includes/defines.php' );
require_once ( JPATH_BASE .'/includes/framework.php' );
// open db
$app = &JFactory::getApplication('site');
$db =& JFactory::getDbo(); 
// prepare search and country strings
$search = strip_tags(trim($_GET['q'])); 
$country = strip_tags(trim($_GET['country']));
// prepare query
$query  = $db->getQuery(true); 
$query  =  "SELECT #__hikashop_zone_link.zone_parent_namekey, #__hikashop_zone_link.zone_child_namekey, #__hikashop_zone.zone_namekey, #__hikashop_zone.zone_name_english ";
$query .=  "FROM  `#__hikashop_zone_link` ";
$query .=  "INNER JOIN  `#__hikashop_zone` ON #__hikashop_zone_link.zone_child_namekey = #__hikashop_zone.zone_namekey ";
$query .=  "AND #__hikashop_zone_link.zone_parent_namekey = '".$db->escape($country)."' ";
$query .=  "AND #__hikashop_zone.zone_published = 1 ";
$query .=  "AND #__hikashop_zone.zone_name_english LIKE '%".$db->escape($search)."%' ";
$query .=  "ORDER BY #__hikashop_zone.zone_name_english ASC ";
$query .=  "LIMIT 40";
// fetch the results
$db->setQuery($query);
$results = $db-> loadAssocList();
// validate to make sure we have a result
if(count($results) > 0){
	foreach ($results as $key => $value) {
		$data[] = array('id' => $value['zone_namekey'], 'text' => $value['zone_name_english']);			 	
	} 
} else {
	$data[] = array('id' => '0', 'text' => 'No Area Found');
}
// close database connection
$db->freeResult();
// return the result in json
echo json_encode($data);
?>

Customise address->show view for front-end as follows:
<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.5.0
 * @author	hikashop.com
 * @copyright	(C) 2010-2015 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
$name = $this->type.'_address';
$uniq_id = 'hikashop_address_'.$this->type.'_'.$this->address_id;
$pfid = '';
if(!empty($this->fieldset_id))
	$pfid = '&fid='.$this->fieldset_id;
else
	$this->fieldset_id = $uniq_id;

$show_url = 'address&task=show&subtask='.$this->type.'&cid='.$this->address_id.$pfid;
$save_url = 'address&task=save&subtask='.$this->type.'&cid='.$this->address_id.$pfid;
$update_url = 'address&task=edit&subtask='.$this->type.'&cid='.$this->address_id.$pfid;
$delete_url = 'address&task=delete&subtask='.$this->type.'&cid='.$this->address_id.'&'.hikashop_getFormToken().'=1';

?><div id="<?php echo $uniq_id; ?>">
<?php
if(!isset($this->edit) || $this->edit !== true ) {
	?>		<div class="hika_edit">
	<a href="<?php echo hikashop_completeLink($update_url, true);?>" id="<?php echo $uniq_id; ?>_edit" onclick="return window.hikashop.get(this,'<?php echo $this->fieldset_id; ?>');"><img src="<?php echo HIKASHOP_IMAGES; ?>edit.png" alt=""/><span><?php echo JText::_('HIKA_EDIT'); ?></span></a>
	<a href="<?php echo hikashop_completeLink($delete_url, true);?>" id="<?php echo $uniq_id; ?>_delete" onclick="return window.addressMgr.delete(this,<?php echo $this->address_id; ?>,'<?php echo $uniq_id; ?>','<?php echo $this->type; ?>');"><img src="<?php echo HIKASHOP_IMAGES; ?>delete.png" alt=""/><span><?php echo JText::_('HIKA_DELETE'); ?></span></a>
</div>
<?php
} else {
	?>		<div class="hika_edit">
	<a href="<?php echo hikashop_completeLink($save_url, true);?>" onclick="return window.hikashop.form(this,'<?php echo $this->fieldset_id; ?>');"><img src="<?php echo HIKASHOP_IMAGES; ?>ok.png" alt=""/><span><?php echo JText::_('HIKA_SAVE'); ?></span></a>
	<a href="<?php echo hikashop_completeLink($show_url, true);?>" onclick="return window.hikashop.get(this,'<?php echo $this->fieldset_id; ?>');"><img src="<?php echo HIKASHOP_IMAGES; ?>cancel.png" alt=""/><span><?php echo JText::_('HIKA_CANCEL'); ?></span></a>
</div>
<?php
}
?>
<?php
$display = 'field_backend';
if(isset($this->edit) && $this->edit === true ) {
	?>
<link href="/ajax/select2.min.css" rel="stylesheet" />
<script src="/ajax/select2.min.js"></script>
	<script>
	jQuery(document).ready(function() {
		jQuery('.<?php echo $this->type;?>_address_country').select2();
		jQuery('.<?php echo $this->type;?>_address_state').select2();
	});
	function refresh_area(type){
		jQuery('.'+type+'_address_state').html('<option value="" id="address_country_" selected="selected">Please make a selection from the list</option>').select2({
			ajax: {
				url: '/ajax/fetch_data.php',
				dataType: 'json',
				delay: 250,
				data: function (params) {
					return {
                q: params.term, // search term
                country: get_country(type) // call to get_country function
            };
        },
        processResults: function (data) {
            return {
            	results: data
            };
        },
        cache: true
    }
});
	}
	function get_country(type){
		var country = jQuery('.'+type+'_address_country').val();
		return country;
	}
	</script>
	<table class="admintable table">
		<?php
		foreach($this->fields as $field){
			if($field->$display && $field->field_namekey != 'address_country' && $field->field_namekey != 'address_state'){
				$fieldname = $field->field_namekey;
				?>
				<tr class="hikashop_<?php echo $this->type;?>_address_<?php echo $fieldname;?>" id="hikashop_<?php echo $this->type; ?>_address_<?php echo $fieldname; ?>">
					<td>
						<div>
							<label><?php echo $this->fieldsClass->trans($field->field_realname);?></label>
						</div>
						<?php
						$onWhat = 'onchange';
						if($field->field_type == 'radio')
							$onWhat = 'onclick';

						$field->table_name = 'order';
						echo $this->fieldsClass->display(
							$field,
							@$this->address->$fieldname,
							'data['.$name.']['.$fieldname.']',
							false,
							' ' . $onWhat . '="hikashopToggleFields(this.value,\''.$fieldname.'\',\''.$name.'\',0);"',
							false,
							$this->fields,
							$this->address
							);
							?></td>
						</tr>
						<?php
					}
				}
				?>
				<tr class="hikashop_<?php echo $this->type;?>_address_address_country" id="hikashop_<?php echo $this->type; ?>_address_address_country">
					<td>
						<div>
							<label>Country</label>
						</div>
						<span class="data_billing_address_address_state_container'"><select id="address_country" onChange="refresh_area('<?php echo $this->type;?>');" class="hikashop_field_dropdown <?php echo $this->type;?>_address_country" name="data[<?php echo $this->type;?>_address][address_country]" style="width: 96%">
							<option value="" id="address_country_" selected="selected">Please make a selection from the list</option>
							<?php
		      // open db
							$app = &JFactory::getApplication('site');
							$db =& JFactory::getDbo(); 
		      // prepare query
							$query = $db->getQuery(true); 
							$query  = "SELECT * ";
							$query .= "FROM `#__hikashop_zone` ";
							$query .= "WHERE zone_type = 'country' ";
							$query .= "AND zone_published = 1 ";
							$query .= "ORDER BY `zone_name_english` ASC";
		      // fetch the results
							$db->setQuery($query);
							$results = $db-> loadAssocList();
		      // validate to make sure we have a result and return
							if(count($results) > 0){
								foreach ($results as $result) {
									echo '<option value="'.$result['zone_namekey'].'" id="address_country_'.$result['zone_namekey'].'">'.$result['zone_name_english']."</option>";      
								} 
							} else {
								echo '<option>No Country Found</option>';
							}
		      // close database connection
							$db->freeResult();
							?>
						</select></span>
						<span class="hikashop_field_required">*</span>
					</td>
				</tr>
				<tr class="hikashop_<?php echo $this->type;?>_address_address_state" id="hikashop_<?php echo $this->type; ?>_address_address_state">
					<td>
						<div>
							<label>City/Suburb/Area</label>
						</div>
						<span id="data_<?php echo $this->type;?>_address_address_state_container">
							<select id="data_<?php echo $this->type;?>_address_address_state" class="hikashop_field_dropdown <?php echo $this->type;?>_address_state" name="data[<?php echo $this->type;?>_address][address_state]" style="width: 96%">
								<option value="" id="data_<?php echo $this->type;?>_address_address_state_" selected="selected">Please make a selection from the list</option>
							</select>
						</span>
						<input type="hidden" id="data_<?php echo $this->type;?>_address_address_state_default_value" name="data_<?php echo $this->type;?>_address_address_state_default_value" value="">
						<span class="hikashop_field_required">*</span>
					</td>
				</tr>
			</table>
						<?php
					} else {

						if(false) {
							?>
							<table class="admintable table">
								<?php
								foreach($this->fields as $field){
									if($field->$display){
										$fieldname = $field->field_namekey;
										?>
										<tr class="hikashop_<?php echo $this->type;?>order_address_<?php echo $fieldname;?>">
											<td class="key"><label><?php echo $this->fieldsClass->trans($field->field_realname);?></label></td>
											<td><span><?php echo $this->fieldsClass->show($field, @$this->address->$fieldname);?></span></td>
										</tr>
										<?php
									}
								}
								?>
							</table>
							<?php
						} else {
							?>
							<div class="hikashop_address_content" onclick="return window.addressMgr.click(this,<?php echo $this->address_id;?>,'<?php echo $uniq_id; ?>','<?php echo $this->type; ?>');">
								<?php
								if(empty($this->addressClass))
									$this->addressClass = hikashop_get('class.address');
								echo $this->addressClass->displayAddress($this->fields,$this->address,'address');
								?>
							</div>
							<?php
						}
					}

					if(isset($this->edit) && $this->edit === true) {
						echo '<input type="hidden" name="data['.$name.'][address_id]" value="'.$this->address_id.'"/>';
						echo JHTML::_( 'form.token' );
					}
					?>
					<script type="text/javascript">
					if(!window.addressMgr) window.addressMgr = {};
					window.addressMgr.update<?php echo ucfirst($this->type);?> = function() {
						window.Oby.xRequest('<?php echo hikashop_completeLink('address&task=show&subtask='.$this->type.'_address&cid='.$this->address_id, true, false, true); ?>',{update:'<?php echo $this->fieldset_id; ?>'});
					};
					<?php
					static $hikashop_address_show_js_init = false;
					if(!$hikashop_address_show_js_init) {
						$hikashop_address_show_js_init = true;
						?>
						window.addressMgr.delete = function(el, cid, uid, type) {
							if(!confirm('<?php echo JText::_('HIKASHOP_CONFIRM_DELETE_ADDRESS', true); ?>'))
							return false;
							var w = window, o = w.Oby, d = document;
							o.xRequest(el.href, null, function(xhr) { if(xhr.status == 200) {
								if(xhr.responseText == '1') {
									var target = d.getElementById(uid);
									if(target) target.parentNode.removeChild(target);
									window.Oby.fireAjax('hikashop_address_deleted',{'type':type,'cid':cid,'uid':uid,'el':el});
								} else if(xhr.responseText != '0')
								o.updateElem(uid, xhr.responseText);
							}});
							return false;
						};
						window.addressMgr.click = function(el, cid, uid, type) { window.Oby.fireAjax('hikashop_address_click',{'type':type,'cid':cid,'uid':uid,'el':el}); }
						<?php
					}

					if(JRequest::getVar('tmpl', '') == 'component') {
						if(empty($this->addressClass))
							$this->addressClass = hikashop_get('class.address');
						$miniFormat = $this->addressClass->miniFormat($this->address);
						?>
						window.Oby.fireAjax('hikashop_address_changed',{'type':'<?php echo $this->type; ?>','edit':<?php echo $this->edit?'1':'0'; ?>,'cid':<?php echo $this->address_id; ?>,'miniFormat':'<?php echo str_replace('\'','\\\'', $miniFormat); ?>'<?php
							$previous_id = JRequest::getVar('previous_cid', null);
							if((!empty($previous_id) || $previous_id === 0) && is_int($previous_id))
								echo ',\'previous_cid\':' . $previous_id;
							?>});
<?php
echo $this->init_js;
}
?>
</script>
</div>

Please Log in or Create an account to join the conversation.

  • Posts: 42
  • Thank you received: 2
9 years 2 months ago #214127

There is one addition that will really help though, how might I auto-load the country and state in the users current address?

Please Log in or Create an account to join the conversation.

  • Posts: 82863
  • Thank you received: 13372
  • MODERATOR
9 years 2 months ago #214136

Hi,

Could you give more context on what you're trying to do ?
Because the states and countries are already loaded for the addresses in HikaShop.
So I don't understand what you're trying to do where and why.

Please Log in or Create an account to join the conversation.

  • Posts: 42
  • Thank you received: 2
9 years 2 months ago #214191

Hi Nicolas!

We've had to do some tweaks as we've been growing with Hikashop over the years, one of which was using the states field for a whole bunch of areas (around 6000). Due to the size of this list, I needed to populate the select field via ajax, which I've done by customising the Address->Show view for front-end. I was able to bypass the fields class by using this condition

if($field->$display && $field->field_namekey != 'address_country' && $field->field_namekey != 'address_state')
Everything is working nicely now, except the country and state fields I've bypassed won't load their stored values if the customer is editing their address, seeing as I've bypassed the code in the fields class that does this. What I'm hoping for is the sql/programming logic that would allow me to leverage this from the address-show view.

So in summary, how could I 'echo' the customer's selected addresses' country and state as a string from the address->show view.

Please Log in or Create an account to join the conversation.

  • Posts: 26158
  • Thank you received: 4028
  • MODERATOR
9 years 2 months ago #214247

Hi,

The zone custom field has not been designed to be overridden.
If you want to customize the zone custom field with your own data and own process ; the best will be to create a new kind of custom field using a custom plugin ; like the "advanced date picker".
Just like I wrote in a previous message

Using the plugin system, you can create new kind of custom fields and in these fields, the plugin can display whatever it wants as long as the data can be read in the POST to store a value in the database.


We are not able to help you for the loading of your data for the zone custom field because it has not been design to support it ; it is a too important modification of the field.

Regards,


Jerome - Obsidev.com
HikaMarket & HikaSerial developer / HikaShop core dev team.

Also helping the HikaShop support team when having some time or couldn't sleep.
By the way, do not send me private message, use the "contact us" form instead.
The following user(s) said Thank You: GSW00d

Please Log in or Create an account to join the conversation.

  • Posts: 42
  • Thank you received: 2
9 years 2 months ago #214352

Thanks Jerome! I understand it is risky.

Is there an easy way to get the users's address info from the address->show view?

Please Log in or Create an account to join the conversation.

  • Posts: 82863
  • Thank you received: 13372
  • MODERATOR
9 years 2 months ago #214361

Sure:
- get the current user_id with a call to the function hikashop_loadUser()
- do a MySQL on the hikashop_address table where the address_user_id is equal to that user_id

Please Log in or Create an account to join the conversation.

Time to create page: 0.077 seconds
Powered by Kunena Forum