Laravel 5: используйте отправленные сервером события для отправки сообщений в браузер

Я смотрел на Это ТАК вопрос а также Эта статья попробовать и реализовать отправленные сервером события в Ларавел 5. Хотя, между двумя, я не могу понять, как push-обновления для клиента основаны на событии. В этом случае событие — это исключение ClientException. ClientException в моем приложении вызвано ошибкой, сделанной пользователем. Когда один из них выброшен, я хочу отправить обновление клиенту, который заполняет универсальную панель ошибок.

Вот что у меня в лезвии:

<script>
var source = new EventSource("{{ route('globalmessages') }}");
source.addEventListener("message", function(e)
{
$("#errors").html(e);
}, false);
</script>

<div id="errors">
</div>

EventSource успешно выполняет действие контроллера:

public function pushErrors()
{
$response = new StreamedResponse(function()
{
$errors = ???; // How do I populate this
if (!empty($error))
{
echo $error;
ob_flush();
flush();
}
else
{
\Log::info("No errors to push");
}
});

$response->headers->set('Content-Type', 'text/event-stream');
return $response;
}

И обработка ошибок происходит в Handler.php@render:

if ($e instanceof ClientException)
{
$message = $e->getMessage(); // Need to send this to client
return \Response::json([]);
}
else
{
return parent::render($request, $e);
}

Итак, что мне нужно сделать, так это как-то выдавать ошибки в маршрут контроллера. Я пытался использовать синглтон, но я не мог заставить это работать. Я пытался дать контроллеру поле для его хранения, но оно всегда было пустым.

Кроме того, эта текущая реализация, кажется, выполняется каждые 5 секунд. Я хотел бы иметь возможность вызывать функцию, когда выбрасывается ClientException для отправки обновления.

Спасибо за любую помощь.

РЕДАКТИРОВАТЬ:

Еще немного информации:

Вот действие контроллера, которое отображает мою страницу:

class HomeController
{
public function getHome()
{
return view('home');
}
}

Мой контроллер использует бесконечный цикл:

class MainController extends RouteController
{
public function pushErrors()
{
$response = new StreamedResponse();
$response->headers->set('Content-Type', 'text/event-stream');
$response->headers->set('Cach-Control', 'no-cache');

$response->setCallback(
function()
{
while (true)
{
$error = MessageQueue::dequeue();
if (!empty($error))
{
echo 'data: ' . $error. "\n\n";
ob_flush();
flush();
}
sleep(1);
}
});

return $response;
}
}

Если я уберу цикл while, этот код сработает, так как сообщение будет отправлено, соединение разорвано, затем восстановлено и снова отправлено. С циклом while страница просто зависает.

Мои маршруты:

+--------+----------+----------------+----------------+--------------------------------------------------+------------+
| Domain | Method   | URI            | Name           | Action                                           | Middleware |
+--------+----------+----------------+----------------+--------------------------------------------------+------------+
|        | GET|HEAD | globalmessages | globalmessages | App\Http\Controllers\MainController@pushErrors   |            |
|        | GET|HEAD | /              | /              | App\Http\Controllers\HomeController@getHome      |            |
|        | POST     | login          | login          | App\Http\Controllers\HomeController@postLogin    |            |
+--------+----------+----------------+----------------+--------------------------------------------------+------------+

HomeController @ postLogin — это маршрут, к которому я обращаюсь, который генерирует исключение ClientException.

1

Решение

В вашем контроллере отсутствует ключевая функция из статьи. В этой статье контроллер выполняет бесконечный цикл. В бесконечном цикле данные проверяются на сходство: если данные изменились, буфер очищается и записывается, чтобы клиент JS мог его прочитать.

Как бы я это реализовал, чтобы создать таблицу, которая содержит все ваши ServerSentEvents, затем в моем запросе контроллера из этой таблицы и получить строки, которые еще не были отправлены.

В этом примере мы проверим, есть ли какие-либо данные в нашем $data массив и отправить его клиенту, если есть.

DB::table('ServerSentEvents')->where('sent', 0)->get();

Вернет пустой массив, если в запросе не будет строк.

public function pushErrors() {

$response = new Symfony\Component\HttpFoundation\StreamedResponse(function() {

$data = $this->getData();

while (true) {
if (!empty($data)) {
echo 'data: ' . json_encode($data) . "\n\n";
ob_flush();
flush();

/* update the table rows as sent */
$ids = [];
foreach($data as $event){
$ids[] = $event->id;
}
DB::table('ServerSentEvents')->whereIn('id', $ids)->update('sent', 1);
}

//sleep for 3 seconds
sleep(3);

//requery for new events that need to be sent
$data = $this->getData();
}

});

$response->headers->set('Content-Type', 'text/event-stream');
return $response;
}

public function getData(){
$data = DB::table('ServerSentEvents')->where('sent', 0)->get();
return $data;
}

Вам также необходимо вставить новые строки в вашу базу данных при возникновении исключения ClientException:

if ($e instanceof ClientException)
{
DB::table('ServerSentEvents')->insert(['message' => $e->getMessage(), 'sent' => 0]);
}
else
{
return parent::render($request, $e);
}

В зависимости от того, какую информацию вы хотите получить, основываясь на ваших событиях, вам может потребоваться добавить или удалить дополнительные столбцы в таблице ServerSentEvents.

Таблица ServerSentEvents — это таблица, которую вам нужно будет создать, я не предоставил здесь структуру, потому что может быть больше информации, которую вы хотите сохранить в строках, и в этом заключается общая концепция извлечения данных из базы данных.

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]