如何使用PHP和Smarty同时获取数据并在浏览器中显示?

PHP*_*ver 8 php mysql ajax smarty output-buffering

我在我的网站上使用PHP,MySQL,Smarty,jQuery,AJAX等.目前,我从MySQL数据库中获取大量数据(匹配问题ID),对其进行处理,将此数据分配给Smarty模板并将其打印在网页上.由于要获取的数据量太大而且正在进行进一步处理,因此获取最终输出数据需要花费太多时间.反过来,向用户显示整个数据需要花费太多时间.

我有一种方法,但无法实现它.我的方法是运行获取单个匹配question_id并同时将其显示到浏览器的两个过程,并重复此循环,直到获取并显示所有匹配的问题ID.当显示单行的加载数据时,加载器图像应显示在该显示的记录下.当所有数据都被打印出来时,加载器图像应该消失.

但我面临的主要问题是我应该如何继续将数据分配给Smarty模板并显示模板,因为Smarty模板引擎首先加载所有内容,并且只有在完全拥有将其打印到浏览器的内容之后.

为了您的参考,我将控制器,模型和视图中的所有现有代码放在下面:

Controller(match_question.php)的PHP代码如下:

<?php 
  require_once("../../includes/application-header.php");

  $objQuestionMatch  = new QuestionMatch();

  $request = empty( $_GET ) ? $_POST : $_GET ;


  if($request['subject_id']!="") 
    $subject_id = $request['subject_id'];
  if($request['topic_id']!="") 
    $topic_id = $request['topic_id'];

  if($subject_id !='' && $topic_id !='')
    $all_match_questions = $objQuestionMatch->GetSimilarQuestionsBySubjectIdTopicId($subject_id, $topic_id);

  $smarty->assign('all_match_questions', $all_match_questions);
  $smarty->display("match-question.tpl")
?>
Run Code Online (Sandbox Code Playgroud)

Model(QuestionMatch.php)的PHP代码如下:

<?php
  class QuestionMatch {

    var $mError = "";
    var $mCheck;
    var $mDb;
    var $mValidator;
    var $mTopicId;
    var $mTableName;

    function __construct() {
      global $gDb;
      global $gFormValidation;

      $this->mDb        = $gDb; 
      $this->mValidator = $gFormValidation;
      $this->mTableName = TBL_QUESTIONS;
    }
/**
     * This function is used to get all the questions from the given subject id and topic id
         */
    function GetSimilarQuestionsBySubjectIdTopicId($subject_id, $topic_id) {

            /*SQL query to find out questions from given subject_id and topic_id*/
            $sql  = " SELECT * FROM ".TBL_QUESTIONS." WHERE question_subject_id=".$subject_id;
            $sql .= " AND question_topic_id=".$topic_id;

            $this->mDb->Query($sql);
            $questions_data = $this->mDb->FetchArray(); 
            /*Same array $questions_data is assigned to new array $questions to avoid the reference mismatching*/
            $questions      = $questions_data;

      /*Array of words to be excluded from comparison process
       *For now it's a static array but when UI design will be there the array would be dynamic
            */
            $exclude_words = array('which','who','what','how','when','whom','wherever','the','is','a','an','and','of','from');  

      /*This loop removes all the words of $exclude_words array from all questions and converts all 
       *converts all questions' text into lower case
      */
      foreach($questions as $index=>$arr) {
        $questions_array = explode(' ',strtolower($arr['question_text']));
        $clean_questions = array_diff($questions_array, $exclude_words);
        $questions[$index]['question_text'] = implode(' ',$clean_questions);
      }      

      /*Now the actual comparison of each question with every other question stats here*/
            foreach ($questions as $index=>$outer_data) {

        /*Logic to find out the no. of count question appeared into tests*/
        $sql  = " SELECT count(*) as question_appeared_count FROM ".TBL_TESTS_QUESTIONS." WHERE test_que_id=";
        $sql .= $outer_data['question_id'];

        $this->mDb->Query($sql);
        $qcount = $this->mDb->FetchArray(MYSQL_FETCH_SINGLE); 

        $question_appeared_count = $qcount['question_appeared_count'];
        $questions_data[$index]['question_appeared_count'] = $question_appeared_count;
        /*Crerated a new key in an array to hold similar question's ids*/
        $questions_data[$index]['similar_questions_ids_and_percentage'] = Array(); 

        $outer_question = $outer_data['question_text'];

        $qpcnt = 0;     
        //foreach ($questions as $inner_data) {
        /*This foreach loop is for getting every question to compare with outer foreach loop's 
        question*/
        foreach ($questions as $secondIndex=>$inner_data) { 
            /*This condition is to avoid comparing the same questions again*/
          if ($secondIndex <= $index) {
            /*This is to avoid comparing the question with itself*/
              if ($outer_data['question_id'] != $inner_data['question_id']) {

              $inner_question = $inner_data['question_text'];  

                /*This is to calculate percentage of match between each question with every other question*/
                similar_text($outer_question, $inner_question, $percent);
                $percentage = number_format((float)$percent, 2, '.', '');

                /*If $percentage is >= $percent_match only then push the respective question_id into an array*/
                if($percentage >= 85) {
                $questions_data[$index]['similar_questions_ids_and_percentage'][$qpcnt]['question_id']       = $inner_data['question_id'];
                $questions_data[$index]['similar_questions_ids_and_percentage'][$qpcnt]['percentage']        = $percentage;
                /*$questions_data[$secondIndex]['similar_questions_ids_and_percentage'][$qpcnt]['question_id'] = $outer_data['question_id'];
                $questions_data[$secondIndex]['similar_questions_ids_and_percentage'][$qpcnt]['percentage']    = $percentage;*/

                /*Logic to find out the no. of count question appeared into tests*/
                $sql  = " SELECT count(*) as question_appeared_count FROM ".TBL_TESTS_QUESTIONS." WHERE test_que_id=";
                $sql .= $inner_data['question_id'];

                $this->mDb->Query($sql);
                $qcount = $this->mDb->FetchArray(MYSQL_FETCH_SINGLE); 

                $question_appeared_count = $qcount['question_appeared_count'];
                $questions_data[$index]['similar_questions_ids_and_percentage'][$qpcnt]['question_appeared_count'] = $question_appeared_count;
                $qpcnt++;
            }
          }
        }   
      }
    }    //}    
    /*Logic to create the return_url when user clicks on any of the displayed matching question_ids*/
    foreach ($questions_data as $index=>$outer_data) {
      if(!empty($outer_data['similar_questions_ids_and_percentage'])) { 
        $return_url  = ADMIN_SITE_URL.'modules/questions/match_question.php?';
        $return_url .= 'op=get_question_detail&question_ids='.$outer_data['question_id'];

        foreach($outer_data['similar_questions_ids_and_percentage'] as $secondIndex=>$inner_data) {
          $return_url = $return_url.','.$inner_data['question_id'];
        }      
        $questions_data[$index]['return_url'] = $return_url.'#searchPopContent';
      }
    }     
          /*This will return the complete array with matching question ids*/
      return $questions_data;
      }
}
?>
Run Code Online (Sandbox Code Playgroud)

View(match-question.tpl)的代码如下:

<table width="100%" class="base-table tbl-practice" cellspacing="0" cellpadding="0" border="0">
  <tr class="evenRow">
    <th width="33%" style="text-align:center;" class="question-id">Que ID</th>
    <th width="33%" style="text-align:center;" class="question-id">Matching Que IDs</th>
    <th width="33%" style="text-align:center;" class="question-id">Percentage(%)</th>
  </tr>
{if $all_match_questions}
  {foreach from=$all_match_questions item=qstn key=key}   
    {if $qstn.similar_questions_ids_and_percentage}
      {assign var=counter value=1}
  <tr class="oddRow">
    <td class="question-id" align="center" valign="top">
      <a href="{$qstn.return_url}" title="View question" class="inline_view_question_detail">QUE{$qstn.question_id}</a>{if $qstn.question_appeared_count gt 0}-Appeared({$qstn.question_appeared_count}){/if}
    </td>
      {foreach from=$qstn.similar_questions_ids_and_percentage item=question key=q_no}
        {if $counter gt 1}
    <tr class="oddRow"><td class="question-id" align="center" valign="top"></td>
        {/if}
    <td class="question" align="center" valign="top">

        {if $question.question_id!=''}
      <a href="{$qstn.return_url}" title="View question" class="inline_view_question_detail">QUE{$question.question_id}</a>{if $question.question_appeared_count gt 0}-Appeared({$question.question_appeared_count}){/if}
        {if $question.question_appeared_count eq 0}
      <a id ="{$question.question_id}" href="#" class="c-icn c-remove delete_question"  title="Delete question"> Delete</a>{/if}
        {/if}

    </td>

    <td class="question" align="center" valign="top">
        {if $question.percentage!=''}{$question.percentage}{/if}
        {assign var=counter value=$counter+1}
    </td>
  </tr>
      {/foreach}               
    {/if}
  {/foreach}
{else}
  <tr>
    <td colspan="2" align="center"><b>No Questions Available</b></td>
  </tr>
{/if}
</table>
Run Code Online (Sandbox Code Playgroud)

