Для моего третьего проекта во Flatiron я решил, что хочу расширить то, что я создал с помощью Sinatra. Ruby on Rails (или сокращенно Rails) — это название фреймворка, который мы изучали в течение последнего месяца или около того. Rails — довольно впечатляющее программное обеспечение, позволяющее быстро создавать необходимые представления, контроллеры и базы данных. Я обнаружил, что процесс повторного посещения предыдущего проекта с этим новым набором инструментов и моими расширенными знаниями был увлекательным, мотивирующим, а также очень познавательным.

С Sourdough Tracker пользователи не могли взаимодействовать друг с другом. Ингредиенты, рецепты и выпечка каждого пользователя были отдельными и разными, чтобы их мог видеть только отдельный пользователь, которому они принадлежали. В этом проекте я хотел расширить возможности, чтобы обеспечить базовое социальное взаимодействие между учетными записями. Я хотел, чтобы пользователи могли обмениваться рецептами или делиться своими успехами с мукой конкретной мельницы или производителя (сам я фанат муки Janie’s Mill). Из этого следует, что пользователи должны иметь возможность оставлять комментарии, делясь своим опытом использования определенного рецепта или ингредиента.

Отображение отношений

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

Несколько запутанная схема, которую вы видите выше, — это то, на чем я остановился. Многое происходит, но вы можете видеть, что некоторые модели должны быть общедоступными, доступными для любого пользователя, в то время как я хотел, чтобы другая информация строго принадлежала пользователю, который ее создал. Важно отметить, что я хотел, чтобы пользователи могли сохранять ингредиенты или рецепты, которые они сочли полезными, чтобы они могли легко ссылаться на них без необходимости искать каждый ингредиент или каждый рецепт, размещенный на сайте.

Хорошо, я делал это раньше. Это отношение has_many к has_many. Все, что мне нужно сделать, это настроить таблицу соединений и сообщить ActiveRecord об отношениях, и все готово. Конечно, мне нужно настроить таблицу users_ingredients и таблицу users_recipes, но это не страшно.

Фу. Ждать…

Комментарии являются также связующей таблицей между пользователем и объектом, но комментарии могут относиться к сообщению, и ингредиенту, и рецепту. И я также хотел добавить возможность «лайкать» вещи, чтобы каждый Like функционировал как таблица соединений между User и любым понравившимся объектом?? На данный момент я просматриваю несколько таблиц соединения для каждого объекта, что начинает казаться довольно повторяющимся. Должен быть более простой способ, верно?

Введите полиморфные ассоциации

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

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

При полиморфных ассоциациях индекс и тип связанной модели хранится в одной строке. Столбцы просто отслеживают, какой столбец является индексным, а какой столбцом типа модели.

Это позволило мне создать модель Bookmark, которая могла бы связать User с несколькими объектами без необходимости в таблице соединений для каждого отношения модели!

Миграция для моей таблицы Bookmarks выглядит следующим образом:

create_table :bookmarks do |t|
  t.belongs_to :user
  t.bigint  :bookmarkable_id
  t.string  :bookmarkable_type
  t.timestamps
end

И соответствующие модельные отношения:

class User < ApplicationRecord
has_many :bookmarks
end
class Bookmark < ApplicationRecord
  
  belongs_to :user
  belongs_to :bookmarkable, polymorphic: true
end
class Recipe < ApplicationRecord
  has_many :bookmarks, as: :bookmarkable
end

Самым интересным было то, что пользователи могли отслеживать рецепты, которые они создали, в дополнение крецептам, которые они хотели добавить в закладки. И эта же модель может позволить User «отмечать» посты, ингредиенты или любую будущую модель, добавленную в базу данных!

Эта модульность сделала добавление или удаление функций относительно легким. Допустим, я добавляю возможность загружать изображения и хочу, чтобы пользователи могли лайкать любое изображение. Все, что я должен написать, это то, что изображение has_many :likes, as: :likeable и я отправляюсь на гонки.

Это оказалось особенно удобным с возможностью абстрагировать части ваших представлений в частичные. Как только я написал код для отображения комментариев или добавления кнопки «Нравится», было относительно легко разместить его там, где это было необходимо, поскольку объект, который отслеживал все, был одним и тем же, и вся информация хранилась в одной базе данных!

Вот книга хлеба

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

Ниже видео-обзор сайта, который я создал. Не стесняйтесь проверить код на Github и, пожалуйста, не стесняйтесь обращаться к нам, если у вас есть какие-либо вопросы или предложения о том, как я могу улучшить!