2
\$\begingroup\$

I've been learning how to code for the past three years. I have made some really good progress I think, and right now I'm trying myself on creating an MVC framework.

I found this awesome idea on making templates.

Here's how my framework basically works:

Config file outside the root contains some constants (database passwords, etc.). All requests are routed through controller.php using htaccess. Controller will analyze the url, and either redirect to the file, to an error page, or to an nicely parsed URL for the page. Model will take the URL parameters and fetch data from db, and determine which view is to be used. The view assembles the entire page and it's HTML code using the content it received from the model and using the template class to piece it all together. There is no actual HTML code in the view files or class.

$controller = new Controller($_SERVER['REQUEST_URI']);
$model      = new Model($controller->page,$controller->elements);

//for example articles/article_id/argument would result in:
// $page = articles; and $elements = ['article_ID','argument'];
// $page_info then holds all the content relevant for displaying the page.
if($model->page_info !== NULL)
{
    $last_page  = $controller->page.'/'.implode('/', $controller->elements);
    $view   = new View($model->page_info);
    $view->show_page();
}

I really like the concept of the MVC in general and particularly the template factory, especially since a webdesigner can now look at and change the HTML code without breaking the PHP/mysql, I can change business functionality in the model without compromising the site integrity, as long as I still pass the return values to the same variables.

As part of the framework I have been loading some db values into a dynamic table, but making each row a separate form, so rows can be deleted, edited, and new rows added.

This is what the result will look like:

table form preview

My first version had not fully utilized the templates, I had pretty much all <form>, <table>, <tr> and <td> tags hard-coded.

Generally there might be a brief moment where my page is loading slower then my previous project have been, but all together it's quite smooth.

However after turning every single tag into a template, so that all the HTML code is in separate files, it takes a good 10 seconds to load the page, and sometimes there will even be a php error saying it's exceeded the 30 second maximum.

Going back to my old version works fine. And when the new version does load there are no errors.

How can I determine the bottleneck in this code?

