novato
novato

Reputation: 61

PHP PDO statement to filter records

I am using PHP PDO to retrieve objects to an iOS app via JSON. There is a database table with event objects. It has three fields to store day, month and year. The owner of the database doesn't want to store dates in a date field, that means that I have to deal with it.

The app sends a URL with params,like this:

http://..hidden domain./myfile.php?dia_inicio=16&dia_final=15&mes_inicio=11&mes_final=12&ano_inicio=2014&ano_final=2015

That means that I am looking for events from 16Nov2014 to 15Dec2015.

This is my PHP code:

 $sql = 'SELECT * FROM tbagenda  WHERE  (dia_evento => :di AND mes_evento = :mi AND ano_evento = :ai) OR (dia_evento <= :df AND mes_evento = :mf AND ano_evento = :af) ';

    // use prepared statements, even if not strictly required is good practice
    $stmt = $dbh->prepare( $sql );
    $stmt->bindParam(':di', $dia_inicio, PDO::PARAM_INT);
    $stmt->bindParam(':df', $dia_final, PDO::PARAM_INT);
    $stmt->bindParam(':mi', $mes_inicio, PDO::PARAM_INT);
    $stmt->bindParam(':mf', $mes_final, PDO::PARAM_INT);
    $stmt->bindParam(':ai', $ano_inicio, PDO::PARAM_INT); 
    $stmt->bindParam(':af', $ano_final, PDO::PARAM_INT);
    // execute the query
    $stmt->execute();

    // fetch the results into an array
    $result = $stmt->fetchAll( PDO::FETCH_ASSOC );

    // convert to json
    $json = json_encode( $result );

    // echo the json string
    echo $json

How should I change my statement to make it work with dates like: 16Nov2014 to 28Nov2014 (same month), 16Nov2014 to 5Dec2014 (different month, same year) and 16Nov2014 to 02May2015(different year)?

ADDED LATER:

iOS log to show the URL send to the PHP file:

recuperar_eventos_dia.php?dia_inicio=11&dia_final=26&mes_inicio=11&mes_final=11&ano_inicio=2014&ano_final=2014

PHP part:

$start = "$ano_inicio-$mes_inicio-$dia_inicio";
$end = "$ano_final-$mes_final-$dia_final";

// In the SQL, concatenate the columns to make a YYYY-MM-DD date string
// and cast it to a MySQL DATE type.
// That makes it possible to use BETWEEN
$sql = 'SELECT 
  *
FROM tbagenda  
WHERE STR_TO_DATE(CONCAT_WS('-', ano_evento, mes_evento, dia_evento), "%Y-%m-%d") 
     BETWEEN :start AND :end';

// Bind and execute the statement with 2 parameters:
$stmt = $dbh->prepare( $sql );
$stmt->bindParam(':start', $start, PDO::PARAM_STR);
$stmt->bindParam(':end', $end, PDO::PARAM_STR);
$stmt->execute();

// fetch, etc...
$result = $stmt->fetchAll( PDO::FETCH_ASSOC );

        // convert to json
        $json = json_encode( $result );

        // echo the json string
        echo $json;

And now a screenshot from the table tbagenda: enter image description here

Upvotes: 4

Views: 1724

Answers (1)

Michael Berkowski
Michael Berkowski

Reputation: 270697

I'm sorry that you're stuck with this unfortunate table structure.

I would suggest concatenating the table's columns to form usable date strings, and cast them to a date value via MySQL's built in function STR_TO_DATE(). Do the same with your query string input values to make proper date comparisons with a BETWEEN operator.

// Begin by concatenating the input values into single date strings YYYY-MM-DD
$start = "$ano_inicio-$mes_inicio-$dia_inicio";
$end = "$ano_final-$mes_final-$dia_final";

// In the SQL, concatenate the columns to make a YYYY-MM-DD date string
// and cast it to a MySQL DATE type.
// That makes it possible to use BETWEEN
$sql = "SELECT * 
        FROM tbagenda  
        WHERE STR_TO_DATE(CONCAT_WS('-', ano_evento, mes_evento, dia_evento), '%Y-%m-%d') 
          BETWEEN :start AND :end";

// Bind and execute the statement with 2 parameters:
$stmt = $dbh->prepare( $sql );
$stmt->bindParam(':start', $start, PDO::PARAM_STR);
$stmt->bindParam(':end', $end, PDO::PARAM_STR);
$stmt->execute();

// fetch, etc...
$result = $stmt->fetchAll( PDO::FETCH_ASSOC );

The string operations will affect performance of this query. It would be better, as you know, if the dates were stored as a proper DATE so MySQL would not need to cast them for comparisons. They could also then be indexed.

Note also that I did not include validation of the date strings. You might consider checking them to be sure they make valid dates before executing the query.

// strtotime() would return false for invalid date strings...
if (strtotime($start) && strtotime($end)) {
  // The dates are valid, and so you can proceed with the query
  $sql = 'SELECT.....';
}

Upvotes: 3

Related Questions