[RUBY] Créer un formulaire de relation parent-enfant avec form_object (j'ai également écrit un test)

Contexte

Il existe des tables avec divers éléments enfants tels que «@ comment» et «@ employee» associés à «@ shop». Au moment de l'enregistrement initial des informations, j'ai pu enregistrer tous les éléments enfants avec l'élément parent tel que @ shop, @ comment, @ employee, mais

Bien que j'aie réalisé cela en utilisant ʻaccepts_nested_attributes_for` sous la forme de l'article ci-dessous,

▼ C'était réalisé comme ça Créer des données de table enfant à la fois avec fields_for (j'écris également un test) [Rails] [Rspec]

Parmi ceux-ci, l'édition de formulaires uniquement pour «@ shop» et la publication / modification de formulaires tels que «@ comment» et «@ employé» sont devenues nécessaires, de sorte que le «modèle» s'est progressivement gonflé de diverses descriptions.

Qu'est-ce que form_object?

↑ Dans la situation ci-dessus, form_object peut simplifier la description du modèle en rassemblant les validations et les paramètres de valeur par défaut liés à un formulaire spécifique en un seul endroit. Personnellement, je suis tombé sur l'introduction, alors je vais écrire un article et en garder une trace.

L'environnement d'exécution est le suivant.

Méthode d'introduction

La manière de base d'écrire form_object, controller et view est la suivante. Cette fois, je voudrais prendre un exemple de formulaire qui vous permet d'enregistrer un @ commentaire lors de votre première inscription @ shop.

Cet article a été le plus utile pour la mise en œuvre.

Enregistrer plusieurs enregistrements enfants sans accepte_nested_attributes_for

Structure de la base de données

shops
name string
category integer

La catégorie ↑ est le type de boutique. Colonne ʻEnum`.

comments
content text
shop_id integer

Fichier créé

form_object

/forms/shop_entry_form.rb


class ShopEntryForm
  include ActiveModel::Model

  # @Description de la boutique-----------------------------
  concerning :ShopBuilder do
    def initialize(params = {})
      super(params)
      @category = params[:category]
    end

    def facility
      @shop ||= Shop.new
    end
  end

  attr_accessor :name, :category
  validates :name, presence: true
  validates :category, presence: true

  # @Description du commentaire-----------------------------
  concerning :CommentBuilder do
    attr_reader :comments_attributes

    def comments
      @comments_attributes ||= Comment.new
    end

    def comments_attributes=(attributes)
      @comments_attributes = Comment.new(attributes)
    end
  end

  attr_accessor :content
 
  #Logique de mise en œuvre------------------------------------
  def save
    #S'il y a une erreur de validation, false est renvoyé et le traitement suivant n'est pas effectué.
    return false if invalid?

    shop.assign_attributes(shop_params)
    build_asscociation

    shop.save ? true : false
  end
  
  private

  def shop_params
    {
      name: name,
      category: @category,
    }
  end

  def build_asscociations
    #Ajoutez un commentaire à l'élément enfant de la boutique. Cependant, si le contenu est vide, il ne sera pas ajouté.
    shop.comments << comments if comments[:content].present?
  end

  
end

Il y avait beaucoup de pierre d'achoppement avec cela seul. .. .. .. Premièrement, la partie «concernant: ShopBuilder do ... end» a la signification suivante.

#Cette description est...
concern :ShopBuilder do
  ...
end

#Identique à ci-dessous
module ShopBuilder
  extend ActiveSupport::Concern
  ...
end

Pour plus de détails, consultez Cet article auquel j'ai fait référence lors de la mise en œuvre.

Ensuite, la partie de ʻinitialize (params = {}) ... end` a la signification suivante.

def initialize(params = {})
  # @Rendre les paramètres de la boutique accessibles
  super(params)
  
  #Description des colonnes pour lesquelles les valeurs par défaut sont définies dans DB
  @category = params[:category]
end

Tout d'abord, concernant super (params), selon l'article suivant, qui était également un article très utile pour la mise en œuvre.

Utiliser la classe de formulaire

Une description qui stocke les paramètres avec super (params), et a la même signification que la description suivante.

@attributes = self.class._default_attributes.deep_dup
assign_attributes(params)

En outre, les colonnes pour lesquelles les valeurs par défaut sont définies du côté de la base de données ne peuvent pas accéder aux paramètres à moins que vous n'écriviez explicitement pour accéder aux paramètres comme indiqué ci-dessous. Il est devenu ... ** **

@category = params[:category]