<?php   

    $tpl_header_field = new Template('_table_rotated_header_field.php');
    $tpl_input_tag  = new Template('_input_tag.php');
    $tpl_td_tag     = new Template('_table_td_tag.php');
    $tpl_th_tag     = new Template('_table_th_tag.php');
    $tpl_tr_tag     = new Template('_table_tr_tag.php');
    $tpl_tbody_tag  = new Template('_table_tbody_tag.php');
    $tpl_thead_tag  = new Template('_table_thead_tag.php');
    $tpl_table_tag  = new Template('_table_tag.php');
    $tpl_form_tag   = new Template('_form.php');

    $tpl_form_tag->set("FORM_ID", '');
    $tpl_form_tag->set("FORM_CLASS", '');
    $tpl_form_tag->set("ACTION", 'posthandler/form_change/_main/0/1/0/0');

    $tpl_input_tag->set("ADDTL_ATTR", '');  

    $tpl_input_tag->set("NAME", 'action');
    $tpl_input_tag->set("TYPE", 'submit');
    $tpl_input_tag->set("VALUE", 'change');
    $change_button = $tpl_input_tag->output();

    $tpl_input_tag->set("VALUE", 'add');
    $add_button = $tpl_input_tag->output();     

    $tpl_input_tag->set("VALUE", 'copy');
    $copy_button = $tpl_input_tag->output();

    $tpl_input_tag->set("VALUE", 'delete');     
    $delete_button = $tpl_input_tag->output();      

    $tpl_input_tag->set("NAME", 'confirm_delete');
    $tpl_input_tag->set("TYPE", 'checkbox');
    $tpl_input_tag->set("VALUE", '1');      
    $delete_checkbox = $tpl_input_tag->output();    

    $content[] = $this->show_errors();
    $content[] = $this->show_messages();
    $content[] = $this->page_info['page_contents']['text'];

    $header = array(); 
    $headernotset = TRUE;
    $table_row = array();

    foreach($this->page_info['page_contents']['all_form_elements'] as $index => $row)
    {

        $table_row[$index] = array();
        $last_row = array();
        foreach($row as $fieldname => $value)
        {
            if($headernotset)
            {
                if(!array_key_exists($fieldname,$header))
                {

                    $tpl_header_field->set("FIELDNAME", strtoupper($fieldname));
                    $header[$fieldname] = $tpl_header_field->output();
                }
            }

            $tpl_input_tag->set("NAME", $fieldname);
            $tpl_input_tag->set("TYPE", 'text');
            $tpl_input_tag->set("VALUE", htmlspecialchars($value));


            $short_fields = array('order_by','type','span_class','input_id','label_class','input_class');

            if($fieldname == 'form_element_ID')
            {
                $tpl_th_tag->set("INNER_HTML", '#'.$value);
                $table_row[$index][] = $tpl_th_tag->output();

                $tpl_input_tag->set("NAME", 'form_element_ID');
                $tpl_input_tag->set("TYPE", 'hidden');
                $tpl_input_tag->set("VALUE", $value);
                $form_element_ID_hidden = $tpl_input_tag->output();

                $table_row[$index][] = $form_element_ID_hidden;

                $tpl_th_tag->set("INNER_HTML", 'new');
                $last_row[] = $tpl_th_tag->output();

                $tpl_th_tag->set("INNER_HTML", '');
                $header[$fieldname] = $tpl_th_tag->output();
            }
            else if($fieldname == 'form_name')
            {
                unset($header[$fieldname]);
                $form_name = $value;

                if(!isset($form_name_hidden))
                {
                    $tpl_input_tag->set("NAME", 'form_name');
                    $tpl_input_tag->set("TYPE", 'hidden');
                    $tpl_input_tag->set("VALUE", $form_name);   
                    $form_name_hidden = $tpl_input_tag->output();               
                }
            }
            else if(in_array($fieldname, $short_fields) )
            {
                $tpl_input_tag->set("ADDTL_ATTR", ' size="3"');
                $tpl_td_tag->set("INNER_HTML", $tpl_input_tag->output());
                $table_row[$index][] = $tpl_td_tag->output();

                $tpl_input_tag->set("VALUE", '');   
                $tpl_td_tag->set("INNER_HTML", $tpl_input_tag->output());
                $last_row[] = $tpl_td_tag->output();

                $tpl_input_tag->set("ADDTL_ATTR", '');
            }           
            else
            {
                $tpl_td_tag->set("INNER_HTML", $tpl_input_tag->output());
                $table_row[$index][] = $tpl_td_tag->output();

                $tpl_input_tag->set("VALUE", '');   
                $tpl_td_tag->set("INNER_HTML", $tpl_input_tag->output());
                $last_row[] = $tpl_td_tag->output();
            }

        }

        $tpl_td_tag->set("INNER_HTML", $form_name_hidden.$change_button);
        $table_row[$index][] = $tpl_td_tag->output();
        $tpl_td_tag->set("INNER_HTML", $copy_button);
        $table_row[$index][] = $tpl_td_tag->output();
        $tpl_td_tag->set("INNER_HTML", $delete_checkbox.$delete_button);
        $table_row[$index][] = $tpl_td_tag->output();
        $tpl_td_tag->set("INNER_HTML", $form_name_hidden.$add_button);
        $last_row[] = $tpl_td_tag->output();
        $tpl_td_tag->set("INNER_HTML", '');
        $last_row[] = $tpl_td_tag->output();
        $last_row[] = $tpl_td_tag->output();

        if($headernotset)
        {
            $tpl_th_tag->set("INNER_HTML", '');
            $header['change_button'] = $tpl_header_field->output();
            $header['copy_button'] = $tpl_header_field->output();

            $tpl_header_field->set("FIELDNAME", 'CHECK TO DELETE');
            $header['check_to_delete'] = $tpl_header_field->output();
        }

        $headernotset = FALSE;

        $tpl_form_tag->set("FORM_CONTENTS", implode(br_str(), $table_row[$index]));

        $tpl_tr_tag->set('INNER_HTML', $tpl_form_tag->output());
        $table_row[$index] = $tpl_tr_tag->output();


    }

    $tpl_form_tag->set("FORM_CONTENTS", implode(br_str(), $last_row));
    // br_str() returns a new line.

    $tpl_tr_tag->set('INNER_HTML', $tpl_form_tag->output());

    $table_row[] = $tpl_tr_tag->output();

    $tpl_tr_tag->set('INNER_HTML', implode(br_str(),$header));
    $tpl_thead_tag->set('INNER_HTML',$tpl_tr_tag->output());
    $thead = $tpl_thead_tag->output();

    $tpl_tr_tag->set('INNER_HTML', implode(br_str(),$table_row));
    $tpl_tbody_tag->set('INNER_HTML',$tpl_tr_tag->output());
    $tbody = $tpl_tbody_tag->output();

    $tpl_table_tag->set('CLASS', 'table-header-rotated');
    $tpl_table_tag->set('INNER_HTML', $thead.$tbody);
    $content[] = $tpl_table_tag->output();

?>

The _input_tag.php template file will contain code like this:

<input type="{@TYPE}" name="{@NAME}" value="{@VALUE}"{@ADDTL_ATTR}></input>

The ->set method will replace the placeholder with the value passed along, and the output method will return the string of HTML code with replaced placeholders.

\$\endgroup\$
5
  • \$\begingroup\$ Okay now this is weird: I just added microtime() to the beginning and the end of the script, and display the difference just before exiting the script: 0.568397 half a second doesn't seem that long, does it? \$\endgroup\$ Jul 20, 2014 at 7:53
  • 2
    \$\begingroup\$ My only recommendation is to load all the rows in one form and use a controller to delete, edit, and add new rows, Ajax would be perfect here, just pass it an ID from a HTML tag ie. the first TD of each row for use in your database queries. Half a second is slow if your not loading much, I have a table that loads 313 items from the DB in 0.43 seconds \$\endgroup\$
    – CodeX
    Jul 21, 2014 at 17:15
  • \$\begingroup\$ How your template class is looking? Maybe including php files for each field is the issue? I suppose you have checks like if file exists, then output the content, or I missed something out? \$\endgroup\$
    – Angel M.
    Jul 27, 2014 at 8:04
  • \$\begingroup\$ My template class looks just like this: broculos.net/2008/03/… \$\endgroup\$ Jul 28, 2014 at 0:30
  • \$\begingroup\$ Good idea @CodeX using AJAX and only one form. Although a bit too reactive for my taste, if you mean "well if it loads slow, then just make sure you load it as infrequently as possible..." I ended up putting it all in one form to reduce the number of calls to the template class. \$\endgroup\$ Jul 28, 2014 at 1:13

1 Answer 1

1
\$\begingroup\$

Okay I think I fixed it with very little decrease in flexibility. Basically I did four things:

  • instead of putting each row in one form, put the entire table in a form. This required changing the field names, but no biggie.
  • consolidating template files (for example instead of input and td being separate, i combined them)
  • leaving a smal amount of html code in the view script, instead of outsourcing it into templates.
  • I also found a few things that I did unnecessarily as a loop. I think I fixed those issues.

Now it looks like this:

