[RUBY] [Rails 6.0] J'ai essayé d'implémenter une fonction de recherche de balises (une fonction pour affiner par balises) [no gem]

J'ai implémenté une fonction de recherche de balises dans une application que je suis en train de développer, je vais donc la résumer ici sous forme de mémorandum. Je n'ai pas osé utiliser des gemmes, j'ai donc pu comprendre l'essentiel.

【environnement】

Aperçu des fonctionnalités

Lorsque vous entrez une balise dans le nouveau formulaire de publication, la balise saisie sera reflétée dans la liste des publications et la barre latérale. C'est une fonction qui vous permet d'affiner les publications avec cette balise en cliquant sur la balise reflétée dans la barre latérale.

Entrez une balise à partir du formulaire de publication tagu_qiita.png

La balise saisie dans ↑ est affichée dans la barre latérale et dans la liste des articles En cliquant sur la balise affichée dans la barre latérale, vous pouvez restreindre les articles qui incluent cette balise. (Puisqu'il s'agit d'un échantillon, un seul cas) tagu_qiita2.png

Procédure de montage

Je parlerai en supposant que le modèle Item et le modèle Post ont déjà été créés. (Ici, il s'agit d'un modèle d'article. Veuillez le remplacer vous-même.)

Tout d'abord, créez le modèle Tag et le modèle Tagmap comme d'habitude.

$ rails g model Tagmap item:references tag:references 
$ rails g model tag tag_name:string

Puisque Tagmap est associé au modèle Item et au modèle Tag, les références sont attachées au type de données. Si vous faites cela, Rails fera du bon travail, donc si vous voulez en savoir plus, veuillez google.

Fichier de migration


class CreateTagmaps < ActiveRecord::Migration[6.0]
  def change
    create_table :tagmaps do |t|
      t.references :item, null: false, foreign_key: true
      t.references :tag, null: false, foreign_key: true

      t.timestamps
    end
  end
end

Ceci est indexé.


class CreateTags < ActiveRecord::Migration[6.0]
  def change
    create_table :tags do |t|
      t.string :tag_name, null: false

      t.timestamps
    end
    add_index :tags, :tag_name, unique: true
  end
end

Puis migrez comme d'habitude.

$ rails db:migrate

association

item.rb


has_many :tagmaps, dependent: :destroy
has_many :tags, through: :tagmaps

tag.rb


class Tag < ApplicationRecord
  has_many :tagmaps, dependent: :destroy
  has_many :items, through: :tagmaps
end

tag est également associé aux tagmaps. ↑

En utilisant approfondie, vous pouvez accéder aux éléments via des tagmaps.

tagmap.rb


class Tagmap < ApplicationRecord
  belongs_to :item
  belongs_to :tag
end

↑ Rails décrira automatiquement la pièce appart_to dans le formulaire des références!

Description du contrôleur et du modèle

items_controller.rb


  def index
    if params[:search].present?
      items = Item.items_serach(params[:search])
    elsif params[:tag_id].present?
      @tag = Tag.find(params[:tag_id])
      items = @tag.items.order(created_at: :desc)
    else
      items = Item.all.order(created_at: :desc)
    end
    @tag_lists = Tag.all
    @items = Kaminari.paginate_array(items).page(params[:page]).per(10)
  end

Je vais expliquer le code ici. L'action d'index utilise l'instruction if pour séparer les trois modèles et la valeur affectée à la variable d'élément est modifiée en fonction de ce résultat.

    1. Lorsqu'il y a une entrée dans le formulaire de recherche
  1. Quand: tag_id est reçu dans les paramètres (S'active lorsque tag_id est reçu en tant que paramètre lorsque l'utilisateur clique dessus. Fonction pour affiner par tag)
    1. Lorsque la page s'affiche normalement Puisque la fonction de pagination est également définie, les variables stockées dans la variable d'élément sont dans 3 modèles, et chacun optimisé peut être affiché. Pagenation utilise une gemme appelée Kaminari.

items_controller


  def create
    @item = Item.new(item_params)
    tag_list = params[:item][:tag_name].split(nil)
    @item.image.attach(params[:item][:image])
    @item.user_id = current_user.id
    if @item.save
       @item.save_items(tag_list)
      redirect_to items_path
    else
      flash.now[:alert] = 'La publication a échoué'
      render 'new'
    end
  end

Le point dans l'action de création du contrôleur d'éléments est la partie affectation à la variable tag_list sur la troisième ligne. ruby's ** String # split ** divise la chaîne par le séparateur spécifié par le premier argument (nil cette fois) et renvoie le résultat sous forme de tableau de chaînes. Si vous spécifiez un bloc, au lieu de renvoyer un tableau, le bloc est appelé avec une chaîne divisée. De plus, si vous envoyez des données de balise séparées par un caractère vierge de 1 octet «» du formulaire, elles seront séparées par une chaîne de caractères vide après avoir supprimé les espaces de début et de fin. Cette fois, nous allons l'utiliser.

Les paramètres envoyés depuis le nouveau formulaire de publication sont séparés par un caractère vide (nil), arrangés et enregistrés dans la base de données à l'aide de la méthode ** save_items ** définie dans la classe Item. (Demandez à l'utilisateur de saisir des balises sous la forme séparées par des espaces.)

item.rb


 #Recherche floue pour la méthode de recherche, le titre et le contenu
 def self.items_serach(search)
   Item.where(['title LIKE ? OR content LIKE ?', "%#{search}%", "%#{search}%"])
 end

 def save_items(tags)
   current_tags = self.tags.pluck(:tag_name) unless self.tags.nil?
   old_tags = current_tags - tags
   new_tags = tags - current_tags

   # Destroy
   old_tags.each do |old_name|
     self.tags.delete Tag.find_by(tag_name:old_name)
   end

   # Create
   new_tags.each do |new_name|
     item_tag = Tag.find_or_create_by(tag_name:new_name)
     self.tags << item_tag
   end
 end

↑ Lors de la sauvegarde des données de tag, s'il existe même un seul nom de tag parmi les données de tag envoyées depuis le formulaire, utilisez la méthode de prélèvement de la colonne tag_name de la table tags pour extraire toutes les données une fois et définissez-le sur current_tags. Remplacer. (Si tout est nouveau, ce sera nul.) Vous pouvez définir d'anciennes balises (old_tags) en soustrayant le tableau de balises transmises par le contrôleur en tant qu'argument de current_tags, qui est un ensemble de données de balises qui existe déjà. C'est parce que ruby peut extraire des éléments communs en soustrayant un tableau.

** Exemple concret: **


tags(tag_list = params[:item][:tag_name].split(nil)Arrangement qui vient du contrôleur) = ["Rails" "ruby" "React"]
current_tags(Données de point qui existent actuellement dans la base de données) = ["Rails" "ruby" "Vue.js"]

old_tags = ["Rails" "ruby" "Vue.js" "Docker"] -  ["Rails" "ruby" "React"] 
old_tags = ["Rails" "ruby"] 
↑ L'élément commun des deux tableaux reste (les anciennes balises déjà existantes peuvent être calculées)

Voir la description

** Extrait de la description du nouveau formulaire de publication (UIkit est utilisé pour le framework CSS) **


.uk-form.new_post_form
  = form_with(model: [@tag,@item], local: true) do |f|
    .uk-margin-small
      = f.text_field :title, placeholder: "Entrez un titre (jusqu'à 35 caractères)", class: 'uk-input'
    .uk-margin-small
      = f.text_field :tag_name, placeholder: "Saisissez jusqu'à 5 balises liées à la technologie de programmation et aux exigences de recrutement, séparées par des espaces(Exemple de rubis Rails)", class: 'uk-input uk-form-small'

** Extrait de vue partielle de la barre latérale **

haml:index.html.haml


%li.search_friend_by_categorize
  .uk-text-secondary.uk-text-bold
Recherche par tag
  %ul.uk-flex.uk-flex-wrap
    - @tag_lists.each do |list|
      %li
        = link_to list.tag_name, items_path(tag_id: list.id), class: 'tag_list'

Affichage des balises dans une liste d'articles publiés

haml:index.html.haml


 %p.tag_list_box
   - item.tags.each do |tag|
     = link_to "##{tag.tag_name}", items_path(tag), class: 'smaller tag_list'

La vue ressemble à ceci. Ce n'est pas particulièrement difficile!

Merci d'avoir lu jusqu'au bout!

Je l'ai écrit à la hâte, il peut donc y avoir des erreurs. Si vous avez des suggestions ou des impressions, je vous serais reconnaissant de bien vouloir commenter! !!

Recommended Posts

[Rails 6.0] J'ai essayé d'implémenter une fonction de recherche de balises (une fonction pour affiner par balises) [no gem]
[Rails] Procédure d'implémentation de la fonction pour taguer les posts sans gem + la fonction pour affiner et afficher les posts par tags
Ajoutez une fonction de balise aux rails. Utilisez actes-comme-taggable-on
[Pour les débutants de Rails] Implémentation de la fonction de recherche multiple sans Gem
Je souhaite définir une fonction dans la console Rails
Implémentez une fonction de recherche affinée pour plusieurs modèles sans gemme Rails5.
Ajoutez une fonction de recherche dans Rails.
Je souhaite ajouter une fonction de navigation avec ruby on rails
Implémentation de la recherche de balises de hachage comme Insta et Twitter dans Rails (pas de gemme)
J'ai essayé de créer une fonction de groupe (babillard) avec Rails
J'ai créé un formulaire de publication Rails, mais je ne peux pas publier (balise de formulaire) / Aucune erreur ne se produit
Convertir en balise dans la chaîne d'URL avec Rails
Fonction de publication implémentée par communication asynchrone dans Rails
Faisons une fonction de recherche avec Rails (ransack)
J'ai essayé de créer une fonction de message de l'extension Rails Tutorial (Partie 1): Créer un modèle
[Ruby on Rails] Comment implémenter la fonction de balisage / recherche incrémentielle pour les articles (sans gemme)
Je veux utiliser une petite icône dans Rails
J'ai essayé de créer une fonction de connexion avec Java
Comment écrire une recherche de comparaison de dates dans Rails
Je souhaite ajouter une fonction de suppression à la fonction de commentaire
J'ai essayé de faire une fonction de réponse de l'extension Rails Tutorial (Partie 3): Correction d'un malentendu des spécifications
[Ruby on Rails] J'obtiens un avertissement lors de l'exécution de RSpec en raison d'une version différente de gem.
J'ai essayé de créer une fonction de message pour l'extension Rails Tutorial (Partie 2): Créer un écran à afficher