I have a controller which performs some extensive and time consuming operations and I'd like to show its partial results to user; I figure I'd have to deal with "progress bars", have looked at some examples and plugins, but they are far beyond my knowledge (at the moment!).
For now I'd be satisfied with 'echoing' the partial results to a view, in a simple way.
My controller:
..
foreach ($response->messages as $msg) {
..
..(do something)..
echo (partial results)."<br>";
}
So the question is, view is rendered only at the end of my loop, and partial results are shown at once, after a long wait of a blank screen.
I'd like that my 'echos' would appear "real time" during foreach loop. Is that a way to do that?
You probably need a flush() with ob_flush().
foreach ($response->messages as $msg) {
// ..(do something)..
echo (partial results) . "<br>";
ob_flush();
flush();
}
exit;
Note that you should call exit at the end of method - since 2.0.14 Yii does not allow echo i controller, so you need to manually stop further response processing.
Related
I got stuck with this problem, I found many posts but seemed it's not useful. So I post again here and hope someone can help me.
Let say I have 2 button, 1 is Start button and 1 is Stop button. When I press start will call ajax function which query very long time. I need when I press Stop will stop immediately this query, not execute anymore.
this is function used to call query and fetch row. (customize Mysqli.php)
public function fetchMultiRowset($params = array()) {
$data = array();
$mysqli = $this->_adapter->getConnection();
$mysqli->multi_query($this->bindParams($this->_sql, $params));
$thread_id = mysqli_thread_id($mysqli);
ignore_user_abort(true);
ob_start();
$index = 0;
do {
if ($result = $mysqli->store_result()) {
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
$data[$index] = $row;
$index++;
echo " ";
ob_flush();
flush();
}
$result->free();
}
}
while ($mysqli->more_results() && $mysqli->next_result());
ob_end_flush();
return $data;
}
Function in Model:
public function select_entries() {
$data = null;
try {
$db = Zend_Db_Adapter_Mysqlicustom::singleton();
$sql = "SELECT * FROM report LIMIT 2000000";
$data = $db->fetchMultiRowset($sql);
$db->closeConnection();
} catch (Exception $exc) {
}
return $data;
}
Controller:
public function testAction(){
$op = $this->report_test->select_entries();
}
In AJAX I used xhr.abort() to stop the AJAX function. But it still runs the query while AJAX was aborted.
How do I stop query? I used Zend Framework.
EDIT: I did not look in detail at your program, now I see that not the query itself is taking so long, but the reading of all the data. So just check every 1000 rows, if the ajax call is still active. Ajax Abort.
Solution in case of a long-running SQL-query:
You would have to allow the application to kill database queries, and you need to implement a more complex interaction between Client and Server, which could lead to security holes if done wrong.
The Start-Request should contain a session and a page id (secure id, so not 3 and 4 and 5 but a non-guessable but unique hash of some kind). The backend then connects this id with the query. This could be done in some extra table of the database, but also via comments in the SQL query, like "Session fid98a08u4j, Page 940jfmkvlz" => s:<session>p:<page>.
/* s:fid98a08u4jp:940jfmkvlz */ select * from ...
If the user presses "stop", you send session and page id to the server. The php-code then fetches the list of your running SQL Queries and searches for session and page and extracts the query id.
Then the php sends a
kill query <id>
to the MySQL-server.
This might lead to trouble when not using transactions, and this might damage replication. And even a kill query might take some time in the state 'killing'.
So be sure that you can and want not to split the long running query into subqueries, which check if the request is still valid every few seconds, or that you do not just want to kill the query for cosmetical reasons.
I'm coming from codeigniter background. Unlike codeigniter helper directory, i just created helper directory within app directory of Laravel. Just want to know how to execute query within this common function. Here is my codeigniter function.
function show_menu($primary_key_col, $parent_id, $sort_order)
{
$output = "";
$ci =& get_instance();
$ci->db->select("*");
$ci->db->where('is_active', "Y");
$ci->db->where('is_delete', "N");
$ci->db->where('parent_id', $parent_id);
($sort_order!="")?$ci->db->order_by($sort_order, "ASC"):"";
$query = $ci->db->get('tbl_cms_menus');
foreach ($query->result() as $row){
$output .= '<option value="'.$row->$primary_key_col.'">'.$indent.$row->menu_name.'</option>';
}
return $output;
}
I tried something like this in laravel file. but this code did't give me any result. Please tell me where i'm doing wrong in this code. thanks
function databaseTable()
{
$table = DB::table('tbl_cms_menus');
$get_rows = $table->get();
$count_rows = $table->count();
if($count_rows > 0){
foreach ($get_rows as $tbl)
{
echo $tbl->menu_name;
}
}
}
This code will rot so hard that it shipped pre-rotten.
But, if you want to just.. ram it into the app all dry like that.. then add something like this to your base controller class...
$whatever = crazyChainingStuff;
foreach ($whatever ...) { $topMenu .= ... }
View::share('topMenu', $topMenu);
If you want to learn how to write code that will do less damage to your company and your clients then I recommend starting by watching Uncle Bob's "Fundamentals" videos. At least the first 5-6. http://cleancoders.com
It looks like you are trying to generate a drop-down/select with some data from your database, in this case, you should pass the data required for the drop-down/select from your controller to the view where you have written your HTML, for example, in your view, you may have a select like this:
echo Form::select('cms_menu', $cms_menu, Input::old('cms_menu'));
Or this (If you are using Blade):
{{ Form::select('cms_menu', $cms_menu, Input::old('cms_menu')) }}
From your controller you should pass the $cms_menu which should contain the menu-items as an arrtay and to populate that array you may try something like this:
$menuItems = DB::table('tbl_cms_menus')->lists('menu_name','id');
return View::make('your_view_name', array('cms_menu' => $menuItems));
Also, you may use something like this:
// Assumed you have a Page model
$menuItems = Page::lists('menu_name', 'id');
return View::make('your_view_name', array('cms_menu' => $menuItems));
You may also read this article which is about building a menu from database using view composer (More Laravelish way). Read more about Form::select on documentation.
It was too late to give an answer. I was also from CodeIgniter background and when I learnt Laravel then first I try to find how can I write a query in Helper. My Team leader helped me.
I have converted your code in a helper function.
function show_menu($primary_key_col, $parent_id, $sort_order)
{
$query = DB::table('tbl_cms_menus')
->select('*')
->where('is_active', '=', 'Y')
->where('is_delete', '=', 'N')
->where('parent_id', '=', $parent_id);
($sort_order != "")? $query->orderBy($sort_order, "ASC") : "";
$resultData = $query->get()->toArray();
}
Here $resultData will be array format. Now, you can create a foreach loop according to your requirement.
Specific example I was working with:
http://api.soundcloud.com/users/dubstep/tracks.json?client_id=YOUR_CLIENT_ID
You'll get their first 50 tracks, but there is not next-href object like what you see in the xml version.
However, you can use offset and limit and it works as expected- but then I would need to "blindly" crawl through tracks until there are no more tracks, unlike with the XML version which gives you the "next page" of results. I wouldn't have even noticed it was paginated except by chance when I was searching the json object and noticed there was exactly 50 tracks (which is suspiciously even).
Is there a plan to support the next-href tag in json? Am I missing something? is it a bug that it's missing?
There is an undocumented parameter you can use linked_partitioning=1, that will add next_href to the response.
http://api.soundcloud.com/users/dubstep/tracks.json?client_id=YOUR_CLIENT_ID&linked_partitioning=1
for ex :
// build our API URL
$clientid = "Your API Client ID"; // Your API Client ID
$userid = "/ IDuser"; // ID of the user you are fetching the information for
// Grab the contents of the URL
//more php get
$number="1483";
$offset=1300;
$limit=200;
$soundcloud_url = "http://api.soundcloud.com/users/{$userid}/tracks.json?client_id={$clientid}&offset={$offset}&limit={$limit}";
$tracks_json = file_get_contents($soundcloud_url);
$tracks = json_decode($tracks_json);
foreach ($tracks as $track) {
echo "<pre>";
echo $track->title . ":";
echo $track->permalink_url . "";
echo "</pre>";
}
sI've seen this code is supposed to help (this is in Ruby):
# start paging through results, 100 at a time
tracks = client.get('/tracks', :order => 'created_at', :limit => page_size,
:linked_partitioning => 1)
tracks.each { |t| puts t.title }
However, the first set of results will show and i'll even see the "next_href" at the end of the response, but what are you supposed to do, to make the next set of results show?
i am using database handler for my sessions which working fine but now i stack into a problem on authentication.
When user login with username/password i do session_regenerate_id and after that i am trying to select the current session_id.
Here is my code
session_regenerate_id();
echo $checkQ=" SELECT * FROM my_sessions WHERE id='".session_id()."' ";
......
but i dont get any results. The session_id is the correct one.
After finish load the page and copy paste the SQL Command to phpMyAdmin i get the results.
I know thats its stupid but the only reason i can think of is that session_regenerate_id() "is too slow" so when i try to read the session_id at next line the session_id has not created in database yet.
Can anyone help me!
I know it has been a while, I hope you have found an answer since this was posted, but I'll add my solution for posterity's sake.
The call to session_generate_id() will cause the value of session_id() to change:
<?php
$before = session_id();
session_regenerate_id();
$after = session_id();
var_dump($before == $after); // outputs false
This problem manifested for me because in the session write handler I was doing this (without such bogus method names, of course):
<?php
class MySQLHandler
{
function read($id)
{
$row = $this->doSelectSql($id);
if ($row) {
$this->foundSessionDuringRead = true;
}
// snip
}
function write($id, $data)
{
if ($this->foundSessionDuringRead) {
$this->doUpdateSql($id, $data);
}
else {
$this->doInsertSql($id, $data);
}
}
}
The write() method worked fine if session_regenerate_id() was never called. If it was called, however, the $id argument to write() is different to the $id passed to read(), so the update won't find any records with the new $id because they've never been inserted.
Some people suggest to use MySQL's "REPLACE INTO" syntax, but that deletes and replaces the row, which plays merry havoc if you want to have a creation date column. What I did to fix the problem was to hold on to the session ID that was passed to read, then update the session ID in the database during write using the id passed to read as the key:
<?php
class MySQLHandler
{
function read($id)
{
$row = $this->doSelectSql($id);
if ($row) {
$this->rowSessionId = $id;
}
// snip
}
function write($id, $data)
{
if ($this->rowSessionId) {
$stmt = $this->pdo->prepare("UPDATE session SET session_id=:id, data=:data WHERE session_id=:rowSessionId AND session_name=:sessionName");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':rowSessionId', $this->rowSessionId);
$stmt->bindValue(':data', $data);
$stmt->bindValue(':sessionName', $this->sessionName);
$stmt->execute();
}
else {
$this->doInsertSql($id, $data);
}
}
}
I think I'm having the same problem you are having. It's unclear to me whether this is a PHP (cache) feature or a bug.
The issue is that, when using a custom SessionHandler and calling session_regenerate_id(true), the new session is not created until the script terminates. I have confirmed that by doing the same thing you did: SELECTing the new session id from the database. And the new session is not there. However, after the script finishes, it is.
This is how I fixed it:
$old_id = session_id();
// If you SELECT your DB and search for $old_id, it will be there.
session_regenerate_id(TRUE);
$new_id = session_id();
// If you SELECT your DB for either $old_id or $new_id, none will be there.
session_write_close();
session_start();
// If you SELECT your DB for $new_id, it will be there.
Therefore the solution (workaround) I came about was to force PHP to write the session. I hope this helps.
I am using CakePHP 1.3 and writing custom shells to run mundane tasks in cronjobs. I am seeing failed Model->save() from time to time but I don't know anyway to find out what the exact problem is.
Is there a way to display the actual SQL statements executed and warning/error returned by MySQL in a CakePHP shell?
Thanks.
You can use the following SQL dump task for shells.
http://bakery.cakephp.org/articles/carcus88/2011/04/08/sql_dump_task_for_shells
One way to do this would be to watch the MySQL log file in a separate terminal.
A couple ways of doing this are listed here:
MySQL Query Logging in CakePHP
I found a way to do it. In your shell, add:
function initialize()
{
Configure::write('debug', 2);
$this->_loadDbConfig();
$this->_loadModels();
}
Then whenever you like to see the log, call this function:
function dump_sql()
{
$sql_dump = '';
if (!class_exists('ConnectionManager') || Configure::read('debug') < 2)
return false;
$noLogs = !isset($logs);
if ($noLogs)
{
$sources = ConnectionManager::sourceList();
$logs = array();
foreach ($sources as $source):
$db =& ConnectionManager::getDataSource($source);
if (!$db->isInterfaceSupported('getLog')):
continue;
endif;
$logs[$source] = $db->getLog();
endforeach;
}
if ($noLogs || isset($_forced_from_dbo_))
{
foreach ($logs as $source => $logInfo)
{
$text = $logInfo['count'] > 1 ? 'queries' : 'query';
$sql_dump .= "cakeSqlLog_" . preg_replace('/[^A-Za-z0-9_]/', '_', uniqid(time(), true));
$sql_dump .= '('.$source.') '. $logInfo['count'] .' '.$text. ' took '.$logInfo['time'].' ms';
$sql_dump .= 'Nr Query Error Affected Num. rows Took (ms)';
foreach ($logInfo['log'] as $k => $i)
{
$sql_dump .= $i['query'];
}
}
}
else
{
$sql_dump .= 'Encountered unexpected $logs cannot generate SQL log';
}
}
One other approach would be to have all your custom queries in the models/behaviors, and just calling the data/updates from shells. This would give you an extra benefit of being able to reuse those custom SQL in other parts of the project. For example, in unit tests.
In CakePHP 1.2, I was able to get the SQL queries to show up in my console output by adding a Configure::write('debug', 2); call to the bottom of the __bootstrap method in the cake/console/cake.php file.
No need to mess around with specifically calling a dump_sql function like some of these answers, I just automatically get the normal queries like at the bottom of a web page.