感谢您花了一些宝贵的时间来理解我的问题.

Meh*_*hdi 10

我相信瓶颈在于循环SQL查询.有一种标准的方法可以在MySQL上对搜索结果进行排名.您可以简单地实现全文搜索.

首先,您需要创建一个表格,如search_results:

SQL:

CREATE TABLE `search_results` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `result_title` varchar(128) CHARACTER SET utf8 NOT NULL,
  `result_content` text CHARACTER SET utf8 NOT NULL,
  `result_short_description` text CHARACTER SET utf8,
  `result_uri` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '',
  `result_resource_id` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `result_title` (`result_title`,`result_content`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)

您必须将表格中的所有有用数据questions(包括问题,主题,答案以及您想要搜索的任何内容)插入到result_title和result_content中(也可以在需要更新时更新此表).对应表的原始记录也有回溯result_title.使用result_content指向您网站中结果的已定义URL的预定义URI ,您可以更快地完成所有操作.您不需要每次都创建URL.

现在,您可以创建一个搜索查询的简单的SQL查询result_resource_idresult_uri:

SQL:

SELECT `result_title`, `result_content`, `result_uri`
FROM `search_results` WHERE MATCH(result_title, result_content) AGAINST('question?');
Run Code Online (Sandbox Code Playgroud)

您还可以将相关性度量添加到查询字符串中.还有其他搜索模式,如布尔值.阅读此处的文档并找到最佳解决方案.

在这些用例中,全文索引更快,也更准确.


Squ*_*Cat 7

假设您希望在浏览器中将内容从服务器流式传输到客户端时加载内容,如果您正在使用表格 - 就像您一样 - 您可能会遇到浏览器问题(由于布局问题)而不是能够呈现表直到加载所有数据.

您可以看到这些有关创建快速加载HTML页面的技巧,并了解相应部分中的表格.

一些关键点:

如果浏览器可以立即确定图像和表格的高度和/或宽度,则可以显示网页而无需重排内容.这不仅可以加快页面的显示速度,还可以在页面完成加载时防止页面布局出现恼人的变化.因此,应尽可能为图像指定高度和宽度.

和:

表应该使用CSS selector:property组合:

table-layout:fixed;

...并应使用COL和COLGROUP HTML标记指定列的宽度.

以及:

表仍被视为有效标记,但应用于显示表格数据.为了帮助浏览器更快地呈现页面,您应该避免嵌套表格.

您可能还想查看PHP的流输出方法.

有关详情,请参阅此问题.


hal*_*fer 7

通常,模板引擎不会零碎地加载内容 - 您需要手动将数据发送到浏览器,并flush在每个位之间.模板库通常将整个文档组合在内存中,然后一次性将其转储到浏览器中.不过,为了以防万一,值得查看Smarty手册.

作为替代方案,您可以在没有大量数据的情况下呈现页面,然后通过AJAX将其加载到各个部分中.虽然制作10个AJAX连接会连续增加额外的开销,但与目前的渲染时间相比,这听起来很小.即使您的总渲染时间可能略长,但用户感知的渲染时间也会快得多,当然他们也可以看到数据到达的好处.

我将在domready上启动jQuery中的第一个AJAX操作,当每个操作完成时,它可以触发另一个请求.如果您的服务器可以使用JSON而不是HTML进行应答,则它将允许服务器返回more_available布尔标志,您可以使用该标志来确定是否需要执行另一次提取.


Pyn*_*ynt 6

