Update CakePhp View each time a record is inserted in the db table

烂漫一生 提交于 2020-01-06 08:04:26

问题


Am making an application that will enable remote viewing of records and reports via a browser. I have used cakePHP to make the application and its working fine,but i have one little problem,since the application does not do any inserts its just reading the data,i want when a user has opened a view and a record has been inserted on the table,it should update all open clients, instead of the user refreshing the page to get the new records. Is there a cakePHP websocket plugin that actually works? Our webhost doesnt allow installing programs or adding apache modules so nodejs or similar solutions wont be applicable here.

Am looking for a purely php and javascript implementation where you just upload your application files to the webserver and everything runs. You dont have to run, install extras or do any configuration on apache or stuff... afteruploading your files

here is a function in one of my controllers(BooksController.php) that retrieves the data to the view

public function list_books()
  {
    $this->Books->recursive = 0;
    $this->paginate = array('order' => array('Serial_No' => 'DESC'));
    $this->set('All_Books', $this->paginate());
  }

and here is one of my views(list_books.ctp) that displays the data in a table paginated.

<div class="row-fluid">

    <div class="span12">
        <?php echo $this->Session->flash() ?>   
        <h4><?php echo __('All Books') ?></h4>
        <hr>               
                    <table class="table table-bordered">
            <thead>
                <tr>    <th><?php echo __('Serial No') ?></th>
                    <th><?php echo __('Title') ?></th>
                                        <th><?php echo __('Author') ?></th>
                                        <th><?php echo __('Publisher') ?></th>
                                        <th><?php echo __('Category') ?></th>
                                        <th><?php echo __('Section') ?></th>
                                        <th><?php echo __('Available') ?></th>
                </tr>
            </thead>
            <tbody>
                <?php foreach( $All_Books as $book ){ ?>
                <tr>
                    <td><?php echo $this->Html->link(__($book['Book']['Serial_No']),'/books/view/'.$book['Book']['Serial_No']) ?></td>                                         
                                        <td><?php echo $book['Book']['Title'] ?></td>
                                        <td><?php echo $book['Book']['Author'] ?></td>
                                        <td><?php echo $book['Book']['Publisher'] ?></td>
                                        <td><?php echo $book['Book']['Category'] ?></td>
                                        <td><?php echo $book['Book']['Section'] ?></td>
                                        <td><?php echo $book['Book']['Available'] ?></td>
                </tr>
                <?php } ?>
            </tbody>
        </table>
                <?php echo $this->Paginator->prev('« Previous', null, null, array('class' => 'disabled'));            
echo $this->Paginator->numbers();
echo $this->Paginator->next('Next »', null, null, array('class' => 'disabled'));
echo $this->Paginator->counter(array(
    'format' => 'Page {:page} of {:pages}, showing {:current} records out of
             {:count} total, starting on record {:start}, ending on {:end}'
));
 ?>
    </div>

</div>

What can i add on my view or controller or model, to make the view auto updating? Can this be achieved using ajax?


回答1:


You can use an AJAX poller, or (HTML5) websockets (using Pusher for instance) for push notification.




回答2:


You've already mentioned it, AJAX. That's the easiest way to accomplish something like that, simply do a check in the background via AJAX, and if necessary reload the page, or update only the affected parts by again using an AJAX request.

Depending on the amount of data you could of course simply load the data directly instead of checking for updates first.

Update I've misunderstood the question, in case the inserts/udpates are made from an external source that you have no direct control over as described in the comments, the only options I could think of for checking whether updating the view is necessary, would be checking the UPDATE_TIME information schema (works on MyISAM only), using triggers for updating a custom information schema that could be checked, or counting the rows, however the latter would only cover inserts.

All methods would fetch the comparison value (update time or row count) in the controller action of the specific view, and pass that value to the view where it's then used in the AJAX call. The AJAX call invokes a controller method where the passed value is compared to the current time/count value in order to determine whether an update is necessary.

Please note that the examples are untested!

Information Schema

The easiest method would be checking the UPDATE_TIME information schema, however as mentioned this only works for MyISAM tables:

Model:

public function getUpdateTime()
{   
    $db = $this->getDataSource();

    $result = $db->fetchAll('
        SELECT
            UNIX_TIMESTAMP(UPDATE_TIME) AS update_time
        FROM
            information_schema.tables
        WHERE
            TABLE_SCHEMA = ?
            AND
            TABLE_NAME = ?',

        array
        (
            $db->config['database'],
            $this->table
        )
    );

    if(empty($result) || !isset($result[0][0]['update_time']))
    {
        return false;
    }

    return (int)$result[0][0]['update_time'];
}

Controller:

