diff --git a/code/search/SearchIndex.php b/code/search/SearchIndex.php index 7e4ab9a..eba8096 100644 --- a/code/search/SearchIndex.php +++ b/code/search/SearchIndex.php @@ -37,6 +37,27 @@ abstract class SearchIndex extends ViewableData */ private static $hide_ancestor; + /** + * Used to separate class name and relation name in the sources array + * this string must not be present in class name + * @var string + * @config + */ + private static $class_delimiter = '_|_'; + + /** + * This is used to clean the source name from suffix + * suffixes are needed to support multiple relations with the same name on different page types + * @param string $source + * @return string + */ + private function getSourceName($source) + { + $source = explode(self::config()->get('class_delimiter'), $source); + + return $source[0]; + } + public function __construct() { parent::__construct(); @@ -77,19 +98,35 @@ abstract class SearchIndex extends ViewableData foreach ($lookups as $lookup) { $next = array(); - foreach ($sources as $source => $options) { - $class = null; + foreach ($sources as $source => $baseOptions) { + $source = $this->getSourceName($source); - foreach (SearchIntrospection::hierarchy($source, $options['include_children']) as $dataclass) { + foreach (SearchIntrospection::hierarchy($source, $baseOptions['include_children']) as $dataclass) { + $class = null; + $options = $baseOptions; $singleton = singleton($dataclass); if ($hasOne = $singleton->has_one($lookup)) { + // we only want to include base class for relation, omit classes that inherited the relation + $relationList = Config::inst()->get($dataclass, 'has_one', Config::UNINHERITED); + $relationList = (!is_null($relationList)) ? $relationList : array(); + if (!array_key_exists($lookup, $relationList)) { + continue; + } + $class = $hasOne; $options['lookup_chain'][] = array( 'call' => 'method', 'method' => $lookup, 'through' => 'has_one', 'class' => $dataclass, 'otherclass' => $class, 'foreignkey' => "{$lookup}ID" ); } elseif ($hasMany = $singleton->has_many($lookup)) { + // we only want to include base class for relation, omit classes that inherited the relation + $relationList = Config::inst()->get($dataclass, 'has_many', Config::UNINHERITED); + $relationList = (!is_null($relationList)) ? $relationList : array(); + if (!array_key_exists($lookup, $relationList)) { + continue; + } + $class = $hasMany; $options['multi_valued'] = true; $options['lookup_chain'][] = array( @@ -97,6 +134,13 @@ abstract class SearchIndex extends ViewableData 'through' => 'has_many', 'class' => $dataclass, 'otherclass' => $class, 'foreignkey' => $singleton->getRemoteJoinField($lookup, 'has_many') ); } elseif ($manyMany = $singleton->many_many($lookup)) { + // we only want to include base class for relation, omit classes that inherited the relation + $relationList = Config::inst()->get($dataclass, 'many_many', Config::UNINHERITED); + $relationList = (!is_null($relationList)) ? $relationList : array(); + if (!array_key_exists($lookup, $relationList)) { + continue; + } + $class = $manyMany[1]; $options['multi_valued'] = true; $options['lookup_chain'][] = array( @@ -109,8 +153,10 @@ abstract class SearchIndex extends ViewableData if (!isset($options['origin'])) { $options['origin'] = $dataclass; } - $next[$class] = $options; - continue 2; + + // we add suffix here to prevent the relation to be overwritten by other instances + // all sources lookups must clean the source name before reading it via getSourceName() + $next[$class . self::config()->get('class_delimiter') . $dataclass] = $options; } } } @@ -123,6 +169,7 @@ abstract class SearchIndex extends ViewableData } foreach ($sources as $class => $options) { + $class = $this->getSourceName($class); $dataclasses = SearchIntrospection::hierarchy($class, $options['include_children']); while (count($dataclasses)) {