Я смотрел на Это ТАК вопрос а также Эта статья попробовать и реализовать отправленные сервером события в Ларавел 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.
В вашем контроллере отсутствует ключевая функция из статьи. В этой статье контроллер выполняет бесконечный цикл. В бесконечном цикле данные проверяются на сходство: если данные изменились, буфер очищается и записывается, чтобы клиент 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 — это таблица, которую вам нужно будет создать, я не предоставил здесь структуру, потому что может быть больше информации, которую вы хотите сохранить в строках, и в этом заключается общая концепция извлечения данных из базы данных.
Других решений пока нет …