【问题标题】:PDO and PHP OOP Code AdvicePDO 和 PHP OOP 代码建议
【发布时间】:2013-03-12 23:38:16
【问题描述】:

我对 PHP 面向对象编程非常陌生,所以想知道是否可以就我创建的数据库对象获得一些好的建议。

我调用类 db 并将我的类包含到每个页面加载中并启动数据库对象using $db = new db。然后,我为我可能需要的每个操作(从数据库构建菜单、获取登录信息等)调用其中的方法,并根据我想要做什么使用不同的参数。

它将它的第一个参数作为带有 ? 的查询。符号作为我要绑定的值的替换,第二个参数是要在数组中绑定到它的值,然后在prepared_statement方法中循环,第三个参数是类型( FETCH_ARRAY 返回一个SELECT语句行的数组, NUM_ROWS 返回受影响的行数,INSERT 返回最后插入的 ID)。

下面是我如何调用此函数的示例:

$db->prepared_execute( "SELECT * FROM whatever WHERE ? = ? ", array( 'password', 'letmein' ), NUM_ROWS );

如果没有要绑定的参数或不需要返回,第二个和第三个参数是可选的。

由于我是 OOP 的新手,我发现我很难准确了解何时正确使用以及什么是公共、私有、静态函数/变量和设计模式(单例等)。

我已经阅读了许多教程以达到我所掌握的程度,但我觉得现在我需要把它带到这里以获得进一步的答案或建议,了解下一步如何使用 OOP 以及我已经构建的这个类。

它按原样工作,这对我来说是一个很好的起点,除了我将在接下来添加的任何错误处理,但我想确保我没有在这里犯任何明显的设计错误。

类的代码如下:

class db {

var $pdo;

public function __construct() {

$this->pdo = new PDO('mysql:dbname=' . DB_NAME . ';host=' . DB_HOST . ';charset=utf8', DB_USER, DB_PASS);
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

}

public function prepared_execute( $query, $bind_values = null, $type = null ) {

$preparedStatement = $this->pdo->prepare( $query );

if( $bind_values ) {

$i = 1;

foreach( $bind_values as $bind_value ) {

$preparedStatement->bindValue($i, $bind_value);

$i++;

} }

$preparedStatement->execute();

if(     $type == FETCH_ARRAY ) { return $preparedStatement->fetchAll();  }

elseif( $type == NUM_ROWS ) { return $preparedStatement->rowCount();     }

elseif( $type == INSERT   ) { return $this->pdo->lastInsertId();         }

else{   return true; }

}

【问题讨论】:

    标签: php oop class methods pdo


    【解决方案1】:

    我开发了类似的东西,它可以帮助你。

    public function select($sql, $array = array(), $fetchMode = PDO::FETCH_ASSOC){      
        $stmt = $this->prepare($sql);
    
        foreach ($array as $key => $value){
            $stmt->bindValue("$key", $value);
        }
    
        $stmt->execute();
        return $stmt->fetchAll();
    }
    
    public function insert($table, $data){
        ksort($data);
    
        $fieldNames = implode('`,`', array_keys($data));
        $fieldValues = ':' .implode(', :', array_keys($data));
    
        $sql = "INSERT INTO $table (`$fieldNames`) VALUES ($fieldValues)";
    
        $stmt = $this->prepare($sql);
    
        foreach ($data as $key => $value){
            $stmt->bindValue(":$key", $value);
        }
    
        $stmt->execute();
    }
    

    【讨论】:

    • 感谢您发布您的代码,但我的问题的真正目的是获得关于我的代码正确/错误之处的建议,并为真正理解 OOP 提供进一步的建议/帮助。我试图让我的 db 类只使用单一方法,以便在编码项目时更容易记住结构,因为我将它用于所有 SQL 语句(SELECT、INSERT、DELETE 等),类型与最后一个不同参数。
    【解决方案2】:

    您的代码有点过时了。您应该使用visibility keywords 之一而不是var 来声明您的属性。在这种情况下,您可能希望使用 protected 以便不能从类外部对其进行修改,但这样任何未来的子类都可以在内部对其进行修改。您可能还需要添加一个 getter,以防您需要直接使用 PDO(您将 - 请参阅我的类示例下方的最后陈述)。

    在类中硬编码 PDO 连接信息是不好的。您应该将这些作为参数传递,就像直接使用 PDO 一样。我还将添加传递预配置 PDO 实例的功能。

    虽然不是必需的,但符合PSR-0 through PSR-2 是个好主意;具体而言,在您的情况下,我正在谈论类和方法命名,它们都应该是 camelCase 并且该类的第一个字符应该是大写字母。与此相关的是,您的代码格式也很丑陋,尤其是您的块语句...如果这只是复制和粘贴的问题,请忽略该注释。

    所以总的来说我会重构你的代码看起来像这样:

    class Db {
    
      protected $pdo;
    
      public function __construct($dsn, $user, $pass, $options = array()) {
    
        if($dsn instanceof PDO) {
          // support passing in a PDO instance directly
          $this->pdo = $dsn;
    
        } else  {
    
          if(is_array($dsn)) {
            // array format
            if(!empty($options)) {
              $dsn['options'] = $options;
            }
    
            $dsn = $this->buildDsn($options);
          } else {
            // string DSN but we need to append connection string options
            if(!empty($options)) {
              $dsn = $this->buildDsn(array('dsn' => $dsn, 'options' => $options));
            }
          }
    
          // otherwise just use the string dsn
          // ans create PDO
    
          $this->pdo = new PDO($dsn, $user, $pass);
        }
    
        // set PDO attributes
        $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      }
    
      public function getConnection()
      {
        return $this->pdo;
      }
    
      protected function buildDsn($options) {
    
        if($isDbParts = isset($options['dbname'], $options['hostname']) || !($isDsn = isset($option['dsn']))) {
          throw new Exception('A dsn OR dbname and hostname are required');
        }
    
        if($isDsn === true) {
          $dsn = $options['dsn'];
        } else if {
          $format = '%s:dbname=%s;host=%s';
          $driver = isset($options['dbtype']) ? $options['dbtype'] : 'mysql';
          $dsn = sprintf($format, $options['dbtype'], $options['dbname'], $options['host']);
        }
    
        if(isset($options['options'])) {
          $opts = array();
    
          foreach($options['options'] as $name => $value) {
            $opts[] = $name . '=' . $value;
          }
    
          if(!empty($opts)) {
            $dsn .= ';' . implode(';', $opts);
          }
        }
    
        return $dsn; 
      }
    
      public function preparedExecute( $query, $bind_values = null, $type = null ) {
    
        $preparedStatement = $this->pdo->prepare( $query );
    
        if( $bind_values ) {
    
          $i = 1;
    
          foreach( $bind_values as $bind_value ) {
    
            $preparedStatement->bindValue($i, $bind_value);
    
            $i++;
          } 
        }
    
        $preparedStatement->execute();
    
        if( $type == FETCH_ARRAY ) { 
          return $preparedStatement->fetchAll();  
        }
        elseif( $type == NUM_ROWS ) { 
          return $preparedStatement->rowCount();     
        }
        elseif( $type == INSERT   ) { 
          return $this->pdo->lastInsertId();         
        }
        else {   
          return true;  
        }
    
      }
    }
    

    最后,除非这只是为了教育目的,否则我不会这样做。有大量不同的查询具有不同的零件装配,这里没有考虑,所以在某些时候这不会支持你需要它做的事情。相反,我会使用 Doctrine DBAL、Zend_Db 或类似的东西,它们将通过其 API 支持更大的查询复杂性。简而言之,不要重新发明轮子。

    【讨论】:

    • 非常感谢您的好评!我会花时间正确阅读所有内容并检查您发布的链接,如果我有任何问题,请告诉您,但非常感谢您的反馈,这正是我正在寻找的东西。
    猜你喜欢
    • 2016-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-03
    • 2013-09-28
    • 2016-11-28
    • 1970-01-01
    • 2013-02-01
    相关资源
    最近更新 更多