使用PDO数据库类而不是每次都创建新的连接?

时间:2017-02-08 16:04:03

标签: php mysql pdo

我有这个PDO数据库类

class clsDatabase{
  // db settings
  private $host   = 'localhost';
  private $user   = 'test';
  private $dbname = 'test';
  private $pass   = 'test1';

  private $dbh;
  private $error;

  public function __construct(){
        // Set DSN
        $dsn = 'mysql: host=' . $this->host . ';dbname=' . $this->dbname;
        // Set options
        $options = array(
            PDO::ATTR_PERSISTENT            => true,
            PDO::ATTR_ERRMODE               => PDO::ERRMODE_EXCEPTION,
            PDO::MYSQL_ATTR_INIT_COMMAND    => 'SET NAMES UTF8'
        );
        // Create a new PDO instanace
        try{
            $this->dbh = new PDO($dsn, $this->user, $this->pass, $options); 
        }
        // Catch any errors
        catch(PDOException $e){
            $this->error = $e->getMessage();
            echo $this->error;
            exit;
        }       
    }

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

我尝试在不同的类中分隔我的代码,例如我有一个连接到clsUserController的clsDBUser。我这样做,所以我知道什么类使用什么数据库代码。我的clsDBUser类看起来像这样

class clsDBUser extends clsDatabase {
    // construct
    public function __construct() {
        parent::__construct();
    }

    // get users
    public function getUsers($users_id){
        $query = "
            SELECT 
                email
            FROM 
                users
            WHERE 
               users_id = :users_id
        ";          
        $this->query($query);
        $this->bind(':users_id', $users_id);

        if($row = $this->single()){
            $this->close();
            return $row;
        }
        $this->close();
        return false;       
    }
}

我想知道这是否可行,或者我现在正在每个班级创建一个新的数据库连接?因为通常在PHP4中(是的,我知道旧)我无法识别我每次都必须建立新的数据库连接。

我是否需要改进这一点,我该如何改进呢?

5 个答案:

答案 0 :(得分:1)

嘿,我会做这样的事情

class DB {
   // connectionStuff goes Here
}

class Model {
   private $db

   public function __construct($db) {
       $this->db = $db;
   }
}

使用:

$db = new DB("your connection stuff goes here");


$model = new Model($db);
$userModel = new UserModel($db);
$anotherModel = new AnotherModel($db);

答案 1 :(得分:1)

重建:

仅包含连接内容的clsDB类

class clsDB{
    // db settings
    private $host   = 'localhost';
    private $user   = 'test';
    private $dbname = 'test';
    private $pass   = 'test';

    private $dbh;
    private $error;

    public function __construct(){
        // Set DSN
        $dsn = 'mysql: host=' . $this->host . ';dbname=' . $this->dbname;
        // Set options
        $options = array(
            PDO::ATTR_PERSISTENT            => true,
            PDO::ATTR_ERRMODE               => PDO::ERRMODE_EXCEPTION,
            PDO::MYSQL_ATTR_INIT_COMMAND    => 'SET NAMES UTF8'
        );
        // Create a new PDO instanace
        try{
            $this->dbh = new PDO($dsn, $this->user, $this->pass, $options); 
        }
        // Catch any errors
        catch(PDOException $e){
            $this->error = $e->getMessage();
            echo $this->error;
            exit;
        }       
    }           
}

clsDBLogin:

class clsDBLogin{   
   private $db;

   public function __construct($db) {
       $this->db = $db;
   }
}

在index.php中我做:

$clsDB      = new clsDB();
$clsDBLogin = new clsDBLogin($clsDB);

在clsDBLogin中我会这样做:

public function validateLogin($email){  
    $email = str_replace(' ', '', $email);
    $email = strtolower($email);

    // Check user in db to start verification
    $query = '
        SELECT
            u.*, ui.*
        FROM
            users as u,
            users_info as ui
        WHERE
            u.users_id = ui.users_id
        AND
            u.email = :email                
    ';
    $this->db->prepare($query);
    $this->db->bindValue(':email', $email, PDO::PARAM_STR);

    if($this->db->execute()){
        if($row = $this->db->fetch(PDO::FETCH_ASSOC)){
            return $row;            
        }       
    }   
}

答案 2 :(得分:0)

你应该采取mr.void答案中显示的道路。简而言之:

