From daa78887be63d3fbbcdb5056bbe778753cfeae55 Mon Sep 17 00:00:00 2001 From: Sid Date: Mon, 11 Sep 2023 17:50:57 +0530 Subject: [PATCH 1/5] ddev fdsu.sh command for project setup --- commands/web/fdsu.sh | 100 ++ extras/CActiveRecord.php | 2466 ++++++++++++++++++++++++++++++++++ extras/cache-local.php | 17 + extras/cache_write-local.php | 17 + extras/common-local.php | 79 ++ 5 files changed, 2679 insertions(+) create mode 100755 commands/web/fdsu.sh create mode 100644 extras/CActiveRecord.php create mode 100644 extras/cache-local.php create mode 100644 extras/cache_write-local.php create mode 100644 extras/common-local.php diff --git a/commands/web/fdsu.sh b/commands/web/fdsu.sh new file mode 100755 index 0000000..978dd93 --- /dev/null +++ b/commands/web/fdsu.sh @@ -0,0 +1,100 @@ +echo "Setting up the project" + +#ask user name and print hello $name +echo "What do you want to call this project?" +read project_name +echo "Let's setup $project_name" + +#ask user if need to install composer or skip +echo "Do you want to install composer? (y/n)[no]" +read install_composer +if [ "$install_composer" = "y" ] || [ "$install_composer" = "Y" ] ] +then + echo "Installing composer" + composer install +else + echo "Skipping composer installation" +fi + +#ask user if need to install yarn or skip +echo "Do you want to install yarn? (y/n)[no]" +read install_yarn +if [ "$install_yarn" = "y" ] || [ "$install_yarn" = "Y" ] ] +then + echo "Installing yarn" + yarn install +else + echo "Skipping yarn installation" +fi + +#ask user if want to import db or skip +echo "Do you want to import db? (y/n)[no]" +read import_db +if [ "$import_db" = "y" ] || [ "$import_db" = "Y" ] ] +then + echo "Importing db" + #ask user for the db file name and unzip if file exists + echo "Enter the db file name" + read db_file_name + if [ -f "$db_file_name" ] + then + echo "Unzipping db file" + gunzip $db_file_name + psql -h db -d db -U db -f $db_file_name + + #delete unzipped file + rm -f $db_file_name + else + echo "File not found" + fi +else + echo "Skipping db import" +fi + + +#ask user if want to recreate files +echo "Do you want to recreate files? (y/n)[no]" +read recreate_files +if [ "recreate_files" = "y" ] || [ "recreate_files" = "Y" ] ] +then + #copy files + for file in site/config/*.dist; do + newname="${file%.dist}" + cp "$file" "$newname" + done + + cp site/web/index.php.dist site/web/index.php + + touch site/config/saml/firstdue-x509.crt + touch site/config/saml/firstdue-x509.key + + echo "defined('YII_DEBUG') or define('YII_DEBUG', true);" >> site/web/index.php + + rm site/config/common-local.php && cp .ddev/extras/common-local.php site/config/common-local.php + rm site/config/cache-local.php && cp .ddev/extras/cache-local.php site/config/cache-local.php + rm site/config/cache-local.php && cp .ddev/extras/cache-local.php site/config/cache_write-local.php +# rm vendor/yiisoft/yii/framework/db/ar/CActiveRecord.php && cp .ddev/extras/CActiveRecord.php vendor/yiisoft/yii/framework/db/ar/CActiveRecord.php + + echo "Necessary files copied!!!" + +else + echo "Skipping file recreation" +fi + + +#ask user if want to run migrations +echo "Do you want to run migrations? (y/n)[no]" +read run_migrations +if [ "$run_migrations" = "y" ] || [ "$run_migrations" = "Y" ] ] +then + echo "Running migrations" + php console/yiic.php migrate +else + echo "Skipping migrations" +fi + + + +#write message in green color and in bold +echo -e "\e[1;32m $name setup complete, use 'ddev start' to start the project\e[0m" + diff --git a/extras/CActiveRecord.php b/extras/CActiveRecord.php new file mode 100644 index 0000000..f613d0e --- /dev/null +++ b/extras/CActiveRecord.php @@ -0,0 +1,2466 @@ + + * @link https://www.yiiframework.com/ + * @copyright 2008-2013 Yii Software LLC + * @license https://www.yiiframework.com/license/ + */ + +/** + * CActiveRecord is the base class for classes representing relational data. + * + * It implements the active record design pattern, a popular Object-Relational Mapping (ORM) technique. + * Please check {@link https://www.yiiframework.com/doc/guide/1.1/en/database.ar the Guide} for more details + * about this class. + * + * @property CDbCriteria $dbCriteria The query criteria that is associated with this model. + * This criteria is mainly used by {@link scopes named scope} feature to accumulate + * different criteria specifications. + * @property CActiveRecordMetaData $metaData The meta for this AR class. + * @property CDbConnection $dbConnection The database connection used by active record. + * @property CDbTableSchema $tableSchema The metadata of the table that this AR belongs to. + * @property CDbCommandBuilder $commandBuilder The command builder used by this AR. + * @property array $attributes Attribute values indexed by attribute names. + * @property boolean $isNewRecord Whether the record is new and should be inserted when calling {@link save}. + * This property is automatically set in constructor and {@link populateRecord}. + * Defaults to false, but it will be set to true if the instance is created using + * the new operator. + * @property mixed $primaryKey The primary key value. An array (column name=>column value) is returned if the primary key is composite. + * If primary key is not defined, null will be returned. + * @property mixed $oldPrimaryKey The old primary key value. An array (column name=>column value) is returned if the primary key is composite. + * If primary key is not defined, null will be returned. + * @property string $tableAlias The default table alias. + * + * @author Qiang Xue + * @package system.db.ar + * @since 1.0 + */ +abstract class CActiveRecord extends CModel +{ + const BELONGS_TO='CBelongsToRelation'; + const HAS_ONE='CHasOneRelation'; + const HAS_MANY='CHasManyRelation'; + const MANY_MANY='CManyManyRelation'; + const STAT='CStatRelation'; + + /** + * @var CDbConnection the default database connection for all active record classes. + * By default, this is the 'db' application component. + * @see getDbConnection + */ + public static $db; + + private static $_models=array(); // class name => model + private static $_md=array(); // class name => meta data + + private $_new=false; // whether this instance is new or not + private $_attributes=array(); // attribute name => attribute value + private $_related=array(); // attribute name => related objects + private $_c; // query criteria (used by finder only) + private $_pk; // old primary key value + private $_alias='t'; // the table alias being used for query + + + /** + * Constructor. + * @param string $scenario scenario name. See {@link CModel::scenario} for more details about this parameter. + * Note: in order to setup initial model parameters use {@link init()} or {@link afterConstruct()}. + * Do NOT override the constructor unless it is absolutely necessary! + */ + public function __construct($scenario='insert') + { + if($scenario===null) // internally used by populateRecord() and model() + return; + + $this->setScenario($scenario); + $this->setIsNewRecord(true); + $this->_attributes=$this->getMetaData()->attributeDefaults; + + $this->init(); + + $this->attachBehaviors($this->behaviors()); + $this->afterConstruct(); + } + + /** + * Initializes this model. + * This method is invoked when an AR instance is newly created and has + * its {@link scenario} set. + * You may override this method to provide code that is needed to initialize the model (e.g. setting + * initial property values.) + */ + public function init() + { + } + + /** + * Sets the parameters about query caching. + * This is a shortcut method to {@link CDbConnection::cache()}. + * It changes the query caching parameter of the {@link dbConnection} instance. + * @param integer $duration the number of seconds that query results may remain valid in cache. + * If this is 0, the caching will be disabled. + * @param CCacheDependency|ICacheDependency $dependency the dependency that will be used when saving + * the query results into cache. + * @param integer $queryCount number of SQL queries that need to be cached after calling this method. Defaults to 1, + * meaning that the next SQL query will be cached. + * @return static the active record instance itself. + * @since 1.1.7 + */ + public function cache($duration, $dependency=null, $queryCount=1) + { + $this->getDbConnection()->cache($duration, $dependency, $queryCount); + return $this; + } + + /** + * PHP sleep magic method. + * This method ensures that the model meta data reference is set to null. + * @return array + */ + public function __sleep() + { + return array_keys((array)$this); + } + + /** + * PHP getter magic method. + * This method is overridden so that AR attributes can be accessed like properties. + * @param string $name property name + * @return mixed property value + * @see getAttribute + */ + public function __get($name) + { + if(isset($this->_attributes[$name])) + return $this->_attributes[$name]; + elseif(isset($this->getMetaData()->columns[$name])) + return null; + elseif(isset($this->_related[$name])) + return $this->_related[$name]; + elseif(isset($this->getMetaData()->relations[$name])) + return $this->getRelated($name); + else + return parent::__get($name); + } + + /** + * PHP setter magic method. + * This method is overridden so that AR attributes can be accessed like properties. + * @param string $name property name + * @param mixed $value property value + * @throws CException + */ + public function __set($name,$value) + { + if($this->setAttribute($name,$value)===false) + { + if(isset($this->getMetaData()->relations[$name])) + $this->_related[$name]=$value; + else + parent::__set($name,$value); + } + } + + /** + * Checks if a property value is null. + * This method overrides the parent implementation by checking + * if the named attribute is null or not. + * @param string $name the property name or the event name + * @return boolean whether the property value is null + */ + public function __isset($name) + { + if(isset($this->_attributes[$name])) + return true; + elseif(isset($this->getMetaData()->columns[$name])) + return false; + elseif(isset($this->_related[$name])) + return true; + elseif(isset($this->getMetaData()->relations[$name])) + return $this->getRelated($name)!==null; + else + return parent::__isset($name); + } + + /** + * Sets a component property to be null. + * This method overrides the parent implementation by clearing + * the specified attribute value. + * @param string $name the property name or the event name + * @throws CException + */ + public function __unset($name) + { + if(isset($this->getMetaData()->columns[$name])) + unset($this->_attributes[$name]); + elseif(isset($this->getMetaData()->relations[$name])) + unset($this->_related[$name]); + else + parent::__unset($name); + } + + /** + * Calls the named method which is not a class method. + * Do not call this method. This is a PHP magic method that we override + * to implement the named scope feature. + * @param string $name the method name + * @param array $parameters method parameters + * @return mixed the method return value + */ + public function __call($name,$parameters) + { + if(isset($this->getMetaData()->relations[$name])) + { + if(empty($parameters)) + return $this->getRelated($name,false); + else + return $this->getRelated($name,false,$parameters[0]); + } + + $scopes=$this->scopes(); + if(isset($scopes[$name])) + { + $this->getDbCriteria()->mergeWith($scopes[$name]); + return $this; + } + + return parent::__call($name,$parameters); + } + + /** + * Returns the related record(s). + * This method will return the related record(s) of the current record. + * If the relation is HAS_ONE or BELONGS_TO, it will return a single object + * or null if the object does not exist. + * If the relation is HAS_MANY or MANY_MANY, it will return an array of objects + * or an empty array. + * @param string $name the relation name (see {@link relations}) + * @param boolean $refresh whether to reload the related objects from database. Defaults to false. + * If the current record is not a new record and it does not have the related objects loaded they + * will be retrieved from the database even if this is set to false. + * If the current record is a new record and this value is false, the related objects will not be + * retrieved from the database. + * @param mixed $params array or CDbCriteria object with additional parameters that customize the query conditions as specified in the relation declaration. + * If this is supplied the related record(s) will be retrieved from the database regardless of the value or {@link $refresh}. + * The related record(s) retrieved when this is supplied will only be returned by this method and will not be loaded into the current record's relation. + * The value of the relation prior to running this method will still be available for the current record if this is supplied. + * @return mixed the related object(s). + * @throws CDbException if the relation is not specified in {@link relations}. + */ + public function getRelated($name,$refresh=false,$params=array()) + { + if(!$refresh && $params===array() && (isset($this->_related[$name]) || array_key_exists($name,$this->_related))) + return $this->_related[$name]; + + $md=$this->getMetaData(); + if(!isset($md->relations[$name])) + throw new CDbException(Yii::t('yii','{class} does not have relation "{name}".', + array('{class}'=>get_class($this), '{name}'=>$name))); + + Yii::trace('lazy loading '.get_class($this).'.'.$name,'system.db.ar.CActiveRecord'); + $relation=$md->relations[$name]; + if($this->getIsNewRecord() && !$refresh && ($relation instanceof CHasOneRelation || $relation instanceof CHasManyRelation)) + return $relation instanceof CHasOneRelation ? null : array(); + + if($params!==array()) // dynamic query + { + $exists=isset($this->_related[$name]) || array_key_exists($name,$this->_related); + if($exists) + $save=$this->_related[$name]; + + if($params instanceof CDbCriteria) + $params = $params->toArray(); + + $r=array($name=>$params); + } + else + $r=$name; + unset($this->_related[$name]); + + $finder=$this->getActiveFinder($r); + $finder->lazyFind($this); + + if(!isset($this->_related[$name])) + { + if($relation instanceof CHasManyRelation) + $this->_related[$name]=array(); + elseif($relation instanceof CStatRelation) + $this->_related[$name]=$relation->defaultValue; + else + $this->_related[$name]=null; + } + + if($params!==array()) + { + $results=$this->_related[$name]; + if($exists) + $this->_related[$name]=$save; + else + unset($this->_related[$name]); + return $results; + } + else + return $this->_related[$name]; + } + + /** + * Returns a value indicating whether the named related object(s) has been loaded. + * @param string $name the relation name + * @return boolean a value indicating whether the named related object(s) has been loaded. + */ + public function hasRelated($name) + { + return isset($this->_related[$name]) || array_key_exists($name,$this->_related); + } + + /** + * Returns the query criteria associated with this model. + * @param boolean $createIfNull whether to create a criteria instance if it does not exist. Defaults to true. + * @return CDbCriteria the query criteria that is associated with this model. + * This criteria is mainly used by {@link scopes named scope} feature to accumulate + * different criteria specifications. + */ + public function getDbCriteria($createIfNull=true) + { + if($this->_c===null) + { + if(($c=$this->defaultScope())!==array() || $createIfNull) + $this->_c=new CDbCriteria($c); + } + return $this->_c; + } + + /** + * Sets the query criteria for the current model. + * @param CDbCriteria $criteria the query criteria + * @since 1.1.3 + */ + public function setDbCriteria($criteria) + { + $this->_c=$criteria; + } + + /** + * Returns the default named scope that should be implicitly applied to all queries for this model. + * Note, default scope only applies to SELECT queries. It is ignored for INSERT, UPDATE and DELETE queries. + * The default implementation simply returns an empty array. You may override this method + * if the model needs to be queried with some default criteria (e.g. only active records should be returned). + * @return array the query criteria. This will be used as the parameter to the constructor + * of {@link CDbCriteria}. + */ + public function defaultScope() + { + return array(); + } + + /** + * Resets all scopes and criterias applied. + * + * @param boolean $resetDefault including default scope. This parameter available since 1.1.12 + * @return static the AR instance itself + * @since 1.1.2 + */ + public function resetScope($resetDefault=true) + { + if($resetDefault) + $this->_c=new CDbCriteria(); + else + $this->_c=null; + + return $this; + } + + /** + * Returns the static model of the specified AR class. + * The model returned is a static instance of the AR class. + * It is provided for invoking class-level methods (something similar to static class methods.) + * + * EVERY derived AR class must override this method as follows, + *
+	 * public static function model($className=__CLASS__)
+	 * {
+	 *     return parent::model($className);
+	 * }
+	 * 
+ * + * @param string $className active record class name. + * @return static active record model instance. + */ + public static function model($className=__CLASS__) + { + if(isset(self::$_models[$className])) + return self::$_models[$className]; + else + { + $model=self::$_models[$className]=new $className(null); + $model->attachBehaviors($model->behaviors()); + return $model; + } + } + + /** + * Returns the meta-data for this AR + * @return CActiveRecordMetaData the meta for this AR class. + */ + public function getMetaData() + { + $className=get_class($this); + if(!array_key_exists($className,self::$_md)) + { +// self::$_md[$className]=null; // preventing recursive invokes of {@link getMetaData()} via {@link __get()} + self::$_md[$className]=new CActiveRecordMetaData($this); + } + return self::$_md[$className]; + } + + /** + * Refreshes the meta data for this AR class. + * By calling this method, this AR class will regenerate the meta data needed. + * This is useful if the table schema has been changed and you want to use the latest + * available table schema. Make sure you have called {@link CDbSchema::refresh} + * before you call this method. Otherwise, old table schema data will still be used. + */ + public function refreshMetaData() + { + $className=get_class($this); + if(array_key_exists($className,self::$_md)) + unset(self::$_md[$className]); + } + + /** + * Returns the name of the associated database table. + * By default this method returns the class name as the table name. + * You may override this method if the table is not named after this convention. + * @return string the table name + */ + public function tableName() + { + $tableName = get_class($this); + if(($pos=strrpos($tableName,'\\')) !== false) + return substr($tableName,$pos+1); + return $tableName; + } + + /** + * Returns the primary key of the associated database table. + * This method is meant to be overridden in case when the table is not defined with a primary key + * (for some legency database). If the table is already defined with a primary key, + * you do not need to override this method. The default implementation simply returns null, + * meaning using the primary key defined in the database. + * @return mixed the primary key of the associated database table. + * If the key is a single column, it should return the column name; + * If the key is a composite one consisting of several columns, it should + * return the array of the key column names. + */ + public function primaryKey() + { + } + + /** + * This method should be overridden to declare related objects. + * + * There are four types of relations that may exist between two active record objects: + * + * + * Besides the above relation types, a special relation called STAT is also supported + * that can be used to perform statistical query (or aggregational query). + * It retrieves the aggregational information about the related objects, such as the number + * of comments for each post, the average rating for each product, etc. + * + * Each kind of related objects is defined in this method as an array with the following elements: + *
+	 * 'varName'=>array('relationType', 'className', 'foreignKey', ...additional options)
+	 * 
+ * where 'varName' refers to the name of the variable/property that the related object(s) can + * be accessed through; 'relationType' refers to the type of the relation, which can be one of the + * following four constants: self::BELONGS_TO, self::HAS_ONE, self::HAS_MANY and self::MANY_MANY; + * 'className' refers to the name of the active record class that the related object(s) is of; + * and 'foreignKey' states the foreign key that relates the two kinds of active record. + * Note, for composite foreign keys, they can be either listed together, separated by commas or specified as an array + * in format of array('key1','key2'). In case you need to specify custom PK->FK association you can define it as + * array('fk'=>'pk'). For composite keys it will be array('fk_c1'=>'pk_с1','fk_c2'=>'pk_c2'). + * For foreign keys used in MANY_MANY relation, the joining table must be declared as well + * (e.g. 'join_table(fk1, fk2)'). + * + * Additional options may be specified as name-value pairs in the rest array elements: + * + * + * The following options are available for certain relations when lazy loading: + * + * + * Below is an example declaring related objects for 'Post' active record class: + *
+	 * return array(
+	 *     'author'=>array(self::BELONGS_TO, 'User', 'author_id'),
+	 *     'comments'=>array(self::HAS_MANY, 'Comment', 'post_id', 'with'=>'author', 'order'=>'create_time DESC'),
+	 *     'tags'=>array(self::MANY_MANY, 'Tag', 'post_tag(post_id, tag_id)', 'order'=>'name'),
+	 * );
+	 * 
+ * + * @return array list of related object declarations. Defaults to empty array. + */ + public function relations() + { + return array(); + } + + /** + * Returns the declaration of named scopes. + * A named scope represents a query criteria that can be chained together with + * other named scopes and applied to a query. This method should be overridden + * by child classes to declare named scopes for the particular AR classes. + * For example, the following code declares two named scopes: 'recently' and + * 'published'. + *
+	 * return array(
+	 *     'published'=>array(
+	 *           'condition'=>'status=1',
+	 *     ),
+	 *     'recently'=>array(
+	 *           'order'=>'create_time DESC',
+	 *           'limit'=>5,
+	 *     ),
+	 * );
+	 * 
+ * If the above scopes are declared in a 'Post' model, we can perform the following + * queries: + *
+	 * $posts=Post::model()->published()->findAll();
+	 * $posts=Post::model()->published()->recently()->findAll();
+	 * $posts=Post::model()->published()->with('comments')->findAll();
+	 * 
+ * Note that the last query is a relational query. + * + * @return array the scope definition. The array keys are scope names; the array + * values are the corresponding scope definitions. Each scope definition is represented + * as an array whose keys must be properties of {@link CDbCriteria}. + */ + public function scopes() + { + return array(); + } + + /** + * Returns the list of all attribute names of the model. + * This would return all column names of the table associated with this AR class. + * @return array list of attribute names. + */ + public function attributeNames() + { + return array_keys($this->getMetaData()->columns); + } + + /** + * Returns the text label for the specified attribute. + * This method overrides the parent implementation by supporting + * returning the label defined in relational object. + * In particular, if the attribute name is in the form of "post.author.name", + * then this method will derive the label from the "author" relation's "name" attribute. + * @param string $attribute the attribute name + * @return string the attribute label + * @see generateAttributeLabel + * @since 1.1.4 + */ + public function getAttributeLabel($attribute) + { + $labels=$this->attributeLabels(); + if(isset($labels[$attribute])) + return $labels[$attribute]; + elseif(strpos($attribute,'.')!==false) + { + $segs=explode('.',$attribute); + $name=array_pop($segs); + $model=$this; + foreach($segs as $seg) + { + $relations=$model->getMetaData()->relations; + if(isset($relations[$seg])) + $model=CActiveRecord::model($relations[$seg]->className); + else + break; + } + return $model->getAttributeLabel($name); + } + else + return $this->generateAttributeLabel($attribute); + } + + /** + * Returns the database connection used by active record. + * By default, the "db" application component is used as the database connection. + * You may override this method if you want to use a different database connection. + * @throws CDbException if "db" application component is not defined + * @return CDbConnection the database connection used by active record. + */ + public function getDbConnection() + { + if(self::$db!==null) + return self::$db; + else + { + self::$db=Yii::app()->getDb(); + if(self::$db instanceof CDbConnection) + return self::$db; + else + throw new CDbException(Yii::t('yii','Active Record requires a "db" CDbConnection application component.')); + } + } + + /** + * Returns the named relation declared for this AR class. + * @param string $name the relation name + * @return CActiveRelation the named relation declared for this AR class. Null if the relation does not exist. + */ + public function getActiveRelation($name) + { + return isset($this->getMetaData()->relations[$name]) ? $this->getMetaData()->relations[$name] : null; + } + + /** + * Returns the metadata of the table that this AR belongs to + * @return CDbTableSchema the metadata of the table that this AR belongs to + */ + public function getTableSchema() + { + return $this->getMetaData()->tableSchema; + } + + /** + * Returns the command builder used by this AR. + * @return CDbCommandBuilder the command builder used by this AR + */ + public function getCommandBuilder() + { + return $this->getDbConnection()->getSchema()->getCommandBuilder(); + } + + /** + * Checks whether this AR has the named attribute + * @param string $name attribute name + * @return boolean whether this AR has the named attribute (table column). + */ + public function hasAttribute($name) + { + return isset($this->getMetaData()->columns[$name]); + } + + /** + * Returns the named attribute value. + * If this is a new record and the attribute is not set before, + * the default column value will be returned. + * If this record is the result of a query and the attribute is not loaded, + * null will be returned. + * You may also use $this->AttributeName to obtain the attribute value. + * @param string $name the attribute name + * @return mixed the attribute value. Null if the attribute is not set or does not exist. + * @see hasAttribute + */ + public function getAttribute($name) + { + if(property_exists($this,$name)) + return $this->$name; + elseif(isset($this->_attributes[$name])) + return $this->_attributes[$name]; + } + + /** + * Sets the named attribute value. + * You may also use $this->AttributeName to set the attribute value. + * @param string $name the attribute name + * @param mixed $value the attribute value. + * @return boolean whether the attribute exists and the assignment is conducted successfully + * @see hasAttribute + */ + public function setAttribute($name,$value) + { + if(property_exists($this,$name)) + $this->$name=$value; + elseif(isset($this->getMetaData()->columns[$name])) + $this->_attributes[$name]=$value; + else + return false; + return true; + } + + /** + * Do not call this method. This method is used internally by {@link CActiveFinder} to populate + * related objects. This method adds a related object to this record. + * @param string $name attribute name + * @param mixed $record the related record + * @param mixed $index the index value in the related object collection. + * If true, it means using zero-based integer index. + * If false, it means a HAS_ONE or BELONGS_TO object and no index is needed. + */ + public function addRelatedRecord($name,$record,$index) + { + if($index!==false) + { + if(!isset($this->_related[$name])) + $this->_related[$name]=array(); + if($record instanceof CActiveRecord) + { + if($index===true) + $this->_related[$name][]=$record; + else + $this->_related[$name][$index]=$record; + } + } + elseif(!isset($this->_related[$name])) + $this->_related[$name]=$record; + } + + /** + * Returns all column attribute values. + * Note, related objects are not returned. + * @param mixed $names names of attributes whose value needs to be returned. + * If this is true (default), then all attribute values will be returned, including + * those that are not loaded from DB (null will be returned for those attributes). + * If this is null, all attributes except those that are not loaded from DB will be returned. + * @return array attribute values indexed by attribute names. + */ + public function getAttributes($names=true) + { + $attributes=$this->_attributes; + foreach($this->getMetaData()->columns as $name=>$column) + { + if(property_exists($this,$name)) + $attributes[$name]=$this->$name; + elseif($names===true && !isset($attributes[$name])) + $attributes[$name]=null; + } + if(is_array($names)) + { + $attrs=array(); + foreach($names as $name) + { + if(property_exists($this,$name)) + $attrs[$name]=$this->$name; + else + $attrs[$name]=isset($attributes[$name])?$attributes[$name]:null; + } + return $attrs; + } + else + return $attributes; + } + + /** + * Saves the current record. + * + * The record is inserted as a row into the database table if its {@link isNewRecord} + * property is true (usually the case when the record is created using the 'new' + * operator). Otherwise, it will be used to update the corresponding row in the table + * (usually the case if the record is obtained using one of those 'find' methods.) + * + * Validation will be performed before saving the record. If the validation fails, + * the record will not be saved. You can call {@link getErrors()} to retrieve the + * validation errors. + * + * If the record is saved via insertion, its {@link isNewRecord} property will be + * set false, and its {@link scenario} property will be set to be 'update'. + * And if its primary key is auto-incremental and is not set before insertion, + * the primary key will be populated with the automatically generated key value. + * + * @param boolean $runValidation whether to perform validation before saving the record. + * If the validation fails, the record will not be saved to database. + * @param array $attributes list of attributes that need to be saved. Defaults to null, + * meaning all attributes that are loaded from DB will be saved. + * @return boolean whether the saving succeeds + */ + public function save($runValidation=true,$attributes=null) + { + if(!$runValidation || $this->validate($attributes)) + return $this->getIsNewRecord() ? $this->insert($attributes) : $this->update($attributes); + else + return false; + } + + /** + * Returns if the current record is new (was never saved to database) + * @return boolean whether the record is new and should be inserted when calling {@link save}. + * This property is automatically set in constructor and {@link populateRecord} and is set + * to false right after inserting record to database. + * Defaults to false, but it will be set to true if the instance is created using + * the new operator. + */ + public function getIsNewRecord() + { + return $this->_new; + } + + /** + * Sets if the record is new. + * @param boolean $value whether the record is new and should be inserted when calling {@link save}. + * @see getIsNewRecord + */ + public function setIsNewRecord($value) + { + $this->_new=$value; + } + + /** + * This event is raised before the record is saved. + * By setting {@link CModelEvent::isValid} to be false, the normal {@link save()} process will be stopped. + * @param CModelEvent $event the event parameter + */ + public function onBeforeSave($event) + { + $this->raiseEvent('onBeforeSave',$event); + } + + /** + * This event is raised after the record is saved. + * @param CEvent $event the event parameter + */ + public function onAfterSave($event) + { + $this->raiseEvent('onAfterSave',$event); + } + + /** + * This event is raised before the record is deleted. + * By setting {@link CModelEvent::isValid} to be false, the normal {@link delete()} process will be stopped. + * @param CModelEvent $event the event parameter + */ + public function onBeforeDelete($event) + { + $this->raiseEvent('onBeforeDelete',$event); + } + + /** + * This event is raised after the record is deleted. + * @param CEvent $event the event parameter + */ + public function onAfterDelete($event) + { + $this->raiseEvent('onAfterDelete',$event); + } + + /** + * This event is raised before an AR finder performs a find call. + * This can be either a call to CActiveRecords find methods or a find call + * when model is loaded in relational context via lazy or eager loading. + * If you want to access or modify the query criteria used for the + * find call, you can use {@link getDbCriteria()} to customize it based on your needs. + * When modifying criteria in beforeFind you have to make sure you are using the right + * table alias which is different on normal find and relational call. + * You can use {@link getTableAlias()} to get the alias used for the upcoming find call. + * Please note that modification of criteria is fully supported as of version 1.1.13. + * Earlier versions had some problems with relational context and applying changes correctly. + * @param CModelEvent $event the event parameter + * @see beforeFind + */ + public function onBeforeFind($event) + { + $this->raiseEvent('onBeforeFind',$event); + } + + /** + * This event is raised after the record is instantiated by a find method. + * @param CEvent $event the event parameter + */ + public function onAfterFind($event) + { + $this->raiseEvent('onAfterFind',$event); + } + + /** + * Given 'with' options returns a new active finder instance. + * + * @param mixed $with the relation names to be actively looked for + * @return CActiveFinder active finder for the operation + * + * @since 1.1.14 + */ + public function getActiveFinder($with) + { + return new CActiveFinder($this,$with); + } + + /** + * This event is raised before an AR finder performs a count call. + * If you want to access or modify the query criteria used for the + * count call, you can use {@link getDbCriteria()} to customize it based on your needs. + * When modifying criteria in beforeCount you have to make sure you are using the right + * table alias which is different on normal count and relational call. + * You can use {@link getTableAlias()} to get the alias used for the upcoming count call. + * @param CModelEvent $event the event parameter + * @see beforeCount + * @since 1.1.14 + */ + public function onBeforeCount($event) + { + $this->raiseEvent('onBeforeCount',$event); + } + + /** + * This method is invoked before saving a record (after validation, if any). + * The default implementation raises the {@link onBeforeSave} event. + * You may override this method to do any preparation work for record saving. + * Use {@link isNewRecord} to determine whether the saving is + * for inserting or updating record. + * Make sure you call the parent implementation so that the event is raised properly. + * @return boolean whether the saving should be executed. Defaults to true. + */ + protected function beforeSave() + { + if($this->hasEventHandler('onBeforeSave')) + { + $event=new CModelEvent($this); + $this->onBeforeSave($event); + return $event->isValid; + } + else + return true; + } + + /** + * This method is invoked after saving a record successfully. + * The default implementation raises the {@link onAfterSave} event. + * You may override this method to do postprocessing after record saving. + * Make sure you call the parent implementation so that the event is raised properly. + */ + protected function afterSave() + { + if($this->hasEventHandler('onAfterSave')) + $this->onAfterSave(new CEvent($this)); + } + + /** + * This method is invoked before deleting a record. + * The default implementation raises the {@link onBeforeDelete} event. + * You may override this method to do any preparation work for record deletion. + * Make sure you call the parent implementation so that the event is raised properly. + * @return boolean whether the record should be deleted. Defaults to true. + */ + protected function beforeDelete() + { + if($this->hasEventHandler('onBeforeDelete')) + { + $event=new CModelEvent($this); + $this->onBeforeDelete($event); + return $event->isValid; + } + else + return true; + } + + /** + * This method is invoked after deleting a record. + * The default implementation raises the {@link onAfterDelete} event. + * You may override this method to do postprocessing after the record is deleted. + * Make sure you call the parent implementation so that the event is raised properly. + */ + protected function afterDelete() + { + if($this->hasEventHandler('onAfterDelete')) + $this->onAfterDelete(new CEvent($this)); + } + + /** + * This method is invoked before an AR finder executes a find call. + * The find calls include {@link find}, {@link findAll}, {@link findByPk}, + * {@link findAllByPk}, {@link findByAttributes}, {@link findAllByAttributes}, + * {@link findBySql} and {@link findAllBySql}. + * The default implementation raises the {@link onBeforeFind} event. + * If you override this method, make sure you call the parent implementation + * so that the event is raised properly. + * For details on modifying query criteria see {@link onBeforeFind} event. + */ + protected function beforeFind() + { + if($this->hasEventHandler('onBeforeFind')) + { + $event=new CModelEvent($this); + $this->onBeforeFind($event); + } + } + + /** + * This method is invoked before an AR finder executes a count call. + * The count calls include {@link count} and {@link countByAttributes} + * The default implementation raises the {@link onBeforeCount} event. + * If you override this method, make sure you call the parent implementation + * so that the event is raised properly. + * @since 1.1.14 + */ + protected function beforeCount() + { + if($this->hasEventHandler('onBeforeCount')) + $this->onBeforeCount(new CEvent($this)); + } + + /** + * This method is invoked after each record is instantiated by a find method. + * The default implementation raises the {@link onAfterFind} event. + * You may override this method to do postprocessing after each newly found record is instantiated. + * Make sure you call the parent implementation so that the event is raised properly. + */ + protected function afterFind() + { + if($this->hasEventHandler('onAfterFind')) + $this->onAfterFind(new CEvent($this)); + } + + /** + * Calls {@link beforeFind}. + * This method is internally used. + */ + public function beforeFindInternal() + { + $this->beforeFind(); + } + + /** + * Calls {@link afterFind}. + * This method is internally used. + */ + public function afterFindInternal() + { + $this->afterFind(); + } + + /** + * Inserts a row into the table based on this active record attributes. + * If the table's primary key is auto-incremental and is null before insertion, + * it will be populated with the actual value after insertion. + * Note, validation is not performed in this method. You may call {@link validate} to perform the validation. + * After the record is inserted to DB successfully, its {@link isNewRecord} property will be set false, + * and its {@link scenario} property will be set to be 'update'. + * @param array $attributes list of attributes that need to be saved. Defaults to null, + * meaning all attributes that are loaded from DB will be saved. + * @return boolean whether the attributes are valid and the record is inserted successfully. + * @throws CDbException if the record is not new + */ + public function insert($attributes=null) + { + if(!$this->getIsNewRecord()) + throw new CDbException(Yii::t('yii','The active record cannot be inserted to database because it is not new.')); + if($this->beforeSave()) + { + Yii::trace(get_class($this).'.insert()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $table=$this->getTableSchema(); + $command=$builder->createInsertCommand($table,$this->getAttributes($attributes)); + if($command->execute()) + { + $primaryKey=$table->primaryKey; + if($table->sequenceName!==null) + { + if(is_string($primaryKey) && $this->$primaryKey===null) + $this->$primaryKey=$builder->getLastInsertID($table); + elseif(is_array($primaryKey)) + { + foreach($primaryKey as $pk) + { + if($this->$pk===null) + { + $this->$pk=$builder->getLastInsertID($table); + break; + } + } + } + } + $this->_pk=$this->getPrimaryKey(); + $this->afterSave(); + $this->setIsNewRecord(false); + $this->setScenario('update'); + return true; + } + } + return false; + } + + /** + * Updates the row represented by this active record. + * All loaded attributes will be saved to the database. + * Note, validation is not performed in this method. You may call {@link validate} to perform the validation. + * @param array $attributes list of attributes that need to be saved. Defaults to null, + * meaning all attributes that are loaded from DB will be saved. + * @return boolean whether the update is successful + * @throws CDbException if the record is new + */ + public function update($attributes=null) + { + if($this->getIsNewRecord()) + throw new CDbException(Yii::t('yii','The active record cannot be updated because it is new.')); + if($this->beforeSave()) + { + Yii::trace(get_class($this).'.update()','system.db.ar.CActiveRecord'); + if($this->_pk===null) + $this->_pk=$this->getPrimaryKey(); + $this->updateByPk($this->getOldPrimaryKey(),$this->getAttributes($attributes)); + $this->_pk=$this->getPrimaryKey(); + $this->afterSave(); + return true; + } + else + return false; + } + + /** + * Saves a selected list of attributes. + * Unlike {@link save}, this method only saves the specified attributes + * of an existing row dataset and does NOT call either {@link beforeSave} or {@link afterSave}. + * Also note that this method does neither attribute filtering nor validation. + * So do not use this method with untrusted data (such as user posted data). + * You may consider the following alternative if you want to do so: + *
+	 * $postRecord=Post::model()->findByPk($postID);
+	 * $postRecord->attributes=$_POST['post'];
+	 * $postRecord->save();
+	 * 
+ * @param array $attributes attributes to be updated. Each element represents an attribute name + * or an attribute value indexed by its name. If the latter, the record's + * attribute will be changed accordingly before saving. + * @throws CDbException if the record is new + * @return boolean whether the update is successful. Note that false is also returned if the saving + * was successfull but no attributes had changed and the database driver returns 0 for the number + * of updated records. + */ + public function saveAttributes($attributes) + { + if(!$this->getIsNewRecord()) + { + Yii::trace(get_class($this).'.saveAttributes()','system.db.ar.CActiveRecord'); + $values=array(); + foreach($attributes as $name=>$value) + { + if(is_integer($name)) + $values[$value]=$this->$value; + else + $values[$name]=$this->$name=$value; + } + if($this->_pk===null) + $this->_pk=$this->getPrimaryKey(); + if($this->updateByPk($this->getOldPrimaryKey(),$values)>0) + { + $this->_pk=$this->getPrimaryKey(); + return true; + } + else + return false; + } + else + throw new CDbException(Yii::t('yii','The active record cannot be updated because it is new.')); + } + + /** + * Saves one or several counter columns for the current AR object. + * Note that this method differs from {@link updateCounters} in that it only + * saves the current AR object. + * An example usage is as follows: + *
+	 * $postRecord=Post::model()->findByPk($postID);
+	 * $postRecord->saveCounters(array('view_count'=>1));
+	 * 
+ * Use negative values if you want to decrease the counters. + * @param array $counters the counters to be updated (column name=>increment value) + * @return boolean whether the saving is successful + * @see updateCounters + * @since 1.1.8 + */ + public function saveCounters($counters) + { + Yii::trace(get_class($this).'.saveCounters()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $table=$this->getTableSchema(); + $criteria=$builder->createPkCriteria($table,$this->getOldPrimaryKey()); + $command=$builder->createUpdateCounterCommand($this->getTableSchema(),$counters,$criteria); + if($command->execute()) + { + foreach($counters as $name=>$value) + $this->$name=$this->$name+$value; + return true; + } + else + return false; + } + + /** + * Deletes the row corresponding to this active record. + * @throws CDbException if the record is new + * @return boolean whether the deletion is successful. + */ + public function delete() + { + if(!$this->getIsNewRecord()) + { + Yii::trace(get_class($this).'.delete()','system.db.ar.CActiveRecord'); + if($this->beforeDelete()) + { + $result=$this->deleteByPk($this->getPrimaryKey())>0; + $this->afterDelete(); + return $result; + } + else + return false; + } + else + throw new CDbException(Yii::t('yii','The active record cannot be deleted because it is new.')); + } + + /** + * Repopulates this active record with the latest data. + * @return boolean whether the row still exists in the database. If true, the latest data will be populated to this active record. + */ + public function refresh() + { + Yii::trace(get_class($this).'.refresh()','system.db.ar.CActiveRecord'); + if(($record=$this->findByPk($this->getPrimaryKey()))!==null) + { + $this->_attributes=array(); + $this->_related=array(); + foreach($this->getMetaData()->columns as $name=>$column) + { + if(property_exists($this,$name)) + $this->$name=$record->$name; + else + $this->_attributes[$name]=$record->$name; + } + return true; + } + else + return false; + } + + /** + * Compares current active record with another one. + * The comparison is made by comparing table name and the primary key values of the two active records. + * @param CActiveRecord $record record to compare to + * @return boolean whether the two active records refer to the same row in the database table. + */ + public function equals($record) + { + return $this->tableName()===$record->tableName() && $this->getPrimaryKey()===$record->getPrimaryKey(); + } + + /** + * Returns the primary key value. + * @return mixed the primary key value. An array (column name=>column value) is returned if the primary key is composite. + * If primary key is not defined, null will be returned. + */ + public function getPrimaryKey() + { + $table=$this->getTableSchema(); + if(is_string($table->primaryKey)) + return $this->{$table->primaryKey}; + elseif(is_array($table->primaryKey)) + { + $values=array(); + foreach($table->primaryKey as $name) + $values[$name]=$this->$name; + return $values; + } + else + return null; + } + + /** + * Sets the primary key value. + * After calling this method, the old primary key value can be obtained from {@link oldPrimaryKey}. + * @param mixed $value the new primary key value. If the primary key is composite, the new value + * should be provided as an array (column name=>column value). + * @since 1.1.0 + */ + public function setPrimaryKey($value) + { + $this->_pk=$this->getPrimaryKey(); + $table=$this->getTableSchema(); + if(is_string($table->primaryKey)) + $this->{$table->primaryKey}=$value; + elseif(is_array($table->primaryKey)) + { + foreach($table->primaryKey as $name) + $this->$name=$value[$name]; + } + } + + /** + * Returns the old primary key value. + * This refers to the primary key value that is populated into the record + * after executing a find method (e.g. find(), findAll()). + * The value remains unchanged even if the primary key attribute is manually assigned with a different value. + * @return mixed the old primary key value. An array (column name=>column value) is returned if the primary key is composite. + * If primary key is not defined, null will be returned. + * @since 1.1.0 + */ + public function getOldPrimaryKey() + { + return $this->_pk; + } + + /** + * Sets the old primary key value. + * @param mixed $value the old primary key value. + * @since 1.1.3 + */ + public function setOldPrimaryKey($value) + { + $this->_pk=$value; + } + + /** + * Performs the actual DB query and populates the AR objects with the query result. + * This method is mainly internally used by other AR query methods. + * @param CDbCriteria $criteria the query criteria + * @param boolean $all whether to return all data + * @return mixed the AR objects populated with the query result + * @since 1.1.7 + */ + protected function query($criteria,$all=false) + { + $this->beforeFind(); + $this->applyScopes($criteria); + + if(empty($criteria->with)) + { + if(!$all) + $criteria->limit=1; + $command=$this->getCommandBuilder()->createFindCommand($this->getTableSchema(),$criteria); + return $all ? $this->populateRecords($command->queryAll(), true, $criteria->index) : $this->populateRecord($command->queryRow()); + } + else + { + $finder=$this->getActiveFinder($criteria->with); + return $finder->query($criteria,$all); + } + } + + /** + * Applies the query scopes to the given criteria. + * This method merges {@link dbCriteria} with the given criteria parameter. + * It then resets {@link dbCriteria} to be null. + * @param CDbCriteria $criteria the query criteria. This parameter may be modified by merging {@link dbCriteria}. + */ + public function applyScopes(&$criteria) + { + if(!empty($criteria->scopes)) + { + $scs=$this->scopes(); + $c=$this->getDbCriteria(); + foreach((array)$criteria->scopes as $k=>$v) + { + if(is_integer($k)) + { + if(is_string($v)) + { + if(isset($scs[$v])) + { + $c->mergeWith($scs[$v],true); + continue; + } + $scope=$v; + $params=array(); + } + elseif(is_array($v)) + { + $scope=key($v); + $params=current($v); + } + } + elseif(is_string($k)) + { + $scope=$k; + $params=$v; + } + + call_user_func_array(array($this,$scope),(array)$params); + } + } + + if(isset($c) || ($c=$this->getDbCriteria(false))!==null) + { + $c->mergeWith($criteria); + $criteria=$c; + $this->resetScope(false); + } + } + + /** + * Returns the table alias to be used by the find methods. + * In relational queries, the returned table alias may vary according to + * the corresponding relation declaration. Also, the default table alias + * set by {@link setTableAlias} may be overridden by the applied scopes. + * @param boolean $quote whether to quote the alias name + * @param boolean $checkScopes whether to check if a table alias is defined in the applied scopes so far. + * This parameter must be set false when calling this method in {@link defaultScope}. + * An infinite loop would be formed otherwise. + * @return string the default table alias + * @since 1.1.1 + */ + public function getTableAlias($quote=false, $checkScopes=true) + { + if($checkScopes && ($criteria=$this->getDbCriteria(false))!==null && $criteria->alias!='') + $alias=$criteria->alias; + else + $alias=$this->_alias; + return $quote ? $this->getDbConnection()->getSchema()->quoteTableName($alias) : $alias; + } + + /** + * Sets the table alias to be used in queries. + * @param string $alias the table alias to be used in queries. The alias should NOT be quoted. + * @since 1.1.3 + */ + public function setTableAlias($alias) + { + $this->_alias=$alias; + } + + /** + * Finds a single active record with the specified condition. + * @param mixed $condition query condition or criteria. + * If a string, it is treated as query condition (the WHERE clause); + * If an array, it is treated as the initial values for constructing a {@link CDbCriteria} object; + * Otherwise, it should be an instance of {@link CDbCriteria}. + * @param array $params parameters to be bound to an SQL statement. + * This is only used when the first parameter is a string (query condition). + * In other cases, please use {@link CDbCriteria::params} to set parameters. + * @return static|null the record found. Null if no record is found. + */ + public function find($condition='',$params=array()) + { + Yii::trace(get_class($this).'.find()','system.db.ar.CActiveRecord'); + $criteria=$this->getCommandBuilder()->createCriteria($condition,$params); + return $this->query($criteria); + } + + /** + * Finds all active records satisfying the specified condition. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return static[] list of active records satisfying the specified condition. An empty array is returned if none is found. + */ + public function findAll($condition='',$params=array()) + { + Yii::trace(get_class($this).'.findAll()','system.db.ar.CActiveRecord'); + $criteria=$this->getCommandBuilder()->createCriteria($condition,$params); + return $this->query($criteria,true); + } + + /** + * Finds a single active record with the specified primary key. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return static|null the record found. Null if none is found. + */ + public function findByPk($pk,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.findByPk()','system.db.ar.CActiveRecord'); + $prefix=$this->getTableAlias(true).'.'; + $criteria=$this->getCommandBuilder()->createPkCriteria($this->getTableSchema(),$pk,$condition,$params,$prefix); + return $this->query($criteria); + } + + /** + * Finds all active records with the specified primary keys. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return static[] the records found. An empty array is returned if none is found. + */ + public function findAllByPk($pk,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.findAllByPk()','system.db.ar.CActiveRecord'); + $prefix=$this->getTableAlias(true).'.'; + $criteria=$this->getCommandBuilder()->createPkCriteria($this->getTableSchema(),$pk,$condition,$params,$prefix); + return $this->query($criteria,true); + } + + /** + * Finds a single active record that has the specified attribute values. + * See {@link find()} for detailed explanation about $condition and $params. + * @param array $attributes list of attribute values (indexed by attribute names) that the active records should match. + * An attribute value can be an array which will be used to generate an IN condition. + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return static|null the record found. Null if none is found. + */ + public function findByAttributes($attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.findByAttributes()','system.db.ar.CActiveRecord'); + $prefix=$this->getTableAlias(true).'.'; + $criteria=$this->getCommandBuilder()->createColumnCriteria($this->getTableSchema(),$attributes,$condition,$params,$prefix); + return $this->query($criteria); + } + + /** + * Finds all active records that have the specified attribute values. + * See {@link find()} for detailed explanation about $condition and $params. + * @param array $attributes list of attribute values (indexed by attribute names) that the active records should match. + * An attribute value can be an array which will be used to generate an IN condition. + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return static[] the records found. An empty array is returned if none is found. + */ + public function findAllByAttributes($attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.findAllByAttributes()','system.db.ar.CActiveRecord'); + $prefix=$this->getTableAlias(true).'.'; + $criteria=$this->getCommandBuilder()->createColumnCriteria($this->getTableSchema(),$attributes,$condition,$params,$prefix); + return $this->query($criteria,true); + } + + /** + * Finds a single active record with the specified SQL statement. + * @param string $sql the SQL statement + * @param array $params parameters to be bound to the SQL statement + * @return static|null the record found. Null if none is found. + */ + public function findBySql($sql,$params=array()) + { + Yii::trace(get_class($this).'.findBySql()','system.db.ar.CActiveRecord'); + $this->beforeFind(); + if(($criteria=$this->getDbCriteria(false))!==null && !empty($criteria->with)) + { + $this->resetScope(false); + $finder=$this->getActiveFinder($criteria->with); + return $finder->findBySql($sql,$params); + } + else + { + $command=$this->getCommandBuilder()->createSqlCommand($sql,$params); + return $this->populateRecord($command->queryRow()); + } + } + + /** + * Finds all active records using the specified SQL statement. + * @param string $sql the SQL statement + * @param array $params parameters to be bound to the SQL statement + * @return static[] the records found. An empty array is returned if none is found. + */ + public function findAllBySql($sql,$params=array()) + { + Yii::trace(get_class($this).'.findAllBySql()','system.db.ar.CActiveRecord'); + $this->beforeFind(); + if(($criteria=$this->getDbCriteria(false))!==null && !empty($criteria->with)) + { + $this->resetScope(false); + $finder=$this->getActiveFinder($criteria->with); + return $finder->findAllBySql($sql,$params); + } + else + { + $command=$this->getCommandBuilder()->createSqlCommand($sql,$params); + return $this->populateRecords($command->queryAll()); + } + } + + /** + * Finds the number of rows satisfying the specified query condition. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return string the number of rows satisfying the specified query condition. Note: type is string to keep max. precision. + */ + public function count($condition='',$params=array()) + { + Yii::trace(get_class($this).'.count()','system.db.ar.CActiveRecord'); + $this->beforeCount(); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createCriteria($condition,$params); + $this->applyScopes($criteria); + + if(empty($criteria->with)) + return $builder->createCountCommand($this->getTableSchema(),$criteria)->queryScalar(); + else + { + $finder=$this->getActiveFinder($criteria->with); + return $finder->count($criteria); + } + } + + /** + * Finds the number of rows that have the specified attribute values. + * See {@link find()} for detailed explanation about $condition and $params. + * @param array $attributes list of attribute values (indexed by attribute names) that the active records should match. + * An attribute value can be an array which will be used to generate an IN condition. + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return string the number of rows satisfying the specified query condition. Note: type is string to keep max. precision. + * @since 1.1.4 + */ + public function countByAttributes($attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.countByAttributes()','system.db.ar.CActiveRecord'); + $prefix=$this->getTableAlias(true).'.'; + $builder=$this->getCommandBuilder(); + $this->beforeCount(); + $criteria=$builder->createColumnCriteria($this->getTableSchema(),$attributes,$condition,$params,$prefix); + $this->applyScopes($criteria); + + if(empty($criteria->with)) + return $builder->createCountCommand($this->getTableSchema(),$criteria)->queryScalar(); + else + { + $finder=$this->getActiveFinder($criteria->with); + return $finder->count($criteria); + } + } + + /** + * Finds the number of rows using the given SQL statement. + * This is equivalent to calling {@link CDbCommand::queryScalar} with the specified + * SQL statement and the parameters. + * @param string $sql the SQL statement + * @param array $params parameters to be bound to the SQL statement + * @return string the number of rows using the given SQL statement. Note: type is string to keep max. precision. + */ + public function countBySql($sql,$params=array()) + { + Yii::trace(get_class($this).'.countBySql()','system.db.ar.CActiveRecord'); + $this->beforeCount(); + return $this->getCommandBuilder()->createSqlCommand($sql,$params)->queryScalar(); + } + + /** + * Checks whether there is row satisfying the specified condition. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return boolean whether there is row satisfying the specified condition. + */ + public function exists($condition='',$params=array()) + { + Yii::trace(get_class($this).'.exists()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createCriteria($condition,$params); + $table=$this->getTableSchema(); + $criteria->select='1'; + $criteria->limit=1; + $this->applyScopes($criteria); + + if(empty($criteria->with)) + return $builder->createFindCommand($table,$criteria,$this->getTableAlias(false, false))->queryRow()!==false; + else + { + $criteria->select='*'; + $finder=$this->getActiveFinder($criteria->with); + return $finder->count($criteria)>0; + } + } + + /** + * Specifies which related objects should be eagerly loaded. + * This method takes variable number of parameters. Each parameter specifies + * the name of a relation or child-relation. For example, + *
+	 * // find all posts together with their author and comments
+	 * Post::model()->with('author','comments')->findAll();
+	 * // find all posts together with their author and the author's profile
+	 * Post::model()->with('author','author.profile')->findAll();
+	 * 
+ * The relations should be declared in {@link relations()}. + * + * By default, the options specified in {@link relations()} will be used + * to do relational query. In order to customize the options on the fly, + * we should pass an array parameter to the with() method. The array keys + * are relation names, and the array values are the corresponding query options. + * For example, + *
+	 * Post::model()->with(array(
+	 *     'author'=>array('select'=>'id, name'),
+	 *     'comments'=>array('condition'=>'approved=1', 'order'=>'create_time'),
+	 * ))->findAll();
+	 * 
+ * + * @return static the AR object itself. + */ + public function with() + { + if(func_num_args()>0) + { + $with=func_get_args(); + if(is_array($with[0])) // the parameter is given as an array + $with=$with[0]; + if(!empty($with)) + $this->getDbCriteria()->mergeWith(array('with'=>$with)); + } + return $this; + } + + /** + * Sets {@link CDbCriteria::together} property to be true. + * This is only used in relational AR query. Please refer to {@link CDbCriteria::together} + * for more details. + * @return static the AR object itself + * @since 1.1.4 + */ + public function together() + { + $this->getDbCriteria()->together=true; + return $this; + } + + /** + * Updates records with the specified primary key(s). + * See {@link find()} for detailed explanation about $condition and $params. + * Note, the attributes are not checked for safety and validation is NOT performed. + * @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). + * @param array $attributes list of attributes (name=>$value) to be updated + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return integer the number of rows being updated + */ + public function updateByPk($pk,$attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.updateByPk()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $table=$this->getTableSchema(); + $criteria=$builder->createPkCriteria($table,$pk,$condition,$params); + $command=$builder->createUpdateCommand($table,$attributes,$criteria); + return $command->execute(); + } + + /** + * Updates records with the specified condition. + * See {@link find()} for detailed explanation about $condition and $params. + * Note, the attributes are not checked for safety and no validation is done. + * @param array $attributes list of attributes (name=>$value) to be updated + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return integer the number of rows being updated + */ + public function updateAll($attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.updateAll()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createCriteria($condition,$params); + $command=$builder->createUpdateCommand($this->getTableSchema(),$attributes,$criteria); + return $command->execute(); + } + + /** + * Updates one or several counter columns. + * Note, this updates all rows of data unless a condition or criteria is specified. + * See {@link find()} for detailed explanation about $condition and $params. + * @param array $counters the counters to be updated (column name=>increment value) + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return integer the number of rows being updated + * @see saveCounters + */ + public function updateCounters($counters,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.updateCounters()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createCriteria($condition,$params); + $command=$builder->createUpdateCounterCommand($this->getTableSchema(),$counters,$criteria); + return $command->execute(); + } + + /** + * Deletes rows with the specified primary key. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return integer the number of rows deleted + */ + public function deleteByPk($pk,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.deleteByPk()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createPkCriteria($this->getTableSchema(),$pk,$condition,$params); + $command=$builder->createDeleteCommand($this->getTableSchema(),$criteria); + return $command->execute(); + } + + /** + * Deletes rows with the specified condition. + * See {@link find()} for detailed explanation about $condition and $params. + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return integer the number of rows deleted + */ + public function deleteAll($condition='',$params=array()) + { + Yii::trace(get_class($this).'.deleteAll()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $criteria=$builder->createCriteria($condition,$params); + $command=$builder->createDeleteCommand($this->getTableSchema(),$criteria); + return $command->execute(); + } + + /** + * Deletes rows which match the specified attribute values. + * See {@link find()} for detailed explanation about $condition and $params. + * @param array $attributes list of attribute values (indexed by attribute names) that the active records should match. + * An attribute value can be an array which will be used to generate an IN condition. + * @param mixed $condition query condition or criteria. + * @param array $params parameters to be bound to an SQL statement. + * @return integer number of rows affected by the execution. + */ + public function deleteAllByAttributes($attributes,$condition='',$params=array()) + { + Yii::trace(get_class($this).'.deleteAllByAttributes()','system.db.ar.CActiveRecord'); + $builder=$this->getCommandBuilder(); + $table=$this->getTableSchema(); + $criteria=$builder->createColumnCriteria($table,$attributes,$condition,$params); + $command=$builder->createDeleteCommand($table,$criteria); + return $command->execute(); + } + + /** + * Creates an active record with the given attributes. + * This method is internally used by the find methods. + * @param array $attributes attribute values (column name=>column value) + * @param boolean $callAfterFind whether to call {@link afterFind} after the record is populated. + * @return static|null the newly created active record. The class of the object is the same as the model class. + * Null is returned if the input data is false. + */ + public function populateRecord($attributes,$callAfterFind=true) + { + if($attributes!==false) + { + $record=$this->instantiate($attributes); + $record->setScenario('update'); + $record->init(); + $md=$record->getMetaData(); + foreach($attributes as $name=>$value) + { + if(property_exists($record,$name)) + $record->$name=$value; + elseif(isset($md->columns[$name])) + $record->_attributes[$name]=$value; + } + $record->_pk=$record->getPrimaryKey(); + $record->attachBehaviors($record->behaviors()); + if($callAfterFind) + $record->afterFind(); + return $record; + } + else + return null; + } + + /** + * Creates a list of active records based on the input data. + * This method is internally used by the find methods. + * @param array $data list of attribute values for the active records. + * @param boolean $callAfterFind whether to call {@link afterFind} after each record is populated. + * @param string $index the name of the attribute whose value will be used as indexes of the query result array. + * If null, it means the array will be indexed by zero-based integers. + * @return static[] list of active records. + */ + public function populateRecords($data,$callAfterFind=true,$index=null) + { + $records=array(); + foreach($data as $attributes) + { + if(($record=$this->populateRecord($attributes,$callAfterFind))!==null) + { + if($index===null) + $records[]=$record; + else + $records[$record->$index]=$record; + } + } + return $records; + } + + /** + * Creates an active record instance. + * This method is called by {@link populateRecord} and {@link populateRecords}. + * You may override this method if the instance being created + * depends the attributes that are to be populated to the record. + * For example, by creating a record based on the value of a column, + * you may implement the so-called single-table inheritance mapping. + * @param array $attributes list of attribute values for the active records. + * @return static the active record + */ + protected function instantiate($attributes) + { + $class=get_class($this); + $model=new $class(null); + return $model; + } + + /** + * Returns whether there is an element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed $offset the offset to check on + * @return boolean + */ + #[ReturnTypeWillChange] + public function offsetExists($offset) + { + return $this->__isset($offset); + } +} + + +/** + * CBaseActiveRelation is the base class for all active relations. + * @author Qiang Xue + * @package system.db.ar + */ +class CBaseActiveRelation extends CComponent +{ + /** + * @var string name of the related object + */ + public $name; + /** + * @var string name of the related active record class + */ + public $className; + /** + * @var mixed the foreign key in this relation + */ + public $foreignKey; + /** + * @var mixed list of column names (an array, or a string of names separated by commas) to be selected. + * Do not quote or prefix the column names unless they are used in an expression. + * In that case, you should prefix the column names with 'relationName.'. + */ + public $select='*'; + /** + * @var string WHERE clause. For {@link CActiveRelation} descendant classes, column names + * referenced in the condition should be disambiguated with prefix 'relationName.'. + */ + public $condition=''; + /** + * @var array the parameters that are to be bound to the condition. + * The keys are parameter placeholder names, and the values are parameter values. + */ + public $params=array(); + /** + * @var string GROUP BY clause. For {@link CActiveRelation} descendant classes, column names + * referenced in this property should be disambiguated with prefix 'relationName.'. + */ + public $group=''; + /** + * @var string how to join with other tables. This refers to the JOIN clause in an SQL statement. + * For example, 'LEFT JOIN users ON users.id=authorID'. + * @since 1.1.3 + */ + public $join=''; + /** + * @var string|array property for setting post-JOIN operations such as USE INDEX. + * String typed value can be used with JOINs for HAS_MANY and MANY_MANY relations, while array typed + * value designed to be used only with MANY_MANY relations. First array element will be used for junction + * table JOIN and second array element will be used for target table JOIN. + * @since 1.1.16 + */ + public $joinOptions=''; + /** + * @var string HAVING clause. For {@link CActiveRelation} descendant classes, column names + * referenced in this property should be disambiguated with prefix 'relationName.'. + */ + public $having=''; + /** + * @var string ORDER BY clause. For {@link CActiveRelation} descendant classes, column names + * referenced in this property should be disambiguated with prefix 'relationName.'. + */ + public $order=''; + + /** + * Constructor. + * @param string $name name of the relation + * @param string $className name of the related active record class + * @param string $foreignKey foreign key for this relation + * @param array $options additional options (name=>value). The keys must be the property names of this class. + */ + public function __construct($name,$className,$foreignKey,$options=array()) + { + $this->name=$name; + $this->className=$className; + $this->foreignKey=$foreignKey; + foreach($options as $name=>$value) + $this->$name=$value; + } + + /** + * Merges this relation with a criteria specified dynamically. + * @param array $criteria the dynamically specified criteria + * @param boolean $fromScope whether the criteria to be merged is from scopes + */ + public function mergeWith($criteria,$fromScope=false) + { + if($criteria instanceof CDbCriteria) + $criteria=$criteria->toArray(); + if(isset($criteria['select']) && $this->select!==$criteria['select']) + { + if($this->select==='*'||$this->select===false) + $this->select=$criteria['select']; + elseif($criteria['select']===false) + $this->select=false; + elseif($criteria['select']!=='*') + { + $select1=is_string($this->select)?preg_split('/\s*,\s*/',trim($this->select),-1,PREG_SPLIT_NO_EMPTY):$this->select; + $select2=is_string($criteria['select'])?preg_split('/\s*,\s*/',trim($criteria['select']),-1,PREG_SPLIT_NO_EMPTY):$criteria['select']; + $this->select=array_merge($select1,array_diff($select2,$select1)); + } + } + + if(isset($criteria['condition']) && $this->condition!==$criteria['condition']) + { + if($this->condition==='') + $this->condition=$criteria['condition']; + elseif($criteria['condition']!=='') + $this->condition="({$this->condition}) AND ({$criteria['condition']})"; + } + + if(isset($criteria['params']) && $this->params!==$criteria['params']) + $this->params=array_merge($this->params,$criteria['params']); + + if(isset($criteria['order']) && $this->order!==$criteria['order']) + { + if($this->order==='') + $this->order=$criteria['order']; + elseif($criteria['order']!=='') + $this->order=$criteria['order'].', '.$this->order; + } + + if(isset($criteria['group']) && $this->group!==$criteria['group']) + { + if($this->group==='') + $this->group=$criteria['group']; + elseif($criteria['group']!=='') + $this->group.=', '.$criteria['group']; + } + + if(isset($criteria['join']) && $this->join!==$criteria['join']) + { + if($this->join==='') + $this->join=$criteria['join']; + elseif($criteria['join']!=='') + $this->join.=' '.$criteria['join']; + } + + if(isset($criteria['having']) && $this->having!==$criteria['having']) + { + if($this->having==='') + $this->having=$criteria['having']; + elseif($criteria['having']!=='') + $this->having="({$this->having}) AND ({$criteria['having']})"; + } + } +} + + +/** + * CStatRelation represents a statistical relational query. + * @author Qiang Xue + * @package system.db.ar + */ +class CStatRelation extends CBaseActiveRelation +{ + /** + * @var string the statistical expression. Defaults to 'COUNT(*)', meaning + * the count of child objects. + */ + public $select='COUNT(*)'; + /** + * @var mixed the default value to be assigned to those records that do not + * receive a statistical query result. Defaults to 0. + */ + public $defaultValue=0; + /** + * @var mixed scopes to apply + * Can be set to the one of the following: + *
    + *
  • Single scope: 'scopes'=>'scopeName'.
  • + *
  • Multiple scopes: 'scopes'=>array('scopeName1','scopeName2').
  • + *
+ * @since 1.1.16 + */ + public $scopes; + + /** + * Merges this relation with a criteria specified dynamically. + * @param array $criteria the dynamically specified criteria + * @param boolean $fromScope whether the criteria to be merged is from scopes + */ + public function mergeWith($criteria,$fromScope=false) + { + if($criteria instanceof CDbCriteria) + $criteria=$criteria->toArray(); + parent::mergeWith($criteria,$fromScope); + + if(isset($criteria['defaultValue'])) + $this->defaultValue=$criteria['defaultValue']; + } +} + + +/** + * CActiveRelation is the base class for representing active relations that bring back related objects. + * @author Qiang Xue + * @package system.db.ar + * @since 1.0 + */ +class CActiveRelation extends CBaseActiveRelation +{ + /** + * @var string join type. Defaults to 'LEFT OUTER JOIN'. + */ + public $joinType='LEFT OUTER JOIN'; + /** + * @var string ON clause. The condition specified here will be appended to the joining condition using AND operator. + */ + public $on=''; + /** + * @var string the alias for the table that this relation refers to. Defaults to null, meaning + * the alias will be the same as the relation name. + */ + public $alias; + /** + * @var string|array specifies which related objects should be eagerly loaded when this related object is lazily loaded. + * For more details about this property, see {@link CActiveRecord::with()}. + */ + public $with=array(); + /** + * @var boolean whether this table should be joined with the primary table. + * When setting this property to be false, the table associated with this relation will + * appear in a separate JOIN statement. + * If this property is set true, then the corresponding table will ALWAYS be joined together + * with the primary table, no matter the primary table is limited or not. + * If this property is not set, the corresponding table will be joined with the primary table + * only when the primary table is not limited. + */ + public $together; + /** + * @var mixed scopes to apply + * Can be set to the one of the following: + *
    + *
  • Single scope: 'scopes'=>'scopeName'.
  • + *
  • Multiple scopes: 'scopes'=>array('scopeName1','scopeName2').
  • + *
+ * @since 1.1.9 + */ + public $scopes; + /** + * @var string the name of the relation that should be used as the bridge to this relation. + * Defaults to null, meaning don't use any bridge. + * @since 1.1.7 + */ + public $through; + + /** + * Merges this relation with a criteria specified dynamically. + * @param array $criteria the dynamically specified criteria + * @param boolean $fromScope whether the criteria to be merged is from scopes + */ + public function mergeWith($criteria,$fromScope=false) + { + if($criteria instanceof CDbCriteria) + $criteria=$criteria->toArray(); + if($fromScope) + { + if(isset($criteria['condition']) && $this->on!==$criteria['condition']) + { + if($this->on==='') + $this->on=$criteria['condition']; + elseif($criteria['condition']!=='') + $this->on="({$this->on}) AND ({$criteria['condition']})"; + } + unset($criteria['condition']); + } + + parent::mergeWith($criteria); + + if(isset($criteria['joinType'])) + $this->joinType=$criteria['joinType']; + + if(isset($criteria['on']) && $this->on!==$criteria['on']) + { + if($this->on==='') + $this->on=$criteria['on']; + elseif($criteria['on']!=='') + $this->on="({$this->on}) AND ({$criteria['on']})"; + } + + if(isset($criteria['with'])) + $this->with=$criteria['with']; + + if(isset($criteria['alias'])) + $this->alias=$criteria['alias']; + + if(isset($criteria['together'])) + $this->together=$criteria['together']; + } +} + + +/** + * CBelongsToRelation represents the parameters specifying a BELONGS_TO relation. + * @author Qiang Xue + * @package system.db.ar + * @since 1.0 + */ +class CBelongsToRelation extends CActiveRelation +{ +} + + +/** + * CHasOneRelation represents the parameters specifying a HAS_ONE relation. + * @author Qiang Xue + * @package system.db.ar + * @since 1.0 + */ +class CHasOneRelation extends CActiveRelation +{ +} + + +/** + * CHasManyRelation represents the parameters specifying a HAS_MANY relation. + * @author Qiang Xue + * @package system.db.ar + * @since 1.0 + */ +class CHasManyRelation extends CActiveRelation +{ + /** + * @var integer limit of the rows to be selected. It is effective only for lazy loading this related object. Defaults to -1, meaning no limit. + */ + public $limit=-1; + /** + * @var integer offset of the rows to be selected. It is effective only for lazy loading this related object. Defaults to -1, meaning no offset. + */ + public $offset=-1; + /** + * @var string the name of the column that should be used as the key for storing related objects. + * Defaults to null, meaning using zero-based integer IDs. + */ + public $index; + + /** + * Merges this relation with a criteria specified dynamically. + * @param array $criteria the dynamically specified criteria + * @param boolean $fromScope whether the criteria to be merged is from scopes + */ + public function mergeWith($criteria,$fromScope=false) + { + if($criteria instanceof CDbCriteria) + $criteria=$criteria->toArray(); + parent::mergeWith($criteria,$fromScope); + if(isset($criteria['limit']) && $criteria['limit']>0) + $this->limit=$criteria['limit']; + + if(isset($criteria['offset']) && $criteria['offset']>=0) + $this->offset=$criteria['offset']; + + if(isset($criteria['index'])) + $this->index=$criteria['index']; + } +} + + +/** + * CManyManyRelation represents the parameters specifying a MANY_MANY relation. + * @author Qiang Xue + * @package system.db.ar + * @since 1.0 + */ +class CManyManyRelation extends CHasManyRelation +{ + /** + * @var string name of the junction table for the many-to-many relation. + */ + private $_junctionTableName=null; + /** + * @var array list of foreign keys of the junction table for the many-to-many relation. + */ + private $_junctionForeignKeys=null; + + /** + * @return string junction table name. + * @since 1.1.12 + */ + public function getJunctionTableName() + { + if ($this->_junctionTableName===null) + $this->initJunctionData(); + return $this->_junctionTableName; + } + + /** + * @return array list of junction table foreign keys. + * @since 1.1.12 + */ + public function getJunctionForeignKeys() + { + if ($this->_junctionForeignKeys===null) + $this->initJunctionData(); + return $this->_junctionForeignKeys; + } + + /** + * Initializes values of {@link junctionTableName} and {@link junctionForeignKeys} parsing + * {@link foreignKey} value. + * @throws CDbException if {@link foreignKey} has been specified in wrong format. + */ + private function initJunctionData() + { + if(!preg_match('/^\s*(.*?)\((.*)\)\s*$/',$this->foreignKey,$matches)) + throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".', + array('{class}'=>$this->className,'{relation}'=>$this->name))); + $this->_junctionTableName=$matches[1]; + $this->_junctionForeignKeys=preg_split('/\s*,\s*/',$matches[2],-1,PREG_SPLIT_NO_EMPTY); + } +} + + +/** + * CActiveRecordMetaData represents the meta-data for an Active Record class. + * + * @author Qiang Xue + * @package system.db.ar + * @since 1.0 + */ +class CActiveRecordMetaData +{ + /** + * @var CDbTableSchema the table schema information + */ + public $tableSchema; + /** + * @var array table columns + */ + public $columns; + /** + * @var array list of relations + */ + public $relations=array(); + /** + * @var array attribute default values + */ + public $attributeDefaults=array(); + + private $_modelClassName; + + /** + * Constructor. + * @param CActiveRecord $model the model instance + * @throws CDbException if specified table for active record class cannot be found in the database + */ + public function __construct($model) + { + $this->_modelClassName=get_class($model); + + $tableName=$model->tableName(); + if(($table=$model->getDbConnection()->getSchema()->getTable($tableName))===null) + throw new CDbException(Yii::t('yii','The table "{table}" for active record class "{class}" cannot be found in the database.', + array('{class}'=>$this->_modelClassName,'{table}'=>$tableName))); + + if(($modelPk=$model->primaryKey())!==null || $table->primaryKey===null) + { + $table->primaryKey=$modelPk; + if(is_string($table->primaryKey) && isset($table->columns[$table->primaryKey])) + $table->columns[$table->primaryKey]->isPrimaryKey=true; + elseif(is_array($table->primaryKey)) + { + foreach($table->primaryKey as $name) + { + if(isset($table->columns[$name])) + $table->columns[$name]->isPrimaryKey=true; + } + } + } + $this->tableSchema=$table; + $this->columns=$table->columns; + + foreach($table->columns as $name=>$column) + { + if(!$column->isPrimaryKey && $column->defaultValue!==null) + $this->attributeDefaults[$name]=$column->defaultValue; + } + + foreach($model->relations() as $name=>$config) + { + $this->addRelation($name,$config); + } + } + + /** + * Adds a relation. + * + * $config is an array with three elements: + * relation type, the related active record class and the foreign key. + * + * @throws CDbException + * @param string $name $name Name of the relation. + * @param array $config $config Relation parameters. + * @return void + * @since 1.1.2 + */ + public function addRelation($name,$config) + { + if(isset($config[0],$config[1],$config[2])) // relation class, AR class, FK + $this->relations[$name]=new $config[0]($name,$config[1],$config[2],array_slice($config,3)); + else + throw new CDbException(Yii::t('yii','Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.', array('{class}'=>$this->_modelClassName,'{relation}'=>$name))); + } + + /** + * Checks if there is a relation with specified name defined. + * + * @param string $name $name Name of the relation. + * @return boolean + * @since 1.1.2 + */ + public function hasRelation($name) + { + return isset($this->relations[$name]); + } + + /** + * Deletes a relation with specified name. + * + * @param string $name $name + * @return void + * @since 1.1.2 + */ + public function removeRelation($name) + { + unset($this->relations[$name]); + } +} diff --git a/extras/cache-local.php b/extras/cache-local.php new file mode 100644 index 0000000..56d0b39 --- /dev/null +++ b/extras/cache-local.php @@ -0,0 +1,17 @@ + 'system.caching.CRedisCache', + 'keyPrefix' => 'dev10', + 'options' => STREAM_CLIENT_CONNECT, + 'behaviors' => [ + 'cacheTag' => [ + 'class' => 'site_app.components.CacheTagBehavior', + ], + ], + 'hostname' => 'redis', + 'port' => '6379', + 'password' => 'yourpassword', + 'database' => 0 +]; diff --git a/extras/cache_write-local.php b/extras/cache_write-local.php new file mode 100644 index 0000000..56d0b39 --- /dev/null +++ b/extras/cache_write-local.php @@ -0,0 +1,17 @@ + 'system.caching.CRedisCache', + 'keyPrefix' => 'dev10', + 'options' => STREAM_CLIENT_CONNECT, + 'behaviors' => [ + 'cacheTag' => [ + 'class' => 'site_app.components.CacheTagBehavior', + ], + ], + 'hostname' => 'redis', + 'port' => '6379', + 'password' => 'yourpassword', + 'database' => 0 +]; diff --git a/extras/common-local.php b/extras/common-local.php new file mode 100644 index 0000000..6e6eb7b --- /dev/null +++ b/extras/common-local.php @@ -0,0 +1,79 @@ + 'system.db.CDbConnection', + 'connectionString' => 'pgsql:host=db;dbname=db', + 'username' => 'db', + 'password' => 'db', + 'charset' => 'utf8', + 'initSQLs' => ['SET search_path TO app, public; SET statement_timeout TO 2500000;'], +]; + +$cacheReadConfigLocal = require $configPath . 'cache-local.php'; +$cacheConfigLocal = [ + 'class' => 'system.caching.CRedisCache', + 'keyPrefix' => 'dev10', + 'options' => STREAM_CLIENT_CONNECT, + 'behaviors' => [ + 'cacheTag' => [ + 'class' => 'site_app.components.CacheTagBehavior', + ], + ], + 'hostname' => 'redis', + 'port' => '6379', + 'password' => 'yourpassword', + 'database' => 0 +]; + +$sitePath = dirname(dirname(__FILE__)); +$basePath = dirname($sitePath); +$configPath = $sitePath . '/config/'; +$imgPath = $basePath.'/site/web/img/'; +$picsPath = $basePath.'/site/web/images/pics/'; +$iconsPath = $basePath.'/site/web/images/icons/'; +$filesPath = $basePath.'/site/web/files/'; +$videosSrcPath = $basePath.'/site/vsrc/'; +$videosPath = $basePath.'/site/web/v/'; +$schema = 'https'; +return [ + 'components' => [ + 'db' => $dbConfigLocal, + 'dbRead' => $dbConfigLocal, + 'dbReadReport' => $dbConfigLocal, + 'dbWrite' => $dbConfigLocal, + 'cache' => $cacheConfigLocal, + 'cacheWrite' => $cacheConfigLocal, + ], + 'params' => [ + 'rootUrl' => $schema . '://sizeup.firstduesizeup.test/', + 'picDbUrl' => $schema . '://sizeup.firstduesizeup.test/images/pics/', + 'googleApiBrowserKey' => 'AIzaSyBXBn3-dNLuOBu2mRfePCUgNL9cn_kPvSo', + 'googleApiServerKey' => 'AIzaSyCwJGvA6LZx_7C3TmS2sHy5kwILLJBed14', + 'fileDbPath' => $sitePath . '/web/files/', + 'picDbPath' => $picsPath, + 'imgPath' => $imgPath, + 'smartyStreets' => [ + 'authId' => '477965af-80ad-d6a6-d211-2e99104fe497', + 'authToken' => '3TAJGbewkU8DyUlGyusq', + ], + 'signatureDbPath' => $basePath . '/site/web/images/signatures/', + 'amqp' => [ + 'host' => 'rabbitmq', + 'vhost' => '/', + 'port' => '5672', + 'login' => 'rabbitmq', + 'password' => 'rabbitmq', + ], + 'smtps' => [ + 'DEFAULT' => [ + 'host' => 'localhost', + 'port' => 1025, + 'encryption' => 0, + 'smtp_credential_provider' => 'swiftmailer' + ], + ], + ] +]; From 5388abcf4cc1b7f0af52b5ab9ec84834b52235ec Mon Sep 17 00:00:00 2001 From: Sid Date: Tue, 12 Sep 2023 13:05:59 +0530 Subject: [PATCH 2/5] reset command --- commands/web/reset.sh | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100755 commands/web/reset.sh diff --git a/commands/web/reset.sh b/commands/web/reset.sh new file mode 100755 index 0000000..0542ef1 --- /dev/null +++ b/commands/web/reset.sh @@ -0,0 +1,34 @@ +echo "Setting up the project" + +#ask user if really want to reset project +echo "Are you sure you want to reset the project? (y/n)[no]" +read -r answer +if [ "$answer" != "${answer#[Yy]}" ] ;then + rm -f db.sql + rm -f db.sql.gz + + #remove files + rm -rf site/config/saml/firstdue-x509.crt + rm -rf site/config/saml/firstdue-x509.key + + rm -rf site/config/cache-local.php + rm -rf site/config/cache_write-local.php + rm -rf site/config/common-local.php + rm -rf site/config/db_read-local.php + rm -rf site/config/db_read_api-local.php + rm -rf site/config/db_read_report-local.php + rm -rf site/config/db_write-local.php + rm -rf site/config/main-local.php + rm -rf site/config/message_broker-local.php + rm -rf site/config/test-local.php + + + #delete vendor folder + rm -rf vendor + + #delete node_modules folder + rm -rf node_modules +else + echo "Reset cancelled" + exit 1 +fi \ No newline at end of file From b63795bc8df7de01328c8e74e6f78c5c027c51dc Mon Sep 17 00:00:00 2001 From: Sid Date: Tue, 12 Sep 2023 14:44:49 +0530 Subject: [PATCH 3/5] extras delete --- commands/web/fdsu.sh | 4 - extras/CActiveRecord.php | 2466 ---------------------------------- extras/cache-local.php | 17 - extras/cache_write-local.php | 17 - extras/common-local.php | 79 -- 5 files changed, 2583 deletions(-) delete mode 100644 extras/CActiveRecord.php delete mode 100644 extras/cache-local.php delete mode 100644 extras/cache_write-local.php delete mode 100644 extras/common-local.php diff --git a/commands/web/fdsu.sh b/commands/web/fdsu.sh index 978dd93..f991fa7 100755 --- a/commands/web/fdsu.sh +++ b/commands/web/fdsu.sh @@ -69,10 +69,6 @@ then touch site/config/saml/firstdue-x509.key echo "defined('YII_DEBUG') or define('YII_DEBUG', true);" >> site/web/index.php - - rm site/config/common-local.php && cp .ddev/extras/common-local.php site/config/common-local.php - rm site/config/cache-local.php && cp .ddev/extras/cache-local.php site/config/cache-local.php - rm site/config/cache-local.php && cp .ddev/extras/cache-local.php site/config/cache_write-local.php # rm vendor/yiisoft/yii/framework/db/ar/CActiveRecord.php && cp .ddev/extras/CActiveRecord.php vendor/yiisoft/yii/framework/db/ar/CActiveRecord.php echo "Necessary files copied!!!" diff --git a/extras/CActiveRecord.php b/extras/CActiveRecord.php deleted file mode 100644 index f613d0e..0000000 --- a/extras/CActiveRecord.php +++ /dev/null @@ -1,2466 +0,0 @@ - - * @link https://www.yiiframework.com/ - * @copyright 2008-2013 Yii Software LLC - * @license https://www.yiiframework.com/license/ - */ - -/** - * CActiveRecord is the base class for classes representing relational data. - * - * It implements the active record design pattern, a popular Object-Relational Mapping (ORM) technique. - * Please check {@link https://www.yiiframework.com/doc/guide/1.1/en/database.ar the Guide} for more details - * about this class. - * - * @property CDbCriteria $dbCriteria The query criteria that is associated with this model. - * This criteria is mainly used by {@link scopes named scope} feature to accumulate - * different criteria specifications. - * @property CActiveRecordMetaData $metaData The meta for this AR class. - * @property CDbConnection $dbConnection The database connection used by active record. - * @property CDbTableSchema $tableSchema The metadata of the table that this AR belongs to. - * @property CDbCommandBuilder $commandBuilder The command builder used by this AR. - * @property array $attributes Attribute values indexed by attribute names. - * @property boolean $isNewRecord Whether the record is new and should be inserted when calling {@link save}. - * This property is automatically set in constructor and {@link populateRecord}. - * Defaults to false, but it will be set to true if the instance is created using - * the new operator. - * @property mixed $primaryKey The primary key value. An array (column name=>column value) is returned if the primary key is composite. - * If primary key is not defined, null will be returned. - * @property mixed $oldPrimaryKey The old primary key value. An array (column name=>column value) is returned if the primary key is composite. - * If primary key is not defined, null will be returned. - * @property string $tableAlias The default table alias. - * - * @author Qiang Xue - * @package system.db.ar - * @since 1.0 - */ -abstract class CActiveRecord extends CModel -{ - const BELONGS_TO='CBelongsToRelation'; - const HAS_ONE='CHasOneRelation'; - const HAS_MANY='CHasManyRelation'; - const MANY_MANY='CManyManyRelation'; - const STAT='CStatRelation'; - - /** - * @var CDbConnection the default database connection for all active record classes. - * By default, this is the 'db' application component. - * @see getDbConnection - */ - public static $db; - - private static $_models=array(); // class name => model - private static $_md=array(); // class name => meta data - - private $_new=false; // whether this instance is new or not - private $_attributes=array(); // attribute name => attribute value - private $_related=array(); // attribute name => related objects - private $_c; // query criteria (used by finder only) - private $_pk; // old primary key value - private $_alias='t'; // the table alias being used for query - - - /** - * Constructor. - * @param string $scenario scenario name. See {@link CModel::scenario} for more details about this parameter. - * Note: in order to setup initial model parameters use {@link init()} or {@link afterConstruct()}. - * Do NOT override the constructor unless it is absolutely necessary! - */ - public function __construct($scenario='insert') - { - if($scenario===null) // internally used by populateRecord() and model() - return; - - $this->setScenario($scenario); - $this->setIsNewRecord(true); - $this->_attributes=$this->getMetaData()->attributeDefaults; - - $this->init(); - - $this->attachBehaviors($this->behaviors()); - $this->afterConstruct(); - } - - /** - * Initializes this model. - * This method is invoked when an AR instance is newly created and has - * its {@link scenario} set. - * You may override this method to provide code that is needed to initialize the model (e.g. setting - * initial property values.) - */ - public function init() - { - } - - /** - * Sets the parameters about query caching. - * This is a shortcut method to {@link CDbConnection::cache()}. - * It changes the query caching parameter of the {@link dbConnection} instance. - * @param integer $duration the number of seconds that query results may remain valid in cache. - * If this is 0, the caching will be disabled. - * @param CCacheDependency|ICacheDependency $dependency the dependency that will be used when saving - * the query results into cache. - * @param integer $queryCount number of SQL queries that need to be cached after calling this method. Defaults to 1, - * meaning that the next SQL query will be cached. - * @return static the active record instance itself. - * @since 1.1.7 - */ - public function cache($duration, $dependency=null, $queryCount=1) - { - $this->getDbConnection()->cache($duration, $dependency, $queryCount); - return $this; - } - - /** - * PHP sleep magic method. - * This method ensures that the model meta data reference is set to null. - * @return array - */ - public function __sleep() - { - return array_keys((array)$this); - } - - /** - * PHP getter magic method. - * This method is overridden so that AR attributes can be accessed like properties. - * @param string $name property name - * @return mixed property value - * @see getAttribute - */ - public function __get($name) - { - if(isset($this->_attributes[$name])) - return $this->_attributes[$name]; - elseif(isset($this->getMetaData()->columns[$name])) - return null; - elseif(isset($this->_related[$name])) - return $this->_related[$name]; - elseif(isset($this->getMetaData()->relations[$name])) - return $this->getRelated($name); - else - return parent::__get($name); - } - - /** - * PHP setter magic method. - * This method is overridden so that AR attributes can be accessed like properties. - * @param string $name property name - * @param mixed $value property value - * @throws CException - */ - public function __set($name,$value) - { - if($this->setAttribute($name,$value)===false) - { - if(isset($this->getMetaData()->relations[$name])) - $this->_related[$name]=$value; - else - parent::__set($name,$value); - } - } - - /** - * Checks if a property value is null. - * This method overrides the parent implementation by checking - * if the named attribute is null or not. - * @param string $name the property name or the event name - * @return boolean whether the property value is null - */ - public function __isset($name) - { - if(isset($this->_attributes[$name])) - return true; - elseif(isset($this->getMetaData()->columns[$name])) - return false; - elseif(isset($this->_related[$name])) - return true; - elseif(isset($this->getMetaData()->relations[$name])) - return $this->getRelated($name)!==null; - else - return parent::__isset($name); - } - - /** - * Sets a component property to be null. - * This method overrides the parent implementation by clearing - * the specified attribute value. - * @param string $name the property name or the event name - * @throws CException - */ - public function __unset($name) - { - if(isset($this->getMetaData()->columns[$name])) - unset($this->_attributes[$name]); - elseif(isset($this->getMetaData()->relations[$name])) - unset($this->_related[$name]); - else - parent::__unset($name); - } - - /** - * Calls the named method which is not a class method. - * Do not call this method. This is a PHP magic method that we override - * to implement the named scope feature. - * @param string $name the method name - * @param array $parameters method parameters - * @return mixed the method return value - */ - public function __call($name,$parameters) - { - if(isset($this->getMetaData()->relations[$name])) - { - if(empty($parameters)) - return $this->getRelated($name,false); - else - return $this->getRelated($name,false,$parameters[0]); - } - - $scopes=$this->scopes(); - if(isset($scopes[$name])) - { - $this->getDbCriteria()->mergeWith($scopes[$name]); - return $this; - } - - return parent::__call($name,$parameters); - } - - /** - * Returns the related record(s). - * This method will return the related record(s) of the current record. - * If the relation is HAS_ONE or BELONGS_TO, it will return a single object - * or null if the object does not exist. - * If the relation is HAS_MANY or MANY_MANY, it will return an array of objects - * or an empty array. - * @param string $name the relation name (see {@link relations}) - * @param boolean $refresh whether to reload the related objects from database. Defaults to false. - * If the current record is not a new record and it does not have the related objects loaded they - * will be retrieved from the database even if this is set to false. - * If the current record is a new record and this value is false, the related objects will not be - * retrieved from the database. - * @param mixed $params array or CDbCriteria object with additional parameters that customize the query conditions as specified in the relation declaration. - * If this is supplied the related record(s) will be retrieved from the database regardless of the value or {@link $refresh}. - * The related record(s) retrieved when this is supplied will only be returned by this method and will not be loaded into the current record's relation. - * The value of the relation prior to running this method will still be available for the current record if this is supplied. - * @return mixed the related object(s). - * @throws CDbException if the relation is not specified in {@link relations}. - */ - public function getRelated($name,$refresh=false,$params=array()) - { - if(!$refresh && $params===array() && (isset($this->_related[$name]) || array_key_exists($name,$this->_related))) - return $this->_related[$name]; - - $md=$this->getMetaData(); - if(!isset($md->relations[$name])) - throw new CDbException(Yii::t('yii','{class} does not have relation "{name}".', - array('{class}'=>get_class($this), '{name}'=>$name))); - - Yii::trace('lazy loading '.get_class($this).'.'.$name,'system.db.ar.CActiveRecord'); - $relation=$md->relations[$name]; - if($this->getIsNewRecord() && !$refresh && ($relation instanceof CHasOneRelation || $relation instanceof CHasManyRelation)) - return $relation instanceof CHasOneRelation ? null : array(); - - if($params!==array()) // dynamic query - { - $exists=isset($this->_related[$name]) || array_key_exists($name,$this->_related); - if($exists) - $save=$this->_related[$name]; - - if($params instanceof CDbCriteria) - $params = $params->toArray(); - - $r=array($name=>$params); - } - else - $r=$name; - unset($this->_related[$name]); - - $finder=$this->getActiveFinder($r); - $finder->lazyFind($this); - - if(!isset($this->_related[$name])) - { - if($relation instanceof CHasManyRelation) - $this->_related[$name]=array(); - elseif($relation instanceof CStatRelation) - $this->_related[$name]=$relation->defaultValue; - else - $this->_related[$name]=null; - } - - if($params!==array()) - { - $results=$this->_related[$name]; - if($exists) - $this->_related[$name]=$save; - else - unset($this->_related[$name]); - return $results; - } - else - return $this->_related[$name]; - } - - /** - * Returns a value indicating whether the named related object(s) has been loaded. - * @param string $name the relation name - * @return boolean a value indicating whether the named related object(s) has been loaded. - */ - public function hasRelated($name) - { - return isset($this->_related[$name]) || array_key_exists($name,$this->_related); - } - - /** - * Returns the query criteria associated with this model. - * @param boolean $createIfNull whether to create a criteria instance if it does not exist. Defaults to true. - * @return CDbCriteria the query criteria that is associated with this model. - * This criteria is mainly used by {@link scopes named scope} feature to accumulate - * different criteria specifications. - */ - public function getDbCriteria($createIfNull=true) - { - if($this->_c===null) - { - if(($c=$this->defaultScope())!==array() || $createIfNull) - $this->_c=new CDbCriteria($c); - } - return $this->_c; - } - - /** - * Sets the query criteria for the current model. - * @param CDbCriteria $criteria the query criteria - * @since 1.1.3 - */ - public function setDbCriteria($criteria) - { - $this->_c=$criteria; - } - - /** - * Returns the default named scope that should be implicitly applied to all queries for this model. - * Note, default scope only applies to SELECT queries. It is ignored for INSERT, UPDATE and DELETE queries. - * The default implementation simply returns an empty array. You may override this method - * if the model needs to be queried with some default criteria (e.g. only active records should be returned). - * @return array the query criteria. This will be used as the parameter to the constructor - * of {@link CDbCriteria}. - */ - public function defaultScope() - { - return array(); - } - - /** - * Resets all scopes and criterias applied. - * - * @param boolean $resetDefault including default scope. This parameter available since 1.1.12 - * @return static the AR instance itself - * @since 1.1.2 - */ - public function resetScope($resetDefault=true) - { - if($resetDefault) - $this->_c=new CDbCriteria(); - else - $this->_c=null; - - return $this; - } - - /** - * Returns the static model of the specified AR class. - * The model returned is a static instance of the AR class. - * It is provided for invoking class-level methods (something similar to static class methods.) - * - * EVERY derived AR class must override this method as follows, - *
-	 * public static function model($className=__CLASS__)
-	 * {
-	 *     return parent::model($className);
-	 * }
-	 * 
- * - * @param string $className active record class name. - * @return static active record model instance. - */ - public static function model($className=__CLASS__) - { - if(isset(self::$_models[$className])) - return self::$_models[$className]; - else - { - $model=self::$_models[$className]=new $className(null); - $model->attachBehaviors($model->behaviors()); - return $model; - } - } - - /** - * Returns the meta-data for this AR - * @return CActiveRecordMetaData the meta for this AR class. - */ - public function getMetaData() - { - $className=get_class($this); - if(!array_key_exists($className,self::$_md)) - { -// self::$_md[$className]=null; // preventing recursive invokes of {@link getMetaData()} via {@link __get()} - self::$_md[$className]=new CActiveRecordMetaData($this); - } - return self::$_md[$className]; - } - - /** - * Refreshes the meta data for this AR class. - * By calling this method, this AR class will regenerate the meta data needed. - * This is useful if the table schema has been changed and you want to use the latest - * available table schema. Make sure you have called {@link CDbSchema::refresh} - * before you call this method. Otherwise, old table schema data will still be used. - */ - public function refreshMetaData() - { - $className=get_class($this); - if(array_key_exists($className,self::$_md)) - unset(self::$_md[$className]); - } - - /** - * Returns the name of the associated database table. - * By default this method returns the class name as the table name. - * You may override this method if the table is not named after this convention. - * @return string the table name - */ - public function tableName() - { - $tableName = get_class($this); - if(($pos=strrpos($tableName,'\\')) !== false) - return substr($tableName,$pos+1); - return $tableName; - } - - /** - * Returns the primary key of the associated database table. - * This method is meant to be overridden in case when the table is not defined with a primary key - * (for some legency database). If the table is already defined with a primary key, - * you do not need to override this method. The default implementation simply returns null, - * meaning using the primary key defined in the database. - * @return mixed the primary key of the associated database table. - * If the key is a single column, it should return the column name; - * If the key is a composite one consisting of several columns, it should - * return the array of the key column names. - */ - public function primaryKey() - { - } - - /** - * This method should be overridden to declare related objects. - * - * There are four types of relations that may exist between two active record objects: - *
    - *
  • BELONGS_TO: e.g. a member belongs to a team;
  • - *
  • HAS_ONE: e.g. a member has at most one profile;
  • - *
  • HAS_MANY: e.g. a team has many members;
  • - *
  • MANY_MANY: e.g. a member has many skills and a skill belongs to a member.
  • - *
- * - * Besides the above relation types, a special relation called STAT is also supported - * that can be used to perform statistical query (or aggregational query). - * It retrieves the aggregational information about the related objects, such as the number - * of comments for each post, the average rating for each product, etc. - * - * Each kind of related objects is defined in this method as an array with the following elements: - *
-	 * 'varName'=>array('relationType', 'className', 'foreignKey', ...additional options)
-	 * 
- * where 'varName' refers to the name of the variable/property that the related object(s) can - * be accessed through; 'relationType' refers to the type of the relation, which can be one of the - * following four constants: self::BELONGS_TO, self::HAS_ONE, self::HAS_MANY and self::MANY_MANY; - * 'className' refers to the name of the active record class that the related object(s) is of; - * and 'foreignKey' states the foreign key that relates the two kinds of active record. - * Note, for composite foreign keys, they can be either listed together, separated by commas or specified as an array - * in format of array('key1','key2'). In case you need to specify custom PK->FK association you can define it as - * array('fk'=>'pk'). For composite keys it will be array('fk_c1'=>'pk_с1','fk_c2'=>'pk_c2'). - * For foreign keys used in MANY_MANY relation, the joining table must be declared as well - * (e.g. 'join_table(fk1, fk2)'). - * - * Additional options may be specified as name-value pairs in the rest array elements: - *
    - *
  • 'select': string|array, a list of columns to be selected. Defaults to '*', meaning all columns. - * Column names should be disambiguated if they appear in an expression (e.g. COUNT(relationName.name) AS name_count).
  • - *
  • 'condition': string, the WHERE clause. Defaults to empty. Note, column references need to - * be disambiguated with prefix 'relationName.' (e.g. relationName.age>20)
  • - *
  • 'order': string, the ORDER BY clause. Defaults to empty. Note, column references need to - * be disambiguated with prefix 'relationName.' (e.g. relationName.age DESC)
  • - *
  • 'with': string|array, a list of child related objects that should be loaded together with this object. - * Note, this is only honored by lazy loading, not eager loading.
  • - *
  • 'joinType': type of join. Defaults to 'LEFT OUTER JOIN'.
  • - *
  • 'alias': the alias for the table associated with this relationship. - * It defaults to null, - * meaning the table alias is the same as the relation name.
  • - *
  • 'params': the parameters to be bound to the generated SQL statement. - * This should be given as an array of name-value pairs.
  • - *
  • 'on': the ON clause. The condition specified here will be appended - * to the joining condition using the AND operator.
  • - *
  • 'index': the name of the column whose values should be used as keys - * of the array that stores related objects. This option is only available to - * HAS_MANY and MANY_MANY relations.
  • - *
  • 'scopes': scopes to apply. In case of a single scope can be used like 'scopes'=>'scopeName', - * in case of multiple scopes can be used like 'scopes'=>array('scopeName1','scopeName2'). - * This option has been available since version 1.1.9.
  • - *
- * - * The following options are available for certain relations when lazy loading: - *
    - *
  • 'group': string, the GROUP BY clause. Defaults to empty. Note, column references need to - * be disambiguated with prefix 'relationName.' (e.g. relationName.age). This option only applies to HAS_MANY and MANY_MANY relations.
  • - *
  • 'having': string, the HAVING clause. Defaults to empty. Note, column references need to - * be disambiguated with prefix 'relationName.' (e.g. relationName.age). This option only applies to HAS_MANY and MANY_MANY relations.
  • - *
  • 'limit': limit of the rows to be selected. This option does not apply to BELONGS_TO relation.
  • - *
  • 'offset': offset of the rows to be selected. This option does not apply to BELONGS_TO relation.
  • - *
  • 'through': name of the model's relation that will be used as a bridge when getting related data. Can be set only for HAS_ONE and HAS_MANY. This option has been available since version 1.1.7.
  • - *
- * - * Below is an example declaring related objects for 'Post' active record class: - *
-	 * return array(
-	 *     'author'=>array(self::BELONGS_TO, 'User', 'author_id'),
-	 *     'comments'=>array(self::HAS_MANY, 'Comment', 'post_id', 'with'=>'author', 'order'=>'create_time DESC'),
-	 *     'tags'=>array(self::MANY_MANY, 'Tag', 'post_tag(post_id, tag_id)', 'order'=>'name'),
-	 * );
-	 * 
- * - * @return array list of related object declarations. Defaults to empty array. - */ - public function relations() - { - return array(); - } - - /** - * Returns the declaration of named scopes. - * A named scope represents a query criteria that can be chained together with - * other named scopes and applied to a query. This method should be overridden - * by child classes to declare named scopes for the particular AR classes. - * For example, the following code declares two named scopes: 'recently' and - * 'published'. - *
-	 * return array(
-	 *     'published'=>array(
-	 *           'condition'=>'status=1',
-	 *     ),
-	 *     'recently'=>array(
-	 *           'order'=>'create_time DESC',
-	 *           'limit'=>5,
-	 *     ),
-	 * );
-	 * 
- * If the above scopes are declared in a 'Post' model, we can perform the following - * queries: - *
-	 * $posts=Post::model()->published()->findAll();
-	 * $posts=Post::model()->published()->recently()->findAll();
-	 * $posts=Post::model()->published()->with('comments')->findAll();
-	 * 
- * Note that the last query is a relational query. - * - * @return array the scope definition. The array keys are scope names; the array - * values are the corresponding scope definitions. Each scope definition is represented - * as an array whose keys must be properties of {@link CDbCriteria}. - */ - public function scopes() - { - return array(); - } - - /** - * Returns the list of all attribute names of the model. - * This would return all column names of the table associated with this AR class. - * @return array list of attribute names. - */ - public function attributeNames() - { - return array_keys($this->getMetaData()->columns); - } - - /** - * Returns the text label for the specified attribute. - * This method overrides the parent implementation by supporting - * returning the label defined in relational object. - * In particular, if the attribute name is in the form of "post.author.name", - * then this method will derive the label from the "author" relation's "name" attribute. - * @param string $attribute the attribute name - * @return string the attribute label - * @see generateAttributeLabel - * @since 1.1.4 - */ - public function getAttributeLabel($attribute) - { - $labels=$this->attributeLabels(); - if(isset($labels[$attribute])) - return $labels[$attribute]; - elseif(strpos($attribute,'.')!==false) - { - $segs=explode('.',$attribute); - $name=array_pop($segs); - $model=$this; - foreach($segs as $seg) - { - $relations=$model->getMetaData()->relations; - if(isset($relations[$seg])) - $model=CActiveRecord::model($relations[$seg]->className); - else - break; - } - return $model->getAttributeLabel($name); - } - else - return $this->generateAttributeLabel($attribute); - } - - /** - * Returns the database connection used by active record. - * By default, the "db" application component is used as the database connection. - * You may override this method if you want to use a different database connection. - * @throws CDbException if "db" application component is not defined - * @return CDbConnection the database connection used by active record. - */ - public function getDbConnection() - { - if(self::$db!==null) - return self::$db; - else - { - self::$db=Yii::app()->getDb(); - if(self::$db instanceof CDbConnection) - return self::$db; - else - throw new CDbException(Yii::t('yii','Active Record requires a "db" CDbConnection application component.')); - } - } - - /** - * Returns the named relation declared for this AR class. - * @param string $name the relation name - * @return CActiveRelation the named relation declared for this AR class. Null if the relation does not exist. - */ - public function getActiveRelation($name) - { - return isset($this->getMetaData()->relations[$name]) ? $this->getMetaData()->relations[$name] : null; - } - - /** - * Returns the metadata of the table that this AR belongs to - * @return CDbTableSchema the metadata of the table that this AR belongs to - */ - public function getTableSchema() - { - return $this->getMetaData()->tableSchema; - } - - /** - * Returns the command builder used by this AR. - * @return CDbCommandBuilder the command builder used by this AR - */ - public function getCommandBuilder() - { - return $this->getDbConnection()->getSchema()->getCommandBuilder(); - } - - /** - * Checks whether this AR has the named attribute - * @param string $name attribute name - * @return boolean whether this AR has the named attribute (table column). - */ - public function hasAttribute($name) - { - return isset($this->getMetaData()->columns[$name]); - } - - /** - * Returns the named attribute value. - * If this is a new record and the attribute is not set before, - * the default column value will be returned. - * If this record is the result of a query and the attribute is not loaded, - * null will be returned. - * You may also use $this->AttributeName to obtain the attribute value. - * @param string $name the attribute name - * @return mixed the attribute value. Null if the attribute is not set or does not exist. - * @see hasAttribute - */ - public function getAttribute($name) - { - if(property_exists($this,$name)) - return $this->$name; - elseif(isset($this->_attributes[$name])) - return $this->_attributes[$name]; - } - - /** - * Sets the named attribute value. - * You may also use $this->AttributeName to set the attribute value. - * @param string $name the attribute name - * @param mixed $value the attribute value. - * @return boolean whether the attribute exists and the assignment is conducted successfully - * @see hasAttribute - */ - public function setAttribute($name,$value) - { - if(property_exists($this,$name)) - $this->$name=$value; - elseif(isset($this->getMetaData()->columns[$name])) - $this->_attributes[$name]=$value; - else - return false; - return true; - } - - /** - * Do not call this method. This method is used internally by {@link CActiveFinder} to populate - * related objects. This method adds a related object to this record. - * @param string $name attribute name - * @param mixed $record the related record - * @param mixed $index the index value in the related object collection. - * If true, it means using zero-based integer index. - * If false, it means a HAS_ONE or BELONGS_TO object and no index is needed. - */ - public function addRelatedRecord($name,$record,$index) - { - if($index!==false) - { - if(!isset($this->_related[$name])) - $this->_related[$name]=array(); - if($record instanceof CActiveRecord) - { - if($index===true) - $this->_related[$name][]=$record; - else - $this->_related[$name][$index]=$record; - } - } - elseif(!isset($this->_related[$name])) - $this->_related[$name]=$record; - } - - /** - * Returns all column attribute values. - * Note, related objects are not returned. - * @param mixed $names names of attributes whose value needs to be returned. - * If this is true (default), then all attribute values will be returned, including - * those that are not loaded from DB (null will be returned for those attributes). - * If this is null, all attributes except those that are not loaded from DB will be returned. - * @return array attribute values indexed by attribute names. - */ - public function getAttributes($names=true) - { - $attributes=$this->_attributes; - foreach($this->getMetaData()->columns as $name=>$column) - { - if(property_exists($this,$name)) - $attributes[$name]=$this->$name; - elseif($names===true && !isset($attributes[$name])) - $attributes[$name]=null; - } - if(is_array($names)) - { - $attrs=array(); - foreach($names as $name) - { - if(property_exists($this,$name)) - $attrs[$name]=$this->$name; - else - $attrs[$name]=isset($attributes[$name])?$attributes[$name]:null; - } - return $attrs; - } - else - return $attributes; - } - - /** - * Saves the current record. - * - * The record is inserted as a row into the database table if its {@link isNewRecord} - * property is true (usually the case when the record is created using the 'new' - * operator). Otherwise, it will be used to update the corresponding row in the table - * (usually the case if the record is obtained using one of those 'find' methods.) - * - * Validation will be performed before saving the record. If the validation fails, - * the record will not be saved. You can call {@link getErrors()} to retrieve the - * validation errors. - * - * If the record is saved via insertion, its {@link isNewRecord} property will be - * set false, and its {@link scenario} property will be set to be 'update'. - * And if its primary key is auto-incremental and is not set before insertion, - * the primary key will be populated with the automatically generated key value. - * - * @param boolean $runValidation whether to perform validation before saving the record. - * If the validation fails, the record will not be saved to database. - * @param array $attributes list of attributes that need to be saved. Defaults to null, - * meaning all attributes that are loaded from DB will be saved. - * @return boolean whether the saving succeeds - */ - public function save($runValidation=true,$attributes=null) - { - if(!$runValidation || $this->validate($attributes)) - return $this->getIsNewRecord() ? $this->insert($attributes) : $this->update($attributes); - else - return false; - } - - /** - * Returns if the current record is new (was never saved to database) - * @return boolean whether the record is new and should be inserted when calling {@link save}. - * This property is automatically set in constructor and {@link populateRecord} and is set - * to false right after inserting record to database. - * Defaults to false, but it will be set to true if the instance is created using - * the new operator. - */ - public function getIsNewRecord() - { - return $this->_new; - } - - /** - * Sets if the record is new. - * @param boolean $value whether the record is new and should be inserted when calling {@link save}. - * @see getIsNewRecord - */ - public function setIsNewRecord($value) - { - $this->_new=$value; - } - - /** - * This event is raised before the record is saved. - * By setting {@link CModelEvent::isValid} to be false, the normal {@link save()} process will be stopped. - * @param CModelEvent $event the event parameter - */ - public function onBeforeSave($event) - { - $this->raiseEvent('onBeforeSave',$event); - } - - /** - * This event is raised after the record is saved. - * @param CEvent $event the event parameter - */ - public function onAfterSave($event) - { - $this->raiseEvent('onAfterSave',$event); - } - - /** - * This event is raised before the record is deleted. - * By setting {@link CModelEvent::isValid} to be false, the normal {@link delete()} process will be stopped. - * @param CModelEvent $event the event parameter - */ - public function onBeforeDelete($event) - { - $this->raiseEvent('onBeforeDelete',$event); - } - - /** - * This event is raised after the record is deleted. - * @param CEvent $event the event parameter - */ - public function onAfterDelete($event) - { - $this->raiseEvent('onAfterDelete',$event); - } - - /** - * This event is raised before an AR finder performs a find call. - * This can be either a call to CActiveRecords find methods or a find call - * when model is loaded in relational context via lazy or eager loading. - * If you want to access or modify the query criteria used for the - * find call, you can use {@link getDbCriteria()} to customize it based on your needs. - * When modifying criteria in beforeFind you have to make sure you are using the right - * table alias which is different on normal find and relational call. - * You can use {@link getTableAlias()} to get the alias used for the upcoming find call. - * Please note that modification of criteria is fully supported as of version 1.1.13. - * Earlier versions had some problems with relational context and applying changes correctly. - * @param CModelEvent $event the event parameter - * @see beforeFind - */ - public function onBeforeFind($event) - { - $this->raiseEvent('onBeforeFind',$event); - } - - /** - * This event is raised after the record is instantiated by a find method. - * @param CEvent $event the event parameter - */ - public function onAfterFind($event) - { - $this->raiseEvent('onAfterFind',$event); - } - - /** - * Given 'with' options returns a new active finder instance. - * - * @param mixed $with the relation names to be actively looked for - * @return CActiveFinder active finder for the operation - * - * @since 1.1.14 - */ - public function getActiveFinder($with) - { - return new CActiveFinder($this,$with); - } - - /** - * This event is raised before an AR finder performs a count call. - * If you want to access or modify the query criteria used for the - * count call, you can use {@link getDbCriteria()} to customize it based on your needs. - * When modifying criteria in beforeCount you have to make sure you are using the right - * table alias which is different on normal count and relational call. - * You can use {@link getTableAlias()} to get the alias used for the upcoming count call. - * @param CModelEvent $event the event parameter - * @see beforeCount - * @since 1.1.14 - */ - public function onBeforeCount($event) - { - $this->raiseEvent('onBeforeCount',$event); - } - - /** - * This method is invoked before saving a record (after validation, if any). - * The default implementation raises the {@link onBeforeSave} event. - * You may override this method to do any preparation work for record saving. - * Use {@link isNewRecord} to determine whether the saving is - * for inserting or updating record. - * Make sure you call the parent implementation so that the event is raised properly. - * @return boolean whether the saving should be executed. Defaults to true. - */ - protected function beforeSave() - { - if($this->hasEventHandler('onBeforeSave')) - { - $event=new CModelEvent($this); - $this->onBeforeSave($event); - return $event->isValid; - } - else - return true; - } - - /** - * This method is invoked after saving a record successfully. - * The default implementation raises the {@link onAfterSave} event. - * You may override this method to do postprocessing after record saving. - * Make sure you call the parent implementation so that the event is raised properly. - */ - protected function afterSave() - { - if($this->hasEventHandler('onAfterSave')) - $this->onAfterSave(new CEvent($this)); - } - - /** - * This method is invoked before deleting a record. - * The default implementation raises the {@link onBeforeDelete} event. - * You may override this method to do any preparation work for record deletion. - * Make sure you call the parent implementation so that the event is raised properly. - * @return boolean whether the record should be deleted. Defaults to true. - */ - protected function beforeDelete() - { - if($this->hasEventHandler('onBeforeDelete')) - { - $event=new CModelEvent($this); - $this->onBeforeDelete($event); - return $event->isValid; - } - else - return true; - } - - /** - * This method is invoked after deleting a record. - * The default implementation raises the {@link onAfterDelete} event. - * You may override this method to do postprocessing after the record is deleted. - * Make sure you call the parent implementation so that the event is raised properly. - */ - protected function afterDelete() - { - if($this->hasEventHandler('onAfterDelete')) - $this->onAfterDelete(new CEvent($this)); - } - - /** - * This method is invoked before an AR finder executes a find call. - * The find calls include {@link find}, {@link findAll}, {@link findByPk}, - * {@link findAllByPk}, {@link findByAttributes}, {@link findAllByAttributes}, - * {@link findBySql} and {@link findAllBySql}. - * The default implementation raises the {@link onBeforeFind} event. - * If you override this method, make sure you call the parent implementation - * so that the event is raised properly. - * For details on modifying query criteria see {@link onBeforeFind} event. - */ - protected function beforeFind() - { - if($this->hasEventHandler('onBeforeFind')) - { - $event=new CModelEvent($this); - $this->onBeforeFind($event); - } - } - - /** - * This method is invoked before an AR finder executes a count call. - * The count calls include {@link count} and {@link countByAttributes} - * The default implementation raises the {@link onBeforeCount} event. - * If you override this method, make sure you call the parent implementation - * so that the event is raised properly. - * @since 1.1.14 - */ - protected function beforeCount() - { - if($this->hasEventHandler('onBeforeCount')) - $this->onBeforeCount(new CEvent($this)); - } - - /** - * This method is invoked after each record is instantiated by a find method. - * The default implementation raises the {@link onAfterFind} event. - * You may override this method to do postprocessing after each newly found record is instantiated. - * Make sure you call the parent implementation so that the event is raised properly. - */ - protected function afterFind() - { - if($this->hasEventHandler('onAfterFind')) - $this->onAfterFind(new CEvent($this)); - } - - /** - * Calls {@link beforeFind}. - * This method is internally used. - */ - public function beforeFindInternal() - { - $this->beforeFind(); - } - - /** - * Calls {@link afterFind}. - * This method is internally used. - */ - public function afterFindInternal() - { - $this->afterFind(); - } - - /** - * Inserts a row into the table based on this active record attributes. - * If the table's primary key is auto-incremental and is null before insertion, - * it will be populated with the actual value after insertion. - * Note, validation is not performed in this method. You may call {@link validate} to perform the validation. - * After the record is inserted to DB successfully, its {@link isNewRecord} property will be set false, - * and its {@link scenario} property will be set to be 'update'. - * @param array $attributes list of attributes that need to be saved. Defaults to null, - * meaning all attributes that are loaded from DB will be saved. - * @return boolean whether the attributes are valid and the record is inserted successfully. - * @throws CDbException if the record is not new - */ - public function insert($attributes=null) - { - if(!$this->getIsNewRecord()) - throw new CDbException(Yii::t('yii','The active record cannot be inserted to database because it is not new.')); - if($this->beforeSave()) - { - Yii::trace(get_class($this).'.insert()','system.db.ar.CActiveRecord'); - $builder=$this->getCommandBuilder(); - $table=$this->getTableSchema(); - $command=$builder->createInsertCommand($table,$this->getAttributes($attributes)); - if($command->execute()) - { - $primaryKey=$table->primaryKey; - if($table->sequenceName!==null) - { - if(is_string($primaryKey) && $this->$primaryKey===null) - $this->$primaryKey=$builder->getLastInsertID($table); - elseif(is_array($primaryKey)) - { - foreach($primaryKey as $pk) - { - if($this->$pk===null) - { - $this->$pk=$builder->getLastInsertID($table); - break; - } - } - } - } - $this->_pk=$this->getPrimaryKey(); - $this->afterSave(); - $this->setIsNewRecord(false); - $this->setScenario('update'); - return true; - } - } - return false; - } - - /** - * Updates the row represented by this active record. - * All loaded attributes will be saved to the database. - * Note, validation is not performed in this method. You may call {@link validate} to perform the validation. - * @param array $attributes list of attributes that need to be saved. Defaults to null, - * meaning all attributes that are loaded from DB will be saved. - * @return boolean whether the update is successful - * @throws CDbException if the record is new - */ - public function update($attributes=null) - { - if($this->getIsNewRecord()) - throw new CDbException(Yii::t('yii','The active record cannot be updated because it is new.')); - if($this->beforeSave()) - { - Yii::trace(get_class($this).'.update()','system.db.ar.CActiveRecord'); - if($this->_pk===null) - $this->_pk=$this->getPrimaryKey(); - $this->updateByPk($this->getOldPrimaryKey(),$this->getAttributes($attributes)); - $this->_pk=$this->getPrimaryKey(); - $this->afterSave(); - return true; - } - else - return false; - } - - /** - * Saves a selected list of attributes. - * Unlike {@link save}, this method only saves the specified attributes - * of an existing row dataset and does NOT call either {@link beforeSave} or {@link afterSave}. - * Also note that this method does neither attribute filtering nor validation. - * So do not use this method with untrusted data (such as user posted data). - * You may consider the following alternative if you want to do so: - *
-	 * $postRecord=Post::model()->findByPk($postID);
-	 * $postRecord->attributes=$_POST['post'];
-	 * $postRecord->save();
-	 * 
- * @param array $attributes attributes to be updated. Each element represents an attribute name - * or an attribute value indexed by its name. If the latter, the record's - * attribute will be changed accordingly before saving. - * @throws CDbException if the record is new - * @return boolean whether the update is successful. Note that false is also returned if the saving - * was successfull but no attributes had changed and the database driver returns 0 for the number - * of updated records. - */ - public function saveAttributes($attributes) - { - if(!$this->getIsNewRecord()) - { - Yii::trace(get_class($this).'.saveAttributes()','system.db.ar.CActiveRecord'); - $values=array(); - foreach($attributes as $name=>$value) - { - if(is_integer($name)) - $values[$value]=$this->$value; - else - $values[$name]=$this->$name=$value; - } - if($this->_pk===null) - $this->_pk=$this->getPrimaryKey(); - if($this->updateByPk($this->getOldPrimaryKey(),$values)>0) - { - $this->_pk=$this->getPrimaryKey(); - return true; - } - else - return false; - } - else - throw new CDbException(Yii::t('yii','The active record cannot be updated because it is new.')); - } - - /** - * Saves one or several counter columns for the current AR object. - * Note that this method differs from {@link updateCounters} in that it only - * saves the current AR object. - * An example usage is as follows: - *
-	 * $postRecord=Post::model()->findByPk($postID);
-	 * $postRecord->saveCounters(array('view_count'=>1));
-	 * 
- * Use negative values if you want to decrease the counters. - * @param array $counters the counters to be updated (column name=>increment value) - * @return boolean whether the saving is successful - * @see updateCounters - * @since 1.1.8 - */ - public function saveCounters($counters) - { - Yii::trace(get_class($this).'.saveCounters()','system.db.ar.CActiveRecord'); - $builder=$this->getCommandBuilder(); - $table=$this->getTableSchema(); - $criteria=$builder->createPkCriteria($table,$this->getOldPrimaryKey()); - $command=$builder->createUpdateCounterCommand($this->getTableSchema(),$counters,$criteria); - if($command->execute()) - { - foreach($counters as $name=>$value) - $this->$name=$this->$name+$value; - return true; - } - else - return false; - } - - /** - * Deletes the row corresponding to this active record. - * @throws CDbException if the record is new - * @return boolean whether the deletion is successful. - */ - public function delete() - { - if(!$this->getIsNewRecord()) - { - Yii::trace(get_class($this).'.delete()','system.db.ar.CActiveRecord'); - if($this->beforeDelete()) - { - $result=$this->deleteByPk($this->getPrimaryKey())>0; - $this->afterDelete(); - return $result; - } - else - return false; - } - else - throw new CDbException(Yii::t('yii','The active record cannot be deleted because it is new.')); - } - - /** - * Repopulates this active record with the latest data. - * @return boolean whether the row still exists in the database. If true, the latest data will be populated to this active record. - */ - public function refresh() - { - Yii::trace(get_class($this).'.refresh()','system.db.ar.CActiveRecord'); - if(($record=$this->findByPk($this->getPrimaryKey()))!==null) - { - $this->_attributes=array(); - $this->_related=array(); - foreach($this->getMetaData()->columns as $name=>$column) - { - if(property_exists($this,$name)) - $this->$name=$record->$name; - else - $this->_attributes[$name]=$record->$name; - } - return true; - } - else - return false; - } - - /** - * Compares current active record with another one. - * The comparison is made by comparing table name and the primary key values of the two active records. - * @param CActiveRecord $record record to compare to - * @return boolean whether the two active records refer to the same row in the database table. - */ - public function equals($record) - { - return $this->tableName()===$record->tableName() && $this->getPrimaryKey()===$record->getPrimaryKey(); - } - - /** - * Returns the primary key value. - * @return mixed the primary key value. An array (column name=>column value) is returned if the primary key is composite. - * If primary key is not defined, null will be returned. - */ - public function getPrimaryKey() - { - $table=$this->getTableSchema(); - if(is_string($table->primaryKey)) - return $this->{$table->primaryKey}; - elseif(is_array($table->primaryKey)) - { - $values=array(); - foreach($table->primaryKey as $name) - $values[$name]=$this->$name; - return $values; - } - else - return null; - } - - /** - * Sets the primary key value. - * After calling this method, the old primary key value can be obtained from {@link oldPrimaryKey}. - * @param mixed $value the new primary key value. If the primary key is composite, the new value - * should be provided as an array (column name=>column value). - * @since 1.1.0 - */ - public function setPrimaryKey($value) - { - $this->_pk=$this->getPrimaryKey(); - $table=$this->getTableSchema(); - if(is_string($table->primaryKey)) - $this->{$table->primaryKey}=$value; - elseif(is_array($table->primaryKey)) - { - foreach($table->primaryKey as $name) - $this->$name=$value[$name]; - } - } - - /** - * Returns the old primary key value. - * This refers to the primary key value that is populated into the record - * after executing a find method (e.g. find(), findAll()). - * The value remains unchanged even if the primary key attribute is manually assigned with a different value. - * @return mixed the old primary key value. An array (column name=>column value) is returned if the primary key is composite. - * If primary key is not defined, null will be returned. - * @since 1.1.0 - */ - public function getOldPrimaryKey() - { - return $this->_pk; - } - - /** - * Sets the old primary key value. - * @param mixed $value the old primary key value. - * @since 1.1.3 - */ - public function setOldPrimaryKey($value) - { - $this->_pk=$value; - } - - /** - * Performs the actual DB query and populates the AR objects with the query result. - * This method is mainly internally used by other AR query methods. - * @param CDbCriteria $criteria the query criteria - * @param boolean $all whether to return all data - * @return mixed the AR objects populated with the query result - * @since 1.1.7 - */ - protected function query($criteria,$all=false) - { - $this->beforeFind(); - $this->applyScopes($criteria); - - if(empty($criteria->with)) - { - if(!$all) - $criteria->limit=1; - $command=$this->getCommandBuilder()->createFindCommand($this->getTableSchema(),$criteria); - return $all ? $this->populateRecords($command->queryAll(), true, $criteria->index) : $this->populateRecord($command->queryRow()); - } - else - { - $finder=$this->getActiveFinder($criteria->with); - return $finder->query($criteria,$all); - } - } - - /** - * Applies the query scopes to the given criteria. - * This method merges {@link dbCriteria} with the given criteria parameter. - * It then resets {@link dbCriteria} to be null. - * @param CDbCriteria $criteria the query criteria. This parameter may be modified by merging {@link dbCriteria}. - */ - public function applyScopes(&$criteria) - { - if(!empty($criteria->scopes)) - { - $scs=$this->scopes(); - $c=$this->getDbCriteria(); - foreach((array)$criteria->scopes as $k=>$v) - { - if(is_integer($k)) - { - if(is_string($v)) - { - if(isset($scs[$v])) - { - $c->mergeWith($scs[$v],true); - continue; - } - $scope=$v; - $params=array(); - } - elseif(is_array($v)) - { - $scope=key($v); - $params=current($v); - } - } - elseif(is_string($k)) - { - $scope=$k; - $params=$v; - } - - call_user_func_array(array($this,$scope),(array)$params); - } - } - - if(isset($c) || ($c=$this->getDbCriteria(false))!==null) - { - $c->mergeWith($criteria); - $criteria=$c; - $this->resetScope(false); - } - } - - /** - * Returns the table alias to be used by the find methods. - * In relational queries, the returned table alias may vary according to - * the corresponding relation declaration. Also, the default table alias - * set by {@link setTableAlias} may be overridden by the applied scopes. - * @param boolean $quote whether to quote the alias name - * @param boolean $checkScopes whether to check if a table alias is defined in the applied scopes so far. - * This parameter must be set false when calling this method in {@link defaultScope}. - * An infinite loop would be formed otherwise. - * @return string the default table alias - * @since 1.1.1 - */ - public function getTableAlias($quote=false, $checkScopes=true) - { - if($checkScopes && ($criteria=$this->getDbCriteria(false))!==null && $criteria->alias!='') - $alias=$criteria->alias; - else - $alias=$this->_alias; - return $quote ? $this->getDbConnection()->getSchema()->quoteTableName($alias) : $alias; - } - - /** - * Sets the table alias to be used in queries. - * @param string $alias the table alias to be used in queries. The alias should NOT be quoted. - * @since 1.1.3 - */ - public function setTableAlias($alias) - { - $this->_alias=$alias; - } - - /** - * Finds a single active record with the specified condition. - * @param mixed $condition query condition or criteria. - * If a string, it is treated as query condition (the WHERE clause); - * If an array, it is treated as the initial values for constructing a {@link CDbCriteria} object; - * Otherwise, it should be an instance of {@link CDbCriteria}. - * @param array $params parameters to be bound to an SQL statement. - * This is only used when the first parameter is a string (query condition). - * In other cases, please use {@link CDbCriteria::params} to set parameters. - * @return static|null the record found. Null if no record is found. - */ - public function find($condition='',$params=array()) - { - Yii::trace(get_class($this).'.find()','system.db.ar.CActiveRecord'); - $criteria=$this->getCommandBuilder()->createCriteria($condition,$params); - return $this->query($criteria); - } - - /** - * Finds all active records satisfying the specified condition. - * See {@link find()} for detailed explanation about $condition and $params. - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return static[] list of active records satisfying the specified condition. An empty array is returned if none is found. - */ - public function findAll($condition='',$params=array()) - { - Yii::trace(get_class($this).'.findAll()','system.db.ar.CActiveRecord'); - $criteria=$this->getCommandBuilder()->createCriteria($condition,$params); - return $this->query($criteria,true); - } - - /** - * Finds a single active record with the specified primary key. - * See {@link find()} for detailed explanation about $condition and $params. - * @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return static|null the record found. Null if none is found. - */ - public function findByPk($pk,$condition='',$params=array()) - { - Yii::trace(get_class($this).'.findByPk()','system.db.ar.CActiveRecord'); - $prefix=$this->getTableAlias(true).'.'; - $criteria=$this->getCommandBuilder()->createPkCriteria($this->getTableSchema(),$pk,$condition,$params,$prefix); - return $this->query($criteria); - } - - /** - * Finds all active records with the specified primary keys. - * See {@link find()} for detailed explanation about $condition and $params. - * @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return static[] the records found. An empty array is returned if none is found. - */ - public function findAllByPk($pk,$condition='',$params=array()) - { - Yii::trace(get_class($this).'.findAllByPk()','system.db.ar.CActiveRecord'); - $prefix=$this->getTableAlias(true).'.'; - $criteria=$this->getCommandBuilder()->createPkCriteria($this->getTableSchema(),$pk,$condition,$params,$prefix); - return $this->query($criteria,true); - } - - /** - * Finds a single active record that has the specified attribute values. - * See {@link find()} for detailed explanation about $condition and $params. - * @param array $attributes list of attribute values (indexed by attribute names) that the active records should match. - * An attribute value can be an array which will be used to generate an IN condition. - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return static|null the record found. Null if none is found. - */ - public function findByAttributes($attributes,$condition='',$params=array()) - { - Yii::trace(get_class($this).'.findByAttributes()','system.db.ar.CActiveRecord'); - $prefix=$this->getTableAlias(true).'.'; - $criteria=$this->getCommandBuilder()->createColumnCriteria($this->getTableSchema(),$attributes,$condition,$params,$prefix); - return $this->query($criteria); - } - - /** - * Finds all active records that have the specified attribute values. - * See {@link find()} for detailed explanation about $condition and $params. - * @param array $attributes list of attribute values (indexed by attribute names) that the active records should match. - * An attribute value can be an array which will be used to generate an IN condition. - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return static[] the records found. An empty array is returned if none is found. - */ - public function findAllByAttributes($attributes,$condition='',$params=array()) - { - Yii::trace(get_class($this).'.findAllByAttributes()','system.db.ar.CActiveRecord'); - $prefix=$this->getTableAlias(true).'.'; - $criteria=$this->getCommandBuilder()->createColumnCriteria($this->getTableSchema(),$attributes,$condition,$params,$prefix); - return $this->query($criteria,true); - } - - /** - * Finds a single active record with the specified SQL statement. - * @param string $sql the SQL statement - * @param array $params parameters to be bound to the SQL statement - * @return static|null the record found. Null if none is found. - */ - public function findBySql($sql,$params=array()) - { - Yii::trace(get_class($this).'.findBySql()','system.db.ar.CActiveRecord'); - $this->beforeFind(); - if(($criteria=$this->getDbCriteria(false))!==null && !empty($criteria->with)) - { - $this->resetScope(false); - $finder=$this->getActiveFinder($criteria->with); - return $finder->findBySql($sql,$params); - } - else - { - $command=$this->getCommandBuilder()->createSqlCommand($sql,$params); - return $this->populateRecord($command->queryRow()); - } - } - - /** - * Finds all active records using the specified SQL statement. - * @param string $sql the SQL statement - * @param array $params parameters to be bound to the SQL statement - * @return static[] the records found. An empty array is returned if none is found. - */ - public function findAllBySql($sql,$params=array()) - { - Yii::trace(get_class($this).'.findAllBySql()','system.db.ar.CActiveRecord'); - $this->beforeFind(); - if(($criteria=$this->getDbCriteria(false))!==null && !empty($criteria->with)) - { - $this->resetScope(false); - $finder=$this->getActiveFinder($criteria->with); - return $finder->findAllBySql($sql,$params); - } - else - { - $command=$this->getCommandBuilder()->createSqlCommand($sql,$params); - return $this->populateRecords($command->queryAll()); - } - } - - /** - * Finds the number of rows satisfying the specified query condition. - * See {@link find()} for detailed explanation about $condition and $params. - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return string the number of rows satisfying the specified query condition. Note: type is string to keep max. precision. - */ - public function count($condition='',$params=array()) - { - Yii::trace(get_class($this).'.count()','system.db.ar.CActiveRecord'); - $this->beforeCount(); - $builder=$this->getCommandBuilder(); - $criteria=$builder->createCriteria($condition,$params); - $this->applyScopes($criteria); - - if(empty($criteria->with)) - return $builder->createCountCommand($this->getTableSchema(),$criteria)->queryScalar(); - else - { - $finder=$this->getActiveFinder($criteria->with); - return $finder->count($criteria); - } - } - - /** - * Finds the number of rows that have the specified attribute values. - * See {@link find()} for detailed explanation about $condition and $params. - * @param array $attributes list of attribute values (indexed by attribute names) that the active records should match. - * An attribute value can be an array which will be used to generate an IN condition. - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return string the number of rows satisfying the specified query condition. Note: type is string to keep max. precision. - * @since 1.1.4 - */ - public function countByAttributes($attributes,$condition='',$params=array()) - { - Yii::trace(get_class($this).'.countByAttributes()','system.db.ar.CActiveRecord'); - $prefix=$this->getTableAlias(true).'.'; - $builder=$this->getCommandBuilder(); - $this->beforeCount(); - $criteria=$builder->createColumnCriteria($this->getTableSchema(),$attributes,$condition,$params,$prefix); - $this->applyScopes($criteria); - - if(empty($criteria->with)) - return $builder->createCountCommand($this->getTableSchema(),$criteria)->queryScalar(); - else - { - $finder=$this->getActiveFinder($criteria->with); - return $finder->count($criteria); - } - } - - /** - * Finds the number of rows using the given SQL statement. - * This is equivalent to calling {@link CDbCommand::queryScalar} with the specified - * SQL statement and the parameters. - * @param string $sql the SQL statement - * @param array $params parameters to be bound to the SQL statement - * @return string the number of rows using the given SQL statement. Note: type is string to keep max. precision. - */ - public function countBySql($sql,$params=array()) - { - Yii::trace(get_class($this).'.countBySql()','system.db.ar.CActiveRecord'); - $this->beforeCount(); - return $this->getCommandBuilder()->createSqlCommand($sql,$params)->queryScalar(); - } - - /** - * Checks whether there is row satisfying the specified condition. - * See {@link find()} for detailed explanation about $condition and $params. - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return boolean whether there is row satisfying the specified condition. - */ - public function exists($condition='',$params=array()) - { - Yii::trace(get_class($this).'.exists()','system.db.ar.CActiveRecord'); - $builder=$this->getCommandBuilder(); - $criteria=$builder->createCriteria($condition,$params); - $table=$this->getTableSchema(); - $criteria->select='1'; - $criteria->limit=1; - $this->applyScopes($criteria); - - if(empty($criteria->with)) - return $builder->createFindCommand($table,$criteria,$this->getTableAlias(false, false))->queryRow()!==false; - else - { - $criteria->select='*'; - $finder=$this->getActiveFinder($criteria->with); - return $finder->count($criteria)>0; - } - } - - /** - * Specifies which related objects should be eagerly loaded. - * This method takes variable number of parameters. Each parameter specifies - * the name of a relation or child-relation. For example, - *
-	 * // find all posts together with their author and comments
-	 * Post::model()->with('author','comments')->findAll();
-	 * // find all posts together with their author and the author's profile
-	 * Post::model()->with('author','author.profile')->findAll();
-	 * 
- * The relations should be declared in {@link relations()}. - * - * By default, the options specified in {@link relations()} will be used - * to do relational query. In order to customize the options on the fly, - * we should pass an array parameter to the with() method. The array keys - * are relation names, and the array values are the corresponding query options. - * For example, - *
-	 * Post::model()->with(array(
-	 *     'author'=>array('select'=>'id, name'),
-	 *     'comments'=>array('condition'=>'approved=1', 'order'=>'create_time'),
-	 * ))->findAll();
-	 * 
- * - * @return static the AR object itself. - */ - public function with() - { - if(func_num_args()>0) - { - $with=func_get_args(); - if(is_array($with[0])) // the parameter is given as an array - $with=$with[0]; - if(!empty($with)) - $this->getDbCriteria()->mergeWith(array('with'=>$with)); - } - return $this; - } - - /** - * Sets {@link CDbCriteria::together} property to be true. - * This is only used in relational AR query. Please refer to {@link CDbCriteria::together} - * for more details. - * @return static the AR object itself - * @since 1.1.4 - */ - public function together() - { - $this->getDbCriteria()->together=true; - return $this; - } - - /** - * Updates records with the specified primary key(s). - * See {@link find()} for detailed explanation about $condition and $params. - * Note, the attributes are not checked for safety and validation is NOT performed. - * @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). - * @param array $attributes list of attributes (name=>$value) to be updated - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return integer the number of rows being updated - */ - public function updateByPk($pk,$attributes,$condition='',$params=array()) - { - Yii::trace(get_class($this).'.updateByPk()','system.db.ar.CActiveRecord'); - $builder=$this->getCommandBuilder(); - $table=$this->getTableSchema(); - $criteria=$builder->createPkCriteria($table,$pk,$condition,$params); - $command=$builder->createUpdateCommand($table,$attributes,$criteria); - return $command->execute(); - } - - /** - * Updates records with the specified condition. - * See {@link find()} for detailed explanation about $condition and $params. - * Note, the attributes are not checked for safety and no validation is done. - * @param array $attributes list of attributes (name=>$value) to be updated - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return integer the number of rows being updated - */ - public function updateAll($attributes,$condition='',$params=array()) - { - Yii::trace(get_class($this).'.updateAll()','system.db.ar.CActiveRecord'); - $builder=$this->getCommandBuilder(); - $criteria=$builder->createCriteria($condition,$params); - $command=$builder->createUpdateCommand($this->getTableSchema(),$attributes,$criteria); - return $command->execute(); - } - - /** - * Updates one or several counter columns. - * Note, this updates all rows of data unless a condition or criteria is specified. - * See {@link find()} for detailed explanation about $condition and $params. - * @param array $counters the counters to be updated (column name=>increment value) - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return integer the number of rows being updated - * @see saveCounters - */ - public function updateCounters($counters,$condition='',$params=array()) - { - Yii::trace(get_class($this).'.updateCounters()','system.db.ar.CActiveRecord'); - $builder=$this->getCommandBuilder(); - $criteria=$builder->createCriteria($condition,$params); - $command=$builder->createUpdateCounterCommand($this->getTableSchema(),$counters,$criteria); - return $command->execute(); - } - - /** - * Deletes rows with the specified primary key. - * See {@link find()} for detailed explanation about $condition and $params. - * @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return integer the number of rows deleted - */ - public function deleteByPk($pk,$condition='',$params=array()) - { - Yii::trace(get_class($this).'.deleteByPk()','system.db.ar.CActiveRecord'); - $builder=$this->getCommandBuilder(); - $criteria=$builder->createPkCriteria($this->getTableSchema(),$pk,$condition,$params); - $command=$builder->createDeleteCommand($this->getTableSchema(),$criteria); - return $command->execute(); - } - - /** - * Deletes rows with the specified condition. - * See {@link find()} for detailed explanation about $condition and $params. - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return integer the number of rows deleted - */ - public function deleteAll($condition='',$params=array()) - { - Yii::trace(get_class($this).'.deleteAll()','system.db.ar.CActiveRecord'); - $builder=$this->getCommandBuilder(); - $criteria=$builder->createCriteria($condition,$params); - $command=$builder->createDeleteCommand($this->getTableSchema(),$criteria); - return $command->execute(); - } - - /** - * Deletes rows which match the specified attribute values. - * See {@link find()} for detailed explanation about $condition and $params. - * @param array $attributes list of attribute values (indexed by attribute names) that the active records should match. - * An attribute value can be an array which will be used to generate an IN condition. - * @param mixed $condition query condition or criteria. - * @param array $params parameters to be bound to an SQL statement. - * @return integer number of rows affected by the execution. - */ - public function deleteAllByAttributes($attributes,$condition='',$params=array()) - { - Yii::trace(get_class($this).'.deleteAllByAttributes()','system.db.ar.CActiveRecord'); - $builder=$this->getCommandBuilder(); - $table=$this->getTableSchema(); - $criteria=$builder->createColumnCriteria($table,$attributes,$condition,$params); - $command=$builder->createDeleteCommand($table,$criteria); - return $command->execute(); - } - - /** - * Creates an active record with the given attributes. - * This method is internally used by the find methods. - * @param array $attributes attribute values (column name=>column value) - * @param boolean $callAfterFind whether to call {@link afterFind} after the record is populated. - * @return static|null the newly created active record. The class of the object is the same as the model class. - * Null is returned if the input data is false. - */ - public function populateRecord($attributes,$callAfterFind=true) - { - if($attributes!==false) - { - $record=$this->instantiate($attributes); - $record->setScenario('update'); - $record->init(); - $md=$record->getMetaData(); - foreach($attributes as $name=>$value) - { - if(property_exists($record,$name)) - $record->$name=$value; - elseif(isset($md->columns[$name])) - $record->_attributes[$name]=$value; - } - $record->_pk=$record->getPrimaryKey(); - $record->attachBehaviors($record->behaviors()); - if($callAfterFind) - $record->afterFind(); - return $record; - } - else - return null; - } - - /** - * Creates a list of active records based on the input data. - * This method is internally used by the find methods. - * @param array $data list of attribute values for the active records. - * @param boolean $callAfterFind whether to call {@link afterFind} after each record is populated. - * @param string $index the name of the attribute whose value will be used as indexes of the query result array. - * If null, it means the array will be indexed by zero-based integers. - * @return static[] list of active records. - */ - public function populateRecords($data,$callAfterFind=true,$index=null) - { - $records=array(); - foreach($data as $attributes) - { - if(($record=$this->populateRecord($attributes,$callAfterFind))!==null) - { - if($index===null) - $records[]=$record; - else - $records[$record->$index]=$record; - } - } - return $records; - } - - /** - * Creates an active record instance. - * This method is called by {@link populateRecord} and {@link populateRecords}. - * You may override this method if the instance being created - * depends the attributes that are to be populated to the record. - * For example, by creating a record based on the value of a column, - * you may implement the so-called single-table inheritance mapping. - * @param array $attributes list of attribute values for the active records. - * @return static the active record - */ - protected function instantiate($attributes) - { - $class=get_class($this); - $model=new $class(null); - return $model; - } - - /** - * Returns whether there is an element at the specified offset. - * This method is required by the interface ArrayAccess. - * @param mixed $offset the offset to check on - * @return boolean - */ - #[ReturnTypeWillChange] - public function offsetExists($offset) - { - return $this->__isset($offset); - } -} - - -/** - * CBaseActiveRelation is the base class for all active relations. - * @author Qiang Xue - * @package system.db.ar - */ -class CBaseActiveRelation extends CComponent -{ - /** - * @var string name of the related object - */ - public $name; - /** - * @var string name of the related active record class - */ - public $className; - /** - * @var mixed the foreign key in this relation - */ - public $foreignKey; - /** - * @var mixed list of column names (an array, or a string of names separated by commas) to be selected. - * Do not quote or prefix the column names unless they are used in an expression. - * In that case, you should prefix the column names with 'relationName.'. - */ - public $select='*'; - /** - * @var string WHERE clause. For {@link CActiveRelation} descendant classes, column names - * referenced in the condition should be disambiguated with prefix 'relationName.'. - */ - public $condition=''; - /** - * @var array the parameters that are to be bound to the condition. - * The keys are parameter placeholder names, and the values are parameter values. - */ - public $params=array(); - /** - * @var string GROUP BY clause. For {@link CActiveRelation} descendant classes, column names - * referenced in this property should be disambiguated with prefix 'relationName.'. - */ - public $group=''; - /** - * @var string how to join with other tables. This refers to the JOIN clause in an SQL statement. - * For example, 'LEFT JOIN users ON users.id=authorID'. - * @since 1.1.3 - */ - public $join=''; - /** - * @var string|array property for setting post-JOIN operations such as USE INDEX. - * String typed value can be used with JOINs for HAS_MANY and MANY_MANY relations, while array typed - * value designed to be used only with MANY_MANY relations. First array element will be used for junction - * table JOIN and second array element will be used for target table JOIN. - * @since 1.1.16 - */ - public $joinOptions=''; - /** - * @var string HAVING clause. For {@link CActiveRelation} descendant classes, column names - * referenced in this property should be disambiguated with prefix 'relationName.'. - */ - public $having=''; - /** - * @var string ORDER BY clause. For {@link CActiveRelation} descendant classes, column names - * referenced in this property should be disambiguated with prefix 'relationName.'. - */ - public $order=''; - - /** - * Constructor. - * @param string $name name of the relation - * @param string $className name of the related active record class - * @param string $foreignKey foreign key for this relation - * @param array $options additional options (name=>value). The keys must be the property names of this class. - */ - public function __construct($name,$className,$foreignKey,$options=array()) - { - $this->name=$name; - $this->className=$className; - $this->foreignKey=$foreignKey; - foreach($options as $name=>$value) - $this->$name=$value; - } - - /** - * Merges this relation with a criteria specified dynamically. - * @param array $criteria the dynamically specified criteria - * @param boolean $fromScope whether the criteria to be merged is from scopes - */ - public function mergeWith($criteria,$fromScope=false) - { - if($criteria instanceof CDbCriteria) - $criteria=$criteria->toArray(); - if(isset($criteria['select']) && $this->select!==$criteria['select']) - { - if($this->select==='*'||$this->select===false) - $this->select=$criteria['select']; - elseif($criteria['select']===false) - $this->select=false; - elseif($criteria['select']!=='*') - { - $select1=is_string($this->select)?preg_split('/\s*,\s*/',trim($this->select),-1,PREG_SPLIT_NO_EMPTY):$this->select; - $select2=is_string($criteria['select'])?preg_split('/\s*,\s*/',trim($criteria['select']),-1,PREG_SPLIT_NO_EMPTY):$criteria['select']; - $this->select=array_merge($select1,array_diff($select2,$select1)); - } - } - - if(isset($criteria['condition']) && $this->condition!==$criteria['condition']) - { - if($this->condition==='') - $this->condition=$criteria['condition']; - elseif($criteria['condition']!=='') - $this->condition="({$this->condition}) AND ({$criteria['condition']})"; - } - - if(isset($criteria['params']) && $this->params!==$criteria['params']) - $this->params=array_merge($this->params,$criteria['params']); - - if(isset($criteria['order']) && $this->order!==$criteria['order']) - { - if($this->order==='') - $this->order=$criteria['order']; - elseif($criteria['order']!=='') - $this->order=$criteria['order'].', '.$this->order; - } - - if(isset($criteria['group']) && $this->group!==$criteria['group']) - { - if($this->group==='') - $this->group=$criteria['group']; - elseif($criteria['group']!=='') - $this->group.=', '.$criteria['group']; - } - - if(isset($criteria['join']) && $this->join!==$criteria['join']) - { - if($this->join==='') - $this->join=$criteria['join']; - elseif($criteria['join']!=='') - $this->join.=' '.$criteria['join']; - } - - if(isset($criteria['having']) && $this->having!==$criteria['having']) - { - if($this->having==='') - $this->having=$criteria['having']; - elseif($criteria['having']!=='') - $this->having="({$this->having}) AND ({$criteria['having']})"; - } - } -} - - -/** - * CStatRelation represents a statistical relational query. - * @author Qiang Xue - * @package system.db.ar - */ -class CStatRelation extends CBaseActiveRelation -{ - /** - * @var string the statistical expression. Defaults to 'COUNT(*)', meaning - * the count of child objects. - */ - public $select='COUNT(*)'; - /** - * @var mixed the default value to be assigned to those records that do not - * receive a statistical query result. Defaults to 0. - */ - public $defaultValue=0; - /** - * @var mixed scopes to apply - * Can be set to the one of the following: - *
    - *
  • Single scope: 'scopes'=>'scopeName'.
  • - *
  • Multiple scopes: 'scopes'=>array('scopeName1','scopeName2').
  • - *
- * @since 1.1.16 - */ - public $scopes; - - /** - * Merges this relation with a criteria specified dynamically. - * @param array $criteria the dynamically specified criteria - * @param boolean $fromScope whether the criteria to be merged is from scopes - */ - public function mergeWith($criteria,$fromScope=false) - { - if($criteria instanceof CDbCriteria) - $criteria=$criteria->toArray(); - parent::mergeWith($criteria,$fromScope); - - if(isset($criteria['defaultValue'])) - $this->defaultValue=$criteria['defaultValue']; - } -} - - -/** - * CActiveRelation is the base class for representing active relations that bring back related objects. - * @author Qiang Xue - * @package system.db.ar - * @since 1.0 - */ -class CActiveRelation extends CBaseActiveRelation -{ - /** - * @var string join type. Defaults to 'LEFT OUTER JOIN'. - */ - public $joinType='LEFT OUTER JOIN'; - /** - * @var string ON clause. The condition specified here will be appended to the joining condition using AND operator. - */ - public $on=''; - /** - * @var string the alias for the table that this relation refers to. Defaults to null, meaning - * the alias will be the same as the relation name. - */ - public $alias; - /** - * @var string|array specifies which related objects should be eagerly loaded when this related object is lazily loaded. - * For more details about this property, see {@link CActiveRecord::with()}. - */ - public $with=array(); - /** - * @var boolean whether this table should be joined with the primary table. - * When setting this property to be false, the table associated with this relation will - * appear in a separate JOIN statement. - * If this property is set true, then the corresponding table will ALWAYS be joined together - * with the primary table, no matter the primary table is limited or not. - * If this property is not set, the corresponding table will be joined with the primary table - * only when the primary table is not limited. - */ - public $together; - /** - * @var mixed scopes to apply - * Can be set to the one of the following: - *
    - *
  • Single scope: 'scopes'=>'scopeName'.
  • - *
  • Multiple scopes: 'scopes'=>array('scopeName1','scopeName2').
  • - *
- * @since 1.1.9 - */ - public $scopes; - /** - * @var string the name of the relation that should be used as the bridge to this relation. - * Defaults to null, meaning don't use any bridge. - * @since 1.1.7 - */ - public $through; - - /** - * Merges this relation with a criteria specified dynamically. - * @param array $criteria the dynamically specified criteria - * @param boolean $fromScope whether the criteria to be merged is from scopes - */ - public function mergeWith($criteria,$fromScope=false) - { - if($criteria instanceof CDbCriteria) - $criteria=$criteria->toArray(); - if($fromScope) - { - if(isset($criteria['condition']) && $this->on!==$criteria['condition']) - { - if($this->on==='') - $this->on=$criteria['condition']; - elseif($criteria['condition']!=='') - $this->on="({$this->on}) AND ({$criteria['condition']})"; - } - unset($criteria['condition']); - } - - parent::mergeWith($criteria); - - if(isset($criteria['joinType'])) - $this->joinType=$criteria['joinType']; - - if(isset($criteria['on']) && $this->on!==$criteria['on']) - { - if($this->on==='') - $this->on=$criteria['on']; - elseif($criteria['on']!=='') - $this->on="({$this->on}) AND ({$criteria['on']})"; - } - - if(isset($criteria['with'])) - $this->with=$criteria['with']; - - if(isset($criteria['alias'])) - $this->alias=$criteria['alias']; - - if(isset($criteria['together'])) - $this->together=$criteria['together']; - } -} - - -/** - * CBelongsToRelation represents the parameters specifying a BELONGS_TO relation. - * @author Qiang Xue - * @package system.db.ar - * @since 1.0 - */ -class CBelongsToRelation extends CActiveRelation -{ -} - - -/** - * CHasOneRelation represents the parameters specifying a HAS_ONE relation. - * @author Qiang Xue - * @package system.db.ar - * @since 1.0 - */ -class CHasOneRelation extends CActiveRelation -{ -} - - -/** - * CHasManyRelation represents the parameters specifying a HAS_MANY relation. - * @author Qiang Xue - * @package system.db.ar - * @since 1.0 - */ -class CHasManyRelation extends CActiveRelation -{ - /** - * @var integer limit of the rows to be selected. It is effective only for lazy loading this related object. Defaults to -1, meaning no limit. - */ - public $limit=-1; - /** - * @var integer offset of the rows to be selected. It is effective only for lazy loading this related object. Defaults to -1, meaning no offset. - */ - public $offset=-1; - /** - * @var string the name of the column that should be used as the key for storing related objects. - * Defaults to null, meaning using zero-based integer IDs. - */ - public $index; - - /** - * Merges this relation with a criteria specified dynamically. - * @param array $criteria the dynamically specified criteria - * @param boolean $fromScope whether the criteria to be merged is from scopes - */ - public function mergeWith($criteria,$fromScope=false) - { - if($criteria instanceof CDbCriteria) - $criteria=$criteria->toArray(); - parent::mergeWith($criteria,$fromScope); - if(isset($criteria['limit']) && $criteria['limit']>0) - $this->limit=$criteria['limit']; - - if(isset($criteria['offset']) && $criteria['offset']>=0) - $this->offset=$criteria['offset']; - - if(isset($criteria['index'])) - $this->index=$criteria['index']; - } -} - - -/** - * CManyManyRelation represents the parameters specifying a MANY_MANY relation. - * @author Qiang Xue - * @package system.db.ar - * @since 1.0 - */ -class CManyManyRelation extends CHasManyRelation -{ - /** - * @var string name of the junction table for the many-to-many relation. - */ - private $_junctionTableName=null; - /** - * @var array list of foreign keys of the junction table for the many-to-many relation. - */ - private $_junctionForeignKeys=null; - - /** - * @return string junction table name. - * @since 1.1.12 - */ - public function getJunctionTableName() - { - if ($this->_junctionTableName===null) - $this->initJunctionData(); - return $this->_junctionTableName; - } - - /** - * @return array list of junction table foreign keys. - * @since 1.1.12 - */ - public function getJunctionForeignKeys() - { - if ($this->_junctionForeignKeys===null) - $this->initJunctionData(); - return $this->_junctionForeignKeys; - } - - /** - * Initializes values of {@link junctionTableName} and {@link junctionForeignKeys} parsing - * {@link foreignKey} value. - * @throws CDbException if {@link foreignKey} has been specified in wrong format. - */ - private function initJunctionData() - { - if(!preg_match('/^\s*(.*?)\((.*)\)\s*$/',$this->foreignKey,$matches)) - throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is specified with an invalid foreign key. The format of the foreign key must be "joinTable(fk1,fk2,...)".', - array('{class}'=>$this->className,'{relation}'=>$this->name))); - $this->_junctionTableName=$matches[1]; - $this->_junctionForeignKeys=preg_split('/\s*,\s*/',$matches[2],-1,PREG_SPLIT_NO_EMPTY); - } -} - - -/** - * CActiveRecordMetaData represents the meta-data for an Active Record class. - * - * @author Qiang Xue - * @package system.db.ar - * @since 1.0 - */ -class CActiveRecordMetaData -{ - /** - * @var CDbTableSchema the table schema information - */ - public $tableSchema; - /** - * @var array table columns - */ - public $columns; - /** - * @var array list of relations - */ - public $relations=array(); - /** - * @var array attribute default values - */ - public $attributeDefaults=array(); - - private $_modelClassName; - - /** - * Constructor. - * @param CActiveRecord $model the model instance - * @throws CDbException if specified table for active record class cannot be found in the database - */ - public function __construct($model) - { - $this->_modelClassName=get_class($model); - - $tableName=$model->tableName(); - if(($table=$model->getDbConnection()->getSchema()->getTable($tableName))===null) - throw new CDbException(Yii::t('yii','The table "{table}" for active record class "{class}" cannot be found in the database.', - array('{class}'=>$this->_modelClassName,'{table}'=>$tableName))); - - if(($modelPk=$model->primaryKey())!==null || $table->primaryKey===null) - { - $table->primaryKey=$modelPk; - if(is_string($table->primaryKey) && isset($table->columns[$table->primaryKey])) - $table->columns[$table->primaryKey]->isPrimaryKey=true; - elseif(is_array($table->primaryKey)) - { - foreach($table->primaryKey as $name) - { - if(isset($table->columns[$name])) - $table->columns[$name]->isPrimaryKey=true; - } - } - } - $this->tableSchema=$table; - $this->columns=$table->columns; - - foreach($table->columns as $name=>$column) - { - if(!$column->isPrimaryKey && $column->defaultValue!==null) - $this->attributeDefaults[$name]=$column->defaultValue; - } - - foreach($model->relations() as $name=>$config) - { - $this->addRelation($name,$config); - } - } - - /** - * Adds a relation. - * - * $config is an array with three elements: - * relation type, the related active record class and the foreign key. - * - * @throws CDbException - * @param string $name $name Name of the relation. - * @param array $config $config Relation parameters. - * @return void - * @since 1.1.2 - */ - public function addRelation($name,$config) - { - if(isset($config[0],$config[1],$config[2])) // relation class, AR class, FK - $this->relations[$name]=new $config[0]($name,$config[1],$config[2],array_slice($config,3)); - else - throw new CDbException(Yii::t('yii','Active record "{class}" has an invalid configuration for relation "{relation}". It must specify the relation type, the related active record class and the foreign key.', array('{class}'=>$this->_modelClassName,'{relation}'=>$name))); - } - - /** - * Checks if there is a relation with specified name defined. - * - * @param string $name $name Name of the relation. - * @return boolean - * @since 1.1.2 - */ - public function hasRelation($name) - { - return isset($this->relations[$name]); - } - - /** - * Deletes a relation with specified name. - * - * @param string $name $name - * @return void - * @since 1.1.2 - */ - public function removeRelation($name) - { - unset($this->relations[$name]); - } -} diff --git a/extras/cache-local.php b/extras/cache-local.php deleted file mode 100644 index 56d0b39..0000000 --- a/extras/cache-local.php +++ /dev/null @@ -1,17 +0,0 @@ - 'system.caching.CRedisCache', - 'keyPrefix' => 'dev10', - 'options' => STREAM_CLIENT_CONNECT, - 'behaviors' => [ - 'cacheTag' => [ - 'class' => 'site_app.components.CacheTagBehavior', - ], - ], - 'hostname' => 'redis', - 'port' => '6379', - 'password' => 'yourpassword', - 'database' => 0 -]; diff --git a/extras/cache_write-local.php b/extras/cache_write-local.php deleted file mode 100644 index 56d0b39..0000000 --- a/extras/cache_write-local.php +++ /dev/null @@ -1,17 +0,0 @@ - 'system.caching.CRedisCache', - 'keyPrefix' => 'dev10', - 'options' => STREAM_CLIENT_CONNECT, - 'behaviors' => [ - 'cacheTag' => [ - 'class' => 'site_app.components.CacheTagBehavior', - ], - ], - 'hostname' => 'redis', - 'port' => '6379', - 'password' => 'yourpassword', - 'database' => 0 -]; diff --git a/extras/common-local.php b/extras/common-local.php deleted file mode 100644 index 6e6eb7b..0000000 --- a/extras/common-local.php +++ /dev/null @@ -1,79 +0,0 @@ - 'system.db.CDbConnection', - 'connectionString' => 'pgsql:host=db;dbname=db', - 'username' => 'db', - 'password' => 'db', - 'charset' => 'utf8', - 'initSQLs' => ['SET search_path TO app, public; SET statement_timeout TO 2500000;'], -]; - -$cacheReadConfigLocal = require $configPath . 'cache-local.php'; -$cacheConfigLocal = [ - 'class' => 'system.caching.CRedisCache', - 'keyPrefix' => 'dev10', - 'options' => STREAM_CLIENT_CONNECT, - 'behaviors' => [ - 'cacheTag' => [ - 'class' => 'site_app.components.CacheTagBehavior', - ], - ], - 'hostname' => 'redis', - 'port' => '6379', - 'password' => 'yourpassword', - 'database' => 0 -]; - -$sitePath = dirname(dirname(__FILE__)); -$basePath = dirname($sitePath); -$configPath = $sitePath . '/config/'; -$imgPath = $basePath.'/site/web/img/'; -$picsPath = $basePath.'/site/web/images/pics/'; -$iconsPath = $basePath.'/site/web/images/icons/'; -$filesPath = $basePath.'/site/web/files/'; -$videosSrcPath = $basePath.'/site/vsrc/'; -$videosPath = $basePath.'/site/web/v/'; -$schema = 'https'; -return [ - 'components' => [ - 'db' => $dbConfigLocal, - 'dbRead' => $dbConfigLocal, - 'dbReadReport' => $dbConfigLocal, - 'dbWrite' => $dbConfigLocal, - 'cache' => $cacheConfigLocal, - 'cacheWrite' => $cacheConfigLocal, - ], - 'params' => [ - 'rootUrl' => $schema . '://sizeup.firstduesizeup.test/', - 'picDbUrl' => $schema . '://sizeup.firstduesizeup.test/images/pics/', - 'googleApiBrowserKey' => 'AIzaSyBXBn3-dNLuOBu2mRfePCUgNL9cn_kPvSo', - 'googleApiServerKey' => 'AIzaSyCwJGvA6LZx_7C3TmS2sHy5kwILLJBed14', - 'fileDbPath' => $sitePath . '/web/files/', - 'picDbPath' => $picsPath, - 'imgPath' => $imgPath, - 'smartyStreets' => [ - 'authId' => '477965af-80ad-d6a6-d211-2e99104fe497', - 'authToken' => '3TAJGbewkU8DyUlGyusq', - ], - 'signatureDbPath' => $basePath . '/site/web/images/signatures/', - 'amqp' => [ - 'host' => 'rabbitmq', - 'vhost' => '/', - 'port' => '5672', - 'login' => 'rabbitmq', - 'password' => 'rabbitmq', - ], - 'smtps' => [ - 'DEFAULT' => [ - 'host' => 'localhost', - 'port' => 1025, - 'encryption' => 0, - 'smtp_credential_provider' => 'swiftmailer' - ], - ], - ] -]; From c7002aa55fc70dc79095dbe2459c67ba0acd8e16 Mon Sep 17 00:00:00 2001 From: Sid Date: Tue, 12 Sep 2023 17:37:11 +0530 Subject: [PATCH 4/5] commands update --- commands/web/fdsu.sh | 76 +++++++++++++++++++++++++------------------ commands/web/reset.sh | 2 ++ 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/commands/web/fdsu.sh b/commands/web/fdsu.sh index f991fa7..7a73b12 100755 --- a/commands/web/fdsu.sh +++ b/commands/web/fdsu.sh @@ -1,10 +1,5 @@ echo "Setting up the project" -#ask user name and print hello $name -echo "What do you want to call this project?" -read project_name -echo "Let's setup $project_name" - #ask user if need to install composer or skip echo "Do you want to install composer? (y/n)[no]" read install_composer @@ -13,7 +8,7 @@ then echo "Installing composer" composer install else - echo "Skipping composer installation" + echo -e "\e[1;33mSkipping composer installation\e[0m" fi #ask user if need to install yarn or skip @@ -21,41 +16,60 @@ echo "Do you want to install yarn? (y/n)[no]" read install_yarn if [ "$install_yarn" = "y" ] || [ "$install_yarn" = "Y" ] ] then - echo "Installing yarn" + echo -e "\e[1;32m Installing yarn \e[0m" yarn install else - echo "Skipping yarn installation" + echo -e "\e[1;33mSkipping yarn installation\e[0m" fi -#ask user if want to import db or skip -echo "Do you want to import db? (y/n)[no]" -read import_db -if [ "$import_db" = "y" ] || [ "$import_db" = "Y" ] ] -then - echo "Importing db" - #ask user for the db file name and unzip if file exists + +function runDatabaseImport() { echo "Enter the db file name" - read db_file_name - if [ -f "$db_file_name" ] - then - echo "Unzipping db file" - gunzip $db_file_name - psql -h db -d db -U db -f $db_file_name - - #delete unzipped file - rm -f $db_file_name + read -r db_file_name + + if [ -f "$db_file_name" ]; then + + #check file extension if gz + if [[ "$db_file_name" == *.gz ]] + then + echo "File is gzipped" + echo "Unzipping db file" + gunzip "$db_file_name" -c > "db.sql" + filename="db.sql" + else + filename="$db_file_name" + fi + + echo "Importing db" + psql -h db -d db -U db -f "$filename" else echo "File not found" + retry="y" + echo "Do you want to retry (y/n)[no]" + read -r retry + if [ "$retry" = "y" ] || [ "$retry" = "Y" ] + then + runDatabaseImport + else + echo -e "\e[1;33mSkipping db import\e[0m" + fi fi +} + +#ask user if want to import db or skip +echo "Do you want to import db? (y/n)[no]" +read -r import_db +if [ "$import_db" = "y" ] || [ "$import_db" = "Y" ] ] +then + runDatabaseImport else - echo "Skipping db import" + echo -e "\e[1;33mSkipping db import\e[0m" fi - #ask user if want to recreate files echo "Do you want to recreate files? (y/n)[no]" -read recreate_files -if [ "recreate_files" = "y" ] || [ "recreate_files" = "Y" ] ] +read -r recreate_files +if [ "$recreate_files" = "y" ] || [ "$recreate_files" = "Y" ] ] then #copy files for file in site/config/*.dist; do @@ -71,10 +85,10 @@ then echo "defined('YII_DEBUG') or define('YII_DEBUG', true);" >> site/web/index.php # rm vendor/yiisoft/yii/framework/db/ar/CActiveRecord.php && cp .ddev/extras/CActiveRecord.php vendor/yiisoft/yii/framework/db/ar/CActiveRecord.php - echo "Necessary files copied!!!" + echo -e "\e[1;32mNecessary files copied!!!\e[0m" else - echo "Skipping file recreation" + echo -e "\e[1;33mSkipping file recreation\e[0m" fi @@ -86,7 +100,7 @@ then echo "Running migrations" php console/yiic.php migrate else - echo "Skipping migrations" + echo -e "\e[1;33mSkipping migrations\e[0m" fi diff --git a/commands/web/reset.sh b/commands/web/reset.sh index 0542ef1..1dc0fc2 100755 --- a/commands/web/reset.sh +++ b/commands/web/reset.sh @@ -28,6 +28,8 @@ if [ "$answer" != "${answer#[Yy]}" ] ;then #delete node_modules folder rm -rf node_modules + + else echo "Reset cancelled" exit 1 From 93c40eb2a87a99d302e475fa8827939444d8fb63 Mon Sep 17 00:00:00 2001 From: Sid Date: Tue, 12 Sep 2023 18:22:24 +0530 Subject: [PATCH 5/5] yarn update --- commands/web/fdsu.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/commands/web/fdsu.sh b/commands/web/fdsu.sh index 7a73b12..2b98d49 100755 --- a/commands/web/fdsu.sh +++ b/commands/web/fdsu.sh @@ -18,6 +18,8 @@ if [ "$install_yarn" = "y" ] || [ "$install_yarn" = "Y" ] ] then echo -e "\e[1;32m Installing yarn \e[0m" yarn install + + cd site && yarn install --ignore-engines && cd .. else echo -e "\e[1;33mSkipping yarn installation\e[0m" fi @@ -79,6 +81,9 @@ then cp site/web/index.php.dist site/web/index.php + #edit file site/web/index.php and uncomment line 3 + sed -i '3 s/\/\/\(.*\)/\1/' ./site/web/index.php + touch site/config/saml/firstdue-x509.crt touch site/config/saml/firstdue-x509.key @@ -103,8 +108,6 @@ else echo -e "\e[1;33mSkipping migrations\e[0m" fi - - #write message in green color and in bold echo -e "\e[1;32m $name setup complete, use 'ddev start' to start the project\e[0m"