diff --git a/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSource.php b/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSource.php index 882dd4c..f328499 100644 --- a/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSource.php +++ b/core_search_facets/src/Plugin/facets/facet_source/CoreNodeSearchFacetSource.php @@ -158,9 +158,11 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea case 'entity_reference': $query_types['string'] = 'core_node_search_string'; break; + case 'created': $query_types['string'] = 'core_node_search_date'; break; + } return $query_types; diff --git a/core_search_facets/src/Plugin/facets/query_type/CoreNodeSearchDate.php b/core_search_facets/src/Plugin/facets/query_type/CoreNodeSearchDate.php index dac0c54..54950c6 100644 --- a/core_search_facets/src/Plugin/facets/query_type/CoreNodeSearchDate.php +++ b/core_search_facets/src/Plugin/facets/query_type/CoreNodeSearchDate.php @@ -11,7 +11,7 @@ use Drupal\facets\QueryType\QueryTypePluginBase; use Drupal\facets\Result\Result; /** - * A string query type for core. + * A date query type for core search. * * @FacetsQueryType( * id = "core_node_search_date", @@ -40,14 +40,17 @@ class CoreNodeSearchDate extends QueryTypePluginBase { return; } + /** @var \Drupal\facets\Utility\FacetsDate $facets_date */ + $facets_date = \Drupal::getContainer()->get('facets.utility.date'); + // Gets facet query and this facet's query info. /** @var \Drupal\core_search_facets\FacetsQuery $facet_query */ $facet_query = $facet_source->getFacetQueryExtender(); $query_info = $facet_source->getQueryInfo($this->facet); - $tables_joined = array(); + $tables_joined = []; // Obtain the start and end dates. - if (preg_match(FACETAPI_REGEX_DATE_RANGE, $active_item, $matches)) { + if (preg_match($facets_date::FACETS_REGEX_DATE_RANGE, $active_item, $matches)) { $start = strtotime($matches[1]); $end = strtotime($matches[8]); } @@ -80,7 +83,8 @@ class CoreNodeSearchDate extends QueryTypePluginBase { */ public function build() { - $build = []; + /** @var \Drupal\facets\Utility\FacetsDate $facets_date */ + $facets_date = \Drupal::getContainer()->get('facets.utility.date'); // Makes sure there was at least one match. /*if (!$this->adapter->hasMatches) { @@ -96,7 +100,6 @@ class CoreNodeSearchDate extends QueryTypePluginBase { $facet_query = $facet_source->getFacetQueryExtender(); $facet_query->addFacetField($query_info); - foreach ($query_info['joins'] as $table_alias => $join_info) { $facet_query->addFacetJoin($query_info, $table_alias); } @@ -105,7 +108,6 @@ class CoreNodeSearchDate extends QueryTypePluginBase { // Executes query, iterates over results. $result = $facet_query->execute(); - foreach ($result as $record) { $raw_values[$record->value] = $record->count; } @@ -139,28 +141,29 @@ class CoreNodeSearchDate extends QueryTypePluginBase { if (NULL === $parent) { if (count($raw_values) > 1) { // @TODO for the moment hard-coding the gap. - $gap = FACETAPI_DATE_DAY;//facetapi_get_timestamp_gap(min($timestamps), max($timestamps)); - } else { - $gap = FACETAPI_DATE_HOUR; + $gap = $facets_date::FACETS_DATE_DAY; + } + else { + $gap = $facets_date::FACETS_DATE_HOUR; } } // Converts all timestamps to dates in ISO 8601 format. $dates = []; foreach ($timestamps as $timestamp) { - $dates[$timestamp] = facetapi_isodate($timestamp, $gap); + $dates[$timestamp] = $facets_date->isoDate($timestamp, $gap); } // Treat each date as the range start and next date as the range end. $range_end = []; $previous = NULL; foreach (array_unique($dates) as $date) { - if (NULL !== $previous) { - $range_end[$previous] = facetapi_get_next_date_increment($previous, $gap); + if (is_null($previous)) { + $range_end[$previous] = $facets_date->getNextDateIncrement($previous, $gap); } $previous = $date; } - $range_end[$previous] = facetapi_get_next_date_increment($previous, $gap); + $range_end[$previous] = $facets_date->getNextDateIncrement($previous, $gap); $facet_results = []; foreach ($raw_values as $value => $count) { @@ -189,7 +192,7 @@ class CoreNodeSearchDate extends QueryTypePluginBase { } // Adds parent information if not already set. - if (NULL !== $parent && !isset($build[$new_value]['#item_parents'])) { + if (is_null($parent) && !isset($build[$new_value]['#item_parents'])) { $build[$parent]['#item_children'][$new_value] = &$build[$new_value]; $build[$new_value]['#item_parents'][$parent] = $parent; }*/ diff --git a/facets.date.inc b/facets.date.inc deleted file mode 100644 index 2d735ed..0000000 --- a/facets.date.inc +++ /dev/null @@ -1,361 +0,0 @@ - 6, - FACETAPI_DATE_MONTH => 5, - FACETAPI_DATE_DAY => 4, - FACETAPI_DATE_HOUR => 3, - FACETAPI_DATE_MINUTE => 2, - FACETAPI_DATE_SECOND => 1, - ); - - // Gets gap numbers for both the gap and minimum gap, checks if the next gap - // is within the limit set by the $min_gap parameter. - $gap_num = isset($gap_numbers[$gap]) ? $gap_numbers[$gap] : 6; - $min_num = isset($gap_numbers[$min_gap]) ? $gap_numbers[$min_gap] : 1; - return ($gap_num > $min_num) ? array_search($gap_num - 1, $gap_numbers) : $min_gap; -} - -/** - * Determines the best search gap to use for an arbitrary date range. - * - * Generally, we use the maximum gap that fits between the start and end date. - * If they are more than a year apart, 1 year; if they are more than a month - * apart, 1 month; etc. - * - * This function uses Unix timestamps for its computation and so is not useful - * for dates outside that range. - * - * @param int $start_time - * A string containing the start date as an ISO date string. - * @param int $end_time - * A string containing the end date as an ISO date string. - * @param string|NULL $min_gap - * (Optional) The minimum gap that should be returned. - * - * @return string - * A string containing the gap, see FACETAPI_DATE_* constants for valid - * values. Returns FALSE of either of the dates cannot be converted to a - * timestamp. - */ -function facetapi_get_timestamp_gap($start_time, $end_time, $min_gap = NULL) { - $time_diff = $end_time - $start_time; - switch (TRUE) { - // NOTE: 31536000 == 60 * 60 * 24 * 365 - case ($time_diff >= 31536000): - $gap = FACETAPI_DATE_YEAR; - break; - - case ($time_diff >= 86400 * gmdate('t', $start_time)): - $gap = FACETAPI_DATE_MONTH; - break; - - case ($time_diff >= 86400): - $gap = FACETAPI_DATE_DAY; - break; - - case ($time_diff >= 3600): - $gap = FACETAPI_DATE_HOUR; - break; - - case ($time_diff >= 60): - $gap = FACETAPI_DATE_MINUTE; - break; - - default: - $gap = FACETAPI_DATE_SECOND; - break; - } - - // Return the calculated gap if a minimum gap was not passed of the calculated - // gap is a larger interval than the minimum gap. - if (null === $min_gap || facetapi_gap_compare($gap, $min_gap) >= 0) { - return $gap; - } - else { - return $min_gap; - } -} - -/** - * Converts ISO date strings to Unix timestamps, passes values to the - * facetapi_get_timestamp_gap() function to calculate the gap. - * - * @param $start_date - * A string containing the start date as an ISO date string. - * @param $end_date - * A string containing the end date as an ISO date string. - * @param string|NULL $min_gap - * (Optional) The minimum gap that should be returned. - * - * @return string - * A string containing the gap, see FACETAPI_DATE_* constants for valid - * values. Returns FALSE of either of the dates cannot be converted to a - * timestamp. - * - * @see facetapi_get_timestamp_gap() - */ -function facetapi_get_date_gap($start_date, $end_date, $min_gap = NULL) { - $range = array(strtotime($start_date), strtotime($end_date)); - if (!in_array(FALSE, $range, TRUE)) { - return facetapi_get_timestamp_gap($range[0], $range[1], $min_gap); - } - return FALSE; -} - -/** - * Returns a formatted date based on the passed timestamp and gap. - * - * This function assumes that gaps less than one day will be displayed in a - * search context in which a larger containing gap including a day is already - * displayed. So, HOUR, MINUTE, and SECOND gaps only display time information, - * without date. - * - * @param $timestamp - * An integer containing the Unix timestamp. - * @param $gap - * A string containing the gap, see FACETAPI_DATE_* constants for valid - * values, defaults to YEAR. - * - * @return - * A gap-appropriate display date used in the facet link. - */ -function facetapi_format_timestamp($timestamp, $gap = FACETAPI_DATE_YEAR) { - switch ($gap) { - case FACETAPI_DATE_MONTH: - return format_date($timestamp, 'custom', 'F Y', 'UTC'); - - case FACETAPI_DATE_DAY: - return format_date($timestamp, 'custom', 'F j, Y', 'UTC'); - - case FACETAPI_DATE_HOUR: - return format_date($timestamp, 'custom', 'g A', 'UTC'); - - case FACETAPI_DATE_MINUTE: - return format_date($timestamp, 'custom', 'g:i A', 'UTC'); - - case FACETAPI_DATE_SECOND: - return format_date($timestamp, 'custom', 'g:i:s A', 'UTC'); - - default: - return format_date($timestamp, 'custom', 'Y', 'UTC'); - } -} - -/** - * Returns a formatted date based on the passed ISO date string and gap. - * - * @param $date - * A string containing the date as an ISO date string. - * @param $gap - * A string containing the gap, see FACETAPI_DATE_* constants for valid - * values, defaults to YEAR. - * @param $callback - * The formatting callback, defaults to "facetapi_format_timestamp". - * - * @return - * A gap-appropriate display date used in the facet link. - * - * @see facetapi_format_timestamp() - */ -function facetapi_format_date($date, $gap = FACETAPI_DATE_YEAR, $callback = 'facetapi_format_timestamp') { - $timestamp = strtotime($date); - return $callback($timestamp, $gap); -} - -/** - * Returns the next increment from the given ISO date and gap. This function is - * useful for getting the upper limit of a date range from the given start - * date. - * - * @param $date - * A string containing the date as an ISO date string. - * @param $gap - * A string containing the gap, see FACETAPI_DATE_* constants for valid - * values, defaults to YEAR. - * - * @return - * A string containing the date, FALSE if the passed date could not be parsed. - */ -function facetapi_get_next_date_increment($date, $gap) { - if (preg_match(FACETAPI_REGEX_DATE, $date, $match)) { - - // Increments the timestamp. - switch ($gap) { - case FACETAPI_DATE_MONTH: - $match[2] += 1; - break; - case FACETAPI_DATE_DAY: - $match[3] += 1; - break; - case FACETAPI_DATE_HOUR: - $match[4] += 1; - break; - case FACETAPI_DATE_MINUTE: - $match[5] += 1; - break; - case FACETAPI_DATE_SECOND: - $match[6] += 1; - break; - default: - $match[1] += 1; - break; - } - - // Gets the next incremenet. - return facetapi_isodate( - gmmktime($match[4], $match[5], $match[6], $match[2], $match[3], $match[1]) - ); - } - return FALSE; -} - -/** - * Compares two timestamp gaps. - * - * @param string $gap1 - * @param string $gap2 - * - * @return int - * Returns -1 if gap1 is less than gap2, 1 if gap1 is greater than gap2, and 0 - * if they are equal. - */ -function facetapi_gap_compare($gap1, $gap2) { - - $gap_numbers = array( - FACETAPI_DATE_YEAR => 6, - FACETAPI_DATE_MONTH => 5, - FACETAPI_DATE_DAY => 4, - FACETAPI_DATE_HOUR => 3, - FACETAPI_DATE_MINUTE => 2, - FACETAPI_DATE_SECOND => 1, - ); - - $gap1_num = isset($gap_numbers[$gap1]) ? $gap_numbers[$gap1] : 6; - $gap2_num = isset($gap_numbers[$gap2]) ? $gap_numbers[$gap2] : 6; - - if ($gap1_num == $gap2_num) { - return 0; - } - else { - return ($gap1_num < $gap2_num) ? -1 : 1; - } -} \ No newline at end of file diff --git a/facets.services.yml b/facets.services.yml index 1be585e..f9693d9 100644 --- a/facets.services.yml +++ b/facets.services.yml @@ -22,3 +22,5 @@ services: - '@plugin.manager.facets.facet_source' - '@plugin.manager.facets.processor' - '@entity_type.manager' + facets.utility.date: + class: Drupal\facets\Utility\FacetsDate diff --git a/src/Utility/FacetsDate.php b/src/Utility/FacetsDate.php new file mode 100644 index 0000000..37b53a2 --- /dev/null +++ b/src/Utility/FacetsDate.php @@ -0,0 +1,380 @@ + 6, + static::FACETS_DATE_MONTH => 5, + static::FACETS_DATE_DAY => 4, + static::FACETS_DATE_HOUR => 3, + static::FACETS_DATE_MINUTE => 2, + static::FACETS_DATE_SECOND => 1, + ]; + + // Gets gap numbers for both the gap and minimum gap, checks if the next gap + // is within the limit set by the $min_gap parameter. + $gap_num = isset($gap_numbers[$gap]) ? $gap_numbers[$gap] : 6; + $min_num = isset($gap_numbers[$min_gap]) ? $gap_numbers[$min_gap] : 1; + + return ($gap_num > $min_num) ? array_search($gap_num - 1, $gap_numbers) : $min_gap; + } + + /** + * Determines the best search gap to use for an arbitrary date range. + * + * Generally, we use the maximum gap that fits between the start and end date. + * If they are more than a year apart, 1 year; if they are more than a month + * apart, 1 month; etc. + * + * This function uses Unix timestamps for its computation and so is not useful + * for dates outside that range. + * + * @param int $start_time + * A string containing the start date as an ISO date string. + * @param int $end_time + * A string containing the end date as an ISO date string. + * @param string|NULL $min_gap + * (Optional) The minimum gap that should be returned. + * + * @return string + * A string containing the gap, see FACETAPI_DATE_* constants for valid + * values. Returns FALSE of either of the dates cannot be converted to a + * timestamp. + */ + public function getTimestampGap($start_time, $end_time, $min_gap = NULL) { + $time_diff = $end_time - $start_time; + + switch (TRUE) { + // NOTE: 31536000 == 60 * 60 * 24 * 365 + case ($time_diff >= 31536000): + $gap = static::FACETS_DATE_YEAR; + break; + + case ($time_diff >= 86400 * gmdate('t', $start_time)): + $gap = static::FACETS_DATE_MONTH; + break; + + case ($time_diff >= 86400): + $gap = static::FACETS_DATE_DAY; + break; + + case ($time_diff >= 3600): + $gap = static::FACETS_DATE_HOUR; + break; + + case ($time_diff >= 60): + $gap = static::FACETS_DATE_MINUTE; + break; + + default: + $gap = static::FACETS_DATE_SECOND; + break; + } + + // Return the calculated gap if a minimum gap was not passed of the calculated + // gap is a larger interval than the minimum gap. + if (is_null($min_gap) || $this->dateGapCompare($gap, $min_gap) >= 0) { + return $gap; + } + else { + return $min_gap; + } + } + + /** + * Converts ISO date strings to Unix timestamps. + * + * The ::facetapi_get_timestamp_gap() function is used to calculate the gap. + * + * @param string $start_date + * A string containing the start date as an ISO date string. + * @param string $end_date + * A string containing the end date as an ISO date string. + * @param string|NULL $min_gap + * (Optional) The minimum gap that should be returned. + * + * @return string|bool + * A string containing the gap, see FACETAPI_DATE_* constants for valid + * values. Returns FALSE of either of the dates cannot be converted to a + * timestamp. + * + * @see getTimestampGap() + */ + public function getDateGap($start_date, $end_date, $min_gap = NULL) { + $range = [strtotime($start_date), strtotime($end_date)]; + + if (!in_array(FALSE, $range, TRUE)) { + return $this->getTimestampGap($range[0], $range[1], $min_gap); + } + return FALSE; + } + + /** + * Returns a formatted date based on the passed timestamp and gap. + * + * This function assumes that gaps less than one day will be displayed in a + * search context in which a larger containing gap including a day is already + * displayed. So, HOUR, MINUTE, and SECOND gaps only display time information, + * without date. + * + * @param int $timestamp + * An integer containing the Unix timestamp. + * @param string $gap + * A string containing the gap, see FACETAPI_DATE_* constants for valid + * values, defaults to YEAR. + * + * @return + * A gap-appropriate display date used in the facet link. + */ + public function formatTimestamp($timestamp, $gap = self::FACETS_DATE_YEAR) { + switch ($gap) { + case static::FACETS_DATE_MONTH: + return format_date($timestamp, 'custom', 'F Y', 'UTC'); + + case static::FACETS_DATE_DAY: + return format_date($timestamp, 'custom', 'F j, Y', 'UTC'); + + case static::FACETS_DATE_HOUR: + return format_date($timestamp, 'custom', 'g A', 'UTC'); + + case static::FACETS_DATE_MINUTE: + return format_date($timestamp, 'custom', 'g:i A', 'UTC'); + + case static::FACETS_DATE_SECOND: + return format_date($timestamp, 'custom', 'g:i:s A', 'UTC'); + + default: + return format_date($timestamp, 'custom', 'Y', 'UTC'); + } + } + + /** + * Returns a formatted date based on the passed ISO date string and gap. + * + * @param string $date + * A string containing the date as an ISO date string. + * @param string $gap + * A string containing the gap, see FACETAPI_DATE_* constants for valid + * values, defaults to YEAR. + * @param string $callback + * The formatting callback, defaults to "facets_format_timestamp". + * + * @return + * A gap-appropriate display date used in the facet link. + * + * @see facetsFormatTimestamp() + */ + public function facetsFormatDate($date, $gap = self::FACETS_DATE_YEAR, $callback = 'facets_format_timestamp') { + $timestamp = strtotime($date); + return $callback($timestamp, $gap); + } + + /** + * Returns the next increment from the given ISO date and gap. This function is + * useful for getting the upper limit of a date range from the given start + * date. + * + * @param string $date + * A string containing the date as an ISO date string. + * @param string $gap + * A string containing the gap, see FACETAPI_DATE_* constants for valid + * values, defaults to YEAR. + * + * @return + * A string containing the date, FALSE if the passed date could not be parsed. + */ + public function getNextDateIncrement($date, $gap) { + if (preg_match(static::FACETS_REGEX_DATE, $date, $match)) { + + // Increments the timestamp. + switch ($gap) { + case static::FACETS_DATE_MONTH: + $match[2] += 1; + break; + case static::FACETS_DATE_DAY: + $match[3] += 1; + break; + case static::FACETS_DATE_HOUR: + $match[4] += 1; + break; + case static::FACETS_DATE_MINUTE: + $match[5] += 1; + break; + case static::FACETS_DATE_SECOND: + $match[6] += 1; + break; + default: + $match[1] += 1; + break; + } + + // Gets the next incremenet. + $time = gmmktime( + $match[4], + $match[5], + $match[6], + $match[2], + $match[3], + $match[1] + ); + return $this->isoDate($time); + } + return FALSE; + } + + /** + * Compares two timestamp gaps. + * + * @param string $gap1 + * @param string $gap2 + * + * @return int + * Returns -1 if gap1 is less than gap2, 1 if gap1 is greater than gap2, and 0 + * if they are equal. + */ + public function dateGapCompare($gap1, $gap2) { + + $gap_numbers = [ + static::FACETS_DATE_YEAR => 6, + static::FACETS_DATE_MONTH => 5, + static::FACETS_DATE_DAY => 4, + static::FACETS_DATE_HOUR => 3, + static::FACETS_DATE_MINUTE => 2, + static::FACETS_DATE_SECOND => 1, + ]; + + $gap1_num = isset($gap_numbers[$gap1]) ? $gap_numbers[$gap1] : 6; + $gap2_num = isset($gap_numbers[$gap2]) ? $gap_numbers[$gap2] : 6; + + if ($gap1_num == $gap2_num) { + return 0; + } + else { + return ($gap1_num < $gap2_num) ? -1 : 1; + } + } + +} diff --git a/tests/src/Unit/Utility/FacetsDateTest.php b/tests/src/Unit/Utility/FacetsDateTest.php new file mode 100644 index 0000000..206f86d --- /dev/null +++ b/tests/src/Unit/Utility/FacetsDateTest.php @@ -0,0 +1,105 @@ +assertEquals($iso_date, $fd->isoDate(564957784, $gap)); + } + + /** + * Tests for ::getNextDateGap. + */ + public function testGetNextDateGap() { + $fd = new FacetsDate(); + + $gap = $fd->getNextDateGap($fd::FACETS_DATE_SECOND); + $this->assertEquals($fd::FACETS_DATE_SECOND, $gap); + + $gap = $fd->getNextDateGap($fd::FACETS_DATE_MINUTE); + $this->assertEquals($fd::FACETS_DATE_SECOND, $gap); + + $gap = $fd->getNextDateGap($fd::FACETS_DATE_SECOND, $fd::FACETS_DATE_MINUTE); + $this->assertEquals($fd::FACETS_DATE_MINUTE, $gap); + + $gap = $fd->getNextDateGap($fd::FACETS_DATE_MINUTE, $fd::FACETS_DATE_MINUTE); + $this->assertEquals($fd::FACETS_DATE_MINUTE, $gap); + + $gap = $fd->getNextDateGap($fd::FACETS_DATE_SECOND, $fd::FACETS_DATE_HOUR); + $this->assertEquals($fd::FACETS_DATE_HOUR, $gap); + + $gap = $fd->getNextDateGap($fd::FACETS_DATE_MINUTE, $fd::FACETS_DATE_HOUR); + $this->assertEquals($fd::FACETS_DATE_HOUR, $gap); + + $gap = $fd->getNextDateGap($fd::FACETS_DATE_HOUR, $fd::FACETS_DATE_HOUR); + $this->assertEquals($fd::FACETS_DATE_HOUR, $gap); + } + + /** + * Tests for ::getTimestampGap + */ + public function testGetTimestampGap() { + $fd = new FacetsDate(); + $gap = $fd->getTimestampGap(564957784, 651962584); + $this->assertEquals($fd::FACETS_DATE_YEAR, $gap); + + $gap = $fd->getTimestampGap(564957784, 651962584, $fd::FACETS_DATE_SECOND); + $this->assertEquals($fd::FACETS_DATE_YEAR, $gap); + + $gap = $fd->getTimestampGap(564957784, 564967784); + $this->assertEquals($fd::FACETS_DATE_HOUR, $gap); + + $gap = $fd->getTimestampGap(564957784, 564967784, $fd::FACETS_DATE_SECOND); + $this->assertEquals($fd::FACETS_DATE_HOUR, $gap); + + $gap = $fd->getTimestampGap(564957784, 564967784, $fd::FACETS_DATE_MONTH); + $this->assertEquals($fd::FACETS_DATE_MONTH, $gap); + } + + /** + * Tests for ::getDateGap. + */ + public function testGetDateGap() { + $fd = new FacetsDate(); + $gap = $fd->getDateGap('1987-11-26T20:43:04Z', '1990-08-29T20:43:04Z'); + $this->assertEquals($fd::FACETS_DATE_YEAR, $gap); + } + + /** + * Returns a data provider for the ::testIsoDate(). + * + * @return array + */ + public function provideIsoDates() { + return [ + ['1987-11-26T20:43:04Z', FacetsDate::FACETS_DATE_SECOND], + ['1987-11-26T20:43:00Z', FacetsDate::FACETS_DATE_MINUTE], + ['1987-11-26T20:00:00Z', FacetsDate::FACETS_DATE_HOUR], + ['1987-11-26T00:00:00Z', FacetsDate::FACETS_DATE_DAY], + ['1987-11-01T00:00:00Z', FacetsDate::FACETS_DATE_MONTH], + ['1987-01-01T00:00:00Z', FacetsDate::FACETS_DATE_YEAR], + ['1987-11-26T20:43:04Z', FacetsDate::FACETS_DATE_ISO8601], + ]; + } + +}