Blog::Converters::Database)]
def article(article : Blog::Entities::Article) :
Blog::Entities::Article
article
end
Та-да! Простой способ предоставления объектов базы данных непосредственно в качестве аргументов действия через их идентификаторы. Хотя на данный момент у нас уже довольно много конечных точек, связанных со статьями, нам все еще не хватает способа обновить или удалить статью. Давайте сначала сосредоточимся на том, как обновить статью.
Обновление статьи
На первый взгляд обновление записей базы данных может показаться простым, но на самом деле оно может быть довольно сложным из-за характера процесса. Например, чтобы обновить сущность, сначала необходимо получить ее текущий экземпляр, а затем применить к нему изменения. Изменения обычно представляются в виде тела запроса к конечной точке PUT
с включенным идентификатором объекта, в отличие от конечной точки POST
. Проблема заключается в том, как применить изменения из нового тела запроса к существующей сущности.
Сериализатор Athena имеет концепцию конструкторов объектов, которые управляют тем, как сначала инициализируется десериализуемый объект. По умолчанию они создаются обычным способом с помощью метода .new
. Он предлагает возможность определять собственные объекты, что мы могли бы сделать, чтобы получить объект из базы данных на основе свойства ID в теле запроса. Затем мы применим остальную часть тела запроса к полученной записи. Это гарантирует правильную обработку скрытых значений базы данных, а также выполнение сложной части применения изменений к объекту.
Однако, поскольку это немного усложняет работу сериализатора Athena, а в нашей статье есть только два свойства, мы не собираемся это реализовывать. Если вам интересно, как это будет выглядеть, или вы хотите попробовать реализовать это самостоятельно, ознакомьтесь с рецептом кулинарной книги: https://athenaframework.org/cookbook/object_constructors/#db. Он использует Granite ORM, но переключить его на наш EntityManager должно быть довольно просто.
Вместо использования конструктора объекта мы просто собираемся вручную сопоставить значения из тела запроса и применить их к объекту, полученному из базы данных. Прежде чем мы сможем это сделать, нам сначала нужно обновить менеджер сущностей для обработки обновлений. Первым шагом является обновление #persist
, чтобы проверить, установлен ли идентификатор с помощью следующего:
def persist(entity : DB::Serializable) : Nil
entity.before_save if entity.responds_to? :before_save
if entity.id?.nil?
entity.after_save self.save entity
else
self.update entity
end
Где метод #update
выглядит следующим образом:
private def update(entity : Blog::Entities::Article) : Nil
@@connection.exec(
%(UPDATE "articles" SET "title" = $1, "body" = $2,
"updated_at" = $3, "deleted_at" = $4 WHERE "id" = $5;),
entity.title,
entity.body,
entity.updated_at,
entity.deleted_at,
entity.id
)
end
Отсюда мы можем обновить нашу конечную точку #update_article
, чтобы она выглядела следующим образом:
@[ARTA::Put("/article/{id}")] @[ATHA::ParamConverter("article_entity", converter:
Blog::Converters::Database)]
@[ATHA::ParamConverter("article", converter:
ATH::RequestBodyConverter)]
def update_article(article_entity : Blog::Entities::Article,
article : Blog::Entities::Article) : Blog::Entities::Article
article_entity.title = article.title
article_entity.body = article.body
@entity_manager.persist article_entity
article_entity
end
В этом примере мы используем два преобразователя параметров. Первый извлекает реальную сущность статьи из базы данных, а второй создает ее на основе тела запроса. Затем мы применяем статью тела запроса к сущности статьи и передаем ее в #persist
. Допустим, мы делаем такой запрос:
curl --request PUT 'http://localhost:3000/article/1' \ --header 'Content-Type: application/json' \
--data-raw '{
"title": "New Title",