Управление отношениями в Laravel, придерживаясь шаблона репозитория



при создании приложения в Laravel 4 после прочтения книги T. Otwell о хороших шаблонах дизайна в Laravel я обнаружил, что создаю репозитории для каждой таблицы в приложении.



Я закончил со следующей структурой таблицы:




  • студенты: id, имя

  • курсы: id, имя, teacher_id

  • учителя: id, имя

  • задания: id, имя, course_id

  • баллы (выступает в качестве стержня между студентами и задания): student_id, assignment_id, scores


У меня есть классы репозитория с методами поиска, создания, обновления и удаления для всех этих таблиц. Каждый репозиторий имеет красноречивую модель, которая взаимодействует с базой данных. Отношения определяются в модели согласно документации Laravel:http://laravel.com/docs/eloquent#relationships.



при создании нового курса все, что я делаю, это вызов метода create в репозитории курса. Этот курс имеет задания, поэтому при их создании я также хочу создать запись в таблице баллов для каждого студента в курсе. Я делаю это через репозиторий назначения. Это означает, что репозиторий присваивания взаимодействует с двумя красноречивыми моделями, с моделью присваивания и студентом.



мой вопрос: поскольку это приложение, вероятно, будет расти в размерах и будет введено больше отношений, это хорошая практика для общения с различными красноречивыми моделями в репозиториях или это должно быть вместо этого используйте другие репозитории (я имею в виду вызов других репозиториев из репозитория назначения) или это должно быть сделано в моделях Eloquent все вместе?



кроме того, это хорошая практика, чтобы использовать таблицу баллов в качестве стержня между заданиями и студентами или это должно быть сделано где-то еще?

704   4  

4 ответов:

имейте в виду, что вы просите мнения :D

вот мой:

TL; DR: Да, это нормально.

у тебя все хорошо!

Я делаю именно то, что вы делаете часто, и считаю, что это отлично работает.

Я часто, однако, организовываю репозитории вокруг бизнес-логики вместо того, чтобы иметь РЕПО на таблицу. Это полезно, поскольку это точка зрения, сосредоточенная вокруг того, как ваше приложение должно решить вашу "бизнес-проблему".

Курс является "сущностью", с атрибутами (название, идентификатор и т. д.) и даже другими сущностями (назначениями, которые имеют свои собственные атрибуты и, возможно, сущности).

ваш репозиторий "курс" должен иметь возможность возвращать курс и атрибуты/назначения курсов (включая назначение).

вы можете сделать это с красноречивым, к счастью.

(Я часто получаю репозиторий для каждой таблицы, но некоторые репозитории используются намного больше, чем другие, и поэтому имеют гораздо больше методов. Ваш репозиторий "курсы" может быть гораздо более полнофункциональным, чем ваш репозиторий назначений, например, если ваше приложение больше ориентируется на курсы и меньше на коллекцию назначений курсов).

самая сложная часть -

Я часто использую репозитории внутри моих репозиториев для выполнения некоторых действий с базой данных.

любой репозиторий, который реализует Eloquent для обработки данных, скорее всего, вернет модели Eloquent. В этом свете, это нормально, если ваш курс модель использует встроенные отношения для извлечения или сохранения назначений (или любого другого варианта использования). Наша "реализация" построена вокруг красноречивого.

с практической точки зрения, это имеет смысл. Мы вряд ли изменим источники данных на что-то красноречивое, с чем нельзя справиться (на источник данных, отличный от sql).

платформы

самая сложная часть этой установки, по крайней мере для меня, определяет, действительно ли красноречие помогает или вредит нам. Ормы-сложная тема, потому что, хотя они очень помогают нам с практической точки зрения, они также связывают ваш код "бизнес-логики" с кодом, выполняющим поиск данных.

этот вид запутывает, является ли ответственность вашего репозитория на самом деле за обработку данных или обработку извлечения / обновления сущностей (сущностей бизнес-домена).

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

