return в контроллере

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
rosolovsky
Сообщения: 119
Зарегистрирован: 2014.06.23, 11:44
Откуда: Украина

return в контроллере

Сообщение rosolovsky » 2018.03.31, 23:38

Если в экшн контроллера аяксом пришел запрос из js скрипта то return в этом случае возвращает ответ скрипту? А если мне в экшене надо сделать return render, то что идёт тогда скрипту? Запутался малость :)

andku83
Сообщения: 988
Зарегистрирован: 2016.07.01, 10:24
Откуда: Харьков

Re: return в контроллере

Сообщение andku83 » 2018.03.31, 23:42

то что вернет функция render() (ответ)

rosolovsky
Сообщения: 119
Зарегистрирован: 2014.06.23, 11:44
Откуда: Украина

Re: return в контроллере

Сообщение rosolovsky » 2018.03.31, 23:54

Но скрипту может не понравится что вернет render. А можно скрипту сначала вернуть ответ, а потом сделать render?

andku83
Сообщения: 988
Зарегистрирован: 2016.07.01, 10:24
Откуда: Харьков

Re: return в контроллере

Сообщение andku83 » 2018.03.31, 23:58

ну значит вы должны так сформировать ответ чтобы скрипты "понравилось", можно использовать JSON формат и в нем в одной переменной отдать один ответ, а в другой результат рендера, но 2 ответа на один запрос вам отдать не получится

rosolovsky
Сообщения: 119
Зарегистрирован: 2014.06.23, 11:44
Откуда: Украина

Re: return в контроллере

Сообщение rosolovsky » 2018.04.01, 00:08

Ну, хорошо, если запрос успешен и я просто рендерю вьюху с данными, то в калбек success скрипта что попадет? Html код который отдала вьюха?

andku83
Сообщения: 988
Зарегистрирован: 2016.07.01, 10:24
Откуда: Харьков

Re: return в контроллере

Сообщение andku83 » 2018.04.01, 00:48

да, сделайте console.log() и посмотрите

nickdenry
Сообщения: 98
Зарегистрирован: 2015.10.28, 04:55

Re: return в контроллере

Сообщение nickdenry » 2018.04.01, 01:19

rosolovsky писал(а):
2018.04.01, 00:08
Ну, хорошо, если запрос успешен и я просто рендерю вьюху с данными, то в калбек success скрипта что попадет? Html код который отдала вьюха?
Да. Еще можно разделить запросы на ajax и не ajax и отдавать в callback success что хочется, например json.

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

public function actionCool()
    {
        $request = Yii::$app->request;
        if ($request->isAjax)
        {
            \Yii::$app->response->format = 'json';            
            return ['success'];
        }
        else
        {
            return $this->render('index');
        }
    }
Ответственные программисты с высоким уровнем технического долга (c)

rosolovsky
Сообщения: 119
Зарегистрирован: 2014.06.23, 11:44
Откуда: Украина

Re: return в контроллере

Сообщение rosolovsky » 2018.04.01, 09:37

Извиняюсь, а как тогда мне тогда динамически обновлять кусок html кода на странице, например статус корзины которая хранится в сессии?
При загрузке страницы в layout я вывожу кусочек кода

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

<?= Yii::$app->view->render('..\cart\minicart.php'); ?>
а вот код

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

<?php
if(isset($_SESSION['cart'])){
     echo ('<p>Товаров: '.$_SESSION['cart.qty'].'</p>');
     echo ('<p>На сумму: '.$_SESSION['cart.sum'].'</p>');
}
   else {
    echo('<p> Ваша корзина пуста</p>');
} 
?>
и вот при изменении в корзине из конролера я хотел сделать так

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

return $this->renderPartial('..\cart\minicart.php');
но получается этот код идет в js скрипт, и там его конечно можно через js показать на странице, но я думал что можно постредством render или renderPartial фреймворка это сделать.

nickdenry
Сообщения: 98
Зарегистрирован: 2015.10.28, 04:55

Re: return в контроллере

Сообщение nickdenry » 2018.04.01, 15:37

rosolovsky писал(а):
2018.04.01, 09:37
Извиняюсь, а как тогда мне тогда динамически обновлять кусок html кода на странице, например статус корзины которая хранится в сессии?
render и renderPartial - осуществляют вывод из php в браузер. К JavaScript отношения не имеют. Код выдуман для примера.

Работа render:

1. Статическая реализация (просто показываем страницу и обновляем целиком). Вывод с помощью php, будь то с Yii(2) или нет. Полагаю, что используем Yii2.

В контроллере, отвечающем за вывод страницы имеем код

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

   // Данные получаем в контроллере, передаем в view
    public function actionDisplayCart
    {
        // Получаем количество и сумму из сессии
    	$cartQuantity = Yii::$app->session->get('cart.qty');
    	$cartSum = Yii::$app->session->get('cart.sum');
    	// Выводим в браузер файл views/<имя_контроллера>/cart.php
        return $this->render('cart', [
            'cartQuantity' => $cartQuantity,
            'cartSum' => $cartSum,
        ]);
    }
