Symfony response-›send() возвращает ошибку 500

В symfony3 я пытаюсь сохранить куки. Но когда cookie создается в первый раз, он возвращает ошибку 500.

Это код для создания файла cookie:

protected function checkForCartId(Request $request)
{
    if(!empty($this->uniqueCartId)) {
        return $this->uniqueCartId;
    }

    $response = new Response();
    if(!$request->cookies->has('uniqueCartId')) {
        $this->uniqueCartId = uniqid();
        $response->headers->setCookie(new Cookie('uniqueCartId', $this->uniqueCartId));
        $response->send();

        return $this->uniqueCartId;
    }

    return $request->cookies->get('uniqueCartId');
}

Когда я удаляю строку $response->send(), ошибка исчезает, но файл cookie не сохраняется.

Любые идеи?

=========== ИЗМЕНИТЬ =============

Файл журнала (после повторного получения ошибки)

[2018-04-22 14:47:26] request.INFO: Matched route "homepage". {"route":"homepage","route_parameters":{"_controller":"AppBundle\\Controller\\DefaultController::indexAction","_route":"homepage"},"request_uri":"http://127.0.0.1:8000/","method":"GET"} []
[2018-04-22 14:47:26] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticators":1} []
[2018-04-22 14:47:26] security.DEBUG: Calling getCredentials() on guard configurator. {"firewall_key":"main","authenticator":"AppBundle\\Security\\LoginFormAuthenticator"} []
[2018-04-22 14:47:26] security.INFO: Populated the TokenStorage with an anonymous Token. [] []
[2018-04-22 14:47:26] doctrine.DEBUG: SELECT t0.id AS id_1, t0.url AS url_2, t0.title AS title_3, t0.content AS content_4, t0.seo_title AS seo_title_5, t0.seo_description AS seo_description_6, t0.published AS published_7 FROM pages t0 WHERE t0.url = ? LIMIT 1 ["/"] []
[2018-04-22 14:47:26] doctrine.DEBUG: SELECT p0_.id AS id_0, p0_.slug AS slug_1, p0_.name AS name_2, p0_.is_published AS is_published_3, p0_.description AS description_4, p0_.price AS price_5, p0_.discount_price AS discount_price_6, p0_.page_title AS page_title_7, p0_.seo_title AS seo_title_8, p0_.seo_description AS seo_description_9 FROM product p0_ WHERE p0_.discount_price > 0 AND p0_.is_published = ? [1] []
[2018-04-22 14:47:26] doctrine.DEBUG: SELECT h0_.id AS id_0, h0_.title AS title_1, h0_.subtitle AS subtitle_2, h0_.image AS image_3, h0_.button_text AS button_text_4, h0_.button_link AS button_link_5, h0_.active AS active_6 FROM homepage_banner h0_ WHERE h0_.active = ? [1] []
[2018-04-22 14:47:26] doctrine.DEBUG: SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.slug AS slug_2, c0_.page_title AS page_title_3, c0_.description AS description_4, c0_.parent_id AS parent_id_5, c0_.created_by AS created_by_6 FROM category c0_ WHERE c0_.parent_id IS NULL [] []
[2018-04-22 14:47:26] doctrine.DEBUG: SELECT t0.id AS id_1, t0.name AS name_2, t0.slug AS slug_3, t0.page_title AS page_title_4, t0.description AS description_5, t0.parent_id AS parent_id_6, t0.created_by AS created_by_7 FROM category t0 WHERE t0.parent_id = ? [1] []
[2018-04-22 14:47:26] doctrine.DEBUG: SELECT t0.id AS id_1, t0.name AS name_2, t0.slug AS slug_3, t0.page_title AS page_title_4, t0.description AS description_5, t0.parent_id AS parent_id_6, t0.created_by AS created_by_7 FROM category t0 WHERE t0.parent_id = ? [2] []
[2018-04-22 14:47:26] doctrine.DEBUG: SELECT s0_.id AS id_0, s0_.cart_id AS cart_id_1, s0_.quantity AS quantity_2, s0_.price AS price_3, s0_.discount_price AS discount_price_4, s0_.ordered AS ordered_5, s0_.created_at AS created_at_6, s0_.product_id AS product_id_7, s0_.user_id AS user_id_8 FROM shopping_cart s0_ WHERE s0_.cart_id = ? ["5adc845e728a9"] []
[2018-04-22 14:47:26] request.CRITICAL: Uncaught PHP Exception UnexpectedValueException: "The Response content must be a string or object implementing __toString(), "boolean" given." at H:\Websites\CMS\vendor\symfony\symfony\src\Symfony\Component\HttpFoundation\Response.php line 406 {"exception":"[object] (UnexpectedValueException(code: 0): The Response content must be a string or object implementing __toString(), \"boolean\" given. at H:\\Websites\\CMS\\vendor\\symfony\\symfony\\src\\Symfony\\Component\\HttpFoundation\\Response.php:406)"} []
[2018-04-22 14:47:27] request.INFO: Matched route "_wdt". {"route":"_wdt","route_parameters":{"_controller":"web_profiler.controller.profiler:toolbarAction","token":"49bddb","_route":"_wdt"},"request_uri":"http://127.0.0.1:8000/_wdt/49bddb","method":"GET"} []

