Класс соединения PDO / код и дизайн класса - PullRequest
2 голосов
/ 15 марта 2012

Я пытаюсь понять, как использовать PDO с классом "подключения".

class db { 

    private static $dbh; 

    private function __construct(){}
    private function __clone(){} 

    public static function connect() { 
        if(!self::$dbh){ 
            self::$dbh = new PDO("mysql:host=localhost;dbname=database", "user", "password");
            self::$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
        } 
        return self::$dbh; 
    } 

    final public static function __callStatic( $chrMethod, $arrArguments ) {   
        $dbh = self::connect(); 
        return call_user_func_array(array($dbh, $chrMethod), $arrArguments);  
    }
} 

Я взял вышеприведенное из http://php.net/manual/en/book.pdo.php, и немного изменил переменные, но я 'Мне интересно, как я тогда подключусь к объекту соединения PDO в этом классе БД?

$dbh = new db; //intiate connection???

$stmt = $dbh->prepare("SELECT * FROM questions WHERE id = :id"); // or should I do db::prepare.. ???
$stmt->bindParam(':id', $_GET['testid'], PDO::PARAM_INT);

if ($stmt->execute()) {
    while ($row = $stmt->fetch()){
        print_r($row);
    }
}

Есть идеи, пожалуйста?спасибо

Ответы [ 2 ]

5 голосов
/ 15 марта 2012

Это более или менее так, как я это делаю. Я не уверен, что это лучший способ сделать это, но он работает для меня.

Мой фабричный класс - ЯДРО моего кода. Отсюда я генерирую все классы, с которыми я работаю. Мой фабричный класс сохраняется в отдельном файле factory.class.php.

Имея фабричный класс, мне нужно включать файлы классов только один раз. Если бы у меня не было этого, мне пришлось бы включать файлы моего класса для каждого файла, который его использовал. Если мне нужно обновить имя файла класса позже, мне нужно только обновить файл класса фабрики.

Еще одной причиной создания объекта фабрики было уменьшение количества подключений к БД.

Я сохраняю каждый класс как отдельный файл

Заводской класс

include_once('person.class.php');
include_once('tracking.class.php');
include_once('costAnalyzis.class.php');
include_once('activity.class.php');

class Factory {
  function new_person_obj($id = NULL) { return new Person(Conn::get_conn(), $id); }  
  function new_tracking_obj($id = NULL) { return new Tracking(Conn::get_conn(), $id); }
  function new_costAnalyzis_obj() { return new CostAnalyzis(Conn::get_conn()); }
  function new_activity_obj() { return new Activity(Conn::get_conn()); }
}    

Класс подключения

// I have this class in the same file as Factory class
// This creates DB connection and returns any error messages
class Conn {
  private static $conn = NULL;

  private function __construct() {}

  private static function init() {
      $conf = self::config();
      try { 
        self::$conn = new PDO($conf['dsn'], $conf['user'], $conf['pass'], array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
      } 
      catch (PDOException $e) {
        // We remove the username if we get [1045] Access denied
        if (preg_match("/\b1045\b/i", $e->getMessage())) 
          echo "SQLSTATE[28000] [1045] Access denied for user 'name removed' @ 'localhost' (using password: YES)";
        else
          echo $e->getMessage();  
      }
  }

  public static function get_conn() {
    if (!self::$conn) { self::init(); }
    return self::$conn;
  }

  // I used to get login info from config file. Now I use Wordpress constants
  private static function config() {
    $conf = array();

    $conf['user']    = DB_USER; //$config['db_user'];
    $conf['pass']    = DB_PASSWORD; //$config['db_password'];
    $conf['dsn']     = 'mysql:dbname='.DB_NAME.';host='.DB_HOST;

    return $conf;
  }  
}

Объекты разных классов

Это ваши занятия. Здесь вы работаете со своими данными. В своем собственном коде я использую трехуровневую архитектуру, отделяющую представление от бизнес-уровня и уровня объекта данных.

class Person extends PersonDAO {

  function getPersonData($id) {
    $result = parent::getPersonData($id);

    // Here you can work with your data. If you do not need to handle data, just return result
    return $result;
  }
}


// I only have SQL queries in this class and I only return RAW results.
class PersonDAO {

  // This variable is also available from you mother class Person 
  private $db;

    // Constructor. It is automatically fired when calling the function.
    // It must have the same name as the class - unless you define 
    // the constructor in your mother class.
    // The &$db variable is the connection passed from the Factory class.
    function PersonDAO (&$db) {
      $this->db = &$db;
    }


  public function get_data($id) {
     $sql ="SELECT a, b, c
          FROM my_table
          WHERE id = :id";

     $stmt = $this->db->prepare($sql);
     $stmt->execute(array(':id'=> $id));
     $result = $stmt->fetchAll(PDO::FETCH_ASSOC);

     return $result;
  }

  public function get_some_other_data() {
    $sql ="SELECT a, b, c
          FROM my_table_b";

    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);

    return $result;      
  }
}

Сделайте то же самое для других ваших классов.

Собираем все вместе

Обратите внимание, что мы включаем только один файл, заводские файлы. Все остальные файлы классов включены в файл классов Factory.

// Include factory file
include_once('factory.class.php');

//Create your factory object
$person = Factory::new_person_obj();

//Get person data
$data = $person->getPersonData('12');

// output data
print_r($data);
3 голосов
/ 18 марта 2012

Как я понял, вы хотите иметь "класс соединения", который реализует ленивую загрузку для экземпляра PDO.И затем вы хотите, чтобы объекты в вашем коде имели доступ к этому соединению из любого места в коде, эффективно создавая одиночный код.

Не делайте этого.

Вы очищаете глобальное состояние в своем приложении, и все ваши классы с поддержкой БД тесно связаны с ИМЯ класса соединения.

Я бы порекомендовал немного другой подход.Как уже @ Steven намекнул, вы должны использовать фабрику для создания объектов, которые требуют подключения к БД.

Вот упрощенная реализация.

class DataMapperFactory
{
    protected $provider = null;
    protected $connection = null;

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

    public function create( $name)
    {
        if ( $this->connection === null )
        {
            $this->connection = call_user_func( $this->provider );
        }
        return new $name( $this->connection );
    }

}

Вы быиспользуйте его примерно так:

$provider = function()
{
    $instance = new PDO('mysql:......');
    $instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $instance->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    return $instance;
};

$factory = new DataMapperFactory( $provider );

Теперь каждый раз, когда вы выполняете $factory->create('SomeClass'), он создаст новый экземпляр этого класса и предоставит ему правильное соединение с БД в конструкторе.И при первом запуске откроется соединение с БД.

...