您当前的数据库查询和后续的smarty-> assign将不允许延迟加载数据以加快进程.

在这种情况下,您可以从查询中识别可以快速显示给用户的最大行集.确定可以显示的最大行集并仍然保持快速响应时间后,您可以修改查询和模板系统以反映多个查询设​​置.这基本上是分页.您将执行初始的行加载,而不是分页,然后通过jquery加载后一组行,直到所有"页面"数据都已成功加载.

对于match_question.php

  • 首先查询数据集以查看您拥有的总数据行数.
  • 将这些行除以您可以显示的总行数,同时保持快速应用程序.这将为您提供您将运行的"页面"或"查询"的总数.
  • 例如:假设您的测试产生100行作为最佳最快响应.您将在预期的数据集上执行COUNT(*),返回2021.您可以将该行数除以最佳的100个结果,这将产生20.21或21"页",或者在您的情况下总共21个查询.您的第一个初始查询,以及另外20个ajax查询.
  • 这将导致对数据库的大量查询,但这会使页面加载时间对最终用户更有效.因此,您应该测量机器上的负载与最终用户的易用性.

    $limit = 100;
    $page = 1;
    ...
    
    if($request['page'] != '')
       $page = $request['page'];
    
    ...
    
    if($subject_id !='' && $topic_id !=''){
       $count_matched_questions = $objQuestionMatch->GetSimilarQuestionsBySubjectIdTopicId($subject_id, $topic_id, true);
    
       $page_count = ceil($count/$limit) //round up if decimal for last page;
    
       $paged_match_questions = $objQuestionMatch->GetSimilarQuestionsBySubjectIdTopicId($subject_id, $topic_id, false, $limit, $page)
    }
    
    $smarty->assign( 'all_match_questions', $paged_match_questions
                    ,'page_count', $page_count);
    //cache each result page separately to support multiple subject/topic/page combinations to properly utilize the cache mechanism
    $smarty->display("match-question-".$subject_id."-".$topic_id."-".$page.".tpl")
    
    Run Code Online (Sandbox Code Playgroud)

对于QuestionMatch.php

  • 调整查询功能(示例):

    function GetSimilarQuestionsBySubjectIdTopicId($subject_id, $topic_id, $count = false, $limit = 0, $page = 0 ) {
        if($count)
        {
           $sql  = " SELECT COUNT(*) FROM ".TBL_QUESTIONS." WHERE question_subject_id=".$subject_id;
        }
        else
        {
           $sql  = " SELECT * FROM ".TBL_QUESTIONS." WHERE question_subject_id=".$subject_id;
        }
        $sql .= " AND question_topic_id=".$topic_id;
    
        if($page > 0 && $limit > 0)
        {
           $sql .= " LIMIT = " . ($limit*$page)-$limit . ", " . ($limit*$page);
        } 
    
    } 
    
    Run Code Online (Sandbox Code Playgroud)

对于View(match-question.tpl)

  • 输出html元素中的'page_count'值,可能是数据页html5值,并将其分配给具有唯一ID的元素.
  • 在页面加载时,您的ajax初始化并获取数据页面值.
  • 使用?page =&subject_id =&topic_id =通过ajax调用你的php文档
  • 使用从page = 2开始的数据页量来循环通过ajax调用,直到您查询了最大页面.
  • 在每次迭代时将返回的html附加到适当的位置.

希望这个想法可以帮助您找到解决方案.干杯!


Ais*_*ina 6

在没有深入了解代码的具体细节的情况下,您所寻找的内容类似于Facebook使用的名为BigPipe的系统,在Facebook工程的本说明中有详细描述.

基本上,他们尝试做的是尽快向浏览器发送回复,包含页面的基本布局,以及稍后将包含加载时间更长的内容的占位符元素 - 他们称之为这些小页面.刷新初始响应后,依次加载每个pagelet,包括从数据库或类似程序加载数据,并发送到客户端 - 仍然是同一HTTP请求的一部分.Javascript用于将内容插入到右侧占位符中.

在我工作的公司,我们试验了一段时间,并取得了很好的成果.我们在GitHub上有一个开源的第三方PHP/Javascript BigPipe实现作为起点.虽然远非琐碎的设置,更重要的是,工作得非常好,但我相信这对于您所面临的问题来说是一个很好的解决方案.