Development

/plugins/sfPropelActAsRatableBehaviorPlugin/trunk/lib/sfPropelActAsRatableBehavior.class.php

You must first sign up to be able to contribute.

root/plugins/sfPropelActAsRatableBehaviorPlugin/trunk/lib/sfPropelActAsRatableBehavior.class.php

Revision 5038, 13.8 kB (checked in by nicolas, 6 years ago)

sfPropelActAsRatableBehaviorPlugin:

  • Reference keys are now stored as a md5 hash
  • Corrected custom refrence keys handling bug in Ajax rater widget
Line 
1 <?php
2 /*
3  * This file is part of the sfPropelActAsRatableBehavior package.
4  *
5  * (c) 2007 Nicolas Perriault <nperriault@gmail.com>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  * This Propel behavior aims at providing rating capabilities on any Propel
13  * object
14  *
15  * @package    plugins
16  * @subpackage rating
17  * @author     Nicolas Perriault <nperriault@gmail.com>
18  */
19 class sfPropelActAsRatableBehavior
20 {
21  
22   /**
23    * Default default max rating
24    */
25   const DEFAULT_MAX_RATING = 5;
26
27   /**
28    * Returns configured reference field name or NULL if none has been provided
29    *
30    * @return string
31    */
32   public function getReferenceField(BaseObject $object)
33   {
34     return sfConfig::get(
35       sprintf('propel_behavior_sfPropelActAsRatableBehavior_%s_reference_field',
36               get_class($object)));
37   }
38
39   /**
40    * Retrieves reference field Propel type
41    *
42    * @return BaseObject  $object
43    * @return string or null if no reference field has been provided
44    */
45   public function getReferenceFieldType(BaseObject $object)
46   {
47     if (is_null($object->getReferenceField()))
48     {
49       return null;
50     }
51     
52     $propel_type = sfConfig::get(
53       sprintf('propel_behavior_sfPropelActAsRatableBehavior_%s_reference_field_type',
54               get_class($object)));
55     if (!is_null($propel_type))
56     {
57       return $propel_type;
58     }
59     
60     $reference_field = $object->getReferenceField();
61     try // to retrieve column value from a phpName
62     {
63       $object_reference_key = $object->getByName($reference_field, BasePeer::TYPE_PHPNAME);
64       self::setReferenceFieldType($object, BasePeer::TYPE_PHPNAME);
65     } catch (Exception $e) {}
66     
67     try // to retrieve column value from a colName
68     {
69       $object_reference_key = $object->getByName($reference_field, BasePeer::TYPE_COLNAME);
70       self::setReferenceFieldType($object, BasePeer::TYPE_COLNAME);
71     } catch (Exception $e) {}
72     
73     try // to retrieve column value from a fieldName
74     {
75       $object_reference_key = $object->getByName($reference_field, BasePeer::TYPE_FIELDNAME);
76       self::setReferenceFieldType($object, BasePeer::TYPE_FIELDNAME);
77     } catch (Exception $e) {}
78     
79     return $this->getReferenceFieldType($object);
80   }
81
82   /**
83    * <p>Retrieves the object reference key. By default, we use the primary key
84    * of the Propel object. It is possible to configure the name of the column to
85    * use as a reference specifying the 'reference_field' parameter of the
86    * behavior in the Propel model class it is applied to, eg:</p>
87    * <pre>
88    * sfPropelBehavior::add('sfTestObject',
89    *                       array('sfPropelActAsRatableBehavior' =>
90    *                             array('reference_field' => sfTestObjectPeer::CUSTOM_ID)));
91    * </pre>
92    *
93    * @param  BaseObject  $object
94    * @return string as a md5 hash of the reference field
95    * @throws sfPropelActAsRatableException
96    */
97   public function getReferenceKey(BaseObject $object)
98   {
99     if ($object->isNew())
100     {
101       throw new sfPropelActAsRatableException(
102         'You cannot rate or retrieve rating for unsaved objects');
103     }
104     
105     $reference_field = sfConfig::get(
106       sprintf('propel_behavior_sfPropelActAsRatableBehavior_%s_reference_field',
107               get_class($object)));
108     
109     if (is_null($reference_field))
110     {
111       return md5($object->getPrimaryKey());
112     }
113
114     $object_reference_key = $object->getByName($reference_field,
115                                                $object->getReferenceFieldType());
116     if (is_null($object_reference_key))
117     {
118       throw new sfPropelActAsRatableException(
119         sprintf('Reference field %s cannot be retrieved for %s class objects',
120                 $reference_field,
121                 get_class($object)));
122     }
123     
124     return md5($object_reference_key);
125   }
126  
127   /**
128    * Retrieves an existing rating object, or return a new empty one
129    *
130    * @param  BaseObject  $object
131    * @param  mixed       $user_reference  Unique user reference
132    * @return sfRating
133    * @throws sfPropelActAsRatableException
134    **/
135   protected function getOrCreate(BaseObject $object, $user_reference=null)
136   {
137     if ($object->isNew())
138     {
139       throw new sfPropelActAsRatableException('Unsaved objects are not ratable');
140     }
141     $c = new Criteria();
142     $c->add(sfRatingPeer::RATABLE_ID, $object->getReferenceKey());
143     $c->add(sfRatingPeer::RATABLE_MODEL, get_class($object));
144     if (!is_null($user_reference))
145     {
146       $c->add(sfRatingPeer::USER_REFERENCE, $user_reference);
147     }
148     $result = sfRatingPeer::doSelectOne($c);
149     return is_null($result) ? new sfRating() : $result;
150   }
151
152   /**
153    * Clear all ratings for an object
154    *
155    * @param  BaseObject  $object
156    **/
157   public function clearRatings(BaseObject $object)
158   {
159     $c = new Criteria();
160     $c->add(sfRatingPeer::RATABLE_ID, $object->getReferenceKey());
161     $c->add(sfRatingPeer::RATABLE_MODEL, get_class($object));
162     return sfRatingPeer::doDelete($c);
163   }
164
165   /**
166    * Clear user rating for an object
167    *
168    * @param  BaseObject  $object
169    * @param  mixed       $user_reference  Unique reference to the user
170    **/
171   public function clearUserRating(BaseObject $object, $user_reference)
172   {
173     if (is_null($user_reference) or trim((string)$user_reference) === '')
174     {
175       throw new sfPropelActAsRatableException('Impossible to clear a user rating with no user reference provided');
176     }
177     $c = new Criteria();
178     $c->add(sfRatingPeer::RATABLE_ID, $object->getReferenceKey());
179     $c->add(sfRatingPeer::RATABLE_MODEL, get_class($object));
180     $c->add(sfRatingPeer::USER_REFERENCE, $user_reference);
181     return sfRatingPeer::doDelete($c);
182   }
183
184   /**
185    * Checks if an Object has been rated
186    *
187    * @param  BaseObject  $object
188    **/
189   public function hasBeenRated(BaseObject $object)
190   {
191     $c = new Criteria();
192     $c->add(sfRatingPeer::RATABLE_ID, $object->getReferenceKey());
193     $c->add(sfRatingPeer::RATABLE_MODEL, get_class($object));
194     return sfRatingPeer::doCount($c) > 0;
195   }
196
197   /**
198    * Checks if an Object has been rated by a user
199    *
200    * @param  BaseObject  $object
201    * @param  mixed       $user_reference  Unique reference to a user
202    **/
203   public function hasBeenRatedByUser(BaseObject $object, $user_reference)
204   {
205     if (is_null($user_reference) or trim((string)$user_reference) === '')
206     {
207       throw new sfPropelActAsRatableException(
208         'Impossible to check a user rating with no user reference provided');
209     }
210     $c = new Criteria();
211     $c->add(sfRatingPeer::RATABLE_ID, $object->getReferenceKey());
212     $c->add(sfRatingPeer::RATABLE_MODEL, get_class($object));
213     $c->add(sfRatingPeer::USER_REFERENCE, $user_reference);
214     return (sfRatingPeer::doCount($c) > 0);
215   }
216  
217   /**
218    * Old method to set maximum rating in a class constant
219    * This stays here for compability purpose
220    *
221    * @param  BaseObject  $object
222    * @return int
223    */
224   protected static function getDefaultMaxRating(BaseObject $object)
225   {
226     $max_rating = @constant(get_class($object).'::MAX_RATING');
227     if (!is_int($max_rating))
228     {
229       $max_rating = self::DEFAULT_MAX_RATING;
230       sfLogger::getInstance()->warning(
231         sprintf('No maximum rating has been set for "%s" ratable objects, '.
232                 'default has been set to %d',
233                 get_class($object),
234                 self::DEFAULT_MAX_RATING));
235     }
236     return $max_rating;
237   }
238
239   /**
240    * Retrieves maximum rating for given object
241    *
242    * @param  BaseObject  $object  Propel object instance
243    * @return int
244    * @throws sfPropelActAsRatableException
245    */
246   public function getMaxRating(BaseObject $object)
247   {
248     $max_rating = sfConfig::get(
249       sprintf('propel_behavior_sfPropelActAsRatableBehavior_%s_max_rating',
250               get_class($object)));
251     
252     if (is_null($max_rating))
253     {
254       $max_rating = self::getDefaultMaxRating($object);
255     }
256     
257     if (!is_int($max_rating))
258     {
259       throw new sfPropelActAsRatableException(
260         'The max_rating parameter must be an integer');
261     }
262     
263     if (is_float($max_rating) && floor($max_rating) != $max_rating) // yeah, php typing sucks...
264     {
265       throw new sfPropelActAsRatableException(
266         sprintf('You cannot type %s::MAX_RATING as float (you provided "%s")',
267                 get_class($object),
268                 $max_rating));
269     }
270     
271     if ($max_rating < 2)
272     {
273       throw new sfPropelActAsRatableException(
274         'The max_rating parameter must be an integer greater than 1');
275     }
276     
277     return $max_rating;
278   }
279
280   /**
281    * Gets the object rating
282    *
283    * @param  BaseObject  $object
284    * @param  int         $floating_point
285    * @return float
286    **/
287   public function getRating(BaseObject $object, $floating_point=2)
288   {
289     $c = new Criteria();
290     $c->add(sfRatingPeer::RATABLE_ID, $object->getReferenceKey());
291     $c->add(sfRatingPeer::RATABLE_MODEL, get_class($object));
292     $c->clearSelectColumns();
293     $c->addAsColumn('nb_ratings', 'COUNT('.sfRatingPeer::ID.')');
294     $c->addAsColumn('total', 'SUM('.sfRatingPeer::RATING.')');
295     $p = array();
296     $sql = BasePeer::createSelectSql($c, $p);
297     $con = Propel::getConnection();
298     $stmt = $con->prepareStatement($sql);
299     $stmt->setString(1, $object->getReferenceKey());
300     $stmt->setString(2, get_class($object));
301     $rs = $stmt->executeQuery(ResultSet::FETCHMODE_ASSOC);
302     $rs->next();
303     $nb_ratings = $rs->getString('nb_ratings');
304     $total      = $rs->getString('total');
305     if (!$nb_ratings or $nb_ratings === 0)
306     {
307       return NULL; // Object has not been rated yet
308     }
309     return round($total / $nb_ratings,
310                  sfConfig::get('app_rating_floatingpoint', $floating_point));
311   }
312  
313   /**
314    * Gets the object rating for given user pk
315    *
316    * @param  BaseObject  $object
317    * @param  mixed       $user_reference  Unique reference to the user
318    **/
319   public function getUserRating(BaseObject $object, $user_reference)
320   {
321     if (is_null($user_reference) or trim((string)$user_reference) === '')
322     {
323       throw new sfPropelActAsRatableException(
324         'Impossible to get a user rating with no user reference provided');
325     }
326     $c = new Criteria();
327     $c->add(sfRatingPeer::RATABLE_ID, $object->getReferenceKey());
328     $c->add(sfRatingPeer::RATABLE_MODEL, get_class($object));
329     $c->add(sfRatingPeer::USER_REFERENCE, $user_reference);
330     $rating_object = sfRatingPeer::doSelectOne($c);
331     if (!is_null($rating_object))
332     {
333       return $rating_object->getRating();
334     }
335   }
336  
337   /**
338    * Retrieves an object instance from a given reference key
339    *
340    * @param  string  $object_name
341    * @param  string  $reference_key
342    * @return BaseObject
343    * @throws sfPropelActAsRatableException
344    */
345   public static function retrieveFromReferenceKey($object_name, $reference_key)
346   {
347     if (!class_exists($object_name))
348     {
349       throw new sfPropelActAsRatableException('Unknown class '.$object_name);
350     }
351     
352     $object = new $object_name;
353     $peer = $object->getPeer();
354     $criteria = new Criteria();
355     $ref_field_type = $object->getReferenceFieldType();
356     if (is_null($ref_field_type))
357     {
358       // retrieve PK column name
359       $table_map = call_user_func(array(get_class($peer), 'getTableMap'));
360       $columns = $table_map->getColumns();
361       foreach(array_keys($columns) as $key)
362       {
363         if ($columns[$key]->isPrimaryKey())
364         {
365           $column_map = $columns[$key];
366           $column = constant(get_class($peer).'::'.$column_map->getColumnName());
367         }
368       }
369     }
370     else
371     {
372       $column = call_user_func(array($peer, 'translateFieldName'),
373                                $object->getReferenceField(),
374                                $ref_field_type,
375                                BasePeer::TYPE_COLNAME);
376     }
377     if (is_null($column))
378     {
379       throw new sfPropelActAsRatableException(
380         'Unable to retrieve reference key column name');
381     }
382     $criteria->add($column
383                    sprintf('MD5(%s) = "%s"', $column, $reference_key),
384                    Criteria::CUSTOM);
385     return call_user_func(array(get_class($peer), 'doSelectOne'), $criteria);
386   }
387  
388   /**
389    * Rates the Object
390    *
391    * @param  BaseObject  $object
392    * @param  int         $rating
393    * @param  mixed       $user_reference  Optionnal unique reference to user
394    * @throws sfPropelActAsRatableException
395    **/
396   public function setRating(BaseObject $object, $rating, $user_reference=null)
397   {
398     if (is_float($rating) && floor($rating) != $rating)
399     {
400       throw new sfPropelActAsRatableException(
401         sprintf('You cannot rate an object with a float (you provided "%s")',
402                 $rating));
403     }
404     
405     $rating = (int)$rating;
406     
407     if ($rating > $object->getMaxRating())
408     {
409       throw new sfPropelActAsRatableException(
410         sprintf('Maximum rating is %d', $object->getMaxRating()));
411     }
412     
413     if ($rating < 1)
414     {
415       throw new sfPropelActAsRatableException('Minimum rating is 1');
416     }
417     
418     $key = $object->getReferenceKey();
419     if (is_null($key))
420     {
421       throw new sfPropelActAsRatableException(
422         sprintf('No reference key available for field %s in class %s',
423                 $object->getReferenceField(),
424                 get_class($object)));
425     }
426     
427     $rating_object = $this->getOrCreate($object, $user_reference);
428     $rating_object->setRatableModel(get_class($object));
429     $rating_object->setRatableId($key);
430     if (!is_null($user_reference))
431     {
432       $rating_object->setUserReference($user_reference);
433     }
434     $rating_object->setRating($rating);
435     
436     return $rating_object->save();
437   }
438  
439   /**
440    * Sets current object reference field propel type
441    *
442    * @param  BaseObject  $object
443    * @param  string      $propel_type
444    */
445   public static function setReferenceFieldType(BaseObject $object, $propel_type)
446   {
447     sfConfig::set(
448         sprintf('propel_behavior_sfPropelActAsRatableBehavior_%s_reference_field_type',
449                 get_class($object)),
450                 $propel_type);
451   }
452
453 }
Note: See TracBrowser for help on using the browser.