great084 / twitter_tool

ツイッターツールの開発
0 stars 0 forks source link

元ツイート画像の再投稿機能実装 #70

Closed nakamitsu-nozomi closed 3 years ago

nakamitsu-nozomi commented 3 years ago

closes62

目的・概要

元ツイート画像の再投稿機能実装

実装内容

UI変更概要

画面キャプチャ

稼働確認チェック

参考資料

連絡事項

元画像だけでなく、新規画像を追加したり削除する 機能を挑戦しましたが、実装できませんでした。

nakamitsu-nozomi commented 3 years ago

新しい画像の追加投稿機能について

どうすれば、 元画像だけでなく、新しい画像の追加投稿機能を実装できるか全くわからず困ってます。

やりたいこと

 元画像だけでなく、新しい画像も追加で投稿できるようにしたい

現状(下記コードの状態)

 - 元画像も新規画像も再投稿できない   エラー文#<ActiveRecord::RecordNotFound: Couldn't find Medium with ID=65 for Tweet with ID=>  - 写真のように元画像の数だけモーダルフォームができる 

スクリーンショット 2020-12-27 21 53 22

試したこと

<ActionController::Parameters {"0"=><ActionController::Parameters {"media_url"=>#<ActionDispatch::Http::UploadedFile:0x00007fb52cc37e70 @tempfile=#<Tempfile:/var/folders/l8/5109jjgs36v7tcj0hjjnlb0c0000gn/T/RackMultipart20201227-3440-pgr17q.png>, @original_filename="couch_color.png", @content_type="image/png", @headers="Content-Disposition: form-data; name=\"tweet[media_attributes][0][media_url]\"; filename=\"couch_color.png\"\r\nContent-Type: image/png\r\n">, "id"=>"65"} permitted: true>, "1"=><ActionController::Parameters {"id"=>"66"} permitted: true>, "2"=><ActionController::Parameters {"id"=>"67"} permitted: true>} permitted: true> が入っていること確認。

-コードは以下のように書きました。

tweet_controller

  def show
    @tweet = Tweet.find(params[:id])
    @tweet.media.build
    redirect_to root_path if @tweet.user_id != current_user.id
  end

  def post_create
    # @tweet = Tweet.new(post_params)
    @tweet = current_user.tweets.build(post_params)
    @tweet_data_all = Tweet.find(params[:id])
    @tweet_new_media = post_params[:media_attributes]

    # 投稿画像のURLを取得
    @tweet_media = @tweet_data_all.media.pluck(:media_url)
    @img = @tweet_media.map { |img_url| URI.parse(img_url).open }

    @client.update_with_media("#{@tweet.text}\r", @img)

    @tweet_data_all.tweet_flag = true
    @tweet_data_all.save
    redirect_to tweet_path(params[:id]), success: "再投稿に成功しました"
  rescue StandardError => e
    redirect_to tweet_path(params[:id]), danger: "再投稿に失敗しました#{e} "
    binding.pry
  end

    def post_params
      params.require(:tweet).permit(:text, :tweet_id, media_attributes: %i[media_url tweet_id id])
    end

tweet.rb

  has_many :media, dependent: :destroy
  accepts_nested_attributes_for :media

_tweet_modal.html.erb

<div class="modal fade" id="modal2" tabindex="-1"
      role="dialog" aria-labelledby="label1" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="label1">再投稿する</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <%= form_with model: @tweet,method: :post, url: "/tweets/#{params[:id]}/post_create", local:true,multipart: true do |f| %>
        <div class="modal-body">
          <div class="p-2">
            <p><%= f.label :label_old_tweet, "元投稿内容" %></p>
            <p><%= f.text_area  :text,maxlength: 140 ,:required => true, class: "form-control", rows: 4, value: @tweet.text %></p>
          </div>
          <div class="p-2">
            <p><%= f.label :label_show_image, "画像" %></p>
             <%= f.fields_for :media,  local: true do |i| %>
                <%= i.file_field :media_url %>
                <%= i.hidden_field :tweet_id, value: @tweet.id %> 
             <% end %>

            <% @tweet.media.each_with_index do |media,index| %>
              <% if index==0 %>
                <%= image_tag media.media_url.to_s,class: "mordal-image " %>    
              <% end %>
            <% end %>

          </div>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
          <%= f.hidden_field :tweet_id, value: @tweet.tweet_id %>
          <%= f.submit "再投稿する", class: "btn btn-primary" %>
        </div>
      <% end %>
    </div>
  </div>
</div>

モーダルフォームから入力した画像のURLがわかれば、元画像を再投稿するのと同じ方法でTwitterに投稿できるのかなと思い上記コードを書いてみましたが全く上手くいかず困ってます。モーダルフォームから入力した画像のURLをどうやって取得したらいいのかわかりません。

そもそもの実装方法に迷走してます。 アドバイスいただけたら嬉しいです。よろしくお願い致します。

great084 commented 3 years ago

@nakamitsu-nozomi

確認しました。 まず思ったのは、一度に色々やろうとしていて、問題が起きたときに切り分けができていない気がしました。 なので、少しずつ積み上げていけるといいかなと思います。(accepts_nested_attributes_forは便利ではありますが、使いこなすには、まず単一の処理がきちんとできる前提で使ったほうがよいです) 例えば、以下の順です

で、私の方で確認できた内容を記載しておきます。

エラーの内容

まず、エラーになっている、tweets_controllerの以下の処理ですが、 Tweetをbuildしようとしていますが、再登録時にTweetモデルにデータを新たに登録する必要はあります?

@tweet = current_user.tweets.build(post_params)

ちなみにエラーは post_param内のmedia_attributesを見ると1つめのブロックにはid'があるのに対して、2つ目には存在しないためCouldn't find Medium with ID=56 for Tweet with ID=というActiveRecordのエラーが出ています。

[27] pry(#<TweetsController>)> post_params[:media_attributes]["0"]
=> <ActionController::Parameters {"media_url"=>#<ActionDispatch::Http::UploadedFile:0x00007fd5e0b5fcf8 @tempfile=#<Tempfile:/var/folders/tp/5kzl3y5s4s54dwlx_4pwnhvm0000gn/T/RackMultipart20201231-4889-1o80xp8.JPG>, @original_filename="DSC_2743 (1).JPG", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"tweet[media_attributes][0][media_url]\"; filename=\"DSC_2743 (1).JPG\"\r\nContent-Type: image/jpeg\r\n">, "tweet_id"=>"455", "id"=>"56"} permitted: true>
[28] pry(#<TweetsController>)> post_params[:media_attributes]["1"]
=> <ActionController::Parameters {"media_url"=>#<ActionDispatch::Http::UploadedFile:0x00007fd5e0b5fb18 @tempfile=#<Tempfile:/var/folders/tp/5kzl3y5s4s54dwlx_4pwnhvm0000gn/T/RackMultipart20201231-4889-1a2ypzx.png>, @original_filename="プロフィール画像.png", @content_type="image/png", @headers="Content-Disposition: form-data; name=\"tweet[media_attributes][1][media_url]\"; filename=\"\xE3\x83\x95\xE3\x82\x9A\xE3\x83\xAD\xE3\x83\x95\xE3\x82\xA3\xE3\x83\xBC\xE3\x83\xAB\xE7\x94\xBB\xE5\x83\x8F.png\"\r\nContent-Type: image/png\r\n">, "tweet_id"=>"455"} permitted: true>

画面からアップロードした画像をTwitterに投稿する方法

モーダルフォームから入力した画像のURLがわかれば、元画像を再投稿するのと同じ方法でTwitterに投稿できるのかなと思い上記コードを書いてみましたが全く上手くいかず困ってます。モーダルフォームから入力した画像のURLをどうやって取得したらいいのかわかりません。

画面から渡されるパラメータ(params)にある、ActionDespatchに含まれています。 今回でいうと、post_params内の以下の部分です。

 post_params[:media_attributes]["1"]["media_url"]
=> #<ActionDispatch::Http::UploadedFile:0x00007fd5e0b5fb18
 @content_type="image/png",
 @headers=
  "Content-Disposition: form-data; name=\"tweet[media_attributes][1][media_url]\"; filename=\"\xE3\x83\x95\xE3\x82\x9A\xE3\x83\xAD\xE3\x83\x95\xE3\x82\xA3\xE3\x83\xBC\xE3\x83\xAB\xE7\x94\xBB\xE5\x83\x8F.png\"\r\nContent-Type: image/png\r\n",
 @original_filename="プロフィール画像.png",
 @tempfile=#<File:/var/folders/tp/5kzl3y5s4s54dwlx_4pwnhvm0000gn/T/RackMultipart20201231-4889-1a2ypzx.png>>

試しに、 画面からファイルをアップロードした後、デバックモードで以下をコマンドで実行いたところ、画像つきのTweetの投稿ができました。

> img_url = post_params[:media_attributes]["1"]["media_url"]
=> #<ActionDispatch::Http::UploadedFile:0x00007fd5e0b5fb18
 @content_type="image/png",
 @headers=
  "Content-Disposition: form-data; name=\"tweet[media_attributes][1][media_url]\"; filename=\"\xE3\x83\x95\xE3\x82\x9A\xE3\x83\xAD\xE3\x83\x95\xE3\x82\xA3\xE3\x83\xBC\xE3\x83\xAB\xE7\x94\xBB\xE5\x83\x8F.png\"\r\nContent-Type: image/png\r\n",
 @original_filename="プロフィール画像.png",
 @tempfile=#<File:/var/folders/tp/5kzl3y5s4s54dwlx_4pwnhvm0000gn/T/RackMultipart20201231-4889-1a2ypzx.png>>
> img_url.path
=> "/var/folders/tp/5kzl3y5s4s54dwlx_4pwnhvm0000gn/T/RackMultipart20201231-4889-1o80xp8.JPG"
> @client.update_with_media("hoge hoge", img_url.path)
=> #<Twitter::Tweet id=1344522573330096131>
nakamitsu-nozomi commented 3 years ago

ご返信ありがとうございます。

現状を報告させて頂きます。

・元ツイートに添付されていた画像を再投稿する
・元ツイートに添付されていた複数画像を再投稿する
・再投稿時に1枚の画像をアップロードして再投稿する

上記を別々に投稿することは可能です。

現時点での課題が2つあります。

課題1

下記のように、新規画像投稿のURLと元画像URLを同時にパラメータに載せることができないが故に、同時に投稿できません。

  def post_create
    @tweet = Tweet.new(post_params)
    @tweet_data_all = Tweet.find(params[:id])
    @new_img=post_params[:media_attributes]["0"]["media_url"]
    # 投稿画像のURLを取得
    @tweet_media = @tweet_data_all.media.pluck(:media_url)
    @img = @tweet_media.map { |img_url| URI.parse(img_url).open }
    @client.update_with_media("#{@tweet.text}\r",@img,,@new_img.path)
  end

  def post_params
      params.require(:tweet).permit(:text, :tweet,:tweet_string_id,media_attributes: [:media_url, :tweet_id ])
    end

コマンド上で確認すると、下記のように出ておりましたが、<String:0x00007f961a55ff70>>が何を表すのか分からず、どうすれば新規画像と元画像が同時表示できるのかわかりません。

[1] pry(#<TweetsController>)> e
=> #<NoMethodError: undefined method `merge' for #<String:0x00007f961a55ff70>>
[2] pry(#<TweetsController>)> @img
=> [#<File:/var/folders/l8/5109jjgs36v7tcj0hjjnlb0c0000gn/T/open-uri20210101-10288-jyhj60>,
 #<File:/var/folders/l8/5109jjgs36v7tcj0hjjnlb0c0000gn/T/open-uri20210101-10288-yeu9mt>]
[3] pry(#<TweetsController>)> @new_img.path
=> "/var/folders/l8/5109jjgs36v7tcj0hjjnlb0c0000gn/T/RackMultipart20210101-10288-vvg1u0.jpg"

課題2

画像フォーム に元画像の数+1個分多く表示される

スクリーンショット 2021-01-01 11 47 36
 <%= form_with model: @tweet,method: :post, url: "/tweets/#{params[:id]}/post_create", local:true,multipart: true do |f| %>
        <div class="modal-body">
          <div class="p-2">
            <p><%= f.label :label_old_tweet, "元投稿内容" %></p>
            <p><%= f.text_area  :text,maxlength: 140 ,:required => true, class: "form-control", rows: 4, value: @tweet.text %></p>
          </div>
          <div class="p-2">
            <p><%= f.label :label_show_image, "画像" %></p>
             <%= f.fields_for :media,  local: true do |i| %>
                <%= i.file_field :media_url %>
           <%= i.hidden_field :tweet_id, value: @tweet.id %>

             <% end %>

            <% @tweet.media.each_with_index do |media,index| %>
              <% if index==0 %>
                <%= image_tag media.media_url.to_s,class: "mordal-image " %>    

              <% end %>
            <% end %>

その時のブラウザのHTMLは以下です


<div class="p-2">
            <p><label for="tweet_label_show_image">画像</label></p>

 <input value="396" type="hidden" name="tweet[media_attributes][0][tweet_id]"id="tweet_media_attributes_0_tweet_id">
<input type="file" name="tweet[media_attributes][0][media_url]" id="tweet_media_attributes_0_media_url"><input type="hidden" value="75" name="tweet[media_attributes][0][id]" id="tweet_media_attributes_0_id">

  <input type="file" name="tweet[media_attributes][1][media_url]" id="tweet_media_attributes_1_media_url">
 <input value="396" type="hidden" name="tweet[media_attributes][1][tweet_id]" id="tweet_media_attributes_1_tweet_id">
<input type="hidden" value="76" name="tweet[media_attributes][1][id]" id="tweet_media_attributes_1_id">

 <input type="file" name="tweet[media_attributes][2][media_url]" id="tweet_media_attributes_2_media_url">
<input value="396" type="hidden" name="tweet[media_attributes][2][tweet_id]" id="tweet_media_attributes_2_tweet_id">

                <img class="mordal-image " src="http://pbs.twimg.com/media/EqchX8XVQAEQlCV.jpg">    

 </div>

原因は、 @tweetに@tweet = Tweet.find(params[:id])が入っている為に、:mediaにツイートの画像の数だけ画像フォームを作ってしまっているのだと思います。 そこで、 ①コントローラー内を下記のように変更

def show
  @tweet = Tweet.find(params[:id])
  @tweet_new=@tweet.new 
  @tweet_new.media.build
end

②viewを変更

     <%= form_with model: @tweet_new,method: :post, url: "/tweets/#{params[:id]}/post_create", local:true,multipart: true do |f| %>
        <div class="modal-body">
          <div class="p-2">
            <p><%= f.label :label_old_tweet, "元投稿内容" %></p>
            <p><%= f.text_area  :text,maxlength: 140 ,:required => true, class: "form-control", rows: 4, value: @tweet.text %></p>
          </div>
          <div class="p-2">
            <p><%= f.label :label_show_image, "画像" %></p>
             <%= f.fields_for :media,  local: true do |i| %>
                <%= i.file_field :media_url %>
           <%= i.hidden_field :tweet_id, value: @tweet.id %>

             <% end %>

            <% @tweet.media.each_with_index do |media,index| %>
              <% if index==0 %>
                <%= image_tag media.media_url.to_s,class: "mordal-image " %>    

              <% end %>

この結果、 フォーム自体は修正されたのですが、パスが通らなくなってしまいました。

fields_for 部分で@tweet.newの:mediaを呼び出すみたいなことはできないのでしょうか。

domi10momo commented 3 years ago

失礼します。 課題1は、'merge'というメソッドは見つからない、ということを言っていると思います。 コードにmergeを見つけられませんでしたが、どこかで使用されていますか? ハッシュに別のハッシュを結合するのであれば、mergeで良いですが、他の処理であれば別の方法があるようです。 そしたら、元画像のハッシュに新規画像の要素を追加できるのではないでしょうか。 参考:https://uxmilk.jp/43303

課題2は考えてみます。 取り急ぎ課題1の考えたことの連絡です。

nakamitsu-nozomi commented 3 years ago

@domi10momo アドバイスありがとうございます!元画像の配列に新規画像のURLを格納したら、課題1は解決しました!! 本当にありがとうございます!

nakamitsu-nozomi commented 3 years ago

現状を報告させて頂きます。

・元ツイートに添付されていた画像を再投稿する
・元ツイートに添付されていた複数画像を再投稿する
・再投稿時に1枚の新規画像をアップロードして再投稿する

上記を同時に投稿することは可能です。

課題3

複数新規画像をパラメータにのせることはできるのですが、画像のURLを一つずつ取り出すことができず詰まっています。

試したこと

仮に二つの新規画像をフォームから登録した際に、画像URLがpost_params[:media_attributes]["0"]["media_url"]に二つの画像の中に入っているので、eachで取り出せるかなと思い下記のコードを書きました。

 しかし、二つ目に登録した画像情報のみしか@new_imgに格納されません。

 def post_create
    @tweet = Tweet.new(post_params)
    @tweet_data_all = Tweet.find(params[:id])
    post_params[:media_attributes]["0"]["media_url"].each do |media_url|
      @new_img= media_url
    end
end

画像二つ追加したときのpost_paramsの中身

[1] pry(#<TweetsController>)> post_params
Unpermitted parameter: :id
Unpermitted parameter: :id
=> <ActionController::Parameters {"text"=>"aaaaaaaa https://t.co/SSB4ISVAmw https://t.co/luS2zv6NQx", "tweet_string_id"=>"1344081679749509120", "media_attributes"=><ActionController::Parameters {"0"=><ActionController::Parameters {"media_url"=>[#<ActionDispatch::Http::UploadedFile:0x00007f85f91beaa0 @tempfile=#<Tempfile:/var/folders/l8/5109jjgs36v7tcj0hjjnlb0c0000gn/T/RackMultipart20210102-26482-11g7dqf.JPG>, @original_filename="IMG_3848.JPG", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"tweet[media_attributes][0][media_url][]\"; filename=\"IMG_3848.JPG\"\r\nContent-Type: image/jpeg\r\n">, #<ActionDispatch::Http::UploadedFile:0x00007f85f91bea50 @tempfile=#<Tempfile:/var/folders/l8/5109jjgs36v7tcj0hjjnlb0c0000gn/T/RackMultipart20210102-26482-1tuk6xs.jpg>, @original_filename="IMG_3849 (1).jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"tweet[media_attributes][0][media_url][]\"; filename=\"IMG_3849 (1).jpg\"\r\nContent-Type: image/jpeg\r\n">], "tweet_id"=>"396"} permitted: true>, "1"=><ActionController::Parameters {"tweet_id"=>"396"} permitted: true>, "2"=><ActionController::Parameters {"tweet_id"=>"396"} permitted: true>} permitted: true>} permitted: true>

画像二つ追加したときの@new_img中身

[3] pry(#<TweetsController>)> post_params[:media_attributes]["0"]["media_url"]
Unpermitted parameter: :id
Unpermitted parameter: :id
=> [#<ActionDispatch::Http::UploadedFile:0x00007f85f91beaa0
  @content_type="image/jpeg",
  @headers=
   "Content-Disposition: form-data; name=\"tweet[media_attributes][0][media_url][]\"; filename=\"IMG_3848.JPG\"\r\nContent-Type: image/jpeg\r\n",
  @original_filename="IMG_3848.JPG",
  @tempfile=
   #<File:/var/folders/l8/5109jjgs36v7tcj0hjjnlb0c0000gn/T/RackMultipart20210102-26482-11g7dqf.JPG>>,
 #<ActionDispatch::Http::UploadedFile:0x00007f85f91bea50
  @content_type="image/jpeg",
  @headers=
   "Content-Disposition: form-data; name=\"tweet[media_attributes][0][media_url][]\"; filename=\"IMG_3849 (1).jpg\"\r\nContent-Type: image/jpeg\r\n",
  @original_filename="IMG_3849 (1).jpg",
  @tempfile=
   #<File:/var/folders/l8/5109jjgs36v7tcj0hjjnlb0c0000gn/T/RackMultipart20210102-26482-1tuk6xs.jpg>>]

画像二つ追加したときの@new_img中身

[2] pry(#<TweetsController>)> @new_img
=> #<ActionDispatch::Http::UploadedFile:0x00007f85f91bea50
 @content_type="image/jpeg",
 @headers=
  "Content-Disposition: form-data; name=\"tweet[media_attributes][0][media_url][]\"; filename=\"IMG_3849 (1).jpg\"\r\nContent-Type: image/jpeg\r\n",
 @original_filename="IMG_3849 (1).jpg",
 @tempfile=
  #<File:/var/folders/l8/5109jjgs36v7tcj0hjjnlb0c0000gn/T/RackMultipart20210102-26482-1tuk6xs.jpg>>

post_params[:media_attributes]["0"]["media_url"]の中身を一つずつ取り出す方法あればヒントいただけませんでしょうか。

それとも、二つの画像を格納する時に post_params[:media_attributes]["0"]["media_url"]とpost_params[:media_attributes]["1"]["media_url"]のように別のハッシュに入れるようにすべきなのでしょうか

great084 commented 3 years ago

@nakamitsu-nozomi 調査しながらコードいじっていたら取りあえず動くものができたのでpushしてます。 以下の動きが実現できています。

コードが汚いのと、view側のレイアウトもいまいちなので、引き続き改善します。

great084 commented 3 years ago

@nakamitsu-nozomi コントローラのソースをきれいにしたのと、解説のためにコメントを付けています。 また、画像登録時にプレビュー表示されるようにしました。

余裕あるようでしたら動作確認お願いします。

あとは、直近masterをマージしてコンフリクト解消が残です。

great084 commented 3 years ago

実装内容

UI変更概要

画面キャプチャ

screencapture-localhost-3000-tweets-768-2021-01-10-11_34_41

稼働確認チェック

参考資料

連絡事項

UIをよりよいものにするならば、ファイル登録のエリア(file_field)をカスタマイズ(ex. 画像をクリックすることで登録可能にする)が考えられるが、今回は見送る。