CommentBehavior.php 6.1 KB
<?php
    
    namespace artweb\artbox\comment\behaviors;
    
    use artweb\artbox\comment\models\CommentModel;
    use artweb\artbox\comment\models\interfaces\RatingCacheInterface;
    use yii\base\Behavior;
    use yii\base\InvalidConfigException;
    use yii\db\ActiveQuery;
    use yii\db\ActiveRecord;
    
    /**
     * Class CommentBehavior
     *
     * @property ActiveQuery    $averageRating
     * @property CommentModel[] $comments
     * @package artweb\artbox\comment\behaviors
     */
    class CommentBehavior extends Behavior
    {
        
        /**
         * Fully-classified name of class to hold rating cache
         *
         * @var string
         */
        private $cacheModelName = '';
        
        /**
         * Class to hold rating cache
         *
         * @var RatingCacheInterface
         */
        private $cacheModel = null;
        
        /**
         * Whether to cache rating or not
         *
         * @var bool
         */
        private $cacheRating = false;
    
        /**
         * @inheritdoc
         */
        public function attach($owner)
        {
            if ($this->cacheRating) {
                if (empty( $this->cacheModelName ) || !is_string($this->cacheModelName)) {
                    throw new InvalidConfigException(
                        'To use rating cache you must set $cacheModelName where to store it'
                    );
                }
                $this->setCacheModel(
                    \Yii::createObject(
                        [
                            'class' => $this->cacheModelName,
                        ]
                    )
                );
            }
            parent::attach($owner);
        }
        
        /**
         * Get fully-classified name of class to hold rating cache
         *
         * @return string
         */
        public function getCacheModelName():string
        {
            return $this->cacheModelName;
        }
        
        /**
         * Set fully-classified name of class to hold rating cache
         *
         * @param string $value
         */
        public function setCacheModelName(string $value)
        {
            $this->cacheModelName = $value;
        }
        
        /**
         * Get model to hold rating cache
         *
         * @return \artweb\artbox\comment\models\interfaces\RatingCacheInterface
         */
        public function getCacheModel():RatingCacheInterface
        {
            return $this->cacheModel;
        }
        
        /**
         * Set model to hold cache
         *
         * @param \artweb\artbox\comment\models\interfaces\RatingCacheInterface|null $value
         */
        private function setCacheModel(RatingCacheInterface $value = null)
        {
            $this->cacheModel = $value;
        }
        
        /**
         * Whether cache rating or not
         *
         * @return bool
         */
        public function getCacheRating(): bool
        {
            return $this->cacheRating;
        }
        
        /**
         * Set whether to cache rating or not
         *
         * @param bool $value
         */
        public function setCacheRating(bool $value)
        {
            $this->cacheRating = $value;
        }
        
        /**
         * Get query to get all comments for current $owner model
         *
         * @return \yii\db\ActiveQuery
         */
        public function getComments(): ActiveQuery
        {
            /**
             * @var ActiveRecord $owner
             */
            $owner = $this->owner;
            $pk = $owner->primaryKey()[ 0 ];
            $query = $owner->hasMany(CommentModel::className(), [ 'entity_id' => $pk ])
                           ->where(
                               [
                                   'artbox_comment.entity'             => $owner::className(),
                                   'artbox_comment.status'             => CommentModel::STATUS_ACTIVE,
                                   'artbox_comment.artbox_comment_pid' => null,
                               ]
                           );
            return $query;
        }
        
        /**
         * Get query to get average rating for current $owner model if it cached
         *
         * @return \yii\db\ActiveQuery
         */
        public function getAverageRating(): ActiveQuery
        {
            /**
             * @var ActiveRecord $owner
             */
            $owner = $this->owner;
            $pk = $owner->primaryKey()[ 0 ];
            $query = $owner->hasOne($this->cacheModelName, [ $this->cacheModel->getLinkAttribute() => $pk ]);
            return $query;
        }
        
        /**
         * Recalculate cache for current $owner model
         *
         * @return bool
         */
        public function recalculateRating(): bool
        {
            /**
             * @var RatingCacheInterface $averageRating
             */
            $average = $this->getComments()
                            ->joinWith('rating')
                            ->select([ 'average' => 'avg(artbox_comment_rating.value)::float' ])
                            ->scalar();
            if (!$average) {
                $average = 0;
            }
            $averageRating = $this->getAverageRating()
                                  ->one();
            if (!empty( $averageRating )) {
                $averageRating->setValue($average);
            } else {
                /**
                 * @var ActiveRecord         $owner
                 * @var RatingCacheInterface $averageRating
                 */
                $owner = $this->owner;
                $pk = $owner->primaryKey()[ 0 ];
                $averageRating = \Yii::createObject(
                    [
                        'class'                               => $this->cacheModelName,
                        $this->cacheModel->getLinkAttribute() => $this->owner->$pk,
                    ]
                );
                $averageRating->setValue($average);
            }
            if ($averageRating->save()) {
                return true;
            } else {
                return false;
            }
        }
    }