================= EDIT ================ Действие контроллера:

public function getCartItems(Request $request)
{
    $shoppingCartRepository = $this->em->getRepository(ShoppingCart::class);

    return $this->render('checkout/cart.html.twig', [
        'cartItems' => $shoppingCartRepository->findAllProductsInCart($this->getUser(), $this->checkForCartId($request))
    ]);
}

================= РЕДАКТИРОВАНИЕ ===============

Моя новая функция getCartItems:

public function getCartItems(Request $request)
{
    $shoppingCartRepository = $this->em->getRepository(ShoppingCart::class);

    if(!$request->cookies->has('uniqueCartId')) {
        $this->checkForCartId($request);
    }

    $response = $this->render('checkout/cart.html.twig', [
        'cartItems' => $shoppingCartRepository->findAllProductsInCart($this->getUser(), $this->checkForCartId($request))
    ]);

    $response->headers->setCookie(new Cookie('uniqueCartId', $this->checkForCartId($request)));
    return $response;
}

============= РЕШЕНИЕ =============

public function getCartItems(Request $request)
{
    $shoppingCartRepository = $this->em->getRepository(ShoppingCart::class);

    $response = new Response();
    if(!$request->cookies->has('uniqueCartId')) {
        $response->headers->setCookie(new Cookie('uniqueCartId', $this->checkForCartId($request)));
        $response->sendHeaders();
    }

    return $this->render('checkout/cart.html.twig', [
        'cartItems' => $shoppingCartRepository->findAllProductsInCart($this->getUser(), $this->checkForCartId($request))
    ], $response);
}

person Refilon    schedule 22.04.2018    source источник
comment
В вашем файле журнала в var/log/ должна быть дополнительная информация, можете ли вы добавить ее к вопросу? Я подозреваю, что вы увидите Невозможно изменить информацию заголовка — заголовки уже куда-то отправлены.   -  person Daniel Alexandrov    schedule 22.04.2018
comment
@DanielAlexandrov Я вставил файл журнала.   -  person Refilon    schedule 22.04.2018
comment
Попробуйте найти ошибку, поставив блок try...catch...   -  person Yash Parekh    schedule 22.04.2018
comment
Вы отправляете логическое значение в качестве полезной нагрузки ответа куда-то (строка перед последней в вашем журнале). Я не могу вывести поток вашего приложения только из этого метода, но вы не должны отправлять ответ из этой функции, потому что я предполагаю, что позже вы делаете это снова. Как правило, если вы ожидаете результата от метода/функции, избегайте побочных эффектов в нем (например, отправки запроса в вашем случае), потому что это часто приводит к таким ошибкам.   -  person Daniel Alexandrov    schedule 22.04.2018
comment
@DanielAlexandrov Когда я удаляю строку $response-›send(), ошибка не возникает. Я просто хочу знать, как я могу сохранить файл cookie, не получая этой ошибки. Я не знаю, как настроить его по-другому. Я могу попробовать это в javascript, но это опасно?   -  person Refilon    schedule 23.04.2018
comment
Откуда вы вызываете этот метод? Что происходит после проверки? Если вы можете вернуть значение cookie и позже установить его для существующего запроса, все будет работать нормально. Однако, не видя кода вашего контроллера, будет сложно дать вам более конкретную информацию.   -  person Daniel Alexandrov    schedule 23.04.2018
comment
Пожалуйста, обновите свой вопрос с кодом контроллера. Поскольку Symfony Controller требует, чтобы объект ответа возвращался, и неявно вызывает метод send возвращаемого объекта ответа, вам следует избегать делать это в сервисе, поскольку заголовки уже были бы отправлены им, когда Symfony попытается это сделать.   -  person Will B.    schedule 23.04.2018
comment
@fyrye обновил код контроллера   -  person Refilon    schedule 24.04.2018
comment
@DanielAlexandrov Я вызываю этот метод из контроллера (см. обновленный пост). И getCartItems() я вызываю прямо в своем шаблоне ветки.   -  person Refilon    schedule 24.04.2018


