[RUBY] Implémentation de la recherche de balises de hachage comme Insta et Twitter dans Rails (pas de gemme)

Aperçu

Je fréquente actuellement une école de programmation appelée DMM WEB CAMP Comme utilisé sur Instagram et Twitter pour le portfolio, qui est le problème du troisième mois Implémentation d'une balise de hachage. J'espère qu'il sera utile pour ceux qui le mettront en œuvre à l'avenir.

Site de référence

Implémenter des balises de hachage dans des légendes de type insta créées avec des rails https://qiita.com/goppy001/items/791c946abdb41c9495bb

Le flux général est le même que celui du site ci-dessus, mais les parties qui ne fonctionnaient pas bien ont été modifiées.

Dessin d'achèvement

Écran de publication

スクリーンショット 2020-06-24 11.28.23.png

Écran des détails de l'article

スクリーンショット 2020-06-24 11.28.55.png

Liste de balises de hachage et écran de liste de publications de balises de hachage

スクリーンショット 2020-06-24 11.29.11.png

Préparation préalable DB

Tableau de poste スクリーンショット 2020-06-24 11.36.41.png

Au départ, c'était une structure de table avec seulement body et user_id, mais nous avons ajouté une colonne hashbody pour entrer des balises de hachage. À propos, l'image est enregistrée dans une autre table en raison des spécifications du portfolio, mais il n'y a pas de problème même si la colonne image est dans cette table.

Création d'un modèle (DB)

Modèle de balise de hachage

$rails g model Hashtag hashname:string

Créez un modèle pour stocker les balises de hachage. Le hashtag est stocké dans la colonne hashname.

Modifier le fichier de migration

create_hashtags.rb


class CreateHashtags < ActiveRecord::Migration[5.2]
  def change
    create_table :hashtags do |t|
      t.string :hashname

      t.timestamps
    end
    add_index :hashtags, :hashname, unique: true
  end
end

Créer une table intermédiaire

$ rails g model HashtagPostImage post_image:references hashtag:references

Une table intermédiaire entre la table Hashtag et la table PostImage. Les commandes ici sont légèrement différentes dans l'article auquel j'ai fait référence. Puisqu'il s'agit d'une table intermédiaire, elle apporte le hashtag et l'identifiant de postimage en tant que clés externes. Puisqu'il s'agit d'un type de références, si vous saisissez hashtag_id au moment de la création Veuillez noter que le nom de colonne complété sera hashtag_id_id.

Fichier de migration

create_hashtag_post_images.rb


class CreateHashtagPostImages < ActiveRecord::Migration[5.2]
  def change
    create_table :hashtag_post_images do |t|
      t.references :post_image, foreign_key: true
      t.references :hashtag, foreign_key: true
    end
  end
end

Mon grand

$ rails db:migrate

DB créé

スクリーンショット 2020-07-01 12.13.30.png

Association de modèles et paramètres de validation

Modèle de balise de hachage

hashtag.rb


class Hashtag < ApplicationRecord
  validates :hashname, presence: true, length: { maximum: 50 }
  has_many :hashtag_post_images, dependent: :destroy
  has_many :post_images, through: :hashtag_post_images
end

Pour le moment, j'ai fixé la limite supérieure à 50 caractères.

Table intermédiaire

hashtag_post_image.rb


class HashtagPostImage < ApplicationRecord
  belongs_to :post_image
  belongs_to :hashtag
  validates :post_image_id, presence: true
  validates :hashtag_id, presence: true
end

Modèle PostImage

post_image.rb


class PostImage < ApplicationRecord
  has_many :hashtag_post_images, dependent: :destroy
  has_many :hashtags, through: :hashtag_post_images
end

Ajout de ce qui suit au modèle PostImage

post_image.rb


