Задать вопрос
@timofey99_kaz

Какие есть best practice по работе с классом для бд?

Здравствуйте! Пишу свою реализацию MVC для сайта (пускай в нем будут модели User и Lot). Сделал абстракцию для работы с бд:
Прячу под кат
<?php
namespace App\Models;

class Database
{
    public $joinData;
    private $dbConnection;
    
    public function __construct($host = 'localhost', $dbName = 'marketplace', $login = 'root', $password = 'root')
    {
        try {
            $this->dbConnection = new \PDO("mysql:host=$host;dbname=$dbName", $login, $password);
        } catch (PDOException $e) {
            echo 'Ошибка: ' . $e->getMessage();
            die();
        }
    }

    /**
	 * Takes table`s name and associative array \w placeholders and inserts it into the table
	 * @param string table name
     * @param array data with placeholders and values
	 * @return void
	 */

    public function insert(string $table, array $data): void
    {
        $queryValues = $this->prepareQueryValues($data);

        $query = $this->dbConnection->prepare("INSERT INTO $table SET $queryValues");
        $query->execute(array_values($data));
    }

    /**
	 * Returning the PDO fetch
	 * @param string table name
     * @param string 'where' expression
     * @param string column name
     * @param array limit
	 * @return array
	 */

    public function getOne(string $table, $what, string $column = 'id', array $limit = [0, 1000]): array
    {
        $query = $this->dbConnection->query("SELECT * FROM $table WHERE $column = \"$what\" LIMIT $limit[0], $limit[1]");

        if ($query === false) {
            throw new \Exception('Data not found');
        }
        
        return $this->show($query);
    }

    /**
	 * Returning the PDO fetch of whole table
	 * @param string table name
     * @param array limit
     * @param bool desc
	 * @return array
	 */

    public function getAll(string $table, array $limit = [0, 1000], bool $desc = false): array
    {
        if ($desc == false) {
            $query = $this->dbConnection->query("SELECT * FROM $table LIMIT $limit[0], $limit[1]");
        } else {
            $query = $this->dbConnection->query("SELECT * FROM $table ORDER BY id DESC LIMIT $limit[0], $limit[1]");
        }

        if ($query === false) {
            throw new \Exception('Data not found');
        }
        
        return $this->show($query);
    }

    public function delete(string $table, $chosen, string $column = 'id'): void
    {
        $query = $this->dbConnection->prepare("DELETE FROM $table WHERE $column = ?");
        $query->execute([$chosen]);
    }

    public function update(string $table, array $data, array $where): void
    {
        $queryValues = $this->prepareQueryValues($data);

        $whereField = key($where);
        $data[$whereField] = current($where);

        $query = $this->dbConnection->prepare("UPDATE $table SET $queryValues, updated_at = now() WHERE $whereField = ?");
        $query->execute(array_values($data));
    }

    /**
	 * ['users.id', 'names.name', 'cities.city'], ['users', 'names', 'cities'], ['users.id', '=', '69'], [['users.id', 'names.user_id'], ['users.city_id', 'cities.id']]
	 * @param array array with selected params like ['users.id', 'names.name', 'cities.city']
     * @param array selected tables like ['users', 'names', 'cities']
     * @param array 'where' like in laravel like ['users.id', '=', '69']
     * @param array array with arrays to join on like [['users.id', 'names.user_id'], ['users.city_id', 'cities.id']]
	 */

    public function prepareJoin(array $selectQuery, array $tables, array $whereQuery, array $joinOn)
    {
        $this->joinData = ['select' => $selectQuery, 'tables' => $tables, 'where' => $whereQuery, 'joinOn' => $joinOn];
        return $this;
    }
    
    /**
     * Join tables. Type of join are chosing via $param. Default it is null.
	 * @param string param \w type of join
	 * @return array
	 */

    public function join(string $param = NULL): array
    {
        $param = strtoupper($param);

        // Для удобства переводим в переменные
        $selectArray = $this->joinData['select'];
        $tablesArray = $this->joinData['tables'];
        $whereArray = $this->joinData['where'];

        // Записываем таблицу, к которой джойним остальные
        $mainTable = array_shift($tablesArray);

        $joinArray = [];
        foreach ($this->joinData['joinOn'] as $elem) {
            $joinArray[] = "$elem[0] = $elem[1]";
        }

        // Циклом проходим по массиву таблиц и джойним их (Одинаковым способом, переданным параметром)
        $joinString = '';
        for ($i = 0; $i <= count($tablesArray) - 1; $i++) {
            $joinString .= "$param JOIN {$tablesArray[$i]} ON {$joinArray[$i]} ";
        }

        $selectString = implode(',', $selectArray);
        $whereString = implode($whereArray);

        return $this->simpleQuery("SELECT $selectString FROM $mainTable $joinString WHERE $whereString");
    }

    /**
	 * Non-fiction query. If it hasn`t placeholders, returns show() method.
	 * @param array query string
     * @param array placeholders
	 * @return array
	 */

    public function rawQuery(string $query, array $prepareData = NULL)
    {
        if ($prepareData) {
            return $this->prepareQuery($query, $prepareData);
        }
        
        return $this->simpleQuery($query);
    }

    // Потенциально это к удалению и замене на rawQuery
    public function getTableCount(string $table, $what, string $column = 'id'): array
    {
        $query = $this->dbConnection->query("SELECT COUNT(*) FROM $table WHERE $column = \"$what\"");

        if ($query === false) {
            throw new \Exception('Data not found');
        }
        
        return $this->show($query);
    }

    /**
	 * Returning the PDO fetch.
	 * @param array PDOObject with data
	 * @return array
	 */

    private function show(\PDOStatement $fetch): array
    {
        for ($data = []; $row = $fetch->fetch(); $data[] = $row);

        return $data;
    }

    /**
	 * Takes the associative array and return SQL string like key=?
	 * @param array associative array where key is DB`s field and value is the DB value
	 * @return string like key=?
	 */

    private function prepareQueryValues(array $queryArray): string
    {
        $queryValues = '';

        foreach ($queryArray as $key => $value) {
            $queryValues .= "$key = ?,";
        }

        $queryValuesArray = str_split($queryValues);
        array_pop($queryValuesArray);

        return implode($queryValuesArray);
    }

    private function prepareQuery(string $queryString, array $data)
    {
        $query = $this->dbConnection->prepare("$queryString");
        $query->execute($data);
        
        return true;
    }

    private function simpleQuery(string $queryString)
    {
        $query = $this->dbConnection->query("$queryString");
        return $this->show($query);
    }
}

Сейчас я хочу сделать (по факту - переделать) модель User и иже с ней. В прошлый раз я внедрял зависимость в свойства классов, но понимаю, что это плохая идея. Подскажите, пожалуйста, как правильно внедрять класс для работы с бд в другие классы, требующие ее? Неужели в каждом методе создавать объект приведенного выше класса?

Для примера, методы из User:
Это, если что, версия до рефакторинга, к которому я еще не приступил. А в методе addUser будет просто вызов метода Database вместо прямого запроса
public function setData(int $id): void
    {
        $info = (new Database)->getFullUserInfo($id);

        foreach ($info as $elem) {
            $this->data = ['id' => $elem['id'], 'login' => $elem['login'],
                    'password' => $elem['password'], 'name_id' => $elem['name_id'],
                    'email' => $elem['email'], 'surname_id' => $elem['surname_id'],
                    'city_id' => $elem['city_id'], 'status_id' => $elem['status_id'],
                    'ban_status' => $elem['ban_status'], 'registration_time' => $elem['registration_time'],
                    'updated_at' => $elem['updated_at'], 'active' => $elem['active']];
        }
    }

    /**
	 * Adding new user into db
	 * @param string login
     * @param string hashed password
     * @param string name
     * @param int user`s city id
	 * @return void
	 */

    public function addUser(string $login, string $password, string $email, int $city_id): void
    {
        $query = $this->db->prepare("INSERT INTO users SET email = ?, password = ?, city_id = ?, status_id = 1,
            ban_status = 0, active = 0, registration_time = NOW(), login = ?");
        $query->execute([$email, $password, $city_id, $login]);
    }

P.S. Я только учусь и это мой первый более-менее достойный проект в портфолио, буду очень признателен вашей помощи:)
P.P.S. Еще у меня была мысль сделать как в Ларавель, сделав методы статическими, но хочется узнать, можно ли иначе
  • Вопрос задан
  • 375 просмотров
Подписаться 2 Простой 22 комментария
Пригласить эксперта
Ответы на вопрос 1
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы