Как задать правило доступа к форме

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Drugpunker
Сообщения: 187
Зарегистрирован: 2014.08.13, 19:44

Как задать правило доступа к форме

Сообщение Drugpunker »

Добрый день всем.
Пытаюсь разобраться в правах доступа.
Не получатся найти верное решение.

Есть статьи.
Есть комментарии для статей.
Вывод комментариев (и формы) сделал, как написано в гайде, то есть в контроллере статей.

В контроллере постов делаю так:

Код: Выделить всё

public function actionView($id)
    {
        $post = $this->findModel($id);
        $comments_form = $this->newComment($post->post_id);
        //...
        }
        
    protected function newComment($post_id)
    {
        $comment = new Comments();

        //$_POST['Comments'] - модель
        if (isset($_POST['Comments']) && isset($_POST['parent_id'])) {
        }
        return $comment;
При такой реализации, случайно обнаружил, что добавление комментария будет отрабатывать, если пользователь не авторизован.
Но работать должно только для авторизованных.
Ок. Моё упущение.
Добавляю вывод формы для комментариев только для зарегистрированных.

Код: Выделить всё

if (!Yii::$app->user->isGuest) {}
Но ведь данные формы можно отправить, подделав запрос.
В контроллере для избежания этого обычно прописываются правила доступа.
Например для действий модели Posts:

Код: Выделить всё

'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    [
                        'actions' => ['view', 'error'],
                        'allow' => true,
                    ],
                    [
                        'actions' => ['create'],
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                ],
            ],
Но проблема здесь в том, что комментарии создаются в действии контроллера Post, и соответственно нельзя прописать права доступа для комментариев.

На ум приходит только вызов actionCreate для модели комментариев, из контроллера постов.
Но такой подход вроде как противоречит парадигме MVC, и как-то уж выглядит фу.

Вот и не вижу правильного решения. Хотя оно должно быть.
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Как задать правило доступа к форме

Сообщение unknownby »

В чем вопрос собственно не понятно из всего что написано.
Права доступа? Какие могут быть права доступа, если речь идёт о комментариях? Открывается статья и в ней есть сервис по оставлению комментария (форма доступная всем), по сути любой человек может оставить комментарий к статье.
Для чего внутрь действия View запихивается создание нового комментария? Это должен быть по сути actionNewComment при выполнении которого, должна твориться магия и создаваться новый комментарий к записи, которая просматривается. Возможно создается комментарий, а через админку идет изменение статуса комментария, чтоб не было спама и нецензурных комментариев. Сервис подтверждения комментария.
Drugpunker
Сообщения: 187
Зарегистрирован: 2014.08.13, 19:44

Re: Как задать правило доступа к форме

Сообщение Drugpunker »

unknownby писал(а): 2020.07.06, 12:45 Для чего внутрь действия View запихивается создание нового комментария? Это должен быть по сути actionNewComment при выполнении которого, должна твориться магия и создаваться новый комментарий к записи
В действие вью запихивается экземляр Comment, говорю же сделал по гайду.

Создание и отображение комментариев
В методе newComment() мы создаем экземпляр класса Comment и проверяем, отправлена ли форма комментария. Если отправлена — пробуем добавить комментарий к записи, вызывая $post->addComment($comment).
Да, я понимаю, что это должен быть по сути actionNewComment.
Но как его вызывать, в текущем исполнении?
Аватара пользователя
Dominus
Сообщения: 892
Зарегистрирован: 2013.03.14, 21:27
Откуда: Россия, Иваново
Контактная информация:

Re: Как задать правило доступа к форме

Сообщение Dominus »

Drugpunker писал(а): 2020.07.06, 11:37 Но проблема здесь в том, что комментарии создаются в действии контроллера Post, и соответственно нельзя прописать права доступа для комментариев.
А что мешает?
Гляньте как организовано actionDelete(){}
Можно и access фильтр повесить
Не спорь с дураком, иначе окружающие не правильно поймут кто из вас дурак!
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Как задать правило доступа к форме

