В последнее время я работаю над несколькими проектами, в которых используются полиморфные отношения, отношения многие-ко-многим и иногда полиморфные отношения многие-ко-многим. При работе с ними стали появляться одни и те же проблемы, которые действительно раздражают меня с точки зрения хорошей архитектуры и традиционных отношений ORM. Поэтому начиная с этой недели, мы будем рассматривать новые шаблоны для создания карт отношений. Сегодня мы рассмотрим стандартные таблицы с отношением многие-ко-многим и их связующую сводную таблицу. Рассмотрим для примера беговой счетчик. Пользователь может вводить день, в котором может быть несколько кругов различных дорожек или маршрутов. Вы подумаете, что это относительно просто, и создадите такую схему: %%(t) days: date temperature # больше информации routes: gis_points public distance title # больше полей day_route: # это будет наша сводная (pivot) таблица многие-ко-многим day_id routes_id laps %% Это работает, но все станет более странным, когда вы захотите вычислить общую дистанцию за день. Что ж, вы можете сделать что-нибудь подобное (реализация на псевдо-PHP, используя Eloquent): %% public function getTotalDistanceAttribute() { // Создает ассоциативный массив, в котором каждый $lap => $distance $routes = $this->routes()->list('laps', 'distance'); $total = 0; foreach($routes as $laps => $distance) { $total += ($laps * $distance); } return $total; } %% Это работает, но похоже что %%Day%% немного больше чем нужно. Например, что будет, если я захочу узнать общую дистанцию за день на одном из маршрутов? Вы можете добавить пользовательские методы доступа (//accessors//), но куда вы поместите эту логику? В модель маршрута? В пользовательскую службу (//service//)? Это кажется совсем странным. У нас уже есть сводная таблица, и мы хотим применить логику к этой таблице, так не должно ли это быть частью модели? Рассмотрим такую стректуру БД вместо прежней: %%(t) days: date temperature # больше информации routes: gis_points public distance title # больше полей runs: # это была наша таблица day_route day_id routes_id lap_distance laps %% Как вы видите, модель %%Run%% - это просто переименованная сводная таблица с той же самой информацией! Теперь %%Run%% будет знать свою общую дистанцию, которая действительно легкодоступна! И что еще лучше, наш метод доступа %%totalDistance%% тоже выигрывает от этого. %% public function getTotalDistanceAttribute() { $sum = 0; $this->runs->each(function($run) use(&$sum) { $sum += $run->distance; }); return $sum; } %% На мой взгляд, это намного понятнее. %%Day%% не должен заботиться о том, как посчитать дистанцию по конкретному маршруту за день, он просто знает, как суммировать расстояния. Теперь %%Run%% готов заботиться обо всей его логике и в то же время сохранять дистанцию по маршруту, когда ему это потребуется (что, если пользователь позднее изменит маршрут, тогда все дистанции прошедших дней будут испорчены). На этой неделе мы рассмотрим похожие решения и увидим, как они помогают нам повсюду, не только сэкономив нам одну строчку в функции %%getTotalDistanceAttribute%%.