во всяком случае, это несколько неполные мысли. Они, как говорится, просто мое мнение, которое случается в результате из чтения Домен Управляемая Конструкция и смотреть видео как лейтмотив"дяди Боба" в Ruby Midwest в течение последнего года.

Я заканчиваю большой проект с использованием Laravel 4 и должен был ответить на все вопросы, которые вы задаете прямо сейчас. После прочтения всех доступных книг Laravel в Leanpub и тонны Google, я придумал следующую структуру.

  1. один красноречивый класс модели на таблицу данных
  2. один класс репозитория на красноречивую модель
  3. класс обслуживания, который может взаимодействовать между несколькими классами репозитория.

так допустим, я создаю базу данных фильмов. У меня были бы, по крайней мере, следующие красноречивые классы моделей:

  • кино
  • студии
  • директор
  • актер
  • комментарий

класс репозитория будет инкапсулировать каждый класс модели Eloquent и отвечать за операции CRUD в базе данных. Классы репозитория могут выглядеть так это:

  • MovieRepository
  • StudioRepository
  • DirectorRepository
  • ActorRepository
  • ReviewRepository

каждый класс репозитория будет расширять класс BaseRepository, который реализует следующий интерфейс:

interface BaseRepositoryInterface
{
    public function errors();

    public function all(array $related = null);

    public function get($id, array $related = null);

    public function getWhere($column, $value, array $related = null);

    public function getRecent($limit, array $related = null);

    public function create(array $data);

    public function update(array $data);

    public function delete($id);

    public function deleteWhere($column, $value);
}

класс обслуживания используется для склеивания нескольких репозиториев вместе и содержит реальную "бизнес-логику" приложения. Контроллеры только взаимодействие с классами обслуживания для создания, обновления и удаления действий.

поэтому, когда я хочу создать новую запись фильма в базе данных, мой класс MovieController может иметь следующие методы:

public function __construct(MovieRepositoryInterface $movieRepository, MovieServiceInterface $movieService)
{
    $this->movieRepository = $movieRepository;
    $this->movieService = $movieService;
}

public function postCreate()
{
    if( ! $this->movieService->create(Input::all()))
    {
        return Redirect::back()->withErrors($this->movieService->errors())->withInput();
    }

    // New movie was saved successfully. Do whatever you need to do here.
}

это до вас, чтобы определить, как вы отправляете данные на контроллеры, но давайте скажем, что данные, возвращаемые Input:: all() в postCreate () метод выглядит примерно так:

$data = array(
    'movie' => array(
        'title'    => 'Iron Eagle',
        'year'     => '1986',
        'synopsis' => 'When Doug\'s father, an Air Force Pilot, is shot down by MiGs belonging to a radical Middle Eastern state, no one seems able to get him out. Doug finds Chappy, an Air Force Colonel who is intrigued by the idea of sending in two fighters piloted by himself and Doug to rescue Doug\'s father after bombing the MiG base.'
    ),
    'actors' => array(
        0 => 'Louis Gossett Jr.',
        1 => 'Jason Gedrick',
        2 => 'Larry B. Scott'
    ),
    'director' => 'Sidney J. Furie',
    'studio' => 'TriStar Pictures'
)

так как MovieRepository не должен знать, как создать актера, Записи режиссера или студии в базе данных мы будем использовать наш класс MovieService, который может выглядеть примерно так:

public function __construct(MovieRepositoryInterface $movieRepository, ActorRepositoryInterface $actorRepository, DirectorRepositoryInterface $directorRepository, StudioRepositoryInterface $studioRepository)
{
    $this->movieRepository = $movieRepository;
    $this->actorRepository = $actorRepository;
    $this->directorRepository = $directorRepository;
    $this->studioRepository = $studioRepository;
}

