• Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files
  • Examples
  • File List

system/classes/term.php

00001 <?php
00002 
00017 namespace Habari;
00018 
00023 class Term extends QueryRecord
00024 {
00025   protected $inforecords = null;
00026 
00027   public $unsetfields = array(
00028     'object_id' => 'object_id',
00029     'type' => 'type',
00030   );
00031   
00036   public static function default_fields()
00037   {
00038     return array(
00039       'id' => 0,
00040       'term' => '',
00041       'term_display' => '',
00042       'vocabulary_id' => 0,
00043       'mptt_left' => 0,
00044       'mptt_right' => 0
00045     );
00046   }
00047 
00054   public function __construct( $paramarray = array() )
00055   {
00056     // Defaults
00057     $this->fields = array_merge(
00058       self::default_fields(),
00059       $this->fields
00060     );
00061 
00062     if ( is_string( $paramarray ) ) {
00063       $paramarray = array(
00064         'term_display' => $paramarray,
00065         'term' => Utils::slugify( $paramarray ),
00066       );
00067     }
00068 
00069     parent::__construct( $paramarray );
00070 
00071     // TODO does term need to be a slug ?
00072     // TODO How should we handle neither being present ?
00073     // A Vocabulary may be used for organization, display, or both.
00074     // Therefore, a Term can be constructed with a term, term_display, or both
00075     if ( $this->term == '' ) {
00076       $this->fields[ 'term' ] = Utils::slugify( $this->fields[ 'term_display' ] );
00077     }
00078 
00079     $this->exclude_fields( 'id' );
00080   }
00081 
00087   protected function setslug()
00088   {
00089     $value = '';
00090     // determine the base value from:
00091     // - the new slug
00092     if ( isset( $this->newfields[ 'term' ] ) && $this->newfields[ 'term' ] != '' ) {
00093       $value = $this->newfields[ 'term' ];
00094     }
00095     // - the existing slug
00096     elseif ( $this->fields[ 'term' ] != '' ) {
00097       $value = $this->fields[ 'term' ];
00098     }
00099     // - the new term display text
00100     elseif ( isset( $this->newfields[ 'term_display' ] ) && $this->newfields[ 'term_display' ] != '' ) {
00101       $value = $this->newfields[ 'term_display' ];
00102     }
00103     // - the existing term display text
00104     elseif ( $this->fields[ 'term_display' ] != '' ) {
00105       $value = $this->fields[ 'term_display' ];
00106     }
00107 
00108     // make sure our slug is unique
00109     $slug = Plugins::filter( 'term_setslug', $value );
00110     $slug = Utils::slugify( $slug );
00111     $postfix = '';
00112     $postfixcount = 0;
00113     do {
00114       if ( ! $slugcount = DB::get_row( 'SELECT COUNT(term) AS ct FROM {terms} WHERE term = ? AND vocabulary_id = ?;', array( $slug . $postfix, $this->fields['vocabulary_id'] ) ) ) {
00115         Utils::debug( DB::get_errors() );
00116         exit;
00117       }
00118       if ( $slugcount->ct != 0 ) {
00119         $postfix = "-" . ( ++$postfixcount );
00120       }
00121     } while ( $slugcount->ct != 0 );
00122 
00123     return $this->newfields[ 'term' ] = $slug . $postfix;
00124   }
00125 
00130   public function insert()
00131   {
00132     $this->setslug();
00133 
00134     // Let plugins disallow and act before we write to the database
00135     $allow = true;
00136     $allow = Plugins::filter( 'term_insert_allow', $allow, $this );
00137     $allow = $this->is_valid();
00138     if ( !$allow ) {
00139       return $allow;
00140     }
00141     Plugins::act( 'term_insert_before', $this );
00142 
00143     $result = parent::insertRecord( DB::table( 'terms' ) );
00144 
00145     // Make sure the id is set in the term object to match the row id
00146     $this->newfields[ 'id' ] = DB::last_insert_id();
00147 
00148     // Update the term's fields with anything that changed
00149     $this->fields = array_merge( $this->fields, $this->newfields );
00150 
00151     // We've inserted the term, reset newfields
00152     $this->newfields = array();
00153 
00154     // Commit the info records
00155     $this->info->commit( $this->fields['id'] );
00156     
00157     EventLog::log( _t( 'New term %1$s: %2$s', array( $this->id, $this->term_display ) ), 'info', 'content', 'habari' );
00158 
00159     // Let plugins act after we write to the database
00160     Plugins::act( 'term_insert_after', $this );
00161 
00162     return $result;
00163   }
00164 
00169   public function update()
00170   {
00171     // Let plugins disallow and act before we write to the database
00172     $allow = true;
00173     $allow = Plugins::filter( 'term_update_allow', $allow, $this );
00174     $allow = $this->is_valid();
00175     if ( !$allow ) {
00176       return;
00177     }
00178     Plugins::act( 'term_update_before', $this );
00179 
00180     // Call setslug() only when term is changed
00181     if ( isset( $this->newfields[ 'term' ] ) && $this->newfields[ 'term' ] != '' ) {
00182       if ( $this->fields[ 'term' ] != $this->newfields[ 'term' ] ) {
00183         $this->setslug();
00184       }
00185     }
00186 
00187     $result = parent::updateRecord( '{terms}', array( 'id' => $this->id ) );
00188     $this->fields = array_merge( $this->fields, $this->newfields );
00189 
00190     $this->info->commit();
00191     
00192     // Let plugins act after we write to the database
00193     Plugins::act( 'term_update_after', $this );
00194 
00195     return $result;
00196   }
00197 
00201   public function delete()
00202   {
00203     // Let plugins disallow and act before we write to the database
00204     $allow = true;
00205     $allow = Plugins::filter( 'term_delete_allow', $allow, $this );
00206     if ( !$allow ) {
00207       return false;
00208     }
00209     Plugins::act( 'term_delete_before', $this );
00210 
00211     // Delete all info records associated with this comment
00212     $this->info->delete_all();
00213 
00214     DB::query( 'DELETE FROM {object_terms} WHERE term_id = :id', array( 'id' => $this->id ) );
00215 
00216     $result = parent::deleteRecord( '{terms}', array( 'id'=>$this->id ) );
00217     EventLog::log( _t( 'Term %1$s (%2$s) deleted.', array( $this->id, $this->term_display ) ), 'info', 'content', 'habari' );
00218 
00219     // Let plugins act after we write to the database
00220     Plugins::act( 'term_delete_after', $this );
00221     return $result;
00222   }
00223 
00228   public function ancestors()
00229   {
00230     $params = array( 'vocab_id' => $this->vocabulary_id, 'left' => $this->mptt_left, 'right' => $this->mptt_right );
00231     $query = 'SELECT * FROM {terms} WHERE vocabulary_id=:vocab_id AND mptt_left<:left AND mptt_right>:right ORDER BY mptt_left ASC';
00232     return DB::get_results( $query, $params, 'Term' );
00233   }
00234 
00239   public function not_ancestors()
00240   {
00241     $params = array( 'vocab_id' => $this->vocabulary_id, 'left' => $this->mptt_left, 'right' => $this->mptt_right );
00242     $query = 'SELECT * FROM {terms} WHERE vocabulary_id=:vocab_id AND (mptt_left>:left OR mptt_right<:right) ORDER BY mptt_left ASC';
00243     return DB::get_results( $query, $params, 'Term' );
00244   }
00245 
00250   public function descendants()
00251   {
00252     $params = array( 'vocab_id' => $this->vocabulary_id, 'left' => $this->mptt_left, 'right' => $this->mptt_right );
00253     $query = 'SELECT * FROM {terms} WHERE vocabulary_id=:vocab_id AND mptt_left>:left AND mptt_right<:right ORDER BY mptt_left ASC';
00254     return DB::get_results( $query, $params, 'Term' );
00255   }
00256 
00261   public function not_descendants()
00262   {
00263     $params = array( 'vocab_id' => $this->vocabulary_id, 'left' => $this->mptt_left, 'right' => $this->mptt_right );
00264     $query = 'SELECT * FROM {terms} WHERE vocabulary_id=:vocab_id AND mptt_left NOT BETWEEN :left AND :right ORDER BY mptt_left ASC';
00265     return DB::get_results( $query, $params, 'Term' );
00266   }
00267 
00272   public function is_descendant_of( Term $term )
00273   {
00274     if ( $this->vocabulary_id != $term->vocabulary_id ) {
00275       return false;
00276     }
00277     if ( ( $this->mptt_left > $term->mptt_left ) && ( $this->mptt_right < $term->mptt_right ) ) {
00278       return true;
00279     }
00280     return false;
00281   }
00282 
00287   public function is_ancestor_of( Term $term )
00288   {
00289     if ( $this->vocabulary_id != $term->vocabulary_id ) {
00290       return false;
00291     }
00292     if ( ( $this->mptt_left < $term->mptt_left ) && ( $this->mptt_right > $term->mptt_right ) ) {
00293       return true;
00294     }
00295     return false;
00296   }
00297 
00302   public function parent()
00303   {
00304     $params = array( 'vocab_id' => $this->vocabulary_id, 'left' => $this->mptt_left, 'right' => $this->mptt_right );
00305     $query = 'SELECT * FROM {terms} WHERE vocabulary_id=:vocab_id AND mptt_left<:left AND mptt_right>:right ORDER BY mptt_left DESC LIMIT 1';
00306     return DB::get_row( $query, $params, 'Term' );
00307   }
00308 
00313   public function siblings()
00314   {
00315     $parent = $this->parent();
00316     if ( $parent ) {
00317       return $parent->children();
00318     }
00319     else {
00320       return $this->vocabulary->get_root_terms();
00321     }
00322   }
00323 
00328   public function children()
00329   {
00330     $params = array( 'vocab' => $this->vocabulary_id,
00331       'left' => $this->mptt_left,
00332       'right' => $this->mptt_right
00333     );
00340     $query = <<<SQL
00341 SELECT child.term as term,
00342   child.term_display as term_display,
00343   child.mptt_left as mptt_left,
00344   child.mptt_right as mptt_right,
00345   child.vocabulary_id as vocabulary_id,
00346   child.id as id
00347 FROM {terms} as parent
00348 INNER JOIN {terms} as child
00349   ON child.mptt_left BETWEEN parent.mptt_left AND parent.mptt_right
00350   AND child.vocabulary_id = parent.vocabulary_id
00351 WHERE parent.mptt_left > :left AND parent.mptt_right < :right
00352   AND parent.vocabulary_id = :vocab
00353 GROUP BY child.term
00354 HAVING COUNT(child.term)=1
00355 ORDER BY mptt_left
00356 SQL;
00357     return new Terms(DB::get_results( $query, $params, 'Term' ));
00358   }
00359 
00366   public function objects( $type )
00367   {
00368     $type_id = Vocabulary::object_type_id( $type );
00369     $results = DB::get_column( "SELECT object_id FROM {object_terms} WHERE term_id = ? AND object_type_id = ?", array( $this->id, $type_id ) );
00370     return $results;
00371   }
00372 
00378   public function object_types()
00379   {
00380     $results = DB::get_results( 'SELECT terms.object_id, types.name as `type` FROM {object_terms} terms, {object_types} types WHERE terms.object_type_id = types.id and term_id = :term_id', array( 'term_id' => $this->id ) );
00381     return $results;
00382   }
00383 
00390   public function object_count( $type )
00391   {
00392     $type_id = Vocabulary::object_type_id( $type );
00393     $result = DB::get_value( "SELECT count(object_id) FROM {object_terms} WHERE term_id = ? AND object_type_id = ?", array( $this->id, $type_id ) );
00394     return $result;
00395   }
00396 
00403   public function associate( $type, $id )
00404   {
00405     $result = true;
00406     $type_id = Vocabulary::object_type_id( $type );
00407 
00408     Plugins::act( 'term_associate_to_object_before', $this->id, $id, $type_id );
00409 
00410     if ( ! DB::exists( "{object_terms}", array( 'term_id' => $this->id, 'object_id' => $id, 'object_type_id' => $type_id ) ) ) {
00411       $result = DB::insert( "{object_terms}", array( 'term_id' => $this->id, 'object_id' => $id, 'object_type_id' => $type_id ) );
00412     }
00413 
00414     Plugins::act( 'term_associate_to_object_after', $this->id, $id, $type_id );
00415 
00416     return $result;
00417   }
00418 
00425   public function dissociate( $type = null, $id = null )
00426   {
00427     $result = true;
00428 
00429     $type_id = Vocabulary::object_type_id( $type );
00430     Plugins::act( 'term_dissociate_from_object_before', $this->id, $id, $type_id );
00431 
00432     $result = DB::query( "DELETE FROM {object_terms} WHERE term_id = ? AND object_id = ? AND object_type_id = ?", array( $this->id, $id, $type_id ) );
00433 
00434     Plugins::act( 'term_dissociate_from_object_after', $this->id, $id, $type_id, $result );
00435 
00436     return $result;
00437   }
00438 
00444   public function __tostring()
00445   {
00446     return $this->term_display;
00447   }
00448 
00455   public function __get( $name )
00456   {
00457     switch ( $name ) {
00458       case 'vocabulary':
00459         $out = Vocabulary::get_by_id( $this->vocabulary_id );
00460         break;
00461       case 'tag_text_searchable':
00462         // if it's got spaces, then quote it.
00463         if ( strpos( $this->term_display, ' ' ) !== false || strpos( $this->term_display, ',' ) !== false ) {
00464           $out = '\'' . str_replace( "'", "\'", $this->term_display ) . '\'';
00465         }
00466         else {
00467           $out = $this->term_display;
00468         }
00469         break;
00470       case 'count':
00471         $out = (int)$this->count();
00472         break;
00473       case 'id':
00474         return (int)parent::__get( $name );
00475       case 'info':
00476         return $this->get_info();
00477       default:
00478         $out = parent::__get( $name );
00479         break;
00480     }
00481     return $out;
00482   }
00483 
00489   protected function get_info()
00490   {
00491     if ( ! $this->inforecords ) {
00492       if ( 0 == $this->id ) {
00493         $this->inforecords = new TermInfo();
00494       }
00495       else {
00496         $this->inforecords = new TermInfo( $this->id );
00497       }
00498     }
00499     return $this->inforecords;
00500   }
00501 
00507   public function count( $object_type = 'post' )
00508   {
00509     return $this->object_count( $object_type );
00510   }
00511 
00518   public function __call( $name, $args )
00519   {
00520     array_unshift( $args, 'term_call_' . $name, null, $this );
00521     return call_user_func_array( Method::create( '\\Habari\\Plugins', 'filter' ), $args );
00522   }
00523 
00531   public static function get( $term, $term_class = 'Term' )
00532   {
00533     $query = '';
00534     if ( $term instanceof Term ) {
00535       // This seems kind of silly, but by passing a different $term_class, the 
00536       // database values are loaded into a different class instance and fire its constructor
00537       $params[ 'term_id' ] = $term->id;
00538       $query = 'SELECT * FROM {terms} WHERE id = ABS(:term_id)';
00539     }
00540     elseif ( is_string( $term ) ) {
00541       $params[ 'term' ] = $term;
00542       $query = 'SELECT * FROM {terms} WHERE (term = :term OR term_display = :term) ORDER BY term <> :term';
00543     }
00544     elseif ( is_int( $term ) ) {
00545       $params[ 'term_id' ] = $term;
00546       $query = 'SELECT * FROM {terms} WHERE id = ABS(:term_id)';
00547     }
00548     return DB::get_row( $query, $params, $term_class );
00549   }
00550 
00555   protected function is_valid()
00556   {
00557     if( strlen( trim( $this->term_display ) )  && strlen( trim( $this->term ) ) && $this->vocabulary_id != 0 ) {
00558       return true;
00559     }
00560     else {
00561       return false;
00562     }
00563   }
00564 }
00565 
00566 ?>

Generated on Sun Aug 4 2013 12:51:43 for Habari by  doxygen 1.7.1