diff --git a/geocoder.install b/geocoder.install new file mode 100644 index 0000000..b79fa53 --- /dev/null +++ b/geocoder.install @@ -0,0 +1,41 @@ + 1) { + $geometry = geocoder_cache_get($cache_id, $processor, $data, $options); + } + else { + $geometry = call_user_func($processor['callback'], $data, $options); + if ($cache_type > 1) { + geocoder_cache_set($cache_id, $processor, $geometry, $data, $options); + } + } if ($cache_type) { $static_cache[$cache_id] = $geometry; @@ -287,3 +299,74 @@ function geocoder_service_check_request($handler, $format, $check_ac = TRUE) { exit(); } } + +// These function implement a caching layer for this module. + +/** + * Retrieve a cached geocoded location, if it exists. + * + * This uses the cache_geocoder bin by default, but this can be overridden in + * the processor definition. Specifying a function that circumvents this process + * wholly is also possible. + * + * @param $cache_id + * A string hash of the location data passed to geocoder(). + * @param $processor + * The processor used to handle the caching request. + * @param mixed $data + * Data that was passed to geocoder() for geocoding. For example an address + * string. + * @param array $options + * Additional options that were passed to geocoder(). + * + * @return Geometry + * A Geometry object if it was found in the cache; FALSE, if the cached result + * of the last geocoding request was FALSE; or NULL, if there was no result + * found. + */ +function geocoder_cache_get($cache_id, $processor, $data, $options = array()) { + if (isset($processor['cache_function']) && is_callable($processor['cache_function'])) { + return call_user_func($processor['cache_function'], 'get', $cache_id, NULL, $processor, $data, $options); + } + $bin = isset($processor['cache_bin']) ? $processor['cache_bin'] : 'cache_geocoder'; + if (!$bin) { + return NULL; + } + geophp_load(); + $cache = cache_get($cache_id, $bin); + return $cache ? $cache->data : NULL; +} + +/** + * Set a cached geocoded location. + * + * This uses the cache_geocoder bin by default, but this can be overridden in + * the processor definition. Specifying a function that circumvents this process + * wholly is also possible, as well as specifying the expiry time. + * + * @param $cache_id + * A string hash of the location data passed to geocoder(). + * @param $processor + * The processor used to handle the caching request. + * @param $geometry + * The result to cache. + */ +function geocoder_cache_set($cache_id, $processor, $geometry, $data, $options = array()) { + if (isset($processor['cache_function']) && is_callable($processor['cache_function'])) { + call_user_func($processor['cache_function'], 'set', $cache_id, $geometry, $processor, $data, $options); + return; + } + $bin = isset($processor['cache_bin']) ? $processor['cache_bin'] : 'cache_geocoder'; + if (!$bin) { + return; + } + $expire = isset($processor['cache_expire']) ? $processor['cache_expire'] : CACHE_PERMANENT; + cache_set($cache_id, $geometry ? $geometry : NULL, $bin, $expire); +} + +/** + * Implements hook_flush_caches(). + */ +function geocoder_flush_caches() { + return array('cache_geocoder', 'cache_geocoder_field'); +} diff --git a/geocoder.widget.inc b/geocoder.widget.inc index 63c8e95..653e41a 100644 --- a/geocoder.widget.inc +++ b/geocoder.widget.inc @@ -91,6 +91,12 @@ function geocoder_field_widget_settings_form($this_field, $instance) { '#tree' => TRUE, ); + $form['cache'] = array( + '#type' => 'checkbox', + '#title' => t('Cache geocoder requests'), + '#default_value' => isset($settings['cache']) ? $settings['cache']: FALSE, + ); + // Add the handler settings forms foreach ($processors as $handler_id => $handler) { if (isset($handler['settings_callback']) || isset($handler['terms_of_service'])) { @@ -188,6 +194,7 @@ function geocoder_widget_get_field_value($entity_type, $field_instance, $entity // Required settings if (isset($field_instance['widget']['settings']['geocoder_handler']) && isset($field_instance['widget']['settings']['geocoder_field'])) { + $handler_name = $field_instance['widget']['settings']['geocoder_handler']; $handler = geocoder_get_handler($field_instance['widget']['settings']['geocoder_handler']); $field_name = is_array($field_instance['widget']['settings']['geocoder_field']) ? reset($field_instance['widget']['settings']['geocoder_field']) : $field_instance['widget']['settings']['geocoder_field']; $target_info = field_info_field($field_instance['field_name']); @@ -248,6 +255,9 @@ function geocoder_widget_get_field_value($entity_type, $field_instance, $entity if (isset($field_instance['widget']['settings']['handler_settings'][$handler['name']])) { $handler_settings = $field_instance['widget']['settings']['handler_settings'][$handler['name']]; } + else { + $handler_settings = array(); + } // Determine how we deal with deltas (multi-value fields) if (empty($field_instance['widget']['settings']['delta_handling'])) { @@ -268,31 +278,40 @@ function geocoder_widget_get_field_value($entity_type, $field_instance, $entity ); if (is_array($source_field_values) && count($source_field_values)) { - $values = array(); + $cache = !empty($field_instance['widget']['settings']['cache']); // Geocode geometries $geometries = array(); foreach ($source_field_values as $delta => $item) { - // Geocode any value from our source field. - try { - if (isset($handler_settings)) { + $geometry = NULL; + + $cache_id = $handler_name . '_' . md5(serialize(array($field_info, $item, $handler_settings))); + if ($cache) { + $geometry = geocoder_widget_cache_get($cache_id, $handler, $item, $field_info, $handler_settings); + } + + if (!$geometry) { + // Geocode any value from our source field. + try { $geometry = call_user_func($handler['field_callback'], $field_info, $item, $handler_settings); + + if ($cache) { + geocoder_widget_cache_set($cache_id, $handler, $geometry, $item, $field_info, $handler_settings); + } + + if ($geometry instanceof Geometry) { + $geometries[] = $geometry; + } + else { + // An error occured + return FALSE; + } } - else { - $geometry = call_user_func($handler['field_callback'], $field_info, $item); - } - if ($geometry instanceof Geometry) { - $geometries[] = $geometry; - } - else { - // An error occured + catch (Exception $e) { + watchdog_exception('geocoder', $e); return FALSE; } } - catch (Exception $e) { - watchdog_exception('geocoder', $e); - return FALSE; - } } if (empty($geometries)) { @@ -364,7 +383,7 @@ function geocoder_widget_resolve_deltas($geometries, $delta_handling = 'default' } // For multiple-to-single handling, run it though geometryReduce - if ($delta_handling == 'm_to_s' || $delta_handling == 'c_to_s') { + if ($delta_handling == 'm_to_s' || $delta_handling == 'c_to_s') { $reduced_geom = geoPHP::geometryReduce($geometries); $values[] = geocoder_widget_values_from_geometry($reduced_geom, $field_type); } @@ -465,3 +484,77 @@ function geocoder_widget_array_recursive_diff($aArray1, $aArray2) { } return $aReturn; } + +/** + * Retrieve a cached geocoded location, if it exists. + * + * This uses the cache_geocoder bin by default, but this can be overridden in + * the processor definition. Specifying a function that circumvents this process + * wholly is also possible. + * + * @param $cache_id + * A string hash of the location data passed to geocoder(). + * @param $processor + * The processor used to handle the caching request. + * @param mixed $data + * Data that was passed to geocoder() for geocoding. For example an address + * string. + * @param array $field_info + * Data about the field instance for the data. + * @param array $options + * Additional options that were passed to geocoder(). + * + * @return Geometry + * A Geometry object if it was found in the cache; FALSE, if the cached result + * of the last geocoding request was FALSE; or NULL, if there was no result + * found. + */ +function geocoder_widget_cache_get($cache_id, $processor, $data, $field_info, $options = array()) { + // geocoder_widget_cache_get($cache_id, $handler_name, $item, $field_info, $handler_settings); + if (isset($processor['widget_cache_function']) && is_callable($processor['widget_cache_function'])) { + return call_user_func($processor['widget_cache_function'], 'get', $cache_id, NULL, $processor, $data, $field_info, $options); + } + $bin = isset($processor['widget_cache_bin']) ? $processor['widget_cache_bin'] : 'cache_geocoder_field'; + if (!$bin) { + return NULL; + } + geophp_load(); + $cache = cache_get($cache_id, $bin); + return $cache ? $cache->data : NULL; +} + +/** + * Set a cached geocoded location. + * + * This uses the cache_geocoder bin by default, but this can be overridden in + * the processor definition. Specifying a function that circumvents this process + * wholly is also possible, as well as specifying the expiry time. + * + * @param $cache_id + * A string hash of the location data passed to geocoder(). + * @param $processor + * The processor used to handle the caching request. + * @param $geometry + * The result to cache. + * @param mixed $data + * Data that was passed to geocoder() for geocoding. For example an address + * string. + * @param array $field_info + * Data about the field instance for the data. + * @param array $options + * Additional options that were passed to geocoder(). + * + */ +function geocoder_widget_cache_set($cache_id, $processor, $geometry, $data, $field_info, $options = array()) { + if (isset($processor['widget_cache_function']) && is_callable($processor['widget_cache_function'])) { + call_user_func($processor['widget_cache_function'], 'set', $cache_id, $geometry, $processor, $data, $field_info, $options); + return; + } + + $bin = isset($processor['widget_cache_bin']) ? $processor['widget_cache_bin'] : 'cache_geocoder_field'; + if (!$bin) { + return; + } + $expire = isset($processor['widget_cache_expire']) ? $processor['widget_cache_expire'] : CACHE_PERMANENT; + cache_set($cache_id, $geometry ? $geometry : NULL, $bin, $expire); +}