Код view

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

// Не используем никаких isset($_SESSION['cart']) - это логика для данных, ее выносим за пределы view (выше в контроллере)
// Получется шаблон, содержащий представление и готовые данные
<p>Товаров: <?= $cartQuantity; ?></p>
<p>На сумму: <?= $cartSum; ?></p>
Теперь при переходе по адресу action DisplayCart, т.е. в браузере при открытии URL /<имя_контроллера>/display-cart показывается страница с кодом указанного view, ко всему прочему обернутым в шаблон layout или другой view, например.

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

<div id="#cart"> <!-- уровень "выше" -->
  <!-- вывод данных корзины -->
</div> <!-- /уровень "выше" -->
Т.е. мы можем обновлять информацию о корзине простой перезагрузкой страницы - данные генерируются php и передаются непосредственно в браузер.

2. Динамическая реализация.

Т.к. перезагружать страницу каждый раз неудобно, используем AJAX для обновления корзины, т.е. отправляем XMLHttpRequest на сервер, обновляем не всю страницу, а только часть.

Самый простой способ - jQuery.

меняем код action для вывода view без шаблона

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

   // Данные получаем в контроллере, передаем в view
    public function actionDisplayCart
    {
    	$cartQuantity = Yii::$app->session->get('cart.qty');
    	$cartSum = Yii::$app->session->get('cart.sum');
    	// Заменяем render на renderPartial
        return $this->renderPartial('cart', [
            'cartQuantity' => $cartQuantity,
            'cartSum' => $cartSum,
        ]);
    }
Отправляем POST запрос к тому же action

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

$(document).ready(function(){
  $.post(
    '/<имя_контроллера>/display-cart',
    {
      // параметры, если есть
    },
  ).done(function(data) {
    // В содержимое элемента с id "cart" выводим ответ сервера.
    // В браузер попадет тот же вывод, что представлен в шаблоне views/<имя_контроллера>/cart.php
    $('#cart').html(data);
  }).fail(function() {
    $('#cart').html('Ошибка обновления корзины');
  });
});
В результате содержимое тэга

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

<div id="cart"><!-- содержимое тут --></div>
заменится выводом

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

  <p>Товаров: <?= $cartQuantity; ?></p>
  <p>На сумму: <?= $cartSum; ?></p>
с подставленными значениями количества и суммы

В этом случае данные генерируются php, передаются в браузер, но частично и за обновление на странице отвечает JavaScript.

3. Более продвинутая реализация, замена только данных.

Контроллер

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

   // Данные получаем в контроллере, возвращаем json
   // {"cartQantity": 3, "cartSum": 100500}
    public function actionDisplayCart
    {
    	$cartQuantity = Yii::$app->session->get('cart.qty');
    	$cartSum = Yii::$app->session->get('cart.sum');
    	$request = Yii::$app->request;
        if ($request->isAjax)
        {
            \Yii::$app->response->format = 'json';            
            // Возвращаем массив с ключами
            return [
            	'cartQuantity' => $cartQuantity,
            	'cartSum' => $cartSum,
            ];
        }
        else
        {
            throw new BadRequestHttpException('Запрос должен осуществляться через AJAX.');
        }
    }

Представление выглядит как

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

<div id="cart">
  <p>Количество: <span class="quantity">0</span></p>
  <p>На сумму: <span class="sum">0</span></p>
</div>

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

$(document).ready(function(){
  $.post(
    '/<имя_контроллера>/display-cart',
    {
      // параметры, если есть
    },
  ).done(function(data) {
    // Меняем только данные
    $('#cart .quantity').html(data.cartQuantity);
    $('#cart .sum').html(data.cartSum);
  }).fail(function() {
    $('#cart').html('Ошибка обновления корзины');
  });
});
Для этого способа придумано множество библиотек, которые автоматически подставляют ответ сервера сразу в html шаблон, например в таком формате

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

<div id="cart">
  <p>Количество: {cart.quantity}</p>
  <p>На сумму: {cart.sum}</p>
</div>
и т.д.
Последний раз редактировалось nickdenry 2018.04.02, 02:58, всего редактировалось 1 раз.
Ответственные программисты с высоким уровнем технического долга (c)

rosolovsky
Сообщения: 119
Зарегистрирован: 2014.06.23, 11:44
Откуда: Украина

Re: return в контроллере

Сообщение rosolovsky » 2018.04.01, 18:16

nickdenry, вот это расписал. Спасибо!!! Вот по факту у меня и получился третий вариант.

nickdenry
Сообщения: 98
Зарегистрирован: 2015.10.28, 04:55

Re: return в контроллере

Сообщение nickdenry » 2018.04.02, 00:21

