За последние 24 часа нас посетили 17717 программистов и 1702 робота. Сейчас ищут 997 программистов ...

Правильно ли я строю свое приложение?

Тема в разделе "PHP для новичков", создана пользователем FireSoul, 13 июн 2017.

  1. FireSoul

    FireSoul Активный пользователь

    С нами с:
    12 июн 2017
    Сообщения:
    7
    Симпатии:
    1
    Собственно решил создать фото-галерею. Создал два класса для БД и User. Сделал User наследует БД (так как методы некоторые использует у себя). Но мне кажется что я делаю это не правильно. Посмотрите и дайте совет.
    Класс к БД
    PHP:
    1. <?php
    2. namespace Modules;
    3.  
    4. use PDO;
    5.  
    6. class MySqlDatabase
    7. {
    8.     protected $_db;
    9.     public $last_query;
    10.     const DB_DSN = 'mysql:host=localhost;dbname=photo_gallery';
    11.     const DB_USER = "admin";
    12.     const DB_PASS = "123qwe";
    13.  
    14.     //Connection to DB
    15.     public function __construct()
    16.     {
    17.         try {
    18.             $this->_db = new PDO(self::DB_DSN, self::DB_USER, self::DB_PASS);
    19.             $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    20.         } catch (\PDOException $e) {
    21.             echo "Connection error: " . $e->getMessage();
    22.             exit;
    23.         }
    24.     }
    25.  
    26.     //Close connection
    27.     public function __destruct()
    28.     {
    29.         unset($this->_db);
    30.     }
    31.  
    32.     //Our query without result
    33.     public function exec($sql)
    34.     {
    35.         try {
    36.             $res = $this->_db->exec($sql);
    37.             return $res;
    38.         } catch (\PDOException $e) {
    39.             echo $e->getMessage() . "<br/>";
    40.             echo $this->last_query = $sql;
    41.         }
    42.     }
    43.  
    44.     //Our query from Select
    45.     public function query($sql)
    46.     {
    47.         try {
    48.             $stmt = $this->_db->query($sql);
    49.             $row = $stmt->fetch(PDO::FETCH_ASSOC);
    50.             return $row;
    51.         } catch (\PDOException $e) {
    52.             echo "Query Select error: " . $e->getMessage() . "<br/>";
    53.             echo $this->last_query = $sql;
    54.         }
    55.     }
    56.  
    57.     //PDO number of rows affected by last SQL
    58.     public function rowCount()
    59.     {
    60.         $row = $this->_db->prepare();
    61.         $row->execute();
    62.         $count = $row->rowCount();
    63.         return $count;
    64.     }
    65.  
    66.     //PDO last insert
    67.     public function lastInsert()
    68.     {
    69.         return $this->_db->lastInsertId();
    70.     }
    71.  
    72.     //Prepare statement Insert
    73.     public function prepareInsert($sql, $username, $pass, $fname, $lname)
    74.     {
    75.         try {
    76.             $stmt = $this->_db->prepare($sql);
    77.             $stmt->bindParam(":username", $username, PDO::PARAM_STR);
    78.             $stmt->bindParam(":password", $pass, PDO::PARAM_STR);
    79.             $stmt->bindParam(":first_name", $fname, PDO::PARAM_STR);
    80.             $stmt->bindParam(":last_name", $lname, PDO::PARAM_STR);
    81.             $stmt->execute();
    82.         } catch (\PDOException $e) {
    83.             echo "Error prepare: " . $e->getMessage() . "<br/>";
    84.             echo $this->last_query = $sql;
    85.         }
    86.     }
    87.  
    88.     //Prepare statement SelectALL
    89.     public function prepareSelectAll($sql, $class)
    90.     {
    91.         try {
    92.             $stmt = $this->_db->prepare($sql);
    93.             $stmt->execute();
    94.             $data = $stmt->fetchAll(PDO::FETCH_CLASS, $class);
    95.             return $data;
    96.             } catch (\PDOException $e) {
    97.             echo "Error prepareALL: " . $e->getMessage() . "<br/>";
    98.             echo $this->last_query = $sql;
    99.         }
    100.     }
    101.  
    102.     //Prepare statement By ID
    103.     public function prepareSelectById($sql, $var1, $class)
    104.     {
    105.         try {
    106.             $stmt = $this->_db->prepare($sql);
    107.             $stmt->bindParam(":id", $var1, PDO::PARAM_INT);
    108.             $stmt->execute();
    109.             $data = $stmt->fetchObject($class);
    110.             return $data;
    111.         } catch (\PDOException $e) {
    112.             echo "Error prepareById: " . $e->getMessage() . "<br/>";
    113.             echo $this->last_query = $sql;
    114.         }
    115.     }
    116.     public function prepareSelectByIdAuthenticate($sql, $username,$password, $class)
    117.     {
    118.         try {
    119.             $stmt = $this->_db->prepare($sql);
    120.             $stmt->bindParam(":username", $username, PDO::PARAM_STR);
    121.             $stmt->bindParam(":password", $password, PDO::PARAM_STR);
    122.             $stmt->execute();
    123.             $data = $stmt->fetchAll(PDO::FETCH_CLASS, $class);
    124.             return $data;
    125.         } catch (\PDOException $e) {
    126.             echo "Error prepareByIdAuthenticate: " . $e->getMessage() . "<br/>";
    127.             echo $this->last_query = $sql;
    128.         }
    129.     }
    130. }
    И класс User
    PHP:
    1. <?php
    2. namespace Modules;
    3. class User extends MySqlDatabase
    4. {
    5.     public $id;
    6.     public $username;
    7.     public $password;
    8.     public $first_name;
    9.     public $last_name;
    10.  
    11.     //Select all
    12.     public function findAll()
    13.     {
    14.         $sql = "SELECT * FROM users";
    15.         $result_set = $this->prepareSelectAll($sql, __CLASS__);
    16.         return $result_set;
    17.     }
    18.  
    19.     //Find by id
    20.     public function findById($id = 0)
    21.     {
    22.         $sql = "SELECT * FROM users WHERE id =:id LIMIT 1";
    23.         $resuls_set = $this->prepareSelectById($sql, $id, __CLASS__);
    24.         return $resuls_set;
    25.  
    26.     }
    27.  
    28.     // Function return full name
    29.     public function full_name()
    30.     {
    31.         if (!empty($this->first_name) && !empty($this->last_name)) {
    32.             return $this->first_name . " " . $this->last_name;
    33.         } else {
    34.             return "";
    35.         }
    36.     }
    37.  
    38.     //
    39.     public function authenticate($username = "", $password = "")
    40.     {
    41.         $sql = "SELECT * FROM users ";
    42.         $sql .= " WHERE username = :username ";
    43.         $sql .= " AND password = :password ";
    44.         $sql .= " LIMIT 1";
    45.         $resuls_set = $this->prepareSelectByIdAuthenticate($sql, $username, $password, __CLASS__);
    46.         if (!empty($resuls_set)) {
    47.             return array_shift($resuls_set);
    48.         } else {
    49.             return false;
    50.         }
    51.  
    52.     }
    53.  
    54. }
     
  2. neverlose

    neverlose Активный пользователь

    С нами с:
    27 авг 2008
    Сообщения:
    1.112
    Симпатии:
    20
    Поизучайте Symfony, так будет проще понять как правильнее.
    Пользоветель точно не должен наследовать базу данных. Пользователь, логически - это человек, живая сущность, и т.д. Он может от таких объектов насловаться, но не от хранилища.
    Да и потом, в MySQL класс данные для подключения нужно передавать, а не вшивать.
     
  3. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    А если потом будет еще класс Images к примеру и там тоже надо будет к базе подключается? тоже будете наследовать от класса MySqlDatabase? Получается тогда будет два подключения к базе данных.. или надо будет сделать два объекта класса User, опять же два подключения...
    Я в поведении классов при наследовании не сильно разбираюсь, но почему то кажется что это не хорошая идея. Если кто то из более опытных коллег меня поправит - буду рад выслушать комменты по этому поводу)

    Я вот так делал что бы не плодить много соединений с базой
    PHP:
    1. class DbConn {
    2.  
    3.     static private $instance = NULL;
    4.     private $_db;
    5.  
    6.     static function getInstance() {
    7.         if (self::$instance == NULL) {
    8.             self::$instance = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    9.             if (mysqli_connect_errno()) {
    10.                 throw new Exception("Database connection failed: " . mysqli_connect_error());
    11.             }
    12.         }
    13.         return self::$instance;
    14.     }
    15.  
    16.     private function __construct() {
    17.        
    18.     }
    19.  
    20.     private function __clone() {
    21.        
    22.     }
    23.  
    24. }
    25.  
    26. $result = DbConn::getInstance()->query('SELECT  что то там');
     
  4. neverlose

    neverlose Активный пользователь

    С нами с:
    27 авг 2008
    Сообщения:
    1.112
    Симпатии:
    20
    Если Вам нужен один экземпляр (одно подключение) - то Вы и создавайте одно. Синглтон в данном случае делать не советую - проблемы возникнут при работе с многопроцессорностью и общей памятью.

    PHP:
    1. $db = new Db(...$params);
    2. $user = new User();
    3. $user->db = $db; // <- передаёте ссылку на объект
     
  5. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    кстати я сейчас тоже делаю потихоньку галерею и загрузчик изображений для своего форума.. вторую версию уже)) так что если что - пишите)) я на многие грабли уже понаступал))
    правда движок моего форума построен без классов, все на функциях реализовано..
    --- Добавлено ---
    почему могут проблемы возникнуть? ведь тот же синглтон живет в рамках жизни выполнения скрипта.. как он на память влияет и тем более на многопроцессорность?

    Спасибо..
     
  6. FireSoul

    FireSoul Активный пользователь

    С нами с:
    12 июн 2017
    Сообщения:
    7
    Симпатии:
    1
    Спасибо, я правда сделал вот так :
    PHP:
    1. <?php
    2. namespace Modules;
    3. use PDO;
    4. class MySqlDatabase
    5. {
    6.     protected $_db;
    7.     public $last_query;
    8.  
    9.     //Connection to DB
    10.     public function __construct(\PDO $pdo)
    11.     {
    12.         $this->_db = $pdo;
    13.     }
    14.  
    15.     //Close connection
    16.     public function __destruct()
    17.     {
    18.         unset($this->_db);
    19.     }
    И Класс Юзер
    PHP:
    1. <?php
    2. namespace Modules;
    3. use Modules\MySqlDatabase;
    4. class User
    5. {
    6.     public $id;
    7.     public $username;
    8.     public $password;
    9.     public $first_name;
    10.     public $last_name;
    11.     private $db;
    12.     public function __construct(MySqlDatabase $db){
    13.         $this->db = $db;
    14.     }
    --- Добавлено ---
    Хорошо , спасибо
     
  7. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    А если в какой-то момент понадобится сделать статический метод который с базой работает?

    П.С. я на самом деле не знаю как правильно)) просто делаю предположения))
     
  8. neverlose

    neverlose Активный пользователь

    С нами с:
    27 авг 2008
    Сообщения:
    1.112
    Симпатии:
    20
    Мне нравится шаблон Active Record. Посмотрите пример реализации в интернете.
    По многозадачности проблемы возникнут, т.к. у каждого процесса синглтон становится общим среди всех процессов и тогда возникают ошибки, путаются указатели на результаты и другие ячейки памяти. Другими словами говоря - для корректной работы в многозадачности синглтон должен быть синглтоном в пределах одного процесса. По этому статические значения нужно использовать только в тех местах где это нужно, а не где это можно.
     
    Алекс8 нравится это.
  9. FireSoul

    FireSoul Активный пользователь

    С нами с:
    12 июн 2017
    Сообщения:
    7
    Симпатии:
    1
    Хорошо , спасибо
     
    Алекс8 нравится это.
  10. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    никогда не сталкивался с многозадачностью на ПХП)) думал даже что это не возможно)) почитал чуть чуть)) оказывается можно)) хотя все равно не понятно зачем) все равно пока все задачи не выполнятся - страницу не отрисует ПХП.... разве что для консольных приложений на ПХП чтоли..
     
  11. neverlose

    neverlose Активный пользователь

    С нами с:
    27 авг 2008
    Сообщения:
    1.112
    Симпатии:
    20
    Да, по большей степени для бэкграунд тасков.
    Например, представьте что у Вас есть 300000 ссылок и Вам нужно загрузить и обработать их содержимое.
    Если разбить на многозадачность, скажем в 300 потоков, то каждому потоку в средмем будет необходимо сделать 1000 итераций.
     
    Алекс8 нравится это.
  12. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    была у меня такая задача.. не ссылки но задача сходная...
    запускал скрипт, он отрабатывал к примеру 1000 итераций, потом делал редирек сам на себя и отсылал в гет параметре значение 1000, показывал на сколько надо сместится указателю в массиве что бы начать новое выполнение скрипта.. согласен если сделать 300 потоков это будет вполне может быть что быстрее..
    надо будет как то разобраться в этом... интересно как это все работает..
     
  13. neverlose

    neverlose Активный пользователь

    С нами с:
    27 авг 2008
    Сообщения:
    1.112
    Симпатии:
    20
    Всё верно, это как раз и делается в целях ускорения выполнения задачи.
     
    Алекс8 нравится это.