Собственно решил создать фото-галерею. Создал два класса для БД и User. Сделал User наследует БД (так как методы некоторые использует у себя). Но мне кажется что я делаю это не правильно. Посмотрите и дайте совет. Класс к БД PHP: <?php namespace Modules; use PDO; class MySqlDatabase { protected $_db; public $last_query; const DB_DSN = 'mysql:host=localhost;dbname=photo_gallery'; const DB_USER = "admin"; const DB_PASS = "123qwe"; //Connection to DB public function __construct() { try { $this->_db = new PDO(self::DB_DSN, self::DB_USER, self::DB_PASS); $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (\PDOException $e) { echo "Connection error: " . $e->getMessage(); exit; } } //Close connection public function __destruct() { unset($this->_db); } //Our query without result public function exec($sql) { try { $res = $this->_db->exec($sql); return $res; } catch (\PDOException $e) { echo $e->getMessage() . "<br/>"; echo $this->last_query = $sql; } } //Our query from Select public function query($sql) { try { $stmt = $this->_db->query($sql); $row = $stmt->fetch(PDO::FETCH_ASSOC); return $row; } catch (\PDOException $e) { echo "Query Select error: " . $e->getMessage() . "<br/>"; echo $this->last_query = $sql; } } //PDO number of rows affected by last SQL public function rowCount() { $row = $this->_db->prepare(); $row->execute(); $count = $row->rowCount(); return $count; } //PDO last insert public function lastInsert() { return $this->_db->lastInsertId(); } //Prepare statement Insert public function prepareInsert($sql, $username, $pass, $fname, $lname) { try { $stmt = $this->_db->prepare($sql); $stmt->bindParam(":username", $username, PDO::PARAM_STR); $stmt->bindParam(":password", $pass, PDO::PARAM_STR); $stmt->bindParam(":first_name", $fname, PDO::PARAM_STR); $stmt->bindParam(":last_name", $lname, PDO::PARAM_STR); $stmt->execute(); } catch (\PDOException $e) { echo "Error prepare: " . $e->getMessage() . "<br/>"; echo $this->last_query = $sql; } } //Prepare statement SelectALL public function prepareSelectAll($sql, $class) { try { $stmt = $this->_db->prepare($sql); $stmt->execute(); $data = $stmt->fetchAll(PDO::FETCH_CLASS, $class); return $data; } catch (\PDOException $e) { echo "Error prepareALL: " . $e->getMessage() . "<br/>"; echo $this->last_query = $sql; } } //Prepare statement By ID public function prepareSelectById($sql, $var1, $class) { try { $stmt = $this->_db->prepare($sql); $stmt->bindParam(":id", $var1, PDO::PARAM_INT); $stmt->execute(); $data = $stmt->fetchObject($class); return $data; } catch (\PDOException $e) { echo "Error prepareById: " . $e->getMessage() . "<br/>"; echo $this->last_query = $sql; } } public function prepareSelectByIdAuthenticate($sql, $username,$password, $class) { try { $stmt = $this->_db->prepare($sql); $stmt->bindParam(":username", $username, PDO::PARAM_STR); $stmt->bindParam(":password", $password, PDO::PARAM_STR); $stmt->execute(); $data = $stmt->fetchAll(PDO::FETCH_CLASS, $class); return $data; } catch (\PDOException $e) { echo "Error prepareByIdAuthenticate: " . $e->getMessage() . "<br/>"; echo $this->last_query = $sql; } } } И класс User PHP: <?php namespace Modules; class User extends MySqlDatabase { public $id; public $username; public $password; public $first_name; public $last_name; //Select all public function findAll() { $sql = "SELECT * FROM users"; $result_set = $this->prepareSelectAll($sql, __CLASS__); return $result_set; } //Find by id public function findById($id = 0) { $sql = "SELECT * FROM users WHERE id =:id LIMIT 1"; $resuls_set = $this->prepareSelectById($sql, $id, __CLASS__); return $resuls_set; } // Function return full name public function full_name() { if (!empty($this->first_name) && !empty($this->last_name)) { return $this->first_name . " " . $this->last_name; } else { return ""; } } // public function authenticate($username = "", $password = "") { $sql = "SELECT * FROM users "; $sql .= " WHERE username = :username "; $sql .= " AND password = :password "; $sql .= " LIMIT 1"; $resuls_set = $this->prepareSelectByIdAuthenticate($sql, $username, $password, __CLASS__); if (!empty($resuls_set)) { return array_shift($resuls_set); } else { return false; } } }
Поизучайте Symfony, так будет проще понять как правильнее. Пользоветель точно не должен наследовать базу данных. Пользователь, логически - это человек, живая сущность, и т.д. Он может от таких объектов насловаться, но не от хранилища. Да и потом, в MySQL класс данные для подключения нужно передавать, а не вшивать.
А если потом будет еще класс Images к примеру и там тоже надо будет к базе подключается? тоже будете наследовать от класса MySqlDatabase? Получается тогда будет два подключения к базе данных.. или надо будет сделать два объекта класса User, опять же два подключения... Я в поведении классов при наследовании не сильно разбираюсь, но почему то кажется что это не хорошая идея. Если кто то из более опытных коллег меня поправит - буду рад выслушать комменты по этому поводу) Я вот так делал что бы не плодить много соединений с базой PHP: class DbConn { static private $instance = NULL; private $_db; static function getInstance() { if (self::$instance == NULL) { self::$instance = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); if (mysqli_connect_errno()) { throw new Exception("Database connection failed: " . mysqli_connect_error()); } } return self::$instance; } private function __construct() { } private function __clone() { } } $result = DbConn::getInstance()->query('SELECT что то там');
Если Вам нужен один экземпляр (одно подключение) - то Вы и создавайте одно. Синглтон в данном случае делать не советую - проблемы возникнут при работе с многопроцессорностью и общей памятью. PHP: $db = new Db(...$params); $user = new User(); $user->db = $db; // <- передаёте ссылку на объект
кстати я сейчас тоже делаю потихоньку галерею и загрузчик изображений для своего форума.. вторую версию уже)) так что если что - пишите)) я на многие грабли уже понаступал)) правда движок моего форума построен без классов, все на функциях реализовано.. --- Добавлено --- почему могут проблемы возникнуть? ведь тот же синглтон живет в рамках жизни выполнения скрипта.. как он на память влияет и тем более на многопроцессорность? Спасибо..
Спасибо, я правда сделал вот так : PHP: <?php namespace Modules; use PDO; class MySqlDatabase { protected $_db; public $last_query; //Connection to DB public function __construct(\PDO $pdo) { $this->_db = $pdo; } //Close connection public function __destruct() { unset($this->_db); } И Класс Юзер PHP: <?php namespace Modules; use Modules\MySqlDatabase; class User { public $id; public $username; public $password; public $first_name; public $last_name; private $db; public function __construct(MySqlDatabase $db){ $this->db = $db; } --- Добавлено --- Хорошо , спасибо
А если в какой-то момент понадобится сделать статический метод который с базой работает? П.С. я на самом деле не знаю как правильно)) просто делаю предположения))
Мне нравится шаблон Active Record. Посмотрите пример реализации в интернете. По многозадачности проблемы возникнут, т.к. у каждого процесса синглтон становится общим среди всех процессов и тогда возникают ошибки, путаются указатели на результаты и другие ячейки памяти. Другими словами говоря - для корректной работы в многозадачности синглтон должен быть синглтоном в пределах одного процесса. По этому статические значения нужно использовать только в тех местах где это нужно, а не где это можно.
никогда не сталкивался с многозадачностью на ПХП)) думал даже что это не возможно)) почитал чуть чуть)) оказывается можно)) хотя все равно не понятно зачем) все равно пока все задачи не выполнятся - страницу не отрисует ПХП.... разве что для консольных приложений на ПХП чтоли..
Да, по большей степени для бэкграунд тасков. Например, представьте что у Вас есть 300000 ссылок и Вам нужно загрузить и обработать их содержимое. Если разбить на многозадачность, скажем в 300 потоков, то каждому потоку в средмем будет необходимо сделать 1000 итераций.
была у меня такая задача.. не ссылки но задача сходная... запускал скрипт, он отрабатывал к примеру 1000 итераций, потом делал редирек сам на себя и отсылал в гет параметре значение 1000, показывал на сколько надо сместится указателю в массиве что бы начать новое выполнение скрипта.. согласен если сделать 300 потоков это будет вполне может быть что быстрее.. надо будет как то разобраться в этом... интересно как это все работает..