В последнее время я работаю над несколькими проектами, в которых используются полиморфные отношения, отношения многие-ко-многим и иногда полиморфные отношения многие-ко-многим. При работе с ними стали появляться одни и те же проблемы, которые действительно раздражают меня с точки зрения хорошей архитектуры и традиционных отношений ORM. Поэтому начиная с этой недели, мы будем рассматривать новые шаблоны для создания карт отношений. Сегодня мы рассмотрим стандартные таблицы с отношением многие-ко-многим и их связующую сводную таблицу.
Рассмотрим для примера беговой счетчик. Пользователь может вводить день, в котором может быть несколько кругов различных дорожек или маршрутов. Вы подумаете, что это относительно просто, и создадите такую схему:
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;
}
Это работает, но похоже что PHPDay немного больше чем нужно. Например, что будет, если я захочу узнать общую дистанцию за день на одном из маршрутов? Вы можете добавить пользовательские методы доступа (accessors), но куда вы поместите эту логику? В модель маршрута? В пользовательскую службу (service)? Это кажется совсем странным.
У нас уже есть сводная таблица, и мы хотим применить логику к этой таблице, так не должно ли это быть частью модели? Рассмотрим такую стректуру БД вместо прежней:
days:
    date
    temperature
    # больше информации
routes:
    gis_points
    public
    distance
    title
    # больше полей
runs: # это была наша таблица day_route
    day_id
    routes_id
    lap_distance
    laps
Как вы видите, модель PHPRun — это просто переименованная сводная таблица с той же самой информацией! Теперь PHPRun будет знать свою общую дистанцию, которая действительно легкодоступна! И что еще лучше, наш метод доступа PHPtotalDistance тоже выигрывает от этого.
public function getTotalDistanceAttribute()
{
    $sum = 0;
    $this->runs->each(function($run) use(&$sum) {
        $sum += $run->distance;
    });
    return $sum;
}
На мой взгляд, это намного понятнее. PHPDay не должен заботиться о том, как посчитать дистанцию по конкретному маршруту за день, он просто знает, как суммировать расстояния. Теперь PHPRun готов заботиться обо всей его логике и в то же время сохранять дистанцию по маршруту, когда ему это потребуется (что, если пользователь позднее изменит маршрут, тогда все дистанции прошедших дней будут испорчены).
На этой неделе мы рассмотрим похожие решения и увидим, как они помогают нам повсюду, не только сэкономив нам одну строчку в функции PHPgetTotalDistanceAttribute.
 Laravel по-русски
Laravel по-русски 
      
      
    