Ce mystère ne peut être résolu. Je voudrais en faire un prochain numéro. .. .. La raison pour laquelle la valeur par défaut est requise du côté db pour les colonnes utilisant enum est [cet article](https://framgia.com/journal/rails%E3%81%AEenum-%E8%AB%B8%E5% Voir 88% 83% E3% 81% AE% E5% 89% A3% EF% BC% 88% EF% BC% 91% EF% BC% 89-% E3% 80% 80from-viblo /).

Et la partie de def comments_attributes = (attributes) ... end,

def comments_attributes=(attributes)
  @comments_attributes = Comment.new(attributes)
end

C'est une façon d'écrire méthode setter que vous voyez rarement si vous ne faites que des Rails, et vous pouvez changer l'élément avec l'argument @ by ** sous la forme de méthode (argument)se terminant par= Je vais. Personnellement, je pense que c'est proche de l'image de faire quelque chose comme ça.

def comments_attributes=(attributes) # ...Ce qui suit est omis

#Une telle image
comments_attributes = attributes

#Donc tu peux l'appeler comme ça
self.comments_attributes
# =>Contenu des attributs

C'est parce que je n'ai pas compris correctement les getters et les setters Ruby. .. .. .. Tohoho. .. .. Je ferai de mon mieux. .. .. Pour la méthode se terminant par `=, voir " Introduction à Ruby pour les professionnels, des spécifications du langage aux techniques de développement / débogage pilotées par les tests " J'ai relu "p215" de /) environ 15 fois.

controller

Ce qui suit est une description du contrôleur. Le contrôleur ressemble à ceci.

app/controllers/shops_controller.rb


class ShopsController < ApplicationController
  
  def new
    @shop = ShopEntryForm.new
  end

  def create
    @shop = ShopEntryForm.new(shop_entry_params)
    if @shop.save
      #Que faire quand ça réussit
    else
      #Que faire en cas d'échec
    end
  end

  private

  def shop_entry_params
    params.require(:shop_entry_form).permit(:caregory,
                                            :name,
                                            comments_attributes: [:content])
  end
end

J'ai l'impression que la description n'a pas diminué de façon inattendue. Au début, je m'attendais à ce que shop_entry_params soit réduit du contrôleur, mais je ne pouvais pas le supprimer du contrôleur après tout. Seule la méthode qui crée l'association peut être supprimée du contrôleur.

En ce qui concerne le modèle, ** les méthodes de validation et de définition des valeurs par défaut, les associations, etc. ont toutes été supprimées! ** Aucune description supplémentaire! !! Après tout, form_object est un moyen pratique d'écrire un modèle mince! !!

View Enfin, la vue ressemble à ceci.

haml:app/views/shops/new.html.haml


= form_with model: @shop, url: shops_path, local: true do |f|
    = f.text_field :name

    = f.fields_for :shop_comments, local: true do |comment_form|
      = comment_form.text_field

    = f.submit "Envoyer"

L'utilisation de fields_for est identique à l'implémentation utilisant ʻaccept_nested_attributes_for` ^^

tester

Le test était également très simple!

spec/forms/shop_entry_form_spec.rb


require 'rails_helper'

RSpec.describe ShopEntryForm, type: :model do
  before do
    @shop_form = ShopEntryForm.new(category: "category1", name: "Atelier de test")
  end

  describe "Test de validation" do
    it "Passez la validation si vous avez un nom et une catégorie" do
      @shop_form.valid?
      expect(@shop_form).to be_valid
    end
    
    #Ce qui suit est omis
  end
end

Tout ce que j'avais à faire était de faire attention à l'emplacement du fichier, à la partie RSpec.describe ShopEntryForm ..., et à la description lors de la création de l'instance de test ^^

C'est un peu vieux, mais je l'ai créé en référence à cet article.

Ecrire un test d'un objet de formulaire avec RSpec

Impressions, matériaux de référence, etc.

Eh bien, la mise en œuvre a été très longue. La forme réelle avait trois types d'éléments enfants imbriqués, et la forme était assez compliquée, mais surtout, je pense que c'était parce que je n'étais pas habitué à écrire du Ruby brut. .. .. Quand je me calmerai, j'aimerais revoir Ruby.

Ceci est un résumé des articles et des documents auxquels j'ai fait référence cette fois.

▼ Rédaction générale Enregistrer plusieurs enregistrements enfants sans accepte_nested_attributes_for

▼ Comment accéder aux paramètres Utiliser la classe de formulaire «Introduction à Ruby pour les professionnels, des spécifications de langage aux techniques de développement et de débogage pilotées par les tests» (p.215)

▼ À propos de Bite-sized separation of concerns

▼ Comment rédiger un test Ecrire un test d'un objet de formulaire avec RSpec

Après cela, les formulaires d'édition et de mise à jour restent également, donc j'aimerais travailler dessus ^^

Recommended Posts

Créer un formulaire de relation parent-enfant avec form_object (j'ai également écrit un test)
J'ai écrit un test avec Spring Boot + JUnit 5 maintenant
J'ai écrit un test CRUD avec SpringBoot + MyBatis + DBUnit (Partie 1)
J'ai écrit un programme de jugement des nombres premiers en Java
[RSpec] J'ai écrit un test pour télécharger une image de profil.
J'ai essayé de créer un environnement de développement java8 avec Chocolatey
[Rails] J'ai essayé de créer une mini application avec FullCalendar
Je souhaite créer un formulaire pour sélectionner la catégorie [Rails]
J'ai essayé de créer un environnement de développement padrino avec Docker
Créez un terrain de jeu avec Xcode 12
J'ai essayé d'imprimer un formulaire avec Spring MVC et Jasper Reports 1/3 (paramètres Jasper Reports)
Ai-je besoin d'un test si je fais DDD dans une langue avec un type?
J'ai essayé d'imprimer un formulaire avec Spring MVC et Jasper Reports 3/3 (contrôle Spring MVC)
Je ne peux pas créer une classe Java avec un nom spécifique dans IntelliJ
J'ai créé un formulaire de recherche simple avec Spring Boot + GitHub Search API.
Créez un environnement Vue3 avec Docker!
J'ai créé une interface graphique avec Swing
Créez des exceptions avec une interface fluide
J'ai essayé d'imprimer un formulaire avec Spring MVC et Jasper Reports 2/3 (création de modèle de formulaire)
Je souhaite créer un SNS Web sombre avec Jakarta EE 8 avec Java 11
J'ai écrit une fonction Lambda en Java et l'ai déployée avec SAM