За последние 24 часа нас посетили 32002 программиста и 1380 роботов. Сейчас ищут 902 программиста ...

Образец кода работы с БД

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

  1. Walk

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

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    Всем привет

    Стыдно в этом признаться - но не знаю как написать хороший класс для работы с БД (MySQL). Конечно, это никак не мешает работать с Yii2/Laravel, где все это уже написано, но внутренний перфекционизм постоянно негодует.

    И при попытках закрыть этот пробел в знаниях постоянно сталкиваюсь с двумя проблемами:

    1. Понятное, что образцовый код можно посмотреть в том же Yii2/Laravel, но... он там сильно переусложнен различными наворотами.
    2. Если искать готовые простые решения в интернете, коих, конечно, масса, то возникает другой вопрос - а где гарантии, что данный код действительно образцовый? Написать "абы как, лишь бы работало" я и сам могу (и даже защита от SQL-инъекций будет). Интересует именно образцовый код.

    У кого есть готовый пример кода - просьба дать ссылку.
     
  2. TeslaFeo

    TeslaFeo Старожил

    С нами с:
    9 мар 2016
    Сообщения:
    2.984
    Симпатии:
    759
    Думаю, надо писать самому и дописывать по мере необходимости.
    Создаешь метод db_conect() и в конструкторе присваиваешь свойству conn соединение с БД.

    И пишешь пару методов db_select() и db_query().
    Это для начала.

    И начинаешь всем этим пользоваться. Когда видишь, что какую-то работу делаешь часто - добавляешь наворот.

    Так у тебя постепенно появляется свой навороченный класс)
     
  3. Walk

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

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    Шел по этому пути - делал подключение к БД через обычный объект, потом узнал, что нужно делать через синглтон.

    И еще много других ошибок можно сделать, с подходом "напишу, чтобы работало, а там, если что доработаю"
     
  4. TeslaFeo

    TeslaFeo Старожил

    С нами с:
    9 мар 2016
    Сообщения:
    2.984
    Симпатии:
    759
    Не ошибается тот, кто ни чего не делает.
    Не возможно чему-то научиться и не набить шишек.
    Не нужно бояться ошибок.
     
  5. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    PDO или mysqli - уже неплохие классы для работы с БД. Усовершенствовать можно, только надо сначала задачу поставить - чем тебя не устраивают эти классы, что ты хочешь сделать? Query Builder, как в Yii2 и Laravel, или просто удобную подстановку массивов в плейсхолдеры, как в SafeSQL, или как там его.

    Ну если делать что-то универсальное, то не синглтон. Ведь может потребоваться несколько подключений к БД одновременно. Соответственно, нужно будет несколько экземпляров этого класса создать. В тех же фрейморках - не синглтоны, если что.
     
  6. Алекс8

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

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    вот я для себя начинал делать.. правда еще не доделал.. плюс там костыли есть.. но задумка такая) делал максимально просто что бы избавиться от синтаксиса mysqli который мне не нравится)

    PHP:
    1.     /**
    2.      *
    3.      * @param string $query - запрос
    4.      * с плейсхолдерами int|float|string|array_int|array_string
    5.      * id={int:id} AND id_member IN(array_ind:ids)
    6.      * @param array $values массив с даными для плейсходеров
    7.      * [ id=>1, ids=>[1,2,3,4,5] ]
    8.      * @return object mysqli_result
    9.      */
    10.     public function dbQuery(string $query, array $values) {
    11.  
    12.  
    13.  
    14.         /* Типы плейсхолдеров */
    15.         $pattern = '~{(int|float|string|array_int|array_string):(\S+?)}~';
    16.  
    17.         /* Выбираем из строки запрос возможные плейсходеры */
    18.         preg_match_all($pattern, $query, $matches, PREG_SET_ORDER);
    19.  
    20.         /* Инициализируем массив с данными плейсхолтера */
    21.         $prepareData = [];
    22.         /* и массив с типами плейсхолдера mysqli
    23.          *   i - соответствующая переменная имеет тип integer
    24.          *   d - соответствующая переменная имеет тип double
    25.          *   s - соответствующая переменная имеет тип string
    26.          *   b - соответствующая переменная является большим двоичным объектом (blob) и будет пересылаться пакетами
    27.          */
    28.         $typesPlaceholders = '';
    29.  
    30.         /* Обходим найденные плейсхолдеры */
    31.         foreach ($matches as $ph) {
    32.             /* Временный плейсхоллер нужен для типов плейсхолдеров array_XXX
    33.               знак вопроса ? для самого запроса */
    34.             $tmpPlaceholers = '';
    35.  
    36.             switch ($ph['1']) {
    37.                 case 'int':
    38.                     $typesPlaceholders .= 'i';
    39.                     $prepareData[] = (int) $values[$ph['2']];
    40.                     $tmpPlaceholers = '?';
    41.                     break;
    42.  
    43.                 case 'array_int':
    44.                     $tmpPlaceholers = '';
    45.                     foreach ($values[$ph['2']] as $intVal) {
    46.                         $typesPlaceholders .= 'i';
    47.                         $prepareData[] = (int) $intVal;
    48.                         $tmpPlaceholers .= '?,';
    49.                     }
    50.                     $tmpPlaceholers = trim($tmpPlaceholers, ',');
    51.                     break;
    52.  
    53.                 case 'string':
    54.                     $typesPlaceholders .= 's';
    55.                     $prepareData[] = (string) $values[$ph['2']];
    56.                     $tmpPlaceholers = '?';
    57.                     break;
    58.  
    59.                 default:
    60.  
    61.                     die('Неверный тип даннных ' . $ph['1']);
    62.                     break;
    63.             }
    64.             /* Заменили плейсхолдер на нужное количество знаков вопросов */
    65.             $query = str_replace($ph['0'], $tmpPlaceholers, $query);
    66.         }
    67.         /* Заменили плейсхолдер префикса таблицы на сам префикс */
    68.         $query = str_replace('{db_prefix}', Config::get('dbPrefix'), $query);
    69.         /* Собрали массив для вызова метода */
    70.         $params['db_types'] = $typesPlaceholders;
    71.         foreach ($prepareData as $key => $val) {
    72.             $params['db_' . $key] = &$prepareData[$key];
    73.         }
    74.  
    75.  
    76.  
    77.         /* Готовим запрос и выполняем */
    78.         $stmt = App::$db->stmt_init();
    79.         $stmt->prepare($query);
    80.  
    81. //        var_dump($stmt->error);
    82.        
    83.         if (!empty($params['db_types'])) {
    84.             call_user_func_array([$stmt, 'bind_param'], $params);
    85.         }
    86.  
    87.         $stmt->execute();
    88.  
    89.         //Если INSERT то возвращаем то что показал stmp
    90.         if (!empty($stmt->insert_id)) {
    91.             $result = new \stdClass();
    92.             foreach ($stmt as $key => $value) {
    93.                 $result->$key = $stmt->$key;
    94.             }
    95.         }
    96.         //Если запрос был SELECT, UPDATE, DELETE - тогда берем результат
    97.         else {
    98.             $result = $stmt->get_result();
    99.         }
    100.         $stmt->close();
    101.  
    102.         return $result;
    103.     }
    --- Добавлено ---
    сделал поверх этого метода еще вот такой вот метод (это тоже пример)
    PHP:
    1.     public function getRows($query, $values) {
    2.         $result = $this->dbQuery($query, $values);
    3.  
    4.         $rows = [];
    5.         if ($this->dbNumRows($result) > 0) {
    6.             while ($row = $this->dbFetchAssoc($result)) {
    7.                 $rows[] = $row;
    8.             }
    9.         }
    10.         $this->dbFreeResult($result);
    11.  
    12.         return $rows;
    13.     }
    и потом если хочу получить какие то данные уже в модели то делаю вот так вот..

    PHP:
    1.     /**
    2.      *
    3.      * @param int $user_id
    4.      * @param int $offset
    5.      * @param int $limit
    6.      * @return type
    7.      */
    8.     public function getByUserId(int $user_id, int $offset = 0, int $limit = 0) {
    9.  
    10.         $queryLimit = (empty($offset) && empty($limit)) ? ('') : ('' . $offset . ', ' . $limit);
    11.  
    12.         return $this->getRows('SELECT
    13.                us.id AS site_id,
    14.                us.name AS site_name,
    15.                us.blank_id,
    16.                us.params,
    17.              
    18.                sb.name AS blank_name,
    19.              
    20.                usr.id AS user_id,
    21.                usr.name AS user_name
    22.            FROM {db_prefix}user_site AS us
    23.            LEFT JOIN {db_prefix}site_blank AS sb ON(sb.id = us.blank_id)
    24.            LEFT JOIN {db_prefix}user AS usr ON(usr.id = us.user_id)
    25.            WHERE us.user_id={int:user_id}
    26.            ' . $queryLimit . '', [
    27.                     'user_id' => $user_id
    28.         ]);
    29.     }
    --- Добавлено ---
    как видишь в самой модели надо только запрос написать с удобными плейсхолдерами и отдельно предоставить данные для плейсхолдеров..
    вообще я решил его делать, эту прослойку, в первую очередь изза плейсхолдера array_int )) мне очень не хватало в mysqli тупо отправки в запрос массива)
     
  7. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.814
    Симпатии:
    1.332
    Адрес:
    Лень
    и тут приходит Мышка со своей Lerma

    Правда еще не залил dev обновленку с планшетуса, там имба функционал, мона плейсхолдеры вставлять
    SELECT * FROM table WHERE id = :id
     
    Алекс8 нравится это.
  8. Алекс8

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

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    у Мауса есть Лерма.. там более продвинуто чем у меня..
    --- Добавлено ---
    сделай плейсхолдеры по моему примеру)) удобно вроде)) и тип сразу указываешь))
     
  9. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.814
    Симпатии:
    1.332
    Адрес:
    Лень
    кумекал долго на тему - что такое плейсхолдеры, а что такое простые подстановки. :) Этого не будет.
     
  10. Алекс8

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

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    так у меня вроде плейсхолдеры.. только вместо знака вопроса и отдельно типов данных - у меня {int:id}
     
  11. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.814
    Симпатии:
    1.332
    Адрес:
    Лень
    От плейсхолдеров тоже по хорошему бы отказаться, из - за лишних действий, где программист сам может лишнюю переменную повторно пропихнуть.
    --- Добавлено ---
    пробежался глазами, увидел. Так и так у тебя всеголишь конструкция другая, куда данные впиливаться будут, да и еще самому типы указывать - Это плагин / мод / дополнение. От таких супер пупер пожалуй откажусь и дам Лерме самой определять какой тип приходит, если не понравится - посылает куда надо.
     
  12. Алекс8

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

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

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.814
    Симпатии:
    1.332
    Адрес:
    Лень
    я даже хз что такое Арррр..
    --- Добавлено ---
    обновлю ток :oops:
     
  14. Алекс8

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

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    AR - Active Records))
    где модель это вроде как сущностью таблицы)
     
  15. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.814
    Симпатии:
    1.332
    Адрес:
    Лень
    хотел что - то подобное зачудить у себя в коде. Пока в голове абстрактно на полочке пылятся идеи :D
     
  16. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.814
    Симпатии:
    1.332
    Адрес:
    Лень
    Обновил... Готово.

    PHP:
    1. <?php
    2.  
    3. use Aero\Supports\Lerma;
    4.  
    5. Lerma::prepare ( 'SELECT * FROM table WHERE cr = :one OR pr = :one', [ ':one' => 123 ] ) -> fetchAll( Lerma::FETCH_KEY_PAIR | Lerma::FETCH_NAMED );
    6.  
    7.  
    8. /* ... */
    PHP:
    1. <?php
    2.  
    3. use Aero\Supports\Lerma;
    4.  
    5. require '../autoload.php';
    6.  
    7. # ------------------------
    8.  
    9. /*
    10. fetch()
    11.     FETCH_NUM
    12.     FETCH_ASSOC
    13.     FETCH_OBJ
    14.     FETCH_BIND
    15.     FETCH_BIND | FETCH_COLUMN
    16.     FETCH_COLUMN
    17.     FETCH_KEY_PAIR
    18.     FETCH_FUNC
    19.     FETCH_CLASS
    20.     FETCH_CLASSTYPE
    21.  
    22. fetchAll()
    23.     FETCH_NUM
    24.     FETCH_ASSOC
    25.     FETCH_OBJ
    26.     FETCH_COLUMN
    27.     FETCH_KEY_PAIR
    28.     FETCH_KEY_PAIR | FETCH_NAMED
    29.     FETCH_UNIQUE
    30.     FETCH_GROUP
    31.     FETCH_GROUP | FETCH_COLUMN
    32.     FETCH_FUNC
    33.     FETCH_CLASS
    34.     FETCH_CLASSTYPE
    35.     Lerma::FETCH_CLASSTYPE | Lerma::FETCH_UNIQUE
    36. */
    37.  
    38. # ------------------------
    39.  
    40. $table = 'lerma';
    41.  
    42. $sql = [ 'SELECT * FROM %s', $table ]; # or 'SELECT * FROM lerma'
    43.  
    44. $query = Lerma::query( $sql ) -> fetchAll( Lerma::FETCH_OBJ );
    45.  
    46. # ------------------------
    47.  
    48. $sql = [ [ 'SELECT * FROM %s WHERE id IN ( :id,?,?,? )', $table ], [ 3,9,81,':id'=>1 ] ];
    49.  
    50. $prepare = Lerma::prepare( ...$sql ) -> fetchAll( Lerma::FETCH_OBJ );
    51.  
    52. # ------------------------
    53.  
    54. print_r ( compact ( 'query', 'prepare' ) );
    --- Добавлено ---
    http://phpfaq.ru/pdo/fetch
    constants
     
  17. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    Определение AR гораздо проще, и конкретнее. Каждый экземпляр модели соответствует строке таблицы.
     
    Алекс8 нравится это.
  18. Walk

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

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    Нашел такой пример подключения к базе (хотя ищу пример не только подключения к бд, но и обработке запросов), вроде бы на уважаемом ресурсе, но, попытался использовать код - он не рабочий. Даже с правками - все равно не работает:

    Я что-то в нем не понимаю, или пример откровенная хрень? Например там указан __construct () у класса, к которому обращаются через статический метод. Разумеется, __construct () не отрабатывает. Складывается ощущение, что его автор даже азы ООП не знает.
     
  19. Алекс8

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

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    там есть return new self; вот когда конструктор срабатывает..
    а так вроде нормальный пример.. синглтон..
     
    Walk нравится это.
  20. Walk

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

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    Спасибо. На этот момент тоже указал знакомый программист, и что после этого к статическому свойству можно обращаться через $this->

    Но, все равно непонятно что это такое:
    PHP:
    1.     private static DB_HOST = '';
    2.     private static DB_NAME = '';
    3.     private static DB_USER = '';
    4.     private static DB_PASS = '';
    Если это переменная - не хватает $, если константа - она задается по-другому.

    И при попытке запустить код (поправив строчки выше), получаю:

    Strict Standards: Accessing static property DB::$_instance as non static in /var/www/....php on line 15
    bool(false)

    +к этому в коде используется нестрогое сравнение (==, !=), меня учили использовать только строгое, что нестрогие сравнения - первый шаг к говнокоду.
     
    #20 Walk, 11 янв 2018
    Последнее редактирование: 11 янв 2018
  21. Алекс8

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

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    таки да..
     
  22. Walk

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

    С нами с:
    7 сен 2008
    Сообщения:
    452
    Симпатии:
    86
    Поговорили со знакомым программистом еще на эту тему, что пример, на который ссылался выше, так или иначе не работает - он таки сказал, что да, пример говно и обращаться к статическому свойству через $this-> нельзя.
     
  23. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    186
    @Walk к статическому свойству, надо обращаться, через 4 точки
     
  24. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.814
    Симпатии:
    1.332
    Адрес:
    Лень
    через 2 двоеточие, а не ....
     
  25. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    186