CommentWidget.php 10.4 KB
<?php
    namespace common\modules\comment\widgets;

    use yii\data\Sort;
    use \yii\helpers\ArrayHelper;
    use \yii\helpers\Html;

    class CommentWidget extends \yii\base\Widget
    {

        /**
         * 'comment_container' - apply to container of one comment. Must have data-key and data-form
         * 'comment_delete' - apply to comment delete link
         * 'comment_reply' - apply to comment reply link
         * 'comment_author' - apply to comment author text wrapper
         * 'widget_container' - apply to comment widget
         * 'form_container' - apply to comment form wrapper
         * 'reply_block' - apply to reply block of comment form
         * 'reply_author' - apply to reply author text block inside of reply block of comment form
         * 'comment_update' - apply to comment update link
         * 'comment_update_submit' - apply to submit button of comment update form
         */
        static $baseClass = [
            'comment_container'     => 'artbox_comment_container',
            'comment_delete'        => 'artbox_comment_delete',
            'comment_reply'         => 'artbox_comment_reply',
            'comment_author'        => 'artbox_comment_author',
            'widget_container'      => 'artbox_comment_widget',
            'form_container'        => 'artbox_comment_form',
            'reply_block'           => 'artbox_comment_reply_block',
            'reply_author'          => 'artbox_comment_reply_author',
            'comment_update'        => 'artbox_comment_update',
            'comment_update_submit' => 'artbox_comment_update_submit',
        ];

        /**
         * @var null|\yii\web\View
         */
        public $context = NULL;

        /**
         * @var array Parts of widgets that can be rendered
         */
        public $parts = [ ];

        public $rating_class = NULL;

        public $rating_options = [ ];

        /**
         * @var string|\common\modules\comment\models\Comment
         */
        public $comment_class;

        /**
         * @var array
         */
        public $class_options = [ ];

        public $provider_options = [ ];

        /**
         * @var bool Wheather to display comment list
         */
        public $display_comment_list = true;

        /**
         * @var bool Whether to display comment form
         */
        public $display_comment_form = true;

        /**
         * @var bool Whether to display success text
         */
        public $display_comment_success = true;

        /**
         * @var bool Whether to allow one user post multiple comments
         */
        public $allow_multiple = true;

        public $allow_reply = true;

        /**
         * @var array Options sent to list part
         */
        public $list_options = [
            'tag'   => 'div',
            'view'  => 'list-comment',
            'class' => 'test-class',
        ];

        /**
         * @var array Options sent to success part
         */
        public $success_options = [
            'tag'     => 'div',
            'content' => NULL,
            'class'   => 'test-class-success',
        ];

        /**
         * @var array Options sent to form part
         */
        public $form_options = [
            'tag'   => 'div',
            'view'  => 'form-comment',
            'class' => 'test-class-form',
        ];

        /**
         * @var bool Indicates whether any successful action happened
         */
        protected $isSuccess = false;

        public $success_text = 'Comment successfully added';

        /**
         * @var string $model Model, to which comments attached
         */
        public $model;

        /**
         * @var integer $model_id Model id, to which comments attached
         */
        public $model_id;

        /**
         * @var string Template of the widget. You may use <code>{success}, {form}, {list}</code>
         *      to render particular parts. You are also able to use common HTML here.
         */
        public $template = "{success}\n{form}\n{list}";

        /**
         * @var array Widget options
         */
        public $options = [ ];

        /**
         * @var \yii\data\DataProviderInterface Data provider of comments
         */
        public $dataProvider;

        /**
         * @inheritdoc
         */
        public function init()
        {
            parent::init();
            \common\modules\comment\assets\CommentAsset::register($this->view);
            if(is_string($this->comment_class)) {
                $this->comment_class = new $this->comment_class($this->class_options);
            } else {
                throw new \yii\base\InvalidConfigException(__CLASS__ . '->comment_class must be defined as object full class name string.');
            }
            if(!empty( $this->rating_class ) && is_string($this->rating_class)) {
                $this->rating_class = new $this->rating_class($this->rating_options);
            } elseif(!empty( $this->rating_class )) {
                throw new \yii\base\InvalidConfigException(__CLASS__ . '->rating_class must be defined as object full class name string.');
            }
            $this->comment_class->model = $this->model;
            $this->comment_class->model_id = $this->model_id;
            $this->createDataProvider();
            $this->process();
            ob_start();
        }

        /**
         * @inheritdoc
         * @return string
         */
        public function run()
        {
            $content = ob_get_clean();
            $this->createParts();
            return $this->renderWidget();
        }

        public function createParts()
        {
            if($this->display_comment_success && $this->isSuccess) {
                $tag = ArrayHelper::remove($this->success_options, 'tag', 'div');
                if(is_callable($this->success_options[ 'content' ])) {
                    $result = call_user_func(ArrayHelper::remove($this->success_options, 'content'), $this->success_text);
                } elseif($this->success_options[ 'content' ] != NULL) {
                    $result = Html::encode(ArrayHelper::remove($this->success_options, 'content', $this->success_text));
                } else {
                    $result = Html::encode($this->success_text);
                }
                $this->parts[ 'success' ] = Html::tag($tag, $result, $this->success_options);
                unset( $tag, $result );
            }

            if($this->display_comment_list) {
                $tag = ArrayHelper::remove($this->list_options, 'tag', 'div');
                $view = ArrayHelper::remove($this->list_options, 'view');
                $this->parts[ 'list' ] = Html::tag($tag, $this->renderItems($view), $this->list_options);
            }

            if($this->display_comment_form && $this->comment_class->checkCreate()) {
                $tag = ArrayHelper::remove($this->form_options, 'tag', 'div');
                $view = ArrayHelper::remove($this->form_options, 'view');
                if(!empty( $this->form_options[ 'class' ] )) {
                    $this->form_options[ 'class' ] .= ' ' . self::$baseClass[ 'form_container' ];
                } else {
                    $this->form_options[ 'class' ] = self::$baseClass[ 'form_container' ];
                }
                $this->parts[ 'form' ] = Html::tag($tag, $this->renderForm($view), $this->form_options);
            }
        }

        public function createDataProvider()
        {
            $this->dataProvider = new \yii\data\ActiveDataProvider([
                'query'      => ArrayHelper::remove($this->provider_options, 'query', $this->comment_class->getComments($this->model, $this->model_id)),
                'sort'       => ArrayHelper::remove($this->provider_options, 'sort', new Sort([
                    'defaultOrder' => [
                        'date_add' => SORT_DESC,
                    ],
                ])),
                'pagination' => ArrayHelper::remove($this->provider_options, 'pagination', [
                    'pageSize' => 10,
                ]),
            ]);
        }

        public function renderItems($view)
        {
            if(empty( $view )) {
                throw new \yii\base\InvalidConfigException("list_options[view] must be set");
            }
            return $this->render($view, [
                'dataProvider' => $this->dataProvider,
                'commentClass' => $this->comment_class,
            ]);
        }

        public function renderForm($view)
        {
            if(empty( $view )) {
                throw new \yii\base\InvalidConfigException("form_options[view] must be set");
            }
            if($this->comment_class->guestComment || !empty( \Yii::$app->user->identity )) {
                return $this->render($view, [
                    'model'        => $this->comment_class,
                    'rating'       => $this->rating_class,
                    'user'         => \Yii::$app->user->identity,
                    'dataProvider' => $this->dataProvider,
                ]);
            } else {
                return '';
            }
        }

        public function renderWidget()
        {
            $template = $this->template;
            $parts = $this->parts;
            $options = $this->options;
            $template = preg_replace('/{success}/', ArrayHelper::remove($parts, 'success', ''), $template);
            $template = preg_replace('/{list}/', ArrayHelper::remove($parts, 'list', ''), $template);
            $template = preg_replace('/{form}/', ArrayHelper::remove($parts, 'form', ''), $template);
            $tag = ArrayHelper::remove($options, 'tag', 'div');
            if(!empty( $options[ 'class' ] )) {
                $options[ 'class' ] .= ' ' . self::$baseClass[ 'widget_container' ];
            } else {
                $options[ 'class' ] = self::$baseClass[ 'widget_container' ];
            }
            return Html::tag($tag, $template, $options);
        }

        public function process()
        {
            $data = \Yii::$app->request->post();
            if($this->comment_class->load($data) && $this->comment_class->postComment()) {
                if(is_object($this->rating_class)) {
                    $this->comment_class->checkRating();
                    if($this->comment_class->rating->load($data) && $this->comment_class->rating->save()) {
                        $this->isSuccess = true;
                        \Yii::$app->response->redirect('');
                    }
                } else {
                    $this->isSuccess = true;
                }
            }
        }

    }