making abstract pdo class

i'm just switching from mysql_* to PDO since i read that mysql_* will be removed in the future and now i have no idea to abstract my current class for insert,update,and delete operation to PDO, maybe some one can point me out how to translate it to PDO based?

this is my connection class for handling all connection and other related function (i already making this one PDO so there is no problem with this)

<?php
require_once(folder.ds."constants.php");

class MySQLDatabase {

    private $dbh;
    private $host = DB_SERVER;
    private $dbname = DB_NAME;

    private $stmt;
    public $query_terakhir;
    public $error_text;

    function __construct(){
        $this->open_connection();
    }

    public function open_connection(){
        $dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
        $options = array(
            PDO::ATTR_PERSISTENT => true, 
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        );

        try{
            $this->dbh = new PDO($dsn,DB_USER,DB_PASS,$options);
        }
        catch(PDOException $e) {
            date_default_timezone_set('Asia/Jakarta');
            $dt = time();
            $waktu = strftime("%Y-%m-%d %H:%M:%S", $dt);
            $log = array_shift(debug_backtrace());
            file_put_contents('PDOErrors.txt',$waktu. ": " .$e->getMessage(). ": " .$log['file']. ": line " .$log['line']. "\n", FILE_APPEND);
        }
    }

    public function query($sql){
        $this->stmt = $this->dbh->prepare($sql);
    }

    public function bind($param, $value, $type = null){
        if (is_null($type)) {
            switch (true) {
                case is_int($value):
                    $type = PDO::PARAM_INT;
                    break;
                case is_bool($value):
                    $type = PDO::PARAM_BOOL;
                    break;
                case is_null($value):
                    $type = PDO::PARAM_NULL;
                    break;
                default:
                    $type = PDO::PARAM_STR;
            }
        }
        $this->stmt->bindValue($param, $value, $type);
    }

    public function execute(){
        return $this->stmt->execute();
    }

    public function fetchall(){
        return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function fetch(){
        return $this->stmt->fetch(PDO::FETCH_ASSOC);
    }

    public function rowCount(){
        return $this->stmt->rowCount();
    }

    public function lastInsertId(){
        return $this->dbh->lastInsertId();
    }

    public function beginTransaction(){
        return $this->dbh->beginTransaction();
    }

    public function endTransaction(){
        return $this->dbh->commit();
    }

    public function cancelTransaction(){
        return $this->dbh->rollBack();
    }

    public function debugDumpParams(){
        return $this->stmt->debugDumpParams();
    }
}

$database = new MySQLDatabase();

?>

and here is one of my class that help me to do saving(create or update), delete and others, with this class i only need to change $nama_tabel for table name, $db_fields for my table fields and public $xxxxx that match with my table fields and create, update and delete function can work perfectly...

but with pdo i just can't figure out how to make it work for create, update and delete with the same method as above....

<?php
require_once('database.php');

class staff{
    public static $nama_tabel="staff";
    protected static $db_fields = array('id','name','job');

    public $id;
    public $name;
    public $job;

    private function has_attribute($attribute){
        $object_var = $this->attributes();
        return array_key_exists($attribute,$object_var);
    }

    protected function attributes(){
        $attributes = array();
        foreach(self::$db_fields as $field){
            if(property_exists($this, $field)){
                $attributes[$field] = $this->$field;
            }
        }
        return $attributes;
    }

    protected function sanitized_attributes(){
        global $database;
        $clean_attributes = array();
        foreach($this->attributes() as $key => $value){
            $clean_attributes[$key] = $database->escape_value($value);
        }
        return $clean_attributes;
    }

    public function create(){
        global $database;
        $attributes = $this->sanitized_attributes();

        $sql = "INSERT INTO " .self::$nama_tabel." (" ;
        $sql .= join(", ", array_keys($attributes));
        $sql .=")VALUES('";
        $sql .= join("', '", array_values($attributes));
        $sql .= "')";
        if($database->query($sql)){
            $this->id_kategori = $database->insert_id();
            return true;
        }else{
            return false;
        }
    }

    public function update(){
        global $database;
        $attributes = $this->sanitized_attributes();
        $attribute_pairs = array();
        foreach($attributes as $key => $value){
            $attribute_pairs[] = "{$key}='{$value}'";
        }

        $sql ="UPDATE " .self::$nama_tabel." SET ";
        $sql .= join(", ", $attribute_pairs);
        $sql .=" WHERE id=" . $database->escape_value($this->id);
        $database->query($sql);

        return($database->affected_rows() == 1) ? true : false;
    }

    public function delete(){
        global $database;

        $sql = "DELETE FROM " .self::$nama_tabel;
        $sql .= " WHERE id=". $database->escape_value($this->id);
        $sql .= " LIMIT 1";
        $database->query($sql);

        if(!empty($this->gambar)){
            $target = website .ds. $this->upload_dir .ds. $this->gambar;
            unlink($target);
        }

        return($database->affected_rows() == 1) ? true : false;
    }



}

?>

updated: this is my approach for create function after tweaking from update function from GolezTrol, but not inserting the value instead it inserting name=:name and content=:content and so on Updated:its already fixed! here is the right one

public function create(){
    global $database;
    $attributes = $this->attributes();

    $attribute_pairs = array();
    foreach($attributes as $key => $value){
        $attribute_pairs[] = ":{$key}";
    }

    $sql = "INSERT INTO " .self::$nama_tabel." (" ;
    $sql .= join(", ", array_keys($attributes));
    $sql .=")VALUES(";
    $sql .= join(", ", $attribute_pairs);
    $sql .= ")";

    $database->query($sql);

    foreach($attributes as $key => $value){
        $database->bind(":$key", $value);
    }

    if($database->execute()){
        $this->id = $database->lastInsertId();
        return true;
    }else{
        return false;
    }
}

2nd update : i experiencing some weird thing in while loop operation where i doing while and inside the while i check if this field id is equal with my other table id then i will show that id name field... and it show, but stoping my while loop, so i only get 1 row while loop (it supposed to show 40 row)

$database->query($sql_tampil);
$database->execute();
while($row = $database->fetch()){
   $output = "<tr>";
        if(!empty($row['id']))

            $output .="<td><a data-toggle=\"tooltip\" data-placement=\"top\" 
                            title=\"Tekan untuk mengubah informasi kegiatan ini\" 
                            href=\"ubah_cuprimer.php?cu={$row['id']}\"
                            >{$row['id']}</a></td>";
        else
            $output .="<td>-</td>";

        if(!empty($row['name'])){
            $y = "";
            $x = $row['name'];
            if(strlen($x)<=40)
                $y = $x;
            else
                $y=substr($x,0,40) . '...';

            $output .="<td><a data-toggle=\"tooltip\" data-placement=\"top\" 
                            title=\"{$row['name']}\" 
                             href=\"ubah_cuprimer.php?cu={$row['id']}\"
                        > {$y} </td>";
        }else
            $output .="<td>-</td>";

        $wilayah_cuprimer->id = $row['wilayah'];
        $sel_kategori = $wilayah_cuprimer->get_subject_by_id();
        if(!empty($sel_kategori))
           $output .="<td><a href=\"#\" class=\"modal1\"
                            data-toggle=\"tooltip\" data-placement=\"top\" 
                            title=\"Tekan untuk mengubah kategori artikel ini\"  
                            name={$row['id']}>{$sel_kategori['name']}</a></td>";
        else
           $output .="<td><a href=\"#\" class=\"modal1\"
                            data-toggle=\"tooltip\" data-placement=\"top\" 
                            title=\"Tekan untuk mengubah kategori artikel ini\"  
                            name={$row['id']}>Tidak masuk wilayah</a></td>";

        if(!empty($row['content'])){
            $content = html_entity_decode($row['content']);
            $content = strip_tags($content);
            $z = "";
            $v = $content;
            if(strlen($v)<=40)
                $z = $v;
            else
                $z=substr($v,0,40) . '...';

            $output .="<td><a data-toggle=\"tooltip\" data-placement=\"top\" 
                            title=\"{$content}\" 
                             href=\"ubah_cuprimer.php?cu={$row['id']}\"
                        >{$z}</a> </td>";
        }else
            $output .="<td>-</td>";


        if(!empty($row['tanggal']))
            $output .="<td>{$row['tanggal']}</td>";
        else
            $output .="<td>-</td>";

        if(!empty($row['id']))
            $output  .="<td><button class=\"btn btn-default modal2\"
                            name=\"{$row['id']}\" 
                            data-toggle=\"tooltip\" data-placement=\"top\" 
                            title=\"Tekan untuk menghapus layanan ini\" ><span 
                            class=\"glyphicon glyphicon-trash\"></span></button></td>";
        else
            $output .="<td>-</td>";

    $output .="</tr>";

   echo $output;
}

And here is my $wilayah_cuprimer->get_subject_by_id(); function

public function get_subject_by_id(){
    global $database;
    $sql = "SELECT * ";
    $sql .= "FROM ".self::$nama_tabel;
    $sql .= " WHERE id = :id" ;
    $sql .= " LIMIT 1";

    $database->query($sql);
    $database->bind(":id",$this->id);
    $database->execute();
    $array = $database->fetch();

    return $array; 
}

Answers


Good that you make the transition to PDO. It's better to do it now, then to find out some day that you can't upgrade PHP, because it would break your application.

As far as I can tell, $database->query($sql); only prepares a statement. That is the first step, but after that you need to execute it as well. You already have the $database->execute() method for that, but you don't call it in your insert, update and delete functions.

Apart from that issue, it would even be better if you used bind parameters for the updates too, and leave escaping strings up to the database.

It's hard to test your class in its entirety, but I hope this gives you some ideas. I added comments to describe the steps.

public function update(){
    global $database;

    // Don't need 'clean' attributes if you bind them as parameters.
    // Any conversion you want to support is better added in $database->bind,
    // but you don't need, for instance, to escape strings.
    $attributes = $this->attributes();

    // Place holders for the parameters. `:ParamName` marks the spot.
    $attribute_pairs = array();
    foreach($attributes as $key => $value){
        $attribute_pairs[] = "{$key}=:{$key}";
    }

    $sql ="UPDATE " .self::$nama_tabel." SET " .
          join(", ", $attribute_pairs) .
          " WHERE id = :UPDATE_ID";
    $database->query($sql);

    // Bind the ID to update.
    $database->bind(':UPDATE_ID', $this->id);

    // Bind other attributes.
    foreach($attributes as $key => $value){
        $database->bind(":$key", $value);
    }

    // Execute the statement.
    $database->execute();

    // Return affected rows. Note that ->execute also returns false on failure.
    return($database->affected_rows() == 1) ? true : false;
}

Need Your Help

Generate All Possible Trees

haskell binary-tree list-comprehension

Given the following data type definition:

AngularJS binding datatimepicker selected value to ng-model

angularjs angular-ngmodel bootstrap-datetimepicker

I have a newbie AngularJS question - I am using a datetimepicker library and I'm having a hard time getting the date selected to bind to my model... I saw some posts talking about not being able to

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.