  1. 摆脱clsDatabase。
  2. 创建PDO实例。
  3. 将它传递给clsDBLogin的属性,就像在mr.void的答案中所示。
  4. 然后以$ this-> db-> prepare()等形式使用此pdo实例
  5. 所以它应该像

    class clsDBLogin
    {
        public function __construct($db)
        {
            $this->db = $db;
        }
    
        public function validateLogin($email)
        {  
            $email = trim($email);
    
            // Check user in db to start verification
            $query = 'SELECT * FROM users u, users_info ui 
                      WHERE u.users_id = ui.users_id AND u.email = ?';
            $stmt = $this->db->prepare($query);
            $stmt->execute([$email]);
            return $stmt->fetch();
        }
    }
    
    $dsn = 'mysql: host=localhost;dbname=test;charset=utf8';
    $options = array(
            PDO::ATTR_PERSISTENT            => true,
            PDO::ATTR_ERRMODE               => PDO::ERRMODE_EXCEPTION,
    );
    // Create a new PDO instanace
    $pdo = new PDO($dsn, $this->user, $this->pass, $options); 
    
    $DBLogin = new clsDBLogin($pdo);
    $user = $DBLogin->validateLogin($email);
    

答案 3 :(得分:0)

这里有三层:

  • 数据库连接器:您可以使用纯PDO或数据库抽象层库(Doctrine DBAL
  • 实体存储库:换句话说,某种ORM。 Doctrine提供高级ORM功能。当然,您可以编写自己的轻量级解决方案。
  • 实体:可以是简单的CRUDActiveRecord或逻辑记录的任何其他对象表示。

当我们手动执行此操作时...首先,不要相互扩展。通常:永远不要将另一层扩展到另一层。请改用Dependency Injection(DI)。

当您将所有特定信息(依赖项)作为构造函数参数传递时,它是一个非常简单的DI案例。我的类似活动对象的示例Entity只知道实体应该如何表现(在存储库中的密钥处)。为简单起见,我使用原始SQL。

存储库类:

class Repository {

    private $oPDO;
    private $tableName;
    private $keyFieldName;

    public function __construct($oPDO, $tableName, $keyFieldName) {
        $this->oPDO = $oPDO;
        $this->tableName = $tableName;
        $this->keyFieldName = $keyFieldName;
    }

    public function getPDO() {
        return $this->oPDO;
    }

    public function getTableName() {
        return $this->tableName;
    }

    public function getKeyFieldName() {
        return $this->keyFieldName;
    }

    public function getEntity($id) {
        return new Entity($this, $id);
    }

    public function createEntity() {
        return new Entity($this, null);
    }

}

实体类:

class Entity implements ArrayAccess {

    private $oRepository;
    private $id;

    private $record = null;

    public function __construct($oRepository, $id) {
        $this->oRepository = $oRepository;
        $this->id = $id;
    }

    public function load($reload = false) {
        if (!$this->record && !$this->id) {
            return false;
        }

        if (!$reload && !is_null($this->record)) {
            return true;
        }

        $quotedTableName = $this->quoteIdentifier($this->oRepository->getTableName());
        $quotedKeyFieldName = $this->quoteIdentifier($this->oRepository->getKeyFieldName());
        $selectSql = "SELECT * FROM {$quotedTableName} WHERE {$quotedKeyFieldName} = ?";
        $oStatement = $this->oRepository->getPDO()->prepare($selectSql);
        $this->bindParam($oStatement, 1, $this->id);
        $oStatement->execute();

        $result = $oStatement->fetch(PDO::FETCH_ASSOC);

        if ($result === false || is_null($result)) {
            return false;
        }

        $this->record = $result;
        return true;
    }

    public function save() {
        $oPDO = $this->oRepository->getPDO();

        $tableName = $this->oRepository->getTableName();
        $keyFieldName = $this->oRepository->getKeyFieldName();
        $quotedTableName = $this->quoteIdentifier($tableName);
        $quotedKeyFieldName = $this->quoteIdentifier($keyFieldName);

        if (is_null($this->id)) {
            $insertSql = "INSERT INTO {$quotedTableName} (";
            $insertSql .= implode(", ", array_map([$this, "quoteIdentifier"], array_keys($this->record)));
            $insertSql .= ") VALUES (";
            $insertSql .= implode(", ", array_fill(0, count($this->record), "?"));
            $insertSql .= ")";
            $oStatement = $oPDO->prepare($insertSql);

            $p = 1;
            foreach ($this->record as $fieldName => $value) {
                $this->bindParam($oStatement, $p, $value);
                $p++;
            }

            if ($oStatement->execute()) {
                $this->id = $oPDO->lastInsertId();
                return true;
            } else {
                return false;
            }
        } else {
            $updateSql = "UPDATE {$quotedTableName} SET ";
            $updateSql .= implode(" = ?, ", array_map([$this, "quoteIdentifier"], array_keys($this->record)));
            $updateSql .= " = ? WHERE {$quotedKeyFieldName} = ?";
            $oStatement = $oPDO->prepare($updateSql);

            $p = 1;
            foreach ($this->record as $fieldName => $value) {
                $this->bindParam($oStatement, $p, $value);
                $p++;
            }
            $this->bindParam($oStatement, $p, $this->id);

            if ($oStatement->execute()) {
                if (isset($this->record[$keyFieldName])) {
                    $this->id = $this->record[$keyFieldName];
                }
                return true;
            } else {
                return false;
            }
        }
    }

    public function isExisting($reload = false) {
        if (!$this->record && !$this->id) {
            return false;
        }

        if (!$reload && !is_null($this->record)) {
            return true;
        }

        $quotedTableName = $this->quoteIdentifier($this->oRepository->getTableName());
        $quotedKeyFieldName = $this->quoteIdentifier($this->oRepository->getKeyFieldName());
        $selectSql = "SELECT 1 FROM {$quotedTableName} WHERE {$quotedKeyFieldName} = ?";
        $oStatement = $this->oRepository->getPDO()->prepare($selectSql);
        $oStatement->bindParam(1, $this->id);
        $oStatement->execute();

        $result = $oStatement->fetch(PDO::FETCH_ASSOC);

        if ($result === false || is_null($result)) {
            return false;
        }

        return true;
    }

    public function getId() {
        return $this->id;
    }

    public function getRecord() {
        $this->load();
        return $this->record;
    }

    public function offsetExists($offset) {
        $this->load();
        return isset($this->record[$offset]);
    }

    public function offsetGet($offset) {
        $this->load();
        return $this->record[$offset];
    }

    public function offsetSet($offset, $value) {
        $this->load();
        $this->record[$offset] = $value;
    }

    public function offsetUnset($offset) {
        $this->load();
        $this->record[$offset] = null;
    }

    private function quoteIdentifier($name) {
        return "`" . str_replace("`", "``", $name) . "`";
    }

    private function bindParam($oStatement, $key, $value) {
        $oStatement->bindParam($key, $value);
    }

}

<强>用法:

$oRepo = new Repository($oPDO, "user", "user_id");

var_dump($oRepo->getEntity(2345235)->isExisting());

$oSameUser = $oRepo->getEntity(1);
var_dump($oSameUser->isExisting());
var_dump($oSameUser->getRecord());

$oNewUser = $oRepo->createEntity();
$oNewUser["username"] = "smith.john";
$oNewUser["password"] = password_hash("ihatesingletons", PASSWORD_DEFAULT);
$oNewUser["name"] = "John Smith";
$oNewUser->save();

$oNewUser["name"] = "John Jack Smith";
$oNewUser->save();

当然,您可以使用特定行为从MoreConcreteRepositoryRepository MoreConcreteEntity扩展Entity

答案 4 :(得分:-1)

只是不要从连接类(clsDBUser)扩展实体(clsDatabase)。

clsDatabase使用单例(或更高级的模式)。

例如:

class clsDatabase {

    static private $instance = null;

    // some other private fields

    private function __construct(/* parameters*/) {
        // do it
    }

    public static function instance() {
        if (is_null(self::$instance)) {
            self::$instance = new self(/* pass any parameters */);
        }
        return self::$instance;
    }

    public function queryRow($query) {
        $oStatement = $this->dbh->prepare($query);

        // ...

        return $row;
    }
}

class clsDBUser {

    public function getUser($id) {
        $query = "...";
        return $clsDatabase::instance()->queryRow($query);
    }

}