Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

just wanted to share some changes I made #1

Open
Yohn opened this issue Nov 29, 2012 · 9 comments
Open

just wanted to share some changes I made #1

Yohn opened this issue Nov 29, 2012 · 9 comments

Comments

@Yohn
Copy link
Contributor

Yohn commented Nov 29, 2012

hey man, Thanks for creating this! so far its making it easier for me to convert out dated mysql_* functions to use pdo.. I also made some changes that I wanted to share with ya to see what you thought of it..

    public function bindValue($name, $value){
        $this->pdoStatement->bindValue($name, $value, $this->getPDOConstantType($value));
        return $this;
    }

    /*
    * check out the insert, update, or delete functions to see how this is used
    */
    public function bindValues($arr){
        foreach ($arr as $k){
            $this->bindValue(':'.$k[0], $k[1]);
        }
        return $this;
    }

    private function getPDOConstantType($var){
        if(is_int($var))
            return PDO::PARAM_INT;
        if(is_bool($var))
            return PDO::PARAM_BOOL;
        if(is_null($var))
            return PDO::PARAM_NULL;
        //Default 
        return PDO::PARAM_STR;
    }

    /*
    * returns the number of rows that were effected with UPDATE or DELETE statements
    */
    public function rowsEffected(){
        return $this->pdoStatement->rowCount();
    }

    /*
    * added a few extra functions to make some things easier
    *
    */
    function insert($table, $arr){
        foreach ($arr as $key => $val) {
            $fields[] = '`'.$key.'`';
            $params[] = ':'.$key;
            $values[] = array($key, $val);
        }

        $fields = implode(',', $fields);
        $params = implode(',', $params);

        $sql = "INSERT INTO `".$table."` (".$fields.") VALUES (".$params.")";
        $this->prepare($sql)->bindValues($values)->execute();
        return $this->lastId();
    }

    function update($table, $arr, $where, $limit = 1){
        foreach($arr as $key => $val){
            $fields[] = '`'.$key.'` = :'.$key;
            $values[] = array($key, $val);
        }
        $i = 0;
        foreach($where as $k => $v){
            $i++;
            // the $i is in there because row wouldnt update with :value already being set above
            $whe[] = '`'.$k.'` = :'.$k.$i;
            $values[] = array($k.$i, $v);
        }

        $fields = implode(',', $fields);
        $where = isset($whe) ? 'WHERE '.implode(' AND ', $whe) : '';

        $sql = "UPDATE `".$table."` SET ".$fields." ".$where." LIMIT ".$limit;
        $this->prepare($sql)->bindValues($values)->execute();
        return $this->rowsEffected();
    }

    function delete($table, $arr, $limit=1){
        foreach($arr as $key => $val){
            $fields[] = '`'.$key.'` = :'.$key;
            $values[] = array($key, $val);
        }

        $fields = implode(' AND ', $fields);

        $sql = "DELETE FROM `".$table."` WHERE ".$fields." LIMIT ".$limit;
        $this->prepare($sql)->bindValues($values)->execute();
        return $this->rowsEffected();
    }
@Yohn
Copy link
Contributor Author

Yohn commented Nov 29, 2012

I also needed to insert multiple rows into the database at the same time, so I created a function to do that as well..

within the pdo class -

    function insertMulti($table, $arr){
        $i = 0;
        $e = 0;
        foreach($arr as $d){
            if(isset($placeholders)) unset($placeholders);
            foreach($d as $key => $val){
                $e++;
                if(!isset($fields) || isset($fields) && !in_array("`".$key."`", $fields)) $fields[] = "`".$key."`";
                $placeholders[] = ":".$key.$e; //"?";
                $values[] = array($key.$e, $val);
                $i ++;
            }
            $params[] = '(' . implode(',', $placeholders) . ')';
        }

        $fields = implode(',', $fields);
        $params = implode(',', $params);

        $sql = "INSERT INTO `".$table."` (".$fields.") VALUES ".$params;
        $this->prepare($sql)->bindValues($values)->execute();
        return $this->lastId();
    }

the return $this->lastId() returns the first rows id number

and then to call the function --

$go = $db->insertMulti('test', array(
    array(
        'test' => 93,
        'test2' => '93 test2',
        'test3' => 'test3 is 93'
    ), array(
        'test' => 94,
        'test2' => '94 test2',
        'test3' => 'test3 is 94'
    ), array(
        'test' => 95,
        'test2' => '95 test2',
        'test3' => 'test3 is 95'
    )
));

@flrnull
Copy link
Owner

flrnull commented Nov 29, 2012

Thank you.
You also did usefull changes, I'll add some of it soon and write my comments.

@flrnull
Copy link
Owner

flrnull commented Nov 29, 2012

Sources have been updated.

Some comments:

  1. We shouldn't mix Driver layer and Data layer. I created DBAL class for inserts, updates and other Data layer methods.
  2. In Driver layer we shouldn't hide ":" in prepared params, because we need more compatibility with \PDO class (for ability to fast moving from PDO to PDOChainer and vice versa). Also PDOChainer is only interface for PDO, it shouldn't wrap any extended logic.
  3. But in DataBaseLayer we could hide ":", because we shouldn't know what's going inside (prepared statement or not).
  4. Method getPDOConstantType — is a bad idea. Param types describe which data we expect in request, but not which data come from user.

@Yohn
Copy link
Contributor Author

Yohn commented Nov 29, 2012

nice, thanks for the tips! I also didnt like having the insert / update / delete strategies within the chainer itself and wanted it to extend the chainer class but didnt do that in the beginning..
I'll give your updates a try and see see how they work with what I already did.. It doesnt look like it would be to much of a hassle to use your way
I do want to add another function for selecting rows to the DBAL because I want to set up an optional caching method for it but I havent tackled it yet.. I'll let ya know when I complete that one

