user2307958 user2307958 - 2 years ago 65
PHP Question

Generating report with while loop

I have a table with various entries such as start date (contract), some random documents and monthly invoices and receipt in it, like this:

1 Contract Yes No
2 Documents No No
3 Policy Yes No
4 Order No No
5 Invoice Yes Yes
6 Receipt Yes Yes


1 1 01/01/2013
2 2 01/01/2013
3 3 01/01/2013
4 5 01/02/2013
5 6 01/02/2013
6 4 14/02/2013
7 5 01/03/2013
8 6 01/03/2013

TODAY 05/05/2013

My goal is to produce the output as shown below.
Display all existent documents, where CHECK? is Yes, and display missing documents where MONTHLY? is Yes.

01 Contract 01/01/2013 OK
02 Documents 01/01/2013 OK
03 Policy 01/01/2013 OK
04 Invoice 01/02/2013 OK
05 Receipt 01/02/2013 OK
06 Invoice 01/03/2013 OK
07 Receipt 01/03/2013 OK
08 Invoice 01/04/2013 Missing
09 Receipt 01/04/2013 Missing
10 Invoice 01/05/2013 Missing
11 Receipt 01/05/2013 Missing

I have written some code, but I not understand how to include monthly loop to display invoices and receipts.


// Get type
$query = "SELECT * FROM doc_type
WHERE check='1'";
$result = mysql_query($query) or die(mysql_error());
$num = mysql_numrows($result);

// Get docs
$check = array();
$query2 = mysql_query("SELECT document_type_id FROM documents");
while ($row = mysql_fetch_assoc($query2)) {
$check[] = $row['document_type_id'];


while ($i < $num) {
$document_type_id = mysql_result($result,$i,"document_type_id");
$document_type = mysql_result($result,$i,"document_type");
<tr class="grade">

<td><?php echo $document_type_id; ?></td>
<td><?php echo $document_type; ?></td>
if (in_array($document_type_id, $check)) {
echo "<p style='color:green'>Ok</p>";
} else {
echo "<p style='color:red'>Miss</p>";

Answer Source

First, I must point out that I am slightly confused by the example data, because it looks like you are loading the data from a database for the first table, but the example implies that there is a list of documents that is being saved elsewhere for each job or project.

If it were me, I'd create either an object or an array representing your final table. That object you can then fill based on the available data. Given the data structure that you seem to be already using, I will assume that you would rather not use an Object Oriented Programming approach.

First, create an array or object that stores your original table information that matches the document type and the monthly setting. Once you choose a data format, you can load the data format from your database for each different group of settings. I will use the settings you have shown in your example.

Personally, I'd use the document type id as the index key and use boolean values to represent Yes (true) and No (false) like this:

$doc_requirements = array(
    1 => array( "name" => "Contract", "check" => true, "monthly" =>  false ),
    2 => array( "name" => "Documents", "check" => false, "monthly" => false ),
    3 => array( "name" => "Policy", "check"=>true, "monthly"=>false ),
    4 => array( "name" => "Order", "check"=>false, "monthly"=>false ),
    5 => array( "name" => "Invoice", "check"=>true, "monthly"=>false ),
    6 => array( "name" => "Receipt", "check"=>true, "monthly"=>false )

Use your database to store as many of these tables as you need and load them before reviewing your document list.

Next I would create an array that represents the output table. Index it by date so you can determine if you have missing documents. Your data implies that on each date, that we can have more than one document, but not more than one document of the same type, so you might be able to use something like this:

/* pseudo_code */ $temp_table = array( 
      [date] => array( 
           [doc_type_name] => array(
                /* I assume that the document id is actually just the line number 
                  on the table, so I will leave the id out of this, but if it is 
                  not just the line on the table, add this field to the array: 
                       "id" => [id], */ 
                "status" => [status] )
           [doc_type_name] => array(
                "status" => [status] )
      [date2] => array(  ... ),

Load this array with the documents that you know about from your document array:

(note: you are using the mysql functions, which are currently deprecated, so you should look at using the msyqli functions instead)

$sql = #### /* SQL query string for your list of documents that are not missing */
$result = mysql_query($sql) or die(mysql_error());
    while( $document = mysql_fetch_assoc( $result ) ){
         $date = $document[ "date" ];
         $doc_type_id = $document[ "type_id" ];
         $doc_type_name = $doc_requirements[ $doc_type_id ]["name"];
         $temp_table[ $date ][ $doc_type_name ]["status"]= "OK" 
         /* we have the document, therefore, we know it is okay, 
            we can set that value immediately */
$start_date=#### /* set start date of contract */
$end_date=##### /*set end date of contract */
foreach( $doc_requirements as $requirement ){
    if( $requirement["monthly"] == true ){
        for( $cur_date = $start_date; $cur_date <= $end_date; $cur_date=#### ){ 
                 /*increment the date by whatever function you choose, 
                   I recommend using a DateTime object 
                   for your dates and incrementing by using the add() method */
            $current_doc_name = $requirement[ "name"];
            if( !isset( $temp_table[$cur_date][ $current_doc_name ] ) ){
                /* if the array value is not set, 
                   then a document of the type_name 
                   and current date specified does not exist,
                   because the requirement for "monthly" == true, 
                    we know that we should have it, so we will 
                   set the status to "Missing" */
                $temp_table[$cur_date][$current_doc_name]["status"] = "Missing";

Now we have an array organized by date, containing one document record for each document that we have a record of in our database (but no more than one of each type per date...If you need to change this, just change the data structure of the array to match your needs or use an object oriented approach that helps map out your thoughts more naturally). For any item that is Monthly = true (YES) we have a "Missing" stub created for it indicating that something was expected there but not found. Having this data array, we can cycle through it and produce the output.

I have noted above that your document id appears to just be the line number on the output table, so I will represent it the same way here:

$doc_id = 1;
foreach($temp_table as $cur_date){
    foreach($cur_date as $doc_name){
        $doc_id_string = your_func_format_id( $doc_id ); 
         /* use whatever function you like to make your doc_id two digits. 
            Perhaps printf() would be useful */
        $color_code = "style='color:green'";
        if( $doc_name["status"]=="Missing" ) $color_code = "style='color:red'";
        echo "<tr class='grade'><td>$doc_id_string</td><td>$doc_name</td><td>$cur_date</td><td><p $color_code>{$doc_name["status"]}</p></td>";

I have learned that using the correct data structure makes everything more simple. I have tried to use a data structure that reflects your example code. I highly recommend using Object Oriented Programming (OOP) techniques to implement your designs, because OOP forces every programmer to consider the shape of the data before considering the programming code, and it solves many problems.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download