1: <?php
2:
3: namespace Alpha\Util\Search;
4:
5: use Alpha\Exception\RecordNotFoundException;
6: use Alpha\Exception\ValidationException;
7: use Alpha\Util\Logging\Logger;
8: use Alpha\Util\Config\ConfigProvider;
9: use Alpha\Util\Cache\CacheProviderFactory;
10: use Alpha\Model\Tag;
11:
12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54:
55: class SearchProviderTags implements SearchProviderInterface
56: {
57: 58: 59: 60: 61: 62: 63:
64: private static $logger;
65:
66: 67: 68: 69: 70: 71: 72:
73: private $numberFound = 0;
74:
75: 76: 77: 78: 79:
80: public function __construct()
81: {
82: self::$logger = new Logger('SearchProviderTags');
83: }
84:
85: 86: 87:
88: public function search($query, $returnType = 'all', $start = 0, $limit = 10)
89: {
90: $config = ConfigProvider::getInstance();
91:
92:
93: $queryTags = Tag::tokenize($query, '', '', false);
94: $matchingTags = array();
95:
96:
97: foreach ($queryTags as $queryTag) {
98: $tags = $queryTag->loadAllByAttribute('content', $queryTag->get('content'));
99: $matchingTags = array_merge($matchingTags, $tags);
100: }
101:
102:
103: $results = array();
104:
105: $matches = array();
106:
107: if ($config->get('cache.provider.name') != '' && count($queryTags) == 1) {
108: $key = $queryTags[0]->get('content');
109: $matches = $this->loadFromCache($key);
110: }
111:
112: if (count($matches) == 0) {
113: 114: 115: 116: 117:
118: foreach ($matchingTags as $tag) {
119: if ($returnType == 'all' || $tag->get('taggedClass') == $returnType) {
120: $key = $tag->get('taggedClass').'-'.$tag->get('taggedOID');
121:
122: if (isset($matches[$key])) {
123:
124: $weight = intval($matches[$key]) + 1;
125: $matches[$key] = $weight;
126: } else {
127: $matches[$key] = 1;
128: }
129: }
130: }
131:
132: if ($config->get('cache.provider.name') != '' && count($queryTags) == 1) {
133: $key = $queryTags[0]->get('content');
134: $this->addToCache($key, $matches);
135: }
136: }
137:
138:
139: arsort($matches);
140:
141: $this->numberFound = count($matches);
142:
143:
144: $matches = array_slice($matches, $start, $limit + 5);
145:
146:
147: foreach ($matches as $key => $weight) {
148: if (count($results) < $limit) {
149: $parts = explode('-', $key);
150:
151: try {
152: $BO = new $parts[0]();
153: $BO->load($parts[1]);
154:
155: $results[] = $BO;
156: } catch (RecordNotFoundException $e) {
157: self::$logger->warn('Orpaned Tag detected pointing to a non-existant BO of OID ['.$parts[1].'] and type ['.$parts[0].'].');
158: }
159: }
160: }
161:
162: return $results;
163: }
164:
165: 166: 167:
168: public function getRelated($sourceObject, $returnType = 'all', $start = 0, $limit = 10, $distinct = '')
169: {
170: $config = ConfigProvider::getInstance();
171:
172:
173: $results = array();
174:
175: $matches = array();
176:
177: $distinctValues = array();
178:
179: if ($config->get('cache.provider.name') != '') {
180: $key = get_class($sourceObject).'-'.$sourceObject->getOID().'-related'.($distinct == '' ? '' : '-distinct');
181: $matches = $this->loadFromCache($key);
182: }
183:
184: if (count($matches) == 0) {
185:
186: $tags = $sourceObject->getPropObject('tags')->getRelatedObjects();
187:
188: foreach ($tags as $tag) {
189: $Tag = new Tag();
190:
191: if ($distinct == '') {
192: $matchingTags = $Tag->query('SELECT * FROM '.$Tag->getTableName()." WHERE
193: content='".$tag->get('content')."' AND NOT
194: (taggedOID = '".$sourceObject->getOID()."' AND taggedClass = '".get_class($sourceObject)."');");
195: } else {
196:
197: $matchingTags = $Tag->query('SELECT * FROM '.$Tag->getTableName()." WHERE
198: content='".$tag->get('content')."' AND NOT
199: (taggedOID = '".$sourceObject->getOID()."' AND taggedClass = '".get_class($sourceObject)."')
200: AND taggedOID IN (SELECT OID FROM ".$sourceObject->getTableName().' WHERE '.$distinct." != '".addslashes($sourceObject->get($distinct))."');");
201: }
202:
203: foreach ($matchingTags as $matchingTag) {
204: if ($returnType == 'all' || $tag->get('taggedClass') == $returnType) {
205: $key = $matchingTag['taggedClass'].'-'.$matchingTag['taggedOID'];
206:
207:
208: if ($distinct != '') {
209: try {
210: $BO = new $matchingTag['taggedClass']();
211: $BO->load($matchingTag['taggedOID']);
212:
213:
214: if ($sourceObject->get($distinct) == $BO->get($distinct)) {
215: continue;
216: }
217:
218: if (!in_array($BO->get($distinct), $distinctValues)) {
219: $distinctValues[] = $BO->get($distinct);
220: } else {
221: continue;
222: }
223: } catch (RecordNotFoundException $e) {
224: self::$logger->warn('Error loading object ['.$matchingTag['taggedOID'].'] of type ['.$matchingTag['taggedClass'].'], probable orphan');
225: }
226: }
227:
228: if (isset($matches[$key])) {
229:
230: $weight = intval($matches[$key]) + 1;
231: $matches[$key] = $weight;
232: } else {
233: $matches[$key] = 1;
234: }
235: }
236: }
237:
238: if ($config->get('cache.provider.name') != '') {
239: $key = get_class($sourceObject).'-'.$sourceObject->getOID().'-related'.($distinct == '' ? '' : '-distinct');
240: $this->addToCache($key, $matches);
241: }
242: }
243: }
244:
245:
246: arsort($matches);
247:
248: $this->numberFound = count($matches);
249:
250:
251: $matches = array_slice($matches, $start, $limit);
252:
253:
254: foreach ($matches as $key => $weight) {
255: $parts = explode('-', $key);
256:
257: $BO = new $parts[0]();
258: $BO->load($parts[1]);
259:
260: $results[] = $BO;
261: }
262:
263: return $results;
264: }
265:
266: 267: 268:
269: public function index($sourceObject)
270: {
271: $taggedAttributes = $sourceObject->getTaggedAttributes();
272:
273: foreach ($taggedAttributes as $tagged) {
274: $tags = Tag::tokenize($sourceObject->get($tagged), get_class($sourceObject), $sourceObject->getOID());
275:
276: foreach ($tags as $tag) {
277: try {
278: $tag->save();
279: } catch (ValidationException $e) {
280: 281: 282: 283:
284: }
285: }
286: }
287: }
288:
289: 290: 291:
292: public function delete($sourceObject)
293: {
294: $tags = $sourceObject->getPropObject('tags')->getRelatedObjects();
295:
296: foreach ($tags as $tag) {
297: $tag->delete();
298: }
299: }
300:
301: 302: 303:
304: public function getNumberFound()
305: {
306: return $this->numberFound;
307: }
308:
309: 310: 311: 312: 313:
314: private function loadFromCache($key)
315: {
316: $config = ConfigProvider::getInstance();
317:
318: try {
319: $cache = CacheProviderFactory::getInstance($config->get('cache.provider.name'));
320: $matches = $cache->get($key);
321:
322: if (!$matches) {
323: self::$logger->debug('Cache miss on key ['.$key.']');
324:
325: return array();
326: } else {
327: self::$logger->debug('Cache hit on key ['.$key.']');
328:
329: return $matches;
330: }
331: } catch (\Exception $e) {
332: self::$logger->error('Error while attempting to load a search result from ['.$config->get('cache.provider.name').']
333: instance: ['.$e->getMessage().']');
334:
335: return array();
336: }
337: }
338:
339: 340: 341: 342: 343:
344: public function addToCache($key, $matches)
345: {
346: $config = ConfigProvider::getInstance();
347:
348: try {
349: $cache = CacheProviderFactory::getInstance($config->get('cache.provider.name'));
350: $cache->set($key, $matches, 86400);
351: } catch (\Exception $e) {
352: self::$logger->error('Error while attempting to store a search matches array to the ['.$config->get('cache.provider.name').']
353: instance: ['.$e->getMessage().']');
354: }
355: }
356: }
357: