Completed
Push — dev2 ( f6a7ae...88484c )
by Gordon
07:43
created

SearchableHelper   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 257
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 9

Test Coverage

Coverage 95.95%

Importance

Changes 6
Bugs 2 Features 0
Metric Value
wmc 48
lcom 0
cbo 9
dl 0
loc 257
ccs 166
cts 173
cp 0.9595
rs 8.4865
c 6
b 2
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
B addIndexedFields() 0 35 5
A assignSpecForRelationship() 0 15 3
B assignSpecForStandardFieldType() 0 17 6
B getFormatForDate() 0 19 5
A getListRelationshipMethods() 0 12 2
B isInSiteTree() 0 14 5
A storeMethodTextValue() 0 12 2
A storeFieldHTMLValue() 0 8 2
B storeRelationshipValue() 0 30 6
A findOrCreateSearchableClass() 0 11 2
B findOrCreateSearchableField() 0 26 4
B fieldsToElasticaConfig() 0 17 6

How to fix   Complexity   

Complex Class

Complex classes like SearchableHelper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SearchableHelper, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace SilverStripe\Elastica;
3
4
class SearchableHelper {
5
6
7 1
	public static function addIndexedFields($name, &$spec, $ownerClassName) {
8
		// in the case of a relationship type will not be set
9 1
		if(isset($spec['type'])) {
10 1
			if($spec['type'] == 'string') {
11 1
				$unstemmed = array();
12 1
				$unstemmed['type'] = "string";
13 1
				$unstemmed['analyzer'] = "unstemmed";
14 1
				$unstemmed['term_vector'] = "yes";
15 1
				$extraFields = array('standard' => $unstemmed);
16
17 1
				$shingles = array();
18 1
				$shingles['type'] = "string";
19 1
				$shingles['analyzer'] = "shingles";
20 1
				$shingles['term_vector'] = "yes";
21 1
				$extraFields['shingles'] = $shingles;
22
23
				//Add autocomplete field if so required
24 1
				$autocomplete = \Config::inst()->get($ownerClassName, 'searchable_autocomplete');
25
26 1
				if(isset($autocomplete) && in_array($name, $autocomplete)) {
27 1
					$autocompleteField = array();
28 1
					$autocompleteField['type'] = "string";
29 1
					$autocompleteField['index_analyzer'] = "autocomplete_index_analyzer";
30 1
					$autocompleteField['search_analyzer'] = "autocomplete_search_analyzer";
31 1
					$autocompleteField['term_vector'] = "yes";
32 1
					$extraFields['autocomplete'] = $autocompleteField;
33 1
				}
34
35 1
				$spec['fields'] = $extraFields;
36
				// FIXME - make index/locale specific, get from settings
37 1
				$spec['analyzer'] = 'stemmed';
38 1
				$spec['term_vector'] = "yes";
39 1
			}
40 1
		}
41 1
	}
42
43
44
	/**
45
	 * @param string &$name
46
	 * @param boolean $storeMethodName
47
	 * @param boolean $recurse
48
	 */
49 1
	public static function assignSpecForRelationship(&$name, $resultType, &$spec, $storeMethodName, $recurse) {
50 1
		$resultTypeInstance = \Injector::inst()->create($resultType);
51 1
		$resultTypeMapping = array();
52
		// get the fields for the result type, but do not recurse
53 1
		if($recurse) {
54 1
			$resultTypeMapping = $resultTypeInstance->getElasticaFields($storeMethodName, false);
55 1
		}
56 1
		$resultTypeMapping['ID'] = array('type' => 'integer');
57 1
		if($storeMethodName) {
58 1
			$resultTypeMapping['__method'] = $name;
59 1
		}
60 1
		$spec = array('properties' => $resultTypeMapping);
61
		// we now change the name to the result type, not the method name
62 1
		$name = $resultType;
63 1
	}
64
65
66
	/**
67
	 * @param string $name
68
	 */
69 1
	public static function assignSpecForStandardFieldType($name, $class, &$spec, &$html_fields, &$mappings) {
0 ignored issues
show
Coding Style Naming introduced by
The parameter $html_fields is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
70 1
		if(($pos = strpos($class, '('))) {
71
			// Valid in the case of Varchar(255)
72 1
			$class = substr($class, 0, $pos);
73 1
		}
74
75 1
		if(array_key_exists($class, $mappings)) {
76 1
			$spec['type'] = $mappings[$class];
77 1
			if($spec['type'] === 'date') {
78 1
				$spec['format'] = SearchableHelper::getFormatForDate($class);
1 ignored issue
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
79 1
			}
80
81 1
			if($class === 'HTMLText' || $class === 'HTMLVarchar') {
82 1
				array_push($html_fields, $name);
83 1
			}
84 1
		}
85 1
	}
86
87
88 1
	public static function getFormatForDate($class) {
89 1
		$format = 'y-M-d'; // default
90
		switch ($class) {
91 1
			case 'Date':
92 1
				$format = 'y-M-d';
93 1
				break;
94 1
			case 'SS_Datetime':
95 1
				$format = 'y-M-d H:m:s';
96 1
				break;
97 1
			case 'Datetime':
98 1
				$format = 'y-M-d H:m:s';
99 1
				break;
100 1
			case 'Time':
101 1
				$format = 'H:m:s';
102 1
				break;
103
		}
104
105 1
		return $format;
106
	}
107
108
109
110 1
	public static function getListRelationshipMethods($instance) {
111 1
		$has_manys = $instance->has_many();
112 1
		$many_manys = $instance->many_many();
113
114
		// array of method name to retuned object ClassName for relationships returning lists
115 1
		$has_lists = $has_manys;
116 1
		foreach(array_keys($many_manys) as $key) {
117 1
			$has_lists[$key] = $many_manys[$key];
118 1
		}
119
120 1
		return $has_lists;
121
	}
122
123
124 1
	public static function isInSiteTree($classname) {
125 1
		$inSiteTree = ($classname === 'SiteTree' ? true : false);
126 1
		if(!$inSiteTree) {
127 1
			$class = new \ReflectionClass($classname);
128 1
			while($class = $class->getParentClass()) {
129 1
				$parentClass = $class->getName();
130 1
				if($parentClass == 'SiteTree') {
131 1
					$inSiteTree = true;
132 1
					break;
133
				}
134 1
			}
135 1
		}
136 1
		return $inSiteTree;
137
	}
138
139
140 1
	public static function storeMethodTextValue($instance, $field, &$fields, $html_fields) {
0 ignored issues
show
Coding Style Naming introduced by
The parameter $html_fields is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
141 1
		if(in_array($field, $html_fields)) {
142
			// Parse short codes in HTML, and then convert to text
143 1
			$fields[$field] = $instance->$field;
144 1
			$html = \ShortcodeParser::get_active()->parse($instance->$field());
145 1
			$txt = \Convert::html2raw($html);
146 1
			$fields[$field] = $txt;
147 1
		} else {
148
			// Plain text
149 1
			$fields[$field] = $instance->$field();
150
		}
151 1
	}
152
153
154 1
	public static function storeFieldHTMLValue($instance, $field, &$fields) {
155 1
		$fields[$field] = $instance->$field;
156 1
		if(gettype($instance->$field) !== 'NULL') {
157 1
			$html = \ShortcodeParser::get_active()->parse($instance->$field);
158 1
			$txt = \Convert::html2raw($html);
159 1
			$fields[$field] = $txt;
160 1
		}
161 1
	}
162
163
164 1
	public static function storeRelationshipValue($instance, $field, &$fields, $config, $recurse) {
165 1
		if(isset($config['properties']['__method'])) {
166 1
			$methodName = $config['properties']['__method'];
167 1
			$data = $instance->$methodName();
168 1
			$relArray = array();
169
170 1
			$has_ones = $instance->has_one();
171
			// get the fields of a has_one relational object
172 1
			if(isset($has_ones[$methodName])) {
173 1
				if($data->ID > 0) {
174
					$item = $data->getFieldValuesAsArray(false);
175
					$relArray = $item;
176
				}
177
178
			// get the fields for a has_many or many_many relational list
179 1
			} else {
180 1
				foreach($data->getIterator() as $item) {
181
					if($recurse) {
182
						// populate the subitem but do not recurse any further if more relationships
183
						$itemDoc = $item->getFieldValuesAsArray(false);
184
						array_push($relArray, $itemDoc);
185
					}
186 1
				}
187
			}
188
			// save the relation as an array (for now)
189 1
			$fields[$methodName] = $relArray;
190 1
		} else {
191 1
			$fields[$field] = $instance->$field;
192
		}
193 1
	}
194
195
196 1
	public static function findOrCreateSearchableClass($classname) {
197 1
		$doSC = \SearchableClass::get()->filter(array('Name' => $classname))->first();
198 1
		if(!$doSC) {
199 1
			$doSC = new \SearchableClass();
200 1
			$doSC->Name = $classname;
201 1
			$inSiteTree = SearchableHelper::isInSiteTree($classname);
1 ignored issue
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
202 1
			$doSC->InSiteTree = $inSiteTree;
203 1
			$doSC->write();
204 1
		}
205 1
		return $doSC;
206
	}
207
208
209 1
	public static function findOrCreateSearchableField($className, $fieldName, $searchableField, $searchableClass) {
210 1
		$filter = array('ClazzName' => $className, 'Name' => $fieldName);
211 1
		$doSF = \SearchableField::get()->filter($filter)->first();
212
213
214 1
		if(!$doSF) {
215 1
			$doSF = new \SearchableField();
216 1
			$doSF->ClazzName = $className;
217 1
			$doSF->Name = $fieldName;
218
219 1
			if(isset($searchableField['type'])) {
220 1
				$doSF->Type = $searchableField['type'];
221 1
			} else {
222 1
				$doSF->Name = $searchableField['properties']['__method'];
223 1
				$doSF->Type = 'relationship';
224
			}
225 1
			$doSF->SearchableClassID = $searchableClass->ID;
226
227 1
			if(isset($searchableField['fields']['autocomplete'])) {
228 1
				$doSF->Autocomplete = true;
229 1
			}
230
231 1
			$doSF->write();
232 1
			\DB::alteration_message("Created new searchable editable field " . $fieldName, "changed");
233 1
		}
234 1
	}
235
236
237
	/*
238
	Evaluate each field, e.g. 'Title', 'Member.Name'
239
	 */
240 1
	public static function fieldsToElasticaConfig($fields) {
241
		// Copied from DataObject::searchableFields() as there is no separate accessible method
242 1
		$rewrite = array();
243 1
		foreach($fields as $name => $specOrName) {
244 1
			$identifer = (is_int($name)) ? $specOrName : $name;
245 1
			$rewrite[$identifer] = array();
246 1
			if(!isset($rewrite[$identifer]['title'])) {
247 1
				$rewrite[$identifer]['title'] = (isset($labels[$identifer]))
248 1
					? $labels[$identifer] : \FormField::name_to_label($identifer);
249 1
			}
250 1
			if(!isset($rewrite[$identifer]['filter'])) {
251 1
				$rewrite[$identifer]['filter'] = 'PartialMatchFilter';
252 1
			}
253 1
		}
254
255 1
		return $rewrite;
256
	}
257
258
259
260
}
261