Я получил этот класс базы данных 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); } }
Я пытаюсь разделить мой код в разных классах, например, я получил clsDBUser, который подключен к clsUserController. Я делаю это, поэтому я знаю, какой класс использует какой код базы данных. Класс 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 (да, я знаю, старый), я не могу распознать, что мне приходилось каждый раз подключать новую базу данных.
Нужно ли мне это улучшать, как мне это нужно улучшить?
Вы должны пройти по дороге, указанной в ответе mr.void. Вкратце:
Так что это должно быть
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);
Эй, я бы сделал что-то вроде этого
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);
Перестройка:
Класс 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; } } }
Здесь есть три слоя:
Когда мы делаем это вручную … Во-первых, не расширяйте их друг от друга. Как правило: никогда не расширяйте другой слой от другого. Вместо этого используйте Injection Dependency (DI).
Это очень простой случай DI, когда вы передаете всю конкретную информацию (зависимости) в качестве параметров конструктора. Мой объект, похожий на активный объект, Entity
просто знает, как сущность должна вести себя вообще (у ключа в репозитории). Для простоты я использую raw 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();
Конечно, вы можете расширить MoreConcreteRepository
из Repository
и MoreConcreteEntity
от Entity
с определенным поведением.
Просто не clsDBUser
сущность ( clsDBUser
) из класса подключения ( clsDatabase
).
Используйте singleton (или что-то более продвинутое) для 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); } }