<?php 

 $tpl_header_field = new Template('table_form/rotated_header_field.php');
 $tpl_table_field  = new Template('table_form/td_input_field.php');
 $tpl_textarea_field  = new Template('table_form/td_textarea.php');
 $tpl_input_tag  = new Template('_input_tag.php');

 $tpl_input_tag->set("ADDTL_ATTR", ''); 

 $header = array(); 
 $new_row = array();
 $headernotset = TRUE;
 $new_rownotset = TRUE;
 $table_row = array();

 $short_fields = array('order_by','type','span_class','input_id','label_class','input_class','login_required');
 $text_fields = array('text');

 $page_name = $this->page_info['page_contents']['page_name'];
 $table_name =  $this->page_info['page_contents']['table_name'];

 foreach($this->page_info['page_contents']['all_page_elements'] as $index => $row)
 {
  $table_row[$index] = array();
  $row_is_empty = TRUE;
  foreach($row as $fieldname => $value)
  {
   if ($value !== '')
   {
    $row_is_empty = FALSE;
   }

   if($headernotset)
   {
    if(!array_key_exists($fieldname,$header))
    {
     $tpl_header_field->set("FIELDNAME", strtoupper($fieldname));
     $header[$fieldname] = $tpl_header_field->output();
    }
   }

   if($fieldname == 'row_ID')
   {
    $table_row[$index][] = '<th>#'.$value.'</th>';
    $row_ID = $value;

    if($new_rownotset)
    {
     $new_row[] = '<th>new</th>';
    }
   }
   else if($pageless_table || $fieldname != 'page_name')
   {    
    if(in_array($fieldname, $text_fields))
    {
     $tpl_textarea_field->set("NAME", 'change_db_table['.$table_name.']['.$page_name.']['.$row_ID.']['.$fieldname.']');
     $tpl_textarea_field->set("VALUE", htmlspecialchars($value));
     $table_row[$index][] = $tpl_textarea_field->output();
     if($new_rownotset)
     {
      $tpl_textarea_field->set("NAME", 'change_db_table['.$table_name.']['.$page_name.'][new_row]['.$fieldname.']');
      $tpl_textarea_field->set("VALUE", '');
      $new_row[] = $tpl_textarea_field->output();
     }     
    }
    else
    {
     $tpl_table_field->set("NAME", 'change_db_table['.$table_name.']['.$page_name.']['.$row_ID.']['.$fieldname.']');
     $tpl_table_field->set("TYPE", 'text');
     $tpl_table_field->set("VALUE", htmlspecialchars($value));

     if(in_array($fieldname, $short_fields) )
     {
      $tpl_table_field->set("ADDTL_ATTR", ' size="3"');
     }
     else
     {
      $tpl_table_field->set("ADDTL_ATTR", '');
     } 

     $table_row[$index][] = $tpl_table_field->output();

     if($new_rownotset)
     {
      $tpl_table_field->set("NAME", 'change_db_table['.$table_name.']['.$page_name.'][new_row]['.$fieldname.']');    
      $tpl_table_field->set("VALUE", ''); 
      $new_row[] = $tpl_table_field->output();
     }
    }
   }

  }

  $tpl_input_tag->set("NAME", 'change_db_table['.$table_name.']['.$page_name.'][rows_to_delete][]');
  $tpl_input_tag->set("TYPE", 'checkbox');
  $tpl_input_tag->set("VALUE", $row_ID);  
  $delete_checkbox = $tpl_input_tag->output();  

  $table_row[$index][] = '<td>'.$delete_checkbox.'</td>';

  $tpl_input_tag->set("NAME", 'change_db_table['.$table_name.']['.$page_name.'][action][copy]['.$row_ID.']');
  $tpl_input_tag->set("TYPE", 'submit');
  $tpl_input_tag->set("VALUE", 'copy row');  
  $copy_button = $tpl_input_tag->output();
  $table_row[$index][] = '<td>'.$copy_button.'</td>';

  if($headernotset)
  {
   $tpl_header_field->set("FIELDNAME", 'CHECK TO DELETE');
   $header['check_to_delete'] = $tpl_header_field->output();

   $tpl_header_field->set("FIELDNAME", '');
   $header['copy_button'] = $tpl_header_field->output();
  }

  $headernotset = FALSE;
  $new_rownotset = FALSE;
  $header['row_ID'] = '<th></th>';
  if($pageless_table==FALSE)
  {
   unset($header['page_name']);
  }

  $table_row[$index] = '<tr>'.br_str().implode(br_str(), $table_row[$index]).br_str().'</tr>'; 
  if($row_is_empty)
  {
   unset($table_row[$index]);
  }
 }

 $tpl_input_tag->set("TYPE", 'submit');
 $tpl_input_tag->set("NAME", 'change_db_table['.$table_name.']['.$page_name.'][action][add]');
 $tpl_input_tag->set("VALUE", 'Add');
 $add_button = $tpl_input_tag->output();   

 $new_row[] = '<td></td>';
 $new_row[] = '<td>'.$add_button.'</td>';

 $tpl_input_tag->set("NAME", 'change_db_table['.$table_name.']['.$page_name.'][action][change]');
 $tpl_input_tag->set("VALUE", 'Save Changes');
 $change_button = $tpl_input_tag->output();

 $tpl_input_tag->set("NAME", 'change_db_table['.$table_name.']['.$page_name.'][action][delete]');
 $tpl_input_tag->set("VALUE", 'Delete Selected');  
 $delete_button = $tpl_input_tag->output(); 

 $bottom_buttons = '<div>'.$change_button.$delete_button.'</div>';

 $table_row['new_row'] = '<tr>'.br_str().implode(br_str(), $new_row).br_str().'</tr>';
 $thead = '<thead><tr>'.br_str().implode(br_str(), $header).br_str().'</tr></thead>';
 $tbody = '<tbody>'.br_str().implode(br_str(), $table_row).br_str().'</tbody>';

 $tpl_table_tag = new Template('_table_tag.php');
 $tpl_table_tag->set('CLASS', 'table-header-rotated');
 $tpl_table_tag->set('INNER_HTML', $thead.br_str().$tbody);
 $table = $tpl_table_tag->output();

 $tpl_form_tag = new Template('_form.php');
 $tpl_form_tag->set("FORM_ID", '');
 $tpl_form_tag->set("FORM_CLASS", '');
 $tpl_form_tag->set("ACTION", $privatevariable); 
 $tpl_form_tag->set("FORM_CONTENTS", $table.$bottom_buttons);


 $content[] = $this->show_errors();
 $content[] = $this->show_messages();
 $content[] = $this->page_info['page_contents']['text'];
 $content[] = '<div style="overflow: scroll;">'.$tpl_form_tag->output().'</div>';


?>
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.