after_create do
    post_image = PostImage.find_by(id: id)
    #Détecte les balises de hachage saisies dans hashbody
    hashtags = hashbody.scan(/[##][\w\p{Han}Ah-Gae-゚]+/)
    hashtags.uniq.map do |hashtag|
      #La balise de hachage est au début#Enregistrer après avoir supprimé
      tag = Hashtag.find_or_create_by(hashname: hashtag.downcase.delete('#'))
      post_image.hashtags << tag
    end
  end
  #Mettre à jour l'action
  before_update do
    post_image = PostImage.find_by(id: id)
    post_image.hashtags.clear
    hashtags = hashbody.scan(/[##][\w\p{Han}Ah-Gae-゚]+/)
    hashtags.uniq.map do |hashtag|
      tag = Hashtag.find_or_create_by(hashname: hashtag.downcase.delete('#'))
      post_image.hashtags << tag
    end
  end

Il est marqué de manière à ce que cette action soit effectuée lors de la création et de la mise à jour.

・ Post_image = PostImage.find_by (id: id) Vous permet de trouver le message que vous avez créé.

・ Hashtags = hashbody.scan (/ [# #] [\ w \ p {Han} a-ga- ゚] + /) Ici, recherchez la balise de hachage entrée et la valeur d'entrée avec [# #] au début. hashbody est le nom de colonne de ma base de données, donc cela dépend de l'application. N'importe quelle colonne pour l'entrée de texte dans la table de publication fera l'affaire.

・ Hashtags.uniq.map do |hashtag|

La balise de hachage est enregistrée après la suppression du # principal

  tag = Hashtag.find_or_create_by(hashname: hashtag.downcase.delete('#'))
  post_image.hashtags << tag
end

En répétant avec map, plusieurs balises de hachage sont enregistrées dans la postimage.

・ Post_image.hashtags.clear Au moment de la mise à jour, il semble que la balise de hachage soit supprimée une fois.

Description de l'itinéraire

routes.rb


get '/post_image/hashtag/:name' => 'post_images#hashtag'
get '/post_image/hashtag' => 'post_images#hashtag'

Dans mon cas, je voulais créer une page de liste de balises de hachage, j'ai donc préparé deux itinéraires.

Modification de l'assistant PostImage

post_images_helper.rb


module PostImagesHelper
  def render_with_hashtags(hashbody)
    hashbody.gsub(/[##][\w\p{Han}Ah-Gae-゚]+/) { |word| link_to word, "/post_image/hashtag/#{word.delete("#")}",data: {"turbolinks" => false} }.html_safe
  end
end

link_to word, "/post_image/hashtag/#{word.delete("#")}" L'URL ici dépend du contenu de l'application. Cela signifie que cliquer sur la balise de hachage vous amènera à l'URL ici. Tapez l'url que vous avez écrite dans route plus tôt.

Contrôleur PostImag

controllers/post_images_controller.rb



class PostImagesController < ApplicationController
  def new
    @postimagenew = PostImage.new
    @postimagenew.post_image_images.new
  end

  def create
    @postimagenew = PostImage.new(post_image_params)
    @postimagenew.user_id = current_user.id

    if @postimagenew.save
      redirect_to post_images_path
    else
      render('post_images/new')
    end
  end

  def destroy
    @postimage = PostImage.find(params[:id])
    @postimage.destroy
    redirect_to post_images_path
  end

private
 def post_image_params
    params.require(:post_image).permit(:body, :hashbody, :user_id, post_image_images_images: [], hashtag_ids: [])
 end

Ne vous inquiétez pas de post_image_images_images: [] pour enregistrer les images dans les paramètres forts sous forme de tableau dans une autre table. hashtag_ids est entré car plusieurs balises de hachage sont enregistrées lors de la création de PostImage.

View

Formulaire de publication

views/post_images/new.html.erb



<div class= "row">
	<div class="col-lg-2 col-md-2">
	</div>
	<div class="col-xs-12 col-lg-8 col-md-8 col-sm-12">
		<div class= "postimage-new-box">
			<% if @postimagenew.errors.any? %>
  				<div id="error_explanation">
    				<h3><%= @postimagenew.errors.count %>Impossible de publier en raison d'une erreur de saisie</h3>
  					<ul>
    					<% @postimagenew.errors.full_messages.each do |msg| %>
      					<li><%= msg %></li>
    					<% end %>
    				</ul>
 				</div>
			<% end %>
			<h3>Nouveau poste</h3>
			<div class="previw">
			</div>
			<%= form_with model:@postimagenew, local: true do |f| %>
				<div class="attachment-field">
					<p>Sélectionnez une image (plusieurs peuvent être spécifiées)</p>
					<%= f.attachment_field :post_image_images_images, multiple:true %>
				</div>
				<br>
				<br>
				<div class= "postimage-body-box">
					<p>Veuillez saisir les détails du message</p>
					<%= f.text_area :body, size:"55x12" %>
					<br>
					<p>Champ de saisie de balise de hachage</p>
					<%= f.text_area :hashbody, size:"55x3" %>
					<br>
					<div class= "postimage-new">
					<%= f.submit "Nouveau poste" ,class:'postimage-new-button' %>
					</div>
			<% end %>
				</div>
		</div>
	</div>
	<div class="col-lg-2 col-md-2">
	</div>
</div>

Où afficher réellement la balise de hachage dans le corps de l'article

スクリーンショット 2020-06-24 11.28.55.png

Ce qui suit est écrit dans View.

ruby:post_images/show.html.erb


<%= render_with_hashtags(@postimage.hashbody) %>

Ce qui précède appelle la méthode créée précédemment avec helper. Au fait, le contrôleur pour post_images / show est ici.

controllers/post_images_controller.rb


def show
    @postimage = PostImage.find(params[:id])
end

Je pense qu'il s'agit simplement de transmettre les informations dans le champ de saisie hashtag de @postimage à Hellber.

Page de liste des balises de hachage

スクリーンショット 2020-06-24 11.29.11.png

Lorsque vous cliquez dessus, cela ressemble à ceci. スクリーンショット 2020-06-24 13.12.07.png

Ajouter une action de hashtag au contrôleur PostImage

post_images_controller.rb


def hashtag
    @user = current_user
    if params[:name].nil?
      @hashtags = Hashtag.all.to_a.group_by{ |hashtag| hashtag.post_images.count}
    else
      @hashtag = Hashtag.find_by(hashname: params[:name])
      @postimage = @hashtag.post_images.page(params[:page]).per(20).reverse_order
      @hashtags = Hashtag.all.to_a.group_by{ |hashtag| hashtag.post_images.count}
    end
  end

La notation ici varie en fonction du site que vous créez. Je voulais créer une page où vous pouvez voir la liste des hashtags, donc dans le cas des paramètres [: name] .nil? Il existe une branche conditionnelle qui n'affiche pas post_image. De plus, bien qu'il s'agisse de group_by, les balises de hachage peuvent être affichées dans l'ordre du nombre de publications associées à la balise de hachage. C'est écrit comme ça.

Vue Hashtag

post_images/hashtag.html.erb


<div class="row">
		<% if params[:name] == nil %>

		<% else %>
		<div class= "col-xs-12 col-lg-12 col-md-12 col-sm-12">
			<div class="hashtag-post-box">
				<h3 class="search-title">#<%= @hashtag.hashname %>:  <%= @postimage.count %>Cas</h3>
				<div class="flex-box">
				<% @postimage.each do |postimage| %>
					<div class= "post-image-index-post-box">
						<p class="index-post-box-top">
							<%= postimage.created_at.strftime("%Y/%m/%d") %>
						</p>
						<span class='far fa-comments index-comment-count' id='comment-count_<%= postimage.id %>' style="color: #777777;">
							<%= render 'post_image_comments/comment-count', postimage:postimage %>
						</span>

						<span id = "favorite-button_<%= postimage.id %>"class="post-box-top-favorite">
							<%= render 'post_image_favorites/favorite',postimage: postimage %>
						</span>
		    			<%= link_to post_image_path(postimage),data: {"turbolinks" => false}  do %>
				  			<ul class="slider">
									<% postimage.post_image_images.each do |post| %>
										<li>
										<%= attachment_image_tag post, :image ,size:'430x360', format:'jpg',class:"image" %>
									</li>
								<% end %>
							</ul>
						<% end %>
						<p class="hashtag-post-box-name">
							<%= link_to user_path(postimage.user) do %>
								<%= attachment_image_tag postimage.user, :profile_image,size:'30x30', format:'jpg',fallback:'no_image.jpg',class:'min-image' %>
								<span class="index-post-box-user"><%= postimage.user.name %>
								</span>
							<% end %>
						</p>
						<div class="image-show-body-hash" style="padding:2%">
							<%= simple_format(postimage.body.truncate(50))%>
							<% if postimage.body.length > 50 %>
								<span class="text-prev"><%= link_to 'Lire la suite', post_image_path(postimage), data: {"turbolinks" => false} %>
								</span>
							<% end %>
						</div>
					</div>
				<% end %>
				</div>
			</div>
			<div class="image-index-pagination" data-turbolinks="false">
				<%= paginate @postimage,class:"paginate" %>
			</div>
		</div>
		<% end %>
	</div>
	<div class="row">
		<div class= "col-xs-12 col-lg-12 col-md-12 col-sm-12">
			<div class= "hashtag-name">
				<% @hashtags.sort.reverse.each do |count| %>
						<% count[1].each do |hashtag| %>
						<p><%= link_to  "##{hashtag.hashname} (#{hashtag.post_images.count})Cas","/post_image/hashtag/#{hashtag.hashname}",data: {"turbolinks" => false} %>
						</p>
						<% end %>
				<% end %>
			</div>
		</div>
	</div>
</div>

Je suis désolé d'avoir du mal à comprendre le nom de la classe et ainsi de suite. Les points importants sont les suivants.

post_images/hashtag.html.erb


<% if params[:name] == nil %>

<% else %>

<% end %>

Avec cette notation, le branchement conditionnel est effectué avec post_image / hashtag et post_image / hashtag /: name écrits dans route plus tôt. En écrivant le traitement lorsque les paramètres sont nuls dans chacun du contrôleur et de la vue, une erreur est évitée.

post_image/hashtag.html.erb



<div class= "hashtag-name">
	<% @hashtags.sort.reverse.each do |count| %>
		<% count[1].each do |hashtag| %>
			<p><%= link_to  "##{hashtag.hashname} (#{hashtag.post_images.count})Cas","/post_image/hashtag/#{hashtag.hashname}",data: {"turbolinks" => false} %>
			</p>
        <% end %>
	<% end %>
</div>

La liste des balises de hachage s'affiche ici. L'affichage est affiché dans l'ordre décroissant des publications liées aux balises de hachage.

Résumé

Si vous ne séparez pas le champ de saisie de la balise de hachage, la balise de hachage restera sous forme de phrase dans la description de l'article, je l'ai donc implémentée sous la forme de l'enregistrer dans une autre colonne. Le hashbody est intentionnellement caché là où le hashtag est affiché avec le message. C'est turbolinks false qui existe beaucoup sur la vue, mais js ne fonctionne pas bien et il est écrit, donc vous pouvez l'ignorer.

Je suis désolé s'il y a des parties difficiles à comprendre dans le premier post. J'espère que cela sera utile pour ceux qui créeront désormais un portfolio.

Postscript

2020/7/1 Correction du fichier de migration de la table intermédiaire. Il y avait un phénomène qui ne pouvait pas être détruit si id False était défini, nous l'avons donc corrigé. En même temps, j'ai ajouté dependant :: destroy au modèle has_many.

Nous avons également ajouté un contrôleur et une vue pour le hashtag et la partie post-sauvegarde. Nous nous excusons pour tout inconvénient.

Recommended Posts

Implémentation de la recherche de balises de hachage comme Insta et Twitter dans Rails (pas de gemme)
[Pour les débutants de Rails] Implémentation de la fonction de recherche multiple sans Gem
[Rails 6.0] J'ai essayé d'implémenter une fonction de recherche de balises (une fonction pour affiner par balises) [no gem]
Implémentation de calendrier simple de Rails Gem et branchement conditionnel
[Rails] Recherche à partir de plusieurs colonnes + conditions avec Gem et ransack
Gemme souvent utilisée dans les rails
Activer jQuery et Bootstrap dans Rails 6 (Rails 6)
Supprimez les "actifs" et les "turbolinks" dans "Rails6".
Fonction CRUD et MVC dans Rails