Сообщение unknownby »

Drugpunker писал(а): 2020.07.06, 14:44 В действие вью запихивается экземляр Comment, говорю же сделал по гайду.

Создание и отображение комментариев

Да, я понимаю, что это должен быть по сути actionNewComment.
Но как его вызывать, в текущем исполнении?
Гайд для версии 1.1
Можно по разному сделать, через ajax или на форму повесить url куда передаваться данные.
zxczxc12
Сообщения: 161
Зарегистрирован: 2013.01.24, 21:16

Re: Как задать правило доступа к форме

Сообщение zxczxc12 »

При такой реализации, случайно обнаружил, что добавление комментария будет отрабатывать, если пользователь не авторизован.
Но работать должно только для авторизованных.
Да < можно проверять в контроле залогинен ли юзер или нет , но считаю что для таких случаев самый кошерный вариант это использовать AccessControl

Вот у меня набор правил для форума :

Код: Выделить всё

public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    ['allow' => true, 'actions' => [
                        'view'
                    ], 'roles' => ['?', '@']],
                    ['allow' => true, 'actions' => [
                        'delete'
                    ], 'roles' => ['moderator']],
                    [
                        'actions' => ['add-message', 'quote-message', 'edit-message', 'rate-message'],
                        'allow' => false,
                        'matchCallback' => function ($rule, $action) {
                            if (SuspendedUserActionModel::isSuspended('forum')) {
                                return true;
                            }

                            return false;
                        },
                        'denyCallback' => function ($rule, $action) {

                            setFlash(FLASH_STATUS_ERROR, 'Вы не имеете прав для данного действия');

                            return $action->controller->redirect(['/forum']);
                        },
                    ],
                    [
                        'allow' => false,
                        'roles' => ['?'],
                        'denyCallback' => function ($rule, $action) {
                            setFlash(FLASH_STATUS_INFO, \Yii::t('system', 'Вам необходимо залогиниться или зарегистрироваться'));
                            return \Yii::$app->getUser()->loginRequired();
                        },
                    ],
                    ['allow' => true, 'roles' => ['@']],
                ],
            ],
        ];
    }
Drugpunker
Сообщения: 187
Зарегистрирован: 2014.08.13, 19:44

Re: Как задать правило доступа к форме

Сообщение Drugpunker »

unknownby писал(а): 2020.07.07, 07:34
Drugpunker писал(а): 2020.07.06, 14:44 В действие вью запихивается экземляр Comment, говорю же сделал по гайду.

Создание и отображение комментариев

Да, я понимаю, что это должен быть по сути actionNewComment.
Но как его вызывать, в текущем исполнении?
Гайд для версии 1.1
Можно по разному сделать, через ajax или на форму повесить url куда передаваться данные.
Как?
Блин, реально "вижу фигу".
Drugpunker
Сообщения: 187
Зарегистрирован: 2014.08.13, 19:44

Re: Как задать правило доступа к форме

Сообщение Drugpunker »

zxczxc12 писал(а): 2020.07.07, 09:36
При такой реализации, случайно обнаружил, что добавление комментария будет отрабатывать, если пользователь не авторизован.
Но работать должно только для авторизованных.
Да < можно проверять в контроле залогинен ли юзер или нет , но считаю что для таких случаев самый кошерный вариант это использовать AccessControl
Я это понимаю.
Но.

Может я непонятно объяснил суть вопроса.

И так.

Есть форма комментариев _form.
Требуется вызывать её в CommentController, actionCreate или в actionUpdate.
И делать этот вызов из PostController, а именно его действия view, потому что нужно знать id статьи, к которой выводятся комментарии.

То есть должен быть выполнен этот экшен, в представлении статей:

Код: Выделить всё

