Overview

Packages

  • alpha::controller
  • alpha::controller::front
  • alpha::exceptions
  • alpha::model
  • alpha::model::types
  • alpha::tasks
  • alpha::tests
  • alpha::util
  • alpha::util::cache
  • alpha::util::codehighlight
  • alpha::util::convertors
  • alpha::util::feeds
  • alpha::util::filters
  • alpha::util::graphs
  • alpha::util::helpers
  • alpha::util::metrics
  • alpha::util::search
  • alpha::view
  • alpha::view::renderers
  • alpha::view::widgets

Classes

  • SearchProviderFactory
  • SearchProviderTags

Interfaces

  • SearchProviderInterface
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  *
  5:  * Uses the TagObject business oject to store searchable tags in the main
  6:  * application database.
  7:  *
  8:  * @package alpha::util::search
  9:  * @since 1.2.3
 10:  * @author John Collins <dev@alphaframework.org>
 11:  * @version $Id: SearchProviderTags.inc 1701 2013-12-18 22:33:25Z alphadevx $
 12:  * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
 13:  * @copyright Copyright (c) 2013, John Collins (founder of Alpha Framework).
 14:  * All rights reserved.
 15:  *
 16:  * <pre>
 17:  * Redistribution and use in source and binary forms, with or
 18:  * without modification, are permitted provided that the
 19:  * following conditions are met:
 20:  *
 21:  * * Redistributions of source code must retain the above
 22:  *   copyright notice, this list of conditions and the
 23:  *   following disclaimer.
 24:  * * Redistributions in binary form must reproduce the above
 25:  *   copyright notice, this list of conditions and the
 26:  *   following disclaimer in the documentation and/or other
 27:  *   materials provided with the distribution.
 28:  * * Neither the name of the Alpha Framework nor the names
 29:  *   of its contributors may be used to endorse or promote
 30:  *   products derived from this software without specific
 31:  *   prior written permission.
 32:  *
 33:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 34:  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 35:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 36:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 37:  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 38:  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 39:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 40:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 41:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 42:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 43:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 44:  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 45:  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 46:  * </pre>
 47:  *
 48:  */
 49: class SearchProviderTags implements SearchProviderInterface {
 50: 
 51:     /**
 52:      * Trace logger
 53:      *
 54:      * @var Logger
 55:      * @since 1.2.3
 56:      */
 57:     private static $logger;
 58: 
 59:     /**
 60:      * The number of matches found for the current search.
 61:      *
 62:      * @var integer
 63:      * @since 1.2.3
 64:      */
 65:     private $numberFound = 0;
 66: 
 67:     /**
 68:      * constructor to set up the object
 69:      *
 70:      * @since 1.2.3
 71:      */
 72:     public function __construct() {
 73:         self::$logger = new Logger('SearchProviderTags');
 74:     }
 75: 
 76:     /**
 77:      * {@inheritdoc}
 78:      */
 79:     public function search($query, $returnType = 'all', $start = 0, $limit = 10) {
 80: 
 81:         // explode the user's query into a set of tokenized transient TagObjects
 82:         $queryTags = TagObject::tokenize($query, '', '', false);
 83:         $matchingTags = array();
 84: 
 85:         // load TagObjects from the DB where content equals the content of one of our transient TagObjects
 86:         foreach($queryTags as $queryTag) {
 87:             $tags = $queryTag->loadAllByAttribute('content', $queryTag->get('content'));
 88:             $matchingTags = array_merge($matchingTags, $tags);
 89:         }
 90: 
 91:         // the result objects
 92:         $results = array();
 93:         // matching tags with weights
 94:         $matches = array();
 95: 
 96:         /*
 97:          * Build an array of BOs for the matching tags from the DB:
 98:          * array key = BO ID
 99:          * array value = weight (the amount of tags matching the BO)
100:          */
101:         foreach($matchingTags as $tag) {
102:             if ($returnType == 'all' || $tag->get('taggedClass') == $returnType) {
103: 
104:                 $key = $tag->get('taggedClass').'-'.$tag->get('taggedOID');
105: 
106:                 if(isset($matches[$key])) {
107:                     // increment the weight if the same BO is tagged more than once
108:                     $weight = intval($matches[$key]) + 1;
109:                     $matches[$key] = $weight;
110:                 }else{
111:                     $matches[$key] = 1;
112:                 }
113:             }
114:         }
115: 
116:         // sort the matches based on tag frequency weight
117:         arsort($matches);
118: 
119:         $this->numberFound = count($matches);
120: 
121:         // now paginate
122:         $matches = array_slice($matches, $start, $limit+5); // the +5 is just some padding in case of orphans
123: 
124:         // now load each object
125:         foreach ($matches as $key => $weight) {
126:             if(count($results) < $limit) {
127:                 $parts = explode('-', $key);
128: 
129:                 try {
130: 
131:                     $BO = new $parts[0];
132:                     $BO->load($parts[1]);
133: 
134:                     $results[] = $BO;
135: 
136:                 }catch(BONotFoundException $e) {
137:                     self::$logger->warn('Orpaned TagObject detected pointing to a non-existant BO of OID ['.$parts[1].'] and type ['.$parts[0].'].');
138:                 }
139:             }
140:         }
141: 
142:         return $results;
143:     }
144: 
145:     /**
146:      * {@inheritdoc}
147:      */
148:     public function getRelated(AlphaDAO $sourceObject, $returnType = 'all', $start = 0, $limit = 10) {
149: 
150:         // all the tags on the source object for comparison
151:         $tags = $sourceObject->getPropObject('tags')->getRelatedObjects();
152: 
153:         // the result objects
154:         $results = array();
155:         // matching tags with weights
156:         $matches = array();
157: 
158:         foreach($tags as $tag) {
159:             $tagObject = new TagObject();
160:             $matchingTags = $tagObject->query("SELECT * FROM ".$tagObject->getTableName()." WHERE 
161:                 content='".$tag->get('content')."' AND NOT 
162:                 (taggedOID = '".$sourceObject->getOID()."' AND taggedClass = '".get_class($sourceObject)."');");
163: 
164:             foreach($matchingTags as $matchingTag) {
165:                 if ($returnType == 'all' || $tag->get('taggedClass') == $returnType) {
166: 
167:                     $key = $matchingTag['taggedClass'].'-'.$matchingTag['taggedOID'];
168: 
169:                     if(isset($matches[$key])) {
170:                         // increment the weight if the same BO is tagged more than once
171:                         $weight = intval($matches[$key]) + 1;
172:                         $matches[$key] = $weight;
173:                     }else{
174:                         $matches[$key] = 1;
175:                     }
176:                 }
177:             }
178:         }
179: 
180:         // sort the matches based on tag frequency weight
181:         arsort($matches);
182: 
183:         $this->numberFound = count($matches);
184: 
185:         // now paginate
186:         $matches = array_slice($matches, $start, $limit);
187: 
188:         // now load each object
189:         foreach ($matches as $key => $weight) {
190:             $parts = explode('-', $key);
191: 
192:             $BO = new $parts[0];
193:             $BO->load($parts[1]);
194: 
195:             $results[] = $BO;
196:         }
197: 
198:         return $results;
199:     }
200: 
201:     /**
202:      * {@inheritdoc}
203:      */
204:     public function index(AlphaDAO $sourceObject) {
205:         $taggedAttributes = $sourceObject->getTaggedAttributes();
206: 
207:         foreach($taggedAttributes as $tagged) {
208:             $tags = TagObject::tokenize($sourceObject->get($tagged), get_class($sourceObject), $sourceObject->getOID());
209: 
210:             foreach($tags as $tag) {
211:                 try {
212:                     $tag->save();
213:                 }catch(ValidationException $e){
214:                     /*
215:                      * The unique key has most-likely been violated because this BO is already tagged with this
216:                      * value, so we can ignore in this case.
217:                      */
218:                 }
219:             }
220:         }
221:     }
222: 
223:     /**
224:      * {@inheritdoc}
225:      */
226:     public function delete(AlphaDAO $sourceObject) {
227:         $tags = $sourceObject->getPropObject('tags')->getRelatedObjects();
228: 
229:         foreach ($tags as $tag)
230:             $tag->delete();
231:     }
232: 
233:     /**
234:      * {@inheritdoc}
235:      */
236:     public function getNumberFound() {
237:         return $this->numberFound;
238:     }
239: }
240: 
241: ?>
Alpha Framework ${alpha.version.new} API Documentation API documentation generated by ApiGen 2.8.0