Ответы (1)


Вам нужно установить файл cookie для объекта Response, который в вашем случае является шаблоном отображаемой ветки. После небольшого рефакторинга ваш код может выглядеть так:

protected function getCardId(Request $request)
{
    if(!$request->cookies->has('uniqueCartId')) {
        $this->uniqueCartId = uniqid();
        return ['is_new' => true, 'unique_id' => $this->uniqueCartId];
    } 

    $this->uniqueId = $request->cookies->get('uniqueCartId');
    return ['is_new' => false, 'unique_id' => $request->cookies->get('uniqueCartId')];
}

...

public function getCartItems(Request $request)
{
    $shoppingCartRepository = $this->em->getRepository(ShoppingCart::class);

    $cartId = $this->getCardId($request);

    $response = $this->render('checkout/cart.html.twig', [
        'cartItems' => $shoppingCartRepository->findAllProductsInCart($this->getUser(), $cartId['uniqueId'])
    ]);

    if($cartId['is_new']) {
        $response->headers->setCookie(new Cookie('uniqueCartId', $this->uniqueCartId));
    }
    return $response;
}

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

person Daniel Alexandrov    schedule 24.04.2018
comment
Привет Даниил, спасибо за ваш ответ. Я уже пробовал это, но при таком способе cookie не создается. - person Refilon; 24.04.2018
comment
Ты уверен? Вы удалили $response-›send(); из вашего метода checkForCartId? Можете попробовать код из моего примера, включая измененный checkForCartId/getCardId? - person Daniel Alexandrov; 24.04.2018
comment
Я использовал ваш код, и это дамп из app.request.cookies ParameterBag {#71 ▼ #parameters: array:1 [▼ "PHPSESSID" => "nljafbnff634j9vl69cvd07bm3" ] } Как видите, cookie не установлен. Ошибка ушла, т. - person Refilon; 24.04.2018
comment
Ой, подождите, я вижу uniqueCartId в Google Chrome, но не в режиме инкогнито Google Chrome. Есть ли способ установить файлы cookie в режиме инкогнито? - person Refilon; 24.04.2018
comment
@Refilon chrome в основном устанавливает кеш во временный каталог, который очищается при закрытии браузера и отключает файлы cookie и файлы cookie Flash. Таким образом, вы не можете сохранять данные cookie, используя режим инкогнито. См.: Что именно делает «Режим инкогнито» Chrome? и en.wikipedia.org/wiki/Privacy_mode - person Will B.; 24.04.2018
comment
@fyrye Это не совсем так. Потому что, когда я использую метод $response->send(), он устанавливает cookie в режиме инкогнито. Режим инкогнито использует файлы cookie и устанавливает их также во временную папку. Поэтому, когда вы закроете браузер, он также удалит файлы cookie из временной папки. - person Refilon; 25.04.2018
comment
@Refilon Пожалуйста, прочитайте статьи, на которые я ссылался, которые объясняют то, что я упомянул. Сохранение данных cookie, в отличие от использования данных cookie, делает их функционально такими же, как сеанс. - person Will B.; 25.04.2018
comment
@fyrye Да, так и должно быть. Но он не сохраняет файл cookie во временной папке, когда я пробую этот метод. Когда я использую $response->send(), он сохраняет файл cookie во временной папке. Поэтому мне интересно, почему он это делает? - person Refilon; 25.04.2018
comment
@Refilon Покажите нам код, который вы используете сейчас, чтобы мы могли воспроизвести проблему. На моем сервере dev apache состояние cookie для приведенного выше ответа отлично работает в режиме инкогнито, проверено путем комментирования вызова setCookie и многократного обновления страницы в Symfony 3.4.8. - person Will B.; 25.04.2018
comment
@fyrye Я только что добавил новую функцию. Это не работает и не устанавливает cookie. Ни в обычном google chrome, ни в google chrome инкогнито - person Refilon; 27.04.2018
comment
@Refilon в отношении вашего решения, имейте в виду, что ваш ответ ветки НЕ сможет отправить свои собственные заголовки, поскольку заголовки уже будут отправлены вашим ответом на файл cookie. См.: https://github.com/symfony/http-foundation/blob/v3.4.8/Response.php#L325 Вы можете проверить это, изменив render на redirectToRoute, хотя обычно это также добавляет метатег обновления. - person Will B.; 27.04.2018
comment
@fyrye Спасибо. Я думаю, он отправляет заголовки? Потому что он сохраняет файл cookie. Но теперь все работает - person Refilon; 29.04.2018