/**
     * Creates a new CommentForm model.
     * If creation is successful, the browser will be refreshed.
     * @param integer $post_id
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionCreate($post_id)
    {
        $postModel = $this->findPostModel($post_id);
        $model = new CommentForm;

        if ($model->load(Yii::$app->request->post())) {
            if ($model->create()) {
                return $this->refresh();
            }
        }

        return $this->renderPartial('_form', [
            'model' => $model,
        ]);
    }
Как?
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Как задать правило доступа к форме

Сообщение unknownby »

ОК.
Что если вот такое свойство задать форме?
Просто добавив свойства action и method в ActiveForm
Остальные свойства как душе угодно.

Код: Выделить всё

<?php $form = ActiveForm::begin([
        'id' => 'newComment',
        'type' => 'vertical',
        'action' => Url::toRoute(['comment/create', $id => $model->post_id]),
        'method' => 'post',
    ]);
    ?>
Данная строчка случаем не лишняя?

Код: Выделить всё

$postModel = $this->findPostModel($post_id);
Сам что-то запутался что и откуда вызывать.
Есть контроллер отображения статьи PostController и нужно для данной статьи оставить комментарий. Ну так и делаем action, который будет срабатывать для создания нового комментария внутри этого контроллера. Зачем контроллер CommentController в данной ситуации?
А узнать id поста вот как

Код: Выделить всё

$model->post_id
Передав его в action
Тогда по сути уже и ясно, почему во view запихана форма. Чтобы не создавать дополнительных экшенов, а проверять на post(), если есть данные, тогда произвести добавление комментария и отобразить снова view.
Drugpunker
Сообщения: 187
Зарегистрирован: 2014.08.13, 19:44

Re: Как задать правило доступа к форме

Сообщение Drugpunker »

unknownby писал(а): 2020.07.07, 14:27 Данная строчка случаем не лишняя?

Код: Выделить всё

$postModel = $this->findPostModel($post_id);
Да, лишняя. Результат экспериментов пока.
unknownby писал(а): 2020.07.07, 14:27 Сам что-то запутался что и откуда вызывать.
Есть контроллер отображения статьи PostController и нужно для данной статьи оставить комментарий. Ну так и делаем action, который будет срабатывать для создания нового комментария внутри этого контроллера. Зачем контроллер CommentController в данной ситуации?
А узнать id поста вот как

Код: Выделить всё

$model->post_id
Передав его в action
Тогда по сути уже и ясно, почему во view запихана форма. Чтобы не создавать дополнительных экшенов, а проверять на post(), если есть данные, тогда произвести добавление комментария и отобразить снова view.
Хорошо. А как же валидация модели?
Если делаем action, который будет срабатывать для создания нового комментария внутри этого контроллера.

Это будет так в контроллере Post.
Создаём экземпляр модели перед показом формы:

Код: Выделить всё

public function actionView($id)
    {
        $post = $this->findModel($id);
        $comment_form = new CommentForm();
Во вьюхе статьи рендерим форму и передаём туда наш экзкмпляр $comment_form:

Код: Выделить всё

echo $this->context->renderPartial('/comments/_form', [
        'model' => $comment_form
    ]);
Тут же в контроллере Post пусть будет экшен:

Код: Выделить всё

/**
     * Creates a new CommentForm model.
     * If creation is successful, the browser will be refreshed.
     * @param integer $post_id
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionCommentCreate($post_id)
    {
        $model = new CommentForm;

        if ($model->load(Yii::$app->request->post())) {
            if ($model->create()) {
                return $this->refresh();
            }
        }
    }
Но в таком случае, новый экземпляр модели в actionCommentCreate, будет затирать пришедшие в $_Post данные.
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Как задать правило доступа к форме

Сообщение unknownby »

Drugpunker писал(а): 2020.07.07, 14:57 Но в таком случае, новый экземпляр модели в actionCommentCreate, будет затирать пришедшие в $_Post данные.
Не кажется ли, что слишком простой функционал приобретает неимоверную запутанность?
По порядку:
1. Должно быть представление вывода информации статьи. Это actionView контроллера Post.
2. Внутри представления view должен быть рендер формы, в который передает модель Post, чтобы можно было отобразить все комментарии к данной статье.

Код: Выделить всё

<?= $this->render('_form', ['model' => $model]) ?>
3. Внутри представления _form нужно отобразить все комментарии и сделать форму для отправки комментария.
4. По нажатию на создание нового комментария, если поле для комментария заполнено, нужно открыть статью заново, взяв id статьи из экшена по созданию нового комментария.

Код: Выделить всё

return $this->render('view', ['model' => Post::findOne($id)]);
Это упрощенная версия статья+комментарии. Никаких затираний не вижу.

Если усложнить, то отправка и обновление комментариев должна быть через ajax.

Код: Выделить всё

$js = "
$(document).ready(function(){

    $('#comment').on('click', function(e){ //тут варианты click, submit, afterSubmit
        $.ajax({
            type: 'POST',
            url: '".$model->url."', //URL по которому будет добавление и переотрисовка комментариев
            data: {
            	id: '".$model->post_id."' //твой URL будет получать в post('id') - идентификатор статьи 
            }
            success: function(result) {
                $('#all_comments').html(result); //в каком-то блоке будут переотрисовываться комментарии
            }
        });
    });

});
";
$this->registerJs($js);
В таком случае нужно будет использовать renderAjax и посидеть над этим подольше, чем простая реализация.
Drugpunker
Сообщения: 187
Зарегистрирован: 2014.08.13, 19:44

Re: Как задать правило доступа к форме

Сообщение Drugpunker »

unknownby писал(а): 2020.07.07, 15:32
Drugpunker писал(а): 2020.07.07, 14:57 Но в таком случае, новый экземпляр модели в actionCommentCreate, будет затирать пришедшие в $_Post данные.
Не кажется ли, что слишком простой функционал приобретает неимоверную запутанность?
По порядку:
1. Должно быть представление вывода информации статьи. Это actionView контроллера Post.
2. Внутри представления view должен быть рендер формы, в который передает модель Post, чтобы можно было отобразить все комментарии к данной статье.

Код: Выделить всё

<?= $this->render('_form', ['model' => $model]) ?>
3. Внутри представления _form нужно отобразить все комментарии и сделать форму для отправки комментария.
4. По нажатию на создание нового комментария, если поле для комментария заполнено, нужно открыть статью заново, взяв id статьи из экшена по созданию нового комментария.

Код: Выделить всё

return $this->render('view', ['model' => Post::findOne($id)]);
Это упрощенная версия статья+комментарии. Никаких затираний не вижу.
Моя ошибка была. Да, нет затираний.

Вобщем сделал так. Не уверен, что вышло оптимально правильно. Но почти нравится.

В PostController вызываю экшен (думал, думал и пришёл к выводу, что такой вызов не противоречит православному MVC):

Код: Выделить всё

public function actionView($id)
    {
        $post = $this->findModel($id);
        $comment_form = Yii::$app->runAction('comments/create', ['post_id' => $post->post_id]);
Экшн в CommentController сделал так:

Код: Выделить всё

/**
     * Creates a new CommentForm model.
     * If creation is successful, the browser will be refreshed.
     * @param integer $post_id
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionCreate($post_id)
    {
        $model = new CommentForm;

        if (isset($_POST['CommentForm']) && isset($_POST['post_id']) && isset($_POST['parent_id'])) {
            $model->attributes = $_POST['CommentForm'];
            $model->post_id = $_POST['post_id'];
            $model->parent_id = $_POST['parent_id'];

                if ($model->create()) {
                    return $this->refresh();
                }
        }

        $model->post_id = $post_id;
        return $this->renderPartial('/comments/_form', [
            'model' => $model,
        ]);
    }
Ну и собственно во вьюхе статьи, уже после виджета вывода комментариев, вывожу форму

Код: Выделить всё

<?php
//Форма комментариев
if (!Yii::$app->user->isGuest) {
    print_r($comment_form);
}
?>
Добавил access rules в CommentsController (ради чего собственно всё и затевалось).

Код: Выделить всё

'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    [
                        'actions' => ['error'],
                        'allow' => true,
                    ],
                    [
                        'actions' => ['create', 'update'],
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                ],
            ],
Но теперь при попытке открыть статью (под неавторизованным), меня бросает на форму входа.
Видимо из-за того, что вызываю форму добавления комментов в контроллере статей так:

Код: Выделить всё

public function actionView($id)
    {
        $post = $this->findModel($id);
        $comment_form = Yii::$app->runAction('comments/create', ['post_id' => $post->post_id]);
За что боролся блин...
Пришлось внести корректировки.

В PostController изменил вызов контроллера на экземпляр модели.
И присваиваю её полю id статьи.

Код: Выделить всё

public function actionView($id)
    {
        $post = $this->findModel($id);
        $comment_form = new CommentForm();
        $comment_form->post_id = $post->post_id;
Экшн Create теперь без входных параметров и просто ловит форму:

Код: Выделить всё

/**
     * Creates a new CommentForm model.
     * If creation is successful, the browser will be refreshed.
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionCreate()
    {
        $model = new CommentForm;

        if (isset($_POST['CommentForm']) && isset($_POST['post_id']) && isset($_POST['parent_id'])) {
            $model->attributes = $_POST['CommentForm'];
            $model->post_id = $_POST['post_id'];
            $model->parent_id = $_POST['parent_id'];

            if ($model->create()) {
                return $this->goBack();
            }
        }
        return $this->redirect('/');
    }
Ну и во view статей вывод формы стал таким:

Код: Выделить всё

<?php
//Форма комментариев
if (!Yii::$app->user->isGuest) {
    echo $this->context->renderPartial('/comments/_form', [
        'model' => $comment_form
    ]);
}
?>
Вроде всё нравится.
И не покидает чувство, что наворачивал круги вокруг ничего.
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Как задать правило доступа к форме

Сообщение unknownby »

Я бы сказал, что у тебя контроллер Comments вообще тут лишний. Все можно было сделать в Post на экшене View. А то прыгаешь туда сюда по экшенам и контроллерам. У тебя по сути, если приходят данные из post() нужно добавить коммент, а если не приходят, то просто отобразить view, без добавления комментария.
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Как задать правило доступа к форме

Сообщение unknownby »

Я представляю это примерно так.
Если не заморачиваться с ajax-ом

Код: Выделить всё

public function actionView($id)
    {
        $model = $this->findModel($id);
        
        //проверка на то, что был отправлен комментарий
        if (\Yii::$app->request->isPost)
        {
            //\Yii::$app->getRequest()->post() тут лежит всё, что пришло из POST-запроса
            //тут магия добавления комментария
            //в итоге добавляется комментарий и дальше по коду рендерится view
        }
        
        return $this->render('view', ['model' => $model]);
    }
Drugpunker
Сообщения: 187
Зарегистрирован: 2014.08.13, 19:44

Re: Как задать правило доступа к форме

Сообщение Drugpunker »

unknownby писал(а): 2020.07.08, 07:57 Я бы сказал, что у тебя контроллер Comments вообще тут лишний. Все можно было сделать в Post на экшене View. А то прыгаешь туда сюда по экшенам и контроллерам. У тебя по сути, если приходят данные из post() нужно добавить коммент, а если не приходят, то просто отобразить view, без добавления комментария.
Согласен, возможно лишний.
Но будет добавлена возможность изменять комментарий, а значит и ещё один экшен.
И хочется, чтобы отдельный контроллер был для каждой модели.
Drugpunker
Сообщения: 187
Зарегистрирован: 2014.08.13, 19:44

Re: Как задать правило доступа к форме

Сообщение Drugpunker »

unknownby писал(а): 2020.07.08, 09:03 Я представляю это примерно так.
Если не заморачиваться с ajax-ом

Код: Выделить всё

public function actionView($id)
    {
        $model = $this->findModel($id);
        
        //проверка на то, что был отправлен комментарий
        if (\Yii::$app->request->isPost)
        {
            //\Yii::$app->getRequest()->post() тут лежит всё, что пришло из POST-запроса
            //тут магия добавления комментария
            //в итоге добавляется комментарий и дальше по коду рендерится view
        }
        
        return $this->render('view', ['model' => $model]);
    }
Кстати, да.
Можно и так сделать. Даже наверно нужно.
Там с отловом/присвоением данных из хиддн полей разбирался, так и оставил.

Кстати, как сам валидируешь данные из hidden input?
Ошибку там показывать не следует.
Наверно с помощью анонимной функции в rules.
А что она должна делать...
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Как задать правило доступа к форме

Сообщение unknownby »

Drugpunker писал(а): 2020.07.08, 11:30 Кстати, как сам валидируешь данные из hidden input?
Ошибку там показывать не следует.
Наверно с помощью анонимной функции в rules.
А что она должна делать...
А какие hidden поля у тебя есть? Ну вообще в модели валидируются сами, в твоём варианте в форме.
Если это целочисленное, то как в обычных рулах

Код: Выделить всё

[['my_id', ], 'integer'],
Смысл валидации скрытых полей конечно есть, но если ты сам их определяешь, то тут не стоит даже париться.
Если у тебя должно быть целое число, так записывай в hidden целое число.
Типо такого, если у тебя только залогиненные могут отправлять комментарии.

Код: Выделить всё

<?= $form->field($model, 'user_id')->hiddenInput(['value' => \Yii::$app->user->identity->id])->label(false); ?>
Однако можно получать идентификатор пользователя не с формы, а в контроллере, там где магия будет :)
Drugpunker
Сообщения: 187
Зарегистрирован: 2014.08.13, 19:44

Re: Как задать правило доступа к форме

Сообщение Drugpunker »

unknownby писал(а): 2020.07.08, 11:50
Drugpunker писал(а): 2020.07.08, 11:30 Кстати, как сам валидируешь данные из hidden input?
Ошибку там показывать не следует.
Наверно с помощью анонимной функции в rules.
А что она должна делать...
А какие hidden поля у тебя есть? Ну вообще в модели валидируются сами, в твоём варианте в форме.
Если это целочисленное, то как в обычных рулах

Код: Выделить всё

[['my_id', ], 'integer'],
Смысл валидации скрытых полей конечно есть, но если ты сам их определяешь, то тут не стоит даже париться.
Если у тебя должно быть целое число, так записывай в hidden целое число.
Типо такого, если у тебя только залогиненные могут отправлять комментарии.

Код: Выделить всё

<?= $form->field($model, 'user_id')->hiddenInput(['value' => \Yii::$app->user->identity->id])->label(false); ?>
Однако можно получать идентификатор пользователя не с формы, а в контроллере, там где магия будет :)
Ниддены у меня - это id статьи и id родителя комментария (для дерева).
В контроллере их врядли можно получить.
Поэтому задумался над валидацией.

А для получения id юзера есть же BlamebleBehavior.
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Как задать правило доступа к форме

Сообщение unknownby »

hidden поле с id статьи? Смысл? Если URL открывается уже с id статьи.
Грубо говоря, можно получить $id статьи, просто используя переменную из контроллера

Код: Выделить всё

public function actionView($id)
А что насчет id родителя комментария, то он ведь где-то должен каждый раз перепрописываться, если я вдруг выбрал, что хочу написать комментарий для одного ответа, а потом решил и поменял свой ответ для другого ответа. Явно не пропишется строка из фразы, вместо цифры.

Каким образом оно должно завалидироваться для пользователя? Напишет ему "Не верный родитель комментария"? :D
Тут надо построить правильную логику, чтоб не сломалось сразу после наплыва комментаторов :)
Можно написать логику по которой будет сохраняться родитель комментария, а при каких-то проблемах записать туда, что комментарий без родителя.
Ответить