@Alk90
php, mysql, jquery, css, html, api

Как правильно преобразовывать данные из БД в структурированные объекты?

Всем привет! Я уже давно программирую на php, но т.к. в основном работаю над собственными проектами (годами), то у меня сложилась некая удобная для меня структура работы с БД, которую я использую постоянно примерно в одном и том же виде. Но боюсь, если я пойду куда-то на собеседование - меня не поймут. Поэтому спрашиваю совета, помогите понять на сколько я правильно делаю или как делать правильно.
Не подружился я с ORM, т.к. при сложных запросах, в них писанины получается в разы больше чем просто написать прямой запрос. К тому же, я люблю контролировать производительность, что сделать в конструкторе запросов сложно.

В самописных проектах или плагинах для wordpress я создаю некий класс или группу классов, в которой методы состоят из запросов к БД. Каждый метод для какой-то собственной реализации, которую нельзя выполнить условиями в одном методе (нужны дополнительные данные).
И в таком классе обязательно есть метод format($dbResult){....}, который закидывает полученные данные в объект, который в свою очередь уже обрабатывается различными классами представления в зависимости от контекста (ClassView->renderShortPost($post)). Ниже я приведу код такого класса.

Вижу преимущество в таком подходе в том, что контроллер или другой обработчик никогда не знает о каких либо таблицах в бд. Он знает только о объекте или массиве объектов который он получит. И так все скрипты в проекте. Вот что у меня получается (я упростил код, чтобы он не получился на сотни строк, но смысл думаю понятен):

// AppModel уже содержит данные о БД и разные общие для всех подобных классов плюшки
// На самом деле, AppModel содержит различные сеттеры, которые я использую как псевдоконструктор запросов. Например:
// setSelect() - через запятую я перечисляю поля, которые мне нужны. А текущий класс содержит маппер, который указывает на то, какие JOIN нужно добавить в запрос и какие достато поля для конкретного select, который мне необходим.

class PostsModel extends AppModel{

	private $table = 'posts';

	public function __construct(){

		// inicialFields это некий маппер, который сложит все необходимые поля в свойство select,
		// все необходимые join в свойство joins. Если необходимые данные, такие как author, title, countComments или что-то еще будут указаны перед запросом (в текущем методе или даже из вне. Где-то в контроллере)
		$this->inicialFields();

		/**
		 * эти данные будут выгружены из базы по умолчанию
		 * setFields это метод внутри AppModel, который на основе map_fields из метода
		 * inicialFields сложит все необходимые поля в свойство select, все необходимые join в свойство joins
		 */
		$this->setFields("id, title");

	}


	/**
	 * Создает правила для полей, которые указаны в качестве полей для вывода
	 */
	protected function inicialFields(){

		$this->map_fields = [
			"id"            => "p.`id`",
			"title"         => "p.`title`",
			"author"      => [
				"fields" => [
					"u.`id` userId",
					"u.`name` userName"
				],
				"join"  => [
					"LEFT JOIN `users` u ON u.`id` = p.`authorId`"
				]
			]
		];
	}

/**
	 * @param int $id
	 * @return PostEntity|null
	 */
	function getById(int $id){

		// тут нам дополнительно понадобился автор.
		$this->setFields("author");
		$this->setWhere("p.`id` = {$id}");

		$res = $this->db->query("
			SELECT {$this->getSelect()}
			FROM `{$this->table}` p 
			{$this->getJoins()}
			{$this->getWhere()} // на основе переданных where сформирует условия либо пустоту, если условий небыло
		");
		if($res->num_rows){
			$row = $res->fetch_assoc();
			return $this->format($row);
		}

		return null;

	}

/**
	 * Метод возвращает список постов
	 * Тут мы ничего не указываем дополнительно для setFields, а значит будут получены данные, которые переданные в конструкторе по умолчанию. т.е. id и title
	 * @param int $userId
	 * @return PostEntity[]
	 */
	function getByList(int $userId){

		$result = [];

		$this->setWhere("p.`authoId` = {$userId}");

		$res = $this->db->query("
				SELECT {$this->getSelect()} 
				FROM `{$this->table}` p 
				{$this->getWhere()}
		");
		if($res->num_rows){
			$row = $res->fetch_assoc();
			$result[] = $this->format($row);
		}

		return $result;
	}

function format($row){

		$post = new PostEntity($row['id'], $row['title']);

		if(isset($row['userId'])){
			$user = new UserEntity($row['userId']);
			if(isset($row['userName'])){
				$user->setName($row['userName']);
			}
			$post->setAuthor($user);
		}

		return $post;

	}


}
  • Вопрос задан
  • 96 просмотров
Решения вопроса 1
php666
@php666
PHP-макака
Если абстрагироваться от советов использовать полноценный ОРМ, то вот:

SELECT {$this->getSelect()}
      FROM `{$this->table}` p 
      {$this->getJoins()}
      {$this->getWhere()} // на основе переданных where сформирует условия либо пустоту, если условий небыло
-- эта дичь в последствии станет неработоспособной и ты придешь к этому:

public function getModelBySql($sql);
public function getModelListBySql($sql)

ибо уместить в вышеприведенную ерунду все возможности sql не возможно. Говорю на личном опыте, сам такое писал.

function format($row){

    $post = new PostEntity($row['id'], $row['title']);

    if(isset($row['userId'])){
      $user = new UserEntity($row['userId']);
      if(isset($row['userName'])){
        $user->setName($row['userName']);
      }
      $post->setAuthor($user);
    }

    return $post;

  }
слишком сложная идеология. PostEntity может содержать немыслимое кол-во связей
PostEntity->Author->Country->Region->City->Street
ты это все ручную будешь прописывать все это?

. К тому же, я люблю контролировать производительность,
велосипеды ты любишь.
Коли так, то остановись на каком-то тривиальном final решении. Иными словами создай колесо, но не пытайся строить машину - все равно не выйдет в одиночку.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@WorksDens
JustDen
Функция create_user работает так, как ожидалось. Она создает нового пользователя с заданным именем и электронной почтой и присваивает ему уникальный ID. Функция также проверяет, что электронная почта имеет правильный формат, и возвращает сообщение об ошибке, если это не так.

В функции get_user пользователь извлекается по его ID и возвращается в виде словаря. Функция также проверяет, существует ли пользователь с заданным ID, и возвращает сообщение об ошибке, если нет.

Функция update_user обновляет имя и email пользователя с заданным ID. Она также проверяет, что электронная почта имеет правильный формат, и возвращает сообщение об ошибке, если это не так.

Функция delete_user удаляет пользователя с заданным идентификатором. Она также проверяет, существует ли пользователь с заданным ID, и возвращает сообщение об ошибке, если это не так.

В целом, функции работают так, как задумано, и эффективно справляются с ошибками.
Ответ написан
Ваш ответ на вопрос

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

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