PDO имеет предопределенные режимы выборки, которые можно передавать в методы fetch
и fetchAll
. Я в основном концентрируюсь на FETCH_CLASS
в моем номере.
Чтобы загрузить свойства и значения в мой класс, я создал черту, позволяющую использовать магические методы __set
.
trait Entity {
public function __construct() {
$this->_attributes = func_get_args();
}
public function __set($prop, $value) {
if (in_array($prop, $this->_attributes))
$this->$prop = $value;
}
}
Это можно легко использовать в приложении, используя черту в классе.
class UserModel {
use Entity;
public function save() {
}
}
Проблема, с которой я столкнулся сейчас, заключается в реализации магического метода __get
, чтобы теперь иметь возможность сохранять данные, основанные на свойствах и значениях класса. Чтобы загрузить данные в UserModel
, я могу просто создать экземпляр PDO и выполнить запрос следующим образом:
$pdo = new PDO();
$stmt = $pdo->prepare('SELECT user_id, email, username, hash FROM app_users WHERE user_id = 1');
$stmt->execute();
$user = $stmt->fetch(PDO::FETCH_CLASS, 'UserModel');
Я попытался сгенерировать дополнительный метод __get
в моей черте, например:
public function __get($prop) {
if(isset($this->$prop)) return $this->$prop;
}
Но теперь я застрял в том, как использовать экземпляр для обновления строки, когда экземпляр PDO находится за пределами своей области видимости. Я попытался реализовать одноэлементную черту для PDO следующим образом:
trait Singleton
{
private static $instance;
public static function getInstance()
{
if(self::$instance) return self::$instance;
self::$instance = new self();
return self::$instance;
}
protected function __construct() {}
private function __clone() {}
}
И действительно быстрое расширение PDO для базовой реализации на данный момент.
class PDOWrapper extends PDO
{
use Singleton;
protected function __construct()
{
parent::_construct('mysql:host=127.0.0.1;dbname=test;charset=utf8mb4', array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
));
}
public function query($sql)
{
return parent::prepare($sql);
}
}
Что затем меняет весь мой подход к извлечению строки в класс, чтобы он выглядел примерно так:
$stmt = PDOWrapper::getInstance()->query('SELECT user_id, email, username, hash FROM app_users WHERE user_id = 1');
$stmt->execute();
$user = $stmt->fetch(PDO::FETCH_CLASS, 'UserModel');
Теперь внутри моего метода save()
у меня есть доступ к тому же экземпляру PDO, используя PDOWrapper::getInstance()
. У меня проблема в том, что вам нужно знать , какие свойства были загружены из SQL-запроса, чтобы получить необходимые значения.
Например, ранее я загружал в класс столбцы email
, user_id
и username
. Я хочу обновить пароль для этого пользователя. Это свойство не существует в первой выборке. Как я могу проверить, какие свойства существуют в классе?
// Won't work
public function save() {
$stmt = PDOWrapper::getInstance()->query('UPDATE app_users SET hash = ? WHERE user_id = ?');
$stmt->execute(array($this->__get('hash'), $this->__get('user_id')));
}
Мне нужно решение, которое является динамическим для набора свойств, например, которое, вероятно, должно быть пакетным обновлением:
// Sudo code
PDOWrapper::getInstance()->beginTransaction(); // Will need to be added
foreach($this->props as $prop)
PDOWrapper::getInstance()->query('UPDATE table SET prop = value where user_id = value')->execute(array($this->$prop, $this->user_id));