public function list_books()
{
    $this->set('lastUpdate', $this->Books->getUpdateTime());

    $this->Books->recursive = 0;
    $this->paginate = array('order' => array('Serial_No' => 'DESC'));
    $this->set('All_Books', $this->paginate());
}

public function checkForUpdate($time)
{
    $updateNecessary = $this->Books->getUpdateTime() > (int)$time;
    return new CakeResponse(array('body' => json_encode($updateNecessary)));
}

View:

<script>
jQuery(function($)
{
    var lastUpdate = <?php echo $lastUpdate; ?>;

    function checkForUpdate()
    {
        $.get('/books/checkForUpdate', {time: lastUpdate}, function(updateNecessary)
        {
            if(updateNecessary === true)
            {
                alert('update necessary');
                // now reload the page or use an additional AJAX request for updating the content
            }
            else
            {
                queueUpdateCheck();
            }
        },
        'json');
    }

    function queueUpdateCheck()
    {
        setTimeout(checkForUpdate, 5000);
    }

    queueUpdateCheck();
});
</script>

Using Triggers

Another option would be using triggers. You'd need an additional table that connects tables and time values using for example the table name, and two triggers, one for inserts, one for updates. These triggers could then update the custom information table.

Information Table

CREATE TABLE IF NOT EXISTS `table_stats` (
  `table_name` varchar(255) NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`table_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO `table_stats` (`table_name`, `update_time`)
VALUES ('books', NOW());

Triggers

CREATE TRIGGER `update_time_after_insert` AFTER INSERT ON `books`
    FOR EACH ROW 
        UPDATE `table_stats` SET `update_time` = NOW() WHERE `table_name` = 'books';

CREATE TRIGGER `update_time_after_update` AFTER UPDATE ON `books`
    FOR EACH ROW 
        UPDATE `table_stats` SET `update_time` = NOW() WHERE `table_name` = 'books';

Model:

public function getUpdateTime()
{   
    $db = $this->getDataSource();

    $result = $db->fetchAll('
        SELECT
            UNIX_TIMESTAMP(update_time) AS update_time
        FROM
            `table_stats`
        WHERE
            `table_name` = ?',

        array
        (
            $this->table
        )
    );

    if(empty($result) || !isset($result[0][0]['update_time']))
    {
        return false;
    }

    return (int)$result[0][0]['update_time'];
}

Controller and View would be the same as in the previous example.


Counting rows

Now the last option would be comparing the row count, which would of course only work for inserts. In this example the Model would stay untouched.

Controller:

public function list_books()
{
    $this->set('rowCount', $this->Books->find('count'));

    $this->Books->recursive = 0;
    $this->paginate = array('order' => array('Serial_No' => 'DESC'));
    $this->set('All_Books', $this->paginate());
}

public function checkForUpdate($rowCount)
{
    $updateNecessary = $this->Books->find('count') != (int)$rowCount;
    return new CakeResponse(array('body' => json_encode($updateNecessary)));
}

View:

<script>
jQuery(function($)
{
    var rowCount = <?php echo $rowCount; ?>;

    function checkForUpdate()
    {
        $.get('/books/checkForUpdate', {rowCount: rowCount}, function(updateNecessary)
        {
            if(updateNecessary === true)
            {
                alert('update necessary');
                // now reload the page or use an additional AJAX request for updating the content
            }
            else
            {
                queueUpdateCheck();
            }
        },
        'json');
    }

    function queueUpdateCheck()
    {
        setTimeout(checkForUpdate, 5000);
    }

    queueUpdateCheck();
});
</script>

Retrieving data together with the update check

Of course you could also submit possible data together with the update check, in order to avoid additional requests. For example:

Model

public function checkForUpdate($time)
{
    $data = '';

    $updateAvailable = $this->Books->getUpdateTime() > (int)$time;
    if($updateAvailable)
    {
        $this->set('books', $this->Books->find('all'));

        // render /Elements/books.ctp in ajax.ctp layout and grab the rendered content
        $this->viewPath = 'Elements';
        $view = $this->render('books', 'ajax');
        $data = $view->body();
    }

    $body = compact('updateAvailable', 'data');

    return new CakeResponse(array('body' => json_encode($body)));
}

View

<script>
jQuery(function($)
{
    var lastUpdate = <?php echo $lastUpdate; ?>;

    function checkForUpdate()
    {
        $.get('/books/checkForUpdate', {time: lastUpdate}, function(response)
        {
            if(response.updateAvailable === true)
            {
                // the data is already available, so directly update the content
                $('#content').html(response.data);
            }

            queueUpdateCheck();
        },
        'json');
    }

    function queueUpdateCheck()
    {
        setTimeout(checkForUpdate, 5000);
    }

    queueUpdateCheck();
});
</script>


来源:https://stackoverflow.com/questions/17906697/update-cakephp-view-each-time-a-record-is-inserted-in-the-db-table

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!