@Yohn
Copy link
Contributor Author

Yohn commented Nov 29, 2012

so after playing with how you set it up a bit, I think it would be useful to extend the PDOChainer class, rather than having 2 variables for the database. doing it that way would let me only pass 1 variable to other classes and functions to make calls to the database instead of passing both variables for PDOChainer, and DBAL.. this would help in case we need to use the chainer ->query() method, or to prepare our select statements while still being able to update and insert rows smoothly with dbal

@Yohn
Copy link
Contributor Author

Yohn commented Nov 30, 2012

heres what I ended up doing to have 1 variable for the classes
PDOChainer --

    public function bindValues(array $binds) {
        foreach($binds as $valuesArray) {
            $this->bindValue($valuesArray[0], $valuesArray[1], isset($valuesArray[2]) ? $valuesArray[2] : \PDO::PARAM_STR);
        }
        return $this;
    }

DBAL class --

    public $pdo;

    public function __construct(array $params) {
        $this->pdo = new PDOChainer($params);
    }

then you just make 1 variable for the database with $dbal = new DBAL($params); and I can access the PDOChainer class with $dbal->pdo->query("SELECT * FROM table")->fetchAll();
there might be a better way at doing it, but I'm still getting the hang of writing classes

I did create a function to cache select statements, but I think they would be better off in a separate class all together..

I dont mean to be a pain with these, I just liked your PDO wrapper versus others out there.. its easier for me to read and it gets right to the point of what its doing easily.. so yeah, I'm just letting ya know what I've been doing for my project..

@Yohn
Copy link
Contributor Author

Yohn commented Dec 4, 2012

figured I'd post this here as well.. this is what I've been using for selecting rows, or sending an execute() from the DBAL, you'll need to update the bindValues function with the code above to use this properly, or make sure you put in the proper \PDO::PARAM_* value..

    function select($sql, $limit = 1, $binds = ''){
        $get = $this->pdo->prepare($sql);
        if($binds != ''){
            $get = $get->bindValues($binds);
        }
        $get = $get->execute();
        if($limit > 0){
            if($limit > 1){
                $get = $get->fetchAll();
            } else {
                $get = $get->fetch();
            }
        }
        return $get;
    }

I did create a caching function as well for some of the selects that I have been using.. if you'd like to see it I can post that as well, but I figured the select function would be a good one to add to your DBAL class as well

@flrnull
Copy link
Owner

flrnull commented Dec 4, 2012

Yes, it will be good if you'll post it here. I'll update DBAL class soon.

Also I did fix bindValues() function as you wrote.

And about DBAL constructor with PDO object encapsulation:
Main reason why I don't do that — it's flexibility. If you encapsulate PDOChainer class, you can't extend it. For example you want to create your class

class PDOChainerAdvanced extends PDOChainer {}

and then you can use DBAL without of changing anything in external classes (it mean you still could update these classes from github without pain)

$dbal = new DBAL(new PDOChainerAdvanced($params));

The better way — to create outstanding function for example createConnection() which will init all needed objects.

@Yohn
Copy link
Contributor Author

Yohn commented Dec 4, 2012

yeah, I dont see any plans for me to extend the PDOChainer class.. I'd rather do everything to that class from the DBAL, or another class like it.. possibly doing class DBAL extends PDOChainer, but for right now its good..

for the caching function I set up, I created a private variable at the top of the DBAL class for the directory to store everything, and added its value to the __construct() function

    private $CacheDir;

    public function __construct(array $options) {
        $this->pdo = new PDOChainer($options);
        $this->CacheDir = $_SERVER['DOCUMENT_ROOT'].'/cache/PDO/';
        if(!is_dir($this->CacheDir)){
            $go = @mkdir($this->CacheDir, '0777');
        }
    }

and then the caching functions..

    function cache_select($table, $sql, $limit, $binds = '', $cache = 0){
        if($cache > 0){
            $check = $this->checkCache($cache, $table, $sql, $binds);
            if($check == false) $runQuery = true; else return $check;
        } else { $runQuery = true; }
        if($runQuery == true){
            $get = $this->pdo->prepare($sql);
            if($binds != ''){
                $get = $get->bindValues($binds);
            }
            $get = $get->execute();
            if($limit > 1){
                $get = $get->fetchAll();
            } else {
                $get = $get->fetch();
            }
            if($cache > 0){
                $sers = serialize($get);
                if($sers != 'a:0:{}'){
                    $file = $this->CacheDir.$table.'/'.md5($sql.serialize($binds)).'.php';
                    $fp = fopen($file, "w");
                    flock($fp, LOCK_EX);
                    fwrite($fp, "<?php die('error..'); ?>\n".$sers);
                    flock($fp, LOCK_UN);
                    fclose($fp);
                }
            }
            return $get;
        }
    }

    function checkCache($cache, $table, $sql, $binds = ''){
        if(is_dir($this->CacheDir.$table)){
            $file = $this->CacheDir.$table.'/'.md5($sql.serialize($binds)).'.php';
            if(is_file($file)){
                $lastUpdate = filemtime($file);
                $ups = time()-($cache*60);
                if($lastUpdate > $ups){
                    $contents = file($file);
                    return unserialize($contents[1]);
                } else {
                    return false;
                }
            } else {
                return false;
            }
        } else {
            $go = @mkdir($this->CacheDir.$table.'/', '0777');
            return false;
        }
    }

and an example..

$UserId = 1;
$sql = "SELECT id, name, about FROM `users` WHERE `id` = :id LIMIT 1";
$binds[] = array(':id', $UserId, \PDO::PARAM_INT);

// the 60 at the end is how many minutes to cache the results for
$get = $DBAL->cache_select('users', $sql, 1, $binds, 60);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants