-
SmokeyCharizard
Junior Member
-
Posts: 25
Threads: 4
Joined: Feb 2020
Reputation:
0
(02-24-2020, 02:09 PM)dave friend Wrote: Because the model is loaded by the controller it should be possible to send the message from the model. The key is sending a proper message. You cannot go to another controller until the processing is done though. But I can send them during the process, yea? And what would it look like? This is what I have been trying to use in my model, but it dosen't work (Works in the controller, though, until I call the model):
Code: header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");
header("Connection: keep-alive");
echo "Products have been found in that range!";
flush();
-
dave friend
Posting Freak
-
Posts: 1,015
Threads: 15
Joined: Jun 2015
Reputation:
50
I built a CodeIgniter version of the server-sent-events sample. It's modified from the Github example a tiny bit but works as expected.
Controller: Eventsource.php
PHP Code: <?php
class Eventsource extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->model('eventsource_m'); }
public function index() { $this->load->view('eventsource_v'); }
public function sse() { $this->eventsource_m->process(); } }
View: eventsource_v.php
PHP Code: <html> <head> <meta charset="UTF-8"> <title>Server-sent events demo</title> </head> <body> <button>Close the connection</button>
<ul> </ul>
<script> var button = document.querySelector('button'); var evtSource = new EventSource(<?= "'".base_url('eventsource/sse', 'https')."'"; ?> ); console.log(evtSource.withCredentials); console.log(evtSource.readyState); console.log(evtSource.url); var eventList = document.querySelector('ul');
evtSource.onopen = function () { console.log("Connection to server opened."); };
evtSource.onmessage = function (e) { var newElement = document.createElement("li");
newElement.textContent = "message: " + e.data; eventList.appendChild(newElement); };
evtSource.onerror = function () { console.log("EventSource failed."); };
button.onclick = function () { console.log('Connection closed'); evtSource.close(); };
</script> </body> </html>
Model: Eventsource_m.php
PHP Code: <?php
class Eventsource_m extends CI_Model { public function process() { date_default_timezone_set("America/New_York"); header("Content-Type: text/event-stream");
// 1 is always true, so repeat the while loop forever (aka event-loop) while (1) { $curDate = date(DATE_ISO8601);
// Send a simple message at 2 second intervals. echo 'data: This is a message at time ' . $curDate, "\n\n";
// flush the output buffer and send echoed messages to the browser while (ob_get_level() > 0) { ob_end_flush(); } flush();
// break the loop if the client aborted the connection (closed the page) if (connection_aborted()) { break; }
// sleep for 2 seconds before running the loop again sleep(2); } } }
Direct the browser to https://your_domain/eventsource and see a new message every two seconds. Clearly the EventSource messages can be sent from a model.
The only tricky bit was in the JavaScript with setting the URL for EventSource.
PHP Code: var evtSource = new EventSource(<?= "'".base_url('eventsource/sse', 'https')."'"; ?> );
Notice how I had to explicitly concatenate single quotes around the output of base_url(). Without those JavaScript could not properly evaluate the argument. This is a common issue when combining PHP and JavaScript.
Using the browsers JavaScript console is important when trying to figure out what, if any, failures are occurring. I spotted the above issue right away.
Hopefully, all this helps.
It's not clear to me what you want to happen when processing is complete.
Based on your view it looks like you want to send data back to drive a progress-bar. If so, a message value equal to "100" would make for a good signal to disconnect the EventSource and move on to whatever is supposed to happen.
-
SmokeyCharizard
Junior Member
-
Posts: 25
Threads: 4
Joined: Feb 2020
Reputation:
0
02-24-2020, 05:39 PM
(This post was last modified: 02-24-2020, 05:54 PM by SmokeyCharizard.)
(02-24-2020, 05:28 PM)dave friend Wrote: I built a CodeIgniter version of the server-sent-events sample. It's modified from the Github example a tiny bit but works as expected.
Controller: Eventsource.php
PHP Code: <?php
class Eventsource extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->model('eventsource_m'); }
public function index() { $this->load->view('eventsource_v'); }
public function sse() { $this->eventsource_m->process(); } }
View: eventsource_v.php
PHP Code: <html> <head> <meta charset="UTF-8"> <title>Server-sent events demo</title> </head> <body> <button>Close the connection</button>
<ul> </ul>
<script> var button = document.querySelector('button'); var evtSource = new EventSource(<?= "'".base_url('eventsource/sse', 'https')."'"; ?> ); console.log(evtSource.withCredentials); console.log(evtSource.readyState); console.log(evtSource.url); var eventList = document.querySelector('ul');
evtSource.onopen = function () { console.log("Connection to server opened."); };
evtSource.onmessage = function (e) { var newElement = document.createElement("li");
newElement.textContent = "message: " + e.data; eventList.appendChild(newElement); };
evtSource.onerror = function () { console.log("EventSource failed."); };
button.onclick = function () { console.log('Connection closed'); evtSource.close(); };
</script> </body> </html>
Model: Eventsource_m.php
PHP Code: <?php
class Eventsource_m extends CI_Model { public function process() { date_default_timezone_set("America/New_York"); header("Content-Type: text/event-stream");
// 1 is always true, so repeat the while loop forever (aka event-loop) while (1) { $curDate = date(DATE_ISO8601);
// Send a simple message at 2 second intervals. echo 'data: This is a message at time ' . $curDate, "\n\n";
// flush the output buffer and send echoed messages to the browser while (ob_get_level() > 0) { ob_end_flush(); } flush();
// break the loop if the client aborted the connection (closed the page) if (connection_aborted()) { break; }
// sleep for 2 seconds before running the loop again sleep(2); } } }
Direct the browser to https://your_domain/eventsource and see a new message every two seconds. Clearly the EventSource messages can be sent from a model.
The only tricky bit was in the JavaScript with setting the URL for EventSource.
PHP Code: var evtSource = new EventSource(<?= "'".base_url('eventsource/sse', 'https')."'"; ?> );
Notice how I had to explicitly concatenate single quotes around the output of base_url(). Without those JavaScript could not properly evaluate the argument. This is a common issue when combining PHP and JavaScript.
Using the browsers JavaScript console is important when trying to figure out what, if any, failures are occurring. I spotted the above issue right away.
Hopefully, all this helps.
It's not clear to me what you want to happen when processing is complete.
Based on your view it looks like you want to send data back to drive a progress-bar. If so, a message value equal to "100" would make for a good signal to disconnect the EventSource and move on to whatever is supposed to happen. Thank you so much! I'm going to try this soon! I suppose I should have mentioned that my cfa_model makes several requests to a SOAP api in the data conversion. While I'm working on implementing your ideas, would these calls effect what we are trying to do in any way? If so, how do I work around them?
Again, thank you a TON!
(02-24-2020, 05:28 PM)dave friend Wrote: I built a CodeIgniter version of the server-sent-events sample. It's modified from the Github example a tiny bit but works as expected.
Controller: Eventsource.php
PHP Code: <?php
class Eventsource extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->model('eventsource_m'); }
public function index() { $this->load->view('eventsource_v'); }
public function sse() { $this->eventsource_m->process(); } }
View: eventsource_v.php
PHP Code: <html> <head> <meta charset="UTF-8"> <title>Server-sent events demo</title> </head> <body> <button>Close the connection</button>
<ul> </ul>
<script> var button = document.querySelector('button'); var evtSource = new EventSource(<?= "'".base_url('eventsource/sse', 'https')."'"; ?> ); console.log(evtSource.withCredentials); console.log(evtSource.readyState); console.log(evtSource.url); var eventList = document.querySelector('ul');
evtSource.onopen = function () { console.log("Connection to server opened."); };
evtSource.onmessage = function (e) { var newElement = document.createElement("li");
newElement.textContent = "message: " + e.data; eventList.appendChild(newElement); };
evtSource.onerror = function () { console.log("EventSource failed."); };
button.onclick = function () { console.log('Connection closed'); evtSource.close(); };
</script> </body> </html>
Model: Eventsource_m.php
PHP Code: <?php
class Eventsource_m extends CI_Model { public function process() { date_default_timezone_set("America/New_York"); header("Content-Type: text/event-stream");
// 1 is always true, so repeat the while loop forever (aka event-loop) while (1) { $curDate = date(DATE_ISO8601);
// Send a simple message at 2 second intervals. echo 'data: This is a message at time ' . $curDate, "\n\n";
// flush the output buffer and send echoed messages to the browser while (ob_get_level() > 0) { ob_end_flush(); } flush();
// break the loop if the client aborted the connection (closed the page) if (connection_aborted()) { break; }
// sleep for 2 seconds before running the loop again sleep(2); } } }
Direct the browser to https://your_domain/eventsource and see a new message every two seconds. Clearly the EventSource messages can be sent from a model.
The only tricky bit was in the JavaScript with setting the URL for EventSource.
PHP Code: var evtSource = new EventSource(<?= "'".base_url('eventsource/sse', 'https')."'"; ?> );
Notice how I had to explicitly concatenate single quotes around the output of base_url(). Without those JavaScript could not properly evaluate the argument. This is a common issue when combining PHP and JavaScript.
Using the browsers JavaScript console is important when trying to figure out what, if any, failures are occurring. I spotted the above issue right away.
Hopefully, all this helps.
It's not clear to me what you want to happen when processing is complete.
Based on your view it looks like you want to send data back to drive a progress-bar. If so, a message value equal to "100" would make for a good signal to disconnect the EventSource and move on to whatever is supposed to happen. So I just tried your code out, and when I submit the form, a white page loads with the timestamp being loaded periodically.
data: This is a message at time 2020-02-24T19:49:21-0500
data: This is a message at time 2020-02-24T19:49:25-0500
data: This is a message at time 2020-02-24T19:49:29-0500
And so on.
This is good, but I want these tamestamps to be added to the original page, the one with the form, and for these timestamps to be updates the appeared beneath the submit button. It seems to be returning a new page instead...
Again, thanks for your help, Dave! Do you think you might know what is going wrong?
-
SmokeyCharizard
Junior Member
-
Posts: 25
Threads: 4
Joined: Feb 2020
Reputation:
0
(02-24-2020, 05:28 PM)dave friend Wrote: I built a CodeIgniter version of the server-sent-events sample. It's modified from the Github example a tiny bit but works as expected.
Controller: Eventsource.php
PHP Code: <?php
class Eventsource extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->model('eventsource_m'); }
public function index() { $this->load->view('eventsource_v'); }
public function sse() { $this->eventsource_m->process(); } }
View: eventsource_v.php
PHP Code: <html> <head> <meta charset="UTF-8"> <title>Server-sent events demo</title> </head> <body> <button>Close the connection</button>
<ul> </ul>
<script> var button = document.querySelector('button'); var evtSource = new EventSource(<?= "'".base_url('eventsource/sse', 'https')."'"; ?> ); console.log(evtSource.withCredentials); console.log(evtSource.readyState); console.log(evtSource.url); var eventList = document.querySelector('ul');
evtSource.onopen = function () { console.log("Connection to server opened."); };
evtSource.onmessage = function (e) { var newElement = document.createElement("li");
newElement.textContent = "message: " + e.data; eventList.appendChild(newElement); };
evtSource.onerror = function () { console.log("EventSource failed."); };
button.onclick = function () { console.log('Connection closed'); evtSource.close(); };
</script> </body> </html>
Model: Eventsource_m.php
PHP Code: <?php
class Eventsource_m extends CI_Model { public function process() { date_default_timezone_set("America/New_York"); header("Content-Type: text/event-stream");
// 1 is always true, so repeat the while loop forever (aka event-loop) while (1) { $curDate = date(DATE_ISO8601);
// Send a simple message at 2 second intervals. echo 'data: This is a message at time ' . $curDate, "\n\n";
// flush the output buffer and send echoed messages to the browser while (ob_get_level() > 0) { ob_end_flush(); } flush();
// break the loop if the client aborted the connection (closed the page) if (connection_aborted()) { break; }
// sleep for 2 seconds before running the loop again sleep(2); } } }
Direct the browser to https://your_domain/eventsource and see a new message every two seconds. Clearly the EventSource messages can be sent from a model.
The only tricky bit was in the JavaScript with setting the URL for EventSource.
PHP Code: var evtSource = new EventSource(<?= "'".base_url('eventsource/sse', 'https')."'"; ?> );
Notice how I had to explicitly concatenate single quotes around the output of base_url(). Without those JavaScript could not properly evaluate the argument. This is a common issue when combining PHP and JavaScript.
Using the browsers JavaScript console is important when trying to figure out what, if any, failures are occurring. I spotted the above issue right away.
Hopefully, all this helps.
It's not clear to me what you want to happen when processing is complete.
Based on your view it looks like you want to send data back to drive a progress-bar. If so, a message value equal to "100" would make for a good signal to disconnect the EventSource and move on to whatever is supposed to happen. I also found another error after adding your code. I get this error on the line where we have ob_end_flush();
<h4>A PHP Error was encountered</h4>
<p>Severity: Warning</p>
<p>Message: Cannot modify header information - headers already sent by (output started at /opt/lampp/htdocs/codeigniter/application/models/cfa_model.php:139)</p>
<p>Filename: core/Common.php</p>
<p>Line Number: 570</p>
-
dave friend
Posting Freak
-
Posts: 1,015
Threads: 15
Joined: Jun 2015
Reputation:
50
(02-24-2020, 05:39 PM)SmokeyCharizard Wrote: So I just tried your code out, and when I submit the form, a white page loads with the timestamp being loaded periodically.
data: This is a message at time 2020-02-24T19:49:21-0500
data: This is a message at time 2020-02-24T19:49:25-0500
data: This is a message at time 2020-02-24T19:49:29-0500
And so on.
This is good, but I want these tamestamps to be added to the original page, the one with the form, and for these timestamps to be updates the appeared beneath the submit button. It seems to be returning a new page instead...
Again, thanks for your help, Dave! Do you think you might know what is going wrong?
Do you understand that when you submit a form the action URL (i.e. c_controller/classicFirearmByDate) is a redirect? That's why the page turns white. You have sent the browser to another controller.
The easiest fix requires more JavaScript. You need to catch the submit event and stop the normal HTML process by preventing the browser from doing the event. Then you use javascript to POST the data using the Fetch API. The argument to Fetch should be the action that you used for the form.
By doing all that you don't leave the page where you want to display the messages from EventSource.
Remove the $this->load->view(...) lines from C_controller::classicFirearmByDate()
As I said very early on in this thread, if you want to go to another page when processing is done, you will have to use javascript. This probably means redirecting from the Promise that Fetch returns. All that said, I have never combined Fetch and EventSource so I don't know what, if any, problems using them at the same time might cause. Happy Experimenting!
-
SmokeyCharizard
Junior Member
-
Posts: 25
Threads: 4
Joined: Feb 2020
Reputation:
0
(02-24-2020, 07:33 PM)dave friend Wrote: (02-24-2020, 05:39 PM)SmokeyCharizard Wrote: So I just tried your code out, and when I submit the form, a white page loads with the timestamp being loaded periodically.
data: This is a message at time 2020-02-24T19:49:21-0500
data: This is a message at time 2020-02-24T19:49:25-0500
data: This is a message at time 2020-02-24T19:49:29-0500
And so on.
This is good, but I want these tamestamps to be added to the original page, the one with the form, and for these timestamps to be updates the appeared beneath the submit button. It seems to be returning a new page instead...
Again, thanks for your help, Dave! Do you think you might know what is going wrong?
Do you understand that when you submit a form the action URL (i.e. c_controller/classicFirearmByDate) is a redirect? That's why the page turns white. You have sent the browser to another controller.
The easiest fix requires more JavaScript. You need to catch the submit event and stop the normal HTML process by preventing the browser from doing the event. Then you use javascript to POST the data using the Fetch API. The argument to Fetch should be the action that you used for the form.
By doing all that you don't leave the page where you want to display the messages from EventSource.
Remove the $this->load->view(...) lines from C_controller::classicFirearmByDate()
As I said very early on in this thread, if you want to go to another page when processing is done, you will have to use javascript. This probably means redirecting from the Promise that Fetch returns. All that said, I have never combined Fetch and EventSource so I don't know what, if any, problems using them at the same time might cause. Happy Experimenting!
Holy cow, thank you an absolute megaton, David! I'm working on implementing fetch now, but there seem to be a few caveats. How do I implement fetch in this instance? You've helped me a bunch, but can you help with this one last thing? Thanks!
-
dave friend
Posting Freak
-
Posts: 1,015
Threads: 15
Joined: Jun 2015
Reputation:
50
You should implement event.preventDefault() and Fetch('some_url') inside the submit event listener. That code can be added inside the <script> tags on the page that displays the form wehre your other JS is.
The documentation links I gave you should provide everything you need to sort this out. If not, there's always Google.
I don't think SOAP will interfere or complicate anything here. But it is hard to know without seeing the implementation.
|