rosolovsky писал(а):
2018.04.01, 18:16
nickdenry, вот это расписал. Спасибо!!! Вот по факту у меня и получился третий вариант.
В "боевом" коде данные для корзины лучше обернуть в модель (не AR-модель), даже для работы с сессиями. AJAX сделать get или post в зависимости от типа операции. Если только получение данных без изменений - лучше get.
Ответственные программисты с высоким уровнем технического долга (c)

rosolovsky
Сообщения: 119
Зарегистрирован: 2014.06.23, 11:44
Откуда: Украина

Re: return в контроллере

Сообщение rosolovsky » 2018.04.02, 10:39

nickdenry писал(а):
2018.04.02, 00:21
В "боевом" коде данные для корзины лучше обернуть в модель (не AR-модель), даже для работы с сессиями.
Ммм, то есть работать не с сессией напрямую а с моделью? С сессией удобно, фактически данные сессии есть везде, и в контроллере и во вью и в модели. Не надо их гонять туда сюда согласно MVC.

nickdenry
Сообщения: 98
Зарегистрирован: 2015.10.28, 04:55

Re: return в контроллере

Сообщение nickdenry » 2018.04.02, 13:55

rosolovsky писал(а):
2018.04.02, 10:39
nickdenry писал(а):
2018.04.02, 00:21
В "боевом" коде данные для корзины лучше обернуть в модель (не AR-модель), даже для работы с сессиями.
Ммм, то есть работать не с сессией напрямую а с моделью?
Основная идея-минимум - собрать в одном месте строковые идентификаторы, это если к текущему частному случаю. Если в общем - избавиться в коде от мест, которые нужно менять многократно. Код, опять же, выдуман для примера.

Т.е. имеем тот же код

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

$_SESSION['cart.qty']
или в случае с Yii2

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

Yii::$app->session->get('cart.qty');
Но
С сессией удобно, фактически данные сессии есть везде, и в контроллере и во вью и в модели. Не надо их гонять туда сюда согласно MVC.
повторяющийся многократно, в модели, контроллере, представлении:

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

Yii::$app->session->get('cart.qty'); // В AR модели для обработки, например, устанавливаем $_SESSION['cart.qty'] = 5;
Yii::$app->session->get('cart.qty'); // В контроллере по каким-то причинам
Yii::$app->session->get('cart.qty'); // В представлении, для отображения
Теперь, если по любой причине строчку 'cart.qty' нужно поменять (например привязываем биндинг в JS и название конфликтует), то менять ее нужно в трех местах. А в реальном проекте она размазана по двум контроллерам в пяти action (показ корзины, статистика, добавление товара в корзину, удаление товара из корзины, пересчет по кнопке "комплект" и т.д.) и двум view. Шанс забыть поменять хотя бы в одном месте близок к 100%, не говоря о том, если таких мест 200 или 1000.

Поэтому минимально - хелпер.

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

<?php

namespace common\helpers;

use Yii;

class CartHelper
{
    const CART_QUANTITY = 'cart.qty';
    const CART_SUMM = 'cart.sum';
    
    // Получаем количество
    public function getQuantity() 
    {
         return Yii::$app->session-get(static::CART_QUANTITY);
    }    
    // Устанавливаем количество
    public function setQuantity($quantity) 
    {
        Yii::$app->session->set(static::CART_QUANTITY, $quantity);
    }    
    // Получаем сумму
    public function getSum() 
    {
        return Yii::$app->session-get(static::CART_SUMM);
    }    
    // Устанавливаем сумму
    public function setSum($sum) 
    {
        Yii::$app->session->set(static::CART_SUMM, $sum);
    }  
}
?> 	
Тогда в контроллере, модели, представлении:

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

CartHelper::getQuantity();
CartHelper::setQuantity(500);
Если идти по более полноценному пути, то модель

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

<?php

namespace common\models;

use Yii;

class Cart
{
    private $quantity;
    private $sum;

    // Получаем количество
    public function getQuantity()
    {
        return $this->quantity;
    }
    // Устанавливаем количество
    public function getQuantity($quantity)
    {
        $this->quantity = $quantity;
    }
    // Получаем сумму
    public function getSum()
    {
        return $this->sum;
    }
    // Устанавливаем сумму
    public function setSum($sum)
    {
        $this->sum = $sum;
    }
}

?>
откуда эта модель будет получать данные - из сессий или key-value хранилища уже не важно.

но в контроллере можно будет возвращать объект целиком, т.е.

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

   // Данные получаем в контроллере, возвращаем json
   // {"cartQantity": 3, "cartSum": 100500}
    public function actionDisplayCart
    {
    	// Объект корзины
	$cart = new Cart();
	// Загрузка, например
	$cart->loadFromSession();
	// или
	$cart->loadFromRedis();
	// или
	CartLoader::load($cart, <storage>);
	// и т.д.
   
        if ($request->isAjax)
        {
            \Yii::$app->response->format = 'json';            
            // Возвращаем объект с данными
            return $cart;
        }
        else
        {
            throw new BadRequestHttpException('Запрос должен осуществляться через AJAX.');
        }
    }

Ответственные программисты с высоким уровнем технического долга (c)

Ответить