Выращивание в тандеме

Для начала позвольте мне объяснить, что я имею в виду под тандемной выборкой. Выборка — это процесс доступа к данным из базы данных или API с помощью запроса на выборку. Подробнее о выборке можно прочитать здесь. Я имею в виду, что этот процесс работает в дуальном режиме. То есть, если я делаю запрос на выборку, мне требуются либо данные из другого запроса на выборку, либо, по крайней мере, процесс, который должен быть запущен. В данном конкретном случае я хотел, чтобы запрос на выборку был вложен в другой, чтобы он выполнялся сразу после другого в рамках одного движения. Так что это не совсем одновременное выполнение, но в моем случае они выполнялись в одной функции-обработчике, поэтому я считаю это одним движением и, следовательно, одновременным.

Теперь, чтобы объяснить, что у меня происходило в этом примере и как я дошел до этого момента, я сначала объясню свою установку. Моя база данных была настроена на отношения «многие-ко-многим», относящиеся к трем таблицам. Этими таблицами были таблица Photos, таблица Tags и таблица PhotoTags. Таблица PhotoTags является объединенной таблицей, соединяющей две другие, с колонкой внешнего ключа для каждой из других соответствующих таблиц.

Для справки вот как выглядит моя схема:

db/schema.rb
create_table "photos", force: :cascade do |t|
  t.string "image"
  t.text "description"
  t.integer "user_id"
end

create_table "tags", force: :cascade do |t|
  t.string "name"
end

create_table "photo_tags", force: :cascade do |t|
  t.integer "photo_id"
  t.integer "tag_id"
end
Войти в полноэкранный режим Выход из полноэкранного режима

Далее я объясню, что у меня произошло. Начнем с того, что в этом примере есть страница с формой, настроенной для добавления фотографий в базу данных. Эта форма имеет три входа: Изображение, Описание и Теги. Изображение — это URL-адрес изображения. Описание и теги являются необязательными. Описание предназначено для того, чтобы пользователь мог добавить описательный текст к фотографии. Теги предназначены для добавления и выбора тегов из базы данных, чтобы их можно было прикрепить к фотографии. Добавление тегов в базу данных обрабатывается функцией обработчика события onKeyDown, но что более важно, прикрепление этих тегов обрабатывается функцией обработчика события onChange для выбора, а затем POST-запросом для прикрепления их к фотографии.

client/AddPhoto.js: onChange
onChange={(event, newValue) => {
  tags.filter( tag => {
    newValue.length > 0 ? (
      newValue.filter( value => {
         if(value === tag.name) {
          setSelectedTags([...selectedTags, tag])
        }
      } )
    ) : (
      setSelectedTags([])
    )
  } )
}}
Вход в полноэкранный режим Выход из полноэкранного режима

Это мой обработчик onChange для ввода формы тегов. Проще говоря, я проверяю, есть ли какие-либо теги во вводе и добавляю их в переменную состояния под названием selectedTags. Я проверил, был ли добавлен тег, посмотрев, был ли реквизит newValue пустым или нет. Затем я сопоставил newValue с tags из базы данных. И tags, и newValue являются массивами, но массив tags взят из базы данных и поэтому имеет идентификаторы, тогда как массив newValue имеет только значение имени тега. Я сопоставляю эти два массива, фильтруя оба. Я делаю это для того, чтобы взять тег из базы данных, который имеет то же имя, что и тег в массиве newValue. По сути, я захватываю тег с его id из массива, отображаемого в DOM. Если они совпадают, я устанавливаю их в массив selectedTags, используя useState. Таким образом, массив selectedTags соответствует тому, что пользователь видит в DOM, но также содержит идентификаторы, которые помогут позже добавить ассоциацию к фотографии через таблицу photo_tags Join.

Самое сложное — добавить ассоциацию, взять выбранные теги и прикрепить их к фотографии. Для этого необходимо выполнить POST-запрос к таблице photo_tags Join, чтобы каждый тег был прикреплен к фотографии. Но сначала нам нужно иметь фотографию, чтобы добавить их к ней. На самом деле это не проблема, за исключением того, что фотография должна быть в базе данных и иметь идентификатор. Это связано с тем, что связь между фотографиями и тегами в таблице Join зависит от id в качестве внешних ключей (обратитесь к схеме для разъяснения этого). Это тоже не проблема, потому что мне просто нужно выполнить запрос POST fetch для добавления фотографии в базу данных, а затем выполнить запрос POST fetch к таблице Join для присоединения тегов. Проблема заключается в том, что это одна форма с одной кнопкой отправки. Поэтому для того, чтобы теги, связанные с фотографией, а также сама фотография были добавлены в базу данных после нажатия одной кнопки отправки, мне нужно запустить оба POST-запроса почти одновременно. Я делаю это, запуская два запроса выборки сразу друг за другом внутри функции обработчика отправки. Таким образом, они оба инициируются при нажатии кнопки submit, но я также контролирую их так, чтобы запрос на выборку фотографии был запущен и с возвратом данных после его завершения, чтобы затем запустить запрос на выборку photo_tag с этими данными.

Вот как выглядит моя функция обработчика отправки:

client/AddPhoto.js: handleSubmit
function handleSubmit(event) {
 event.preventDefault()

 fetch("/photos", {
   method: "POST",
   headers: {
     "Content-Type": "application/json"
   },
   body: JSON.stringify(formData)
 })
 .then((r) => {
   if (r.ok) {
     r.json().then((photo) => {
       if(selectedTags.length > 0) {
         selectedTags.map( tag => {
           fetch("/photo_tags", {
             method: "POST",
             headers: {
               "Content-Type": "application/json"
             },
             body: JSON.stringify({ photo_id: photo.id, tag_id: tag.id })
           })
           .then((r) => {
             if (r.ok) {
               r.json().then((photoTag) => {
                 console.log(photoTag)
               })
             }
             else {
               r.json().then((err) => setErrors(err.errors))
             }
           })
         })
       }
     }).then(() => {
       history.push(`/users/${currentUser.id}`)
     })
   } else {
     r.json().then((err) => {
       setErrors(err.errors.map((error) => error.toLowerCase()))
     })
   }
 })
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Здесь formData — это объект, содержащий URL изображения и описание для фотографии. Они добавляются через useState из других входов формы. У меня есть ошибки, установленные для обоих запросов выборки, но поскольку запрос выборки photo_tag не может быть запущен без идентификатора фотографии или идентификатора тегов, он никогда не доходит до этого момента. Я также записал в консоль данные из запроса photo_tag, чтобы убедиться, что все работает правильно. Я настроил запрос на выборку photo_tag на выполнение после запроса на выборку фотографий, вложив его внутрь ответа, и он также будет выполняться только в том случае, если выбраны теги (поскольку добавление тегов к фотографии необязательно). Если есть selectedTags, он будет перебирать их и запускать запрос выборки photo_tag для каждого тега. После выполнения всех запросов на выборку он будет перенаправлять на страницу пользователя, которую я настроил с идентификатором пользователя, который вошел в систему (он же currentUser). Кстати, добавление фотографий и тегов также требует, чтобы пользователь вошел в систему. Таблица photo также имеет внешний ключ user_id, но это обрабатывается в контроллере, здесь:

app/controllers/photos_controller.rb: create
def create
  photo = @current_user.photos.create!(photo_params)
  render json: photo, status: :created
end
Вход в полноэкранный режим Выйти из полноэкранного режима

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

Оцените статью
devanswers.ru
Добавить комментарий