public function create(array $input)
{
    $movieData    = $input['movie'];
    $actorsData   = $input['actors'];
    $directorData = $input['director'];
    $studioData   = $input['studio'];

    // In a more complete example you would probably want to implement database transactions and perform input validation using the Laravel Validator class here.

    // Create the new movie record
    $movie = $this->movieRepository->create($movieData);

    // Create the new actor records and associate them with the movie record
    foreach($actors as $actor)
    {
        $actorModel = $this->actorRepository->create($actor);
        $movie->actors()->save($actorModel);
    }

    // Create the director record and associate it with the movie record
    $director = $this->directorRepository->create($directorData);
    $director->movies()->associate($movie);

    // Create the studio record and associate it with the movie record
    $studio = $this->studioRepository->create($studioData);
    $studio->movies()->associate($movie);

    // Assume everything worked. In the real world you'll need to implement checks.
    return true;
}

так что мы остались с хорошим, разумным разделением проблем. Репозитории знают только о модели Eloquent, которую они вставляют и извлекают из базы данных. Контроллеры не заботятся о репозиториях, они просто передают данные, которые они собирают от пользователя, и передают их в соответствующую службу. Службе все равно как данные он получает сохраняется в базе данных, он просто передает соответствующие данные, которые были предоставлены контроллером в соответствующие репозитории.

Мне нравится думать об этом с точки зрения того, что делает мой код и за что он отвечает, а не "правильно или неправильно". Вот как я разрываю свои обязанности:

  • контроллеры-это HTTP-уровень и маршрутизируют запросы через базовые API (он же управляет потоком)
  • модели представляют схему базы данных и сообщают приложению, как выглядят данные, какие отношения они могут иметь, а также любые глобальные атрибуты, которые могут быть необходимо (например, метод name для возврата сцепленных имени и фамилии)
  • репозитории представляют собой более сложные запросы и взаимодействия с моделями (я не делаю никаких запросов к методам модели).
  • поисковые системы - классы, которые помогают мне строить сложные запросы поиска.

имея это в виду, имеет смысл каждый раз использовать репозиторий (независимо от того, создаете ли вы интерфейсы.так далее. это совсем другая тема). Мне нравится такой подход, потому что это означает Я точно знаю, куда идти, когда мне нужно сделать определенную работу.

Я также склонен создавать базовый репозиторий, обычно абстрактный класс, который определяет основные значения по умолчанию - в основном операции CRUD, а затем каждый ребенок может просто расширять и добавлять методы по мере необходимости или перегружать значения по умолчанию. Инъекция вашей модели также помогает этой модели быть довольно надежной.

подумайте о репозиториях как о последовательном картотеке ваших данных (а не только ваших ORMs). Идея заключается в том, что вы хотите захватить данные в последовательном простом в использовании API.

Если вы обнаружите, что просто делаете Model::all (), Model::find (), Model::create (), вы, вероятно, не выиграете от абстрагирования репозитория. С другой стороны, если вы хотите сделать немного больше бизнес-логики для своих запросов или действий, вы можете создать репозиторий, чтобы упростить использование API для работа с данными.

Я думаю, вы спрашивали, будет ли репозиторий лучшим способом справиться с некоторыми из более подробных синтаксисов, необходимых для подключения связанных моделей. В зависимости от ситуации, есть несколько вещей, которые я могу сделать:

  1. повесив новую дочернюю модель от родительской модели (один-один или один-много), я бы добавил метод в дочерний репозиторий что-то вроде createWithParent($attributes, $parentModelInstance) и это будет просто добавить $parentModelInstance->id на parent_id поле атрибутов и призыв создавать.

  2. прикрепляя много-много отношений, я фактически создаю функции на моделях, чтобы я мог запускать $instance - >attachChild($childInstance). Обратите внимание, что для этого требуются существующие элементы с обеих сторон.

  3. создавая связанные модели в одном запуске, я создаю то, что я называю шлюзом (это может быть немного от определений Фаулера). Как я могу вызвать $gateway - >createParentAndChild ($parentAttributes, $childAttributes) вместо a куча логики, которая может измениться или которая усложнит логику, которую я имею в контроллере или команде.

Comments

    Ничего не найдено.