質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.23%

[CakePHP3] ブログで非同期の「いいね」ボタン実装

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,324

xjaPANDA

score 116

CakePHP3で、ブログのビュー画面に非同期のいいねボタンの実装を試していますが、うまくいかない部分を質問したいと思います。

今回の実装の概要は、LikesテーブルにBlog_idといいねを押したユーザーのidを紐づけるかたちで登録した時点で、「いいね」が押された状態になります。表示に関ては、「いいね」ボタンと「いいねをはずす」ボタンをつくり、条件分岐により、データベースの中を確認し、まだ押していなければ、「いいねボタン」が表示され、既に押している場合は「いいねをはずす」ボタンを表示します。関連するテーブルはBlogsテーブルと、Likesテーブルで、こちらは、モデルの方でアソシエイトされているのと、BlogsコントローラーのViewアクションでコンテインされているので、関連付はされています。こちらは、非同期処理を加えないばあいは、正常に動きます。しかし、
これをjqueryのajaxメソッドを利用して非同期にした時に、動作がうまくいかない点があるので、そのあたりを説明します。

まず今のところ非同期で正常に動く部分は、

「いいね」を押す ⇒「いいねをはずす」に切り替わる。データベースに登録。
「いいねをはずす」を押す⇒「いいね」に切り替わる。  データベースから削除。

ここまでは正常に動きますが、問題があるのはここからで、再び「いいね」を押したときに、ボタンが切り替わらず、確認してみるとデータベースにも登録されません。また、ここでリロードされてしまいます。このあたりがよくわかりません。

一連のながれとしては、「いいね」ボタンをクリックした場合は、非同期で、LikesコントローラーのaddアクションにPostデータが渡されます。そしてデータベースにuser_idとBlog_idが追加されます。そして、Likes/add.ctp に書いた、「いいねをはずす」ボタンの表示が、Blogs/view.ctpに表示されます。その時には、「いいね」ボタンは隠されます。

次に、その表示された「いいねをはずす」ボタンをクリックすると、Likesコントローラーのdeleteアクションにそのいいねのidが渡され、そのレコードが削除されます。それと同時に、「いいねをはずす」ボタンは隠して、Likes/renew.ctpにある「いいね」ボタンの表示をBlogs/view.ctpの方で読み込みます。問題は、2回目の「いいね」を押したときにうまく動かないということです。

下記にコードを載せます。

Blogs/view.ctp

<script>
$(document).ready(function(){

  $("#like_button").submit(function(){

    $.post('<?=$this->Url->build('/likes/add/') ?>',$('#like_button').serialize(),function(data){
      //alert("Data Loaded: " + data);
      $("#like_sta").append(data);
      $("#like_button").hide();


     });
  return false;

  });


  $("#unlike_button").submit(function(){
       var id = $(this).attr('rel');
    $.post('<?=h($this->Url->build('/likes/delete/')) ?>'+id ,$('#unlike_button').serialize(),function(data){
      //alert("Data Loaded: " + data);
       $("#like_sta").append(data);
       $("#unlike_button").hide();  
     });
  return false;

  });


});
</script>
<!-- 記事部分省略-->

<div id="like_sta">

<?php if( isset ($blog->likes[0]['user_id']) && ($blog->likes[0]['user_id']) == $id['id'] ){ ?>

   <form id="unlike_button" rel="<?=$blog->likes[0]['id']?>" >
        <?php
            echo $this->Form->hidden('user_id',['value'=> $blog->likes[0]['id'] ]);
        ?>
   <input class="btn btn-default" type="submit"  value="いいねをはずす" >
   </form>

<?php }else{ ?>

   <form id="like_button">    
        <?php
            echo $this->Form->hidden('user_id',['value'=> $id['id'] ]);
            echo $this->Form->hidden('blog_id',['value'=> $blog->id]);
        ?>
   <input class="btn btn-default" type="submit"  value="いいね" >
   </form>

<?php } ?>


</div>

LikesController.php

<?php
namespace App\Controller;

use App\Controller\AppController;

class LikesController extends AppController
{

  //その他の処理省略

    public function add($like_id = null)
    {
        $like = $this->Likes->newEntity();
        if ($this->request->is('post')) {
            $like = $this->Likes->patchEntity($like, $this->request->data);
            if ($this->Likes->save($like)) {
                return $this->redirect(['controller'=>'likes','action' => 'add', $like->id]); //第三引数 いいねid
            } else {
                return $this->redirect($this->referer());
            }
        }

        $like_info = $this->Likes->get($like_id);
        $this->set(compact('like_info'));       
    }

    public function delete($like_id = null)
    {
        $this->request->allowMethod(['post', 'delete']);
        $like = $this->Likes->get($like_id);
        if ($this->Likes->delete($like)) {
            return $this->redirect(['action' => 'renew', $like->blog_id ]);   //renewにブログidをわたす。
        } else {
            return $this->redirect($this->referer());
        }

    }

    public function renew($blog_id = null)
    {
        $id = $this->Auth->user();
        $this->set(compact('id'));

        $this->set(compact('blog_id'));
    }        

}

Likes/add.ctp  ・・・いいねが押された後に表示するいいねをはずすボタンと処理

<script>
$(document).ready(function(){

  $("#unlike_button").submit(function(){
       var id = $(this).attr('rel');
    $.post('<?=$this->Url->build('/likes/delete/') ?>'+id ,$('#unlike_button').serialize(),function(data){
      //alert("Data Loaded: " + data);
      $("#unlike_button").hide();
      $("#like_sta").append(data);
     });
  return false;

  });



});
</script>

<form id="unlike_button" rel="<?=$like_info['id'] ?>" >
    <?php
        echo $this->Form->hidden('user_id',['value'=> $like_info['blog_id'] ]);        
    ?>
<input class="btn btn-default" type="submit"  value="いいねをはずす" >
</form>

Likes/renew.ctp  ・・・非同期上での「いいね」の表示とクリック時の処理。

<script>
$(document).ready(function(){

  $("#like_button").submit(function(){

    $.post('<?=$this->Url->build('/likes/add/') ?>',$('#like_button').serialize(),function(data){
      //alert("Data Loaded: " + data);
      $("#like_sta").append(data);
      $("#like_button").hide();


     });
  return false;

  });

});
</script>

<form id="like_button">    
    <?php
         echo $this->Form->hidden('user_id',['value'=> $id['id'] ]);
         echo $this->Form->hidden('blog_id',['value'=> $blog_id]);
     ?>
<input class="btn btn-default" type="submit"  value="いいね" >
</form>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

.hide()ではdisplay:none;を加えて見えなくしているだけなので、最初にいいねボタンを表示したときの要素が残っているにもかかわらず、.append()で再度いいねボタンを加えているからではないでしょうか。

最初に「いいね」を押したとき

$("#like_sta").append(data);
$("#like_button").hide(); // いいねボタンをdisplay:none;で隠す(要素自体は残る)


続けて「いいねをはずす」を押したとき

$("#unlike_button").hide();
$("#like_sta").append(data); // いいねボタンを追加(要素が2つになる)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/08/07 12:19

    ありがとうございます。まだまだ未熟なもので、大変参考になります。読み込まれている各ビューにポストする値をechoしたら、クリック後に画面がかわっても、消えずに残っていました。フォームは隠れているだけなのですね。そこで、

    $("#like_sta").append(data);
    $("#like_button").hide();
    $("#like_button")[0].reset();

    $("#like_sta").append(data);
    $("#unlike_button").hide();
    $("#unlike_button")[0].reset();

    のうよに .reset(); を加えて、リセットして消すことができないかと思ったのですけど、できませんでした(涙)。ひきつづき調べてみます。もしわかりましたら概要だけでも教えて下さい。

    キャンセル

  • 2016/08/07 13:18

    やりたいことはほぼできているようなので、あと一息です!

    .reset()ではなく、.html()で<div id="like_sta"></div>の中身を全て入れかえてしまいましょう。

    $("#like_sta").html(data);

    あと、Likes/add.ctpとLikes/renew.ctpで出力する内容で、jQueryの部分も同じものが複数出力されないようにしないといけませんね。

    なお、別の方法もあります。
    ボタンを用意してボタン本体はそのままで、ボタンのテキストやid、classなどのみを書き換える方法です。
    一度にいろいろやっても混乱するので、指摘だけにとどめておきます。

    キャンセル

  • 2016/08/07 14:24

    ありがとうございます。丸ごと入れ替えてしまうのですね。 $("#like_sta").append(data);で後ろに追加していことしか考えていたので、新しい発見です。
    同じ物が出力されてしまう点も、まったく考えていませんでした。始めの画面の「いいね」の処理のスクリプトを、<div id="like_sta"></div>の中に入れてしまい、ボタン押したら、$("#like_sta").html(data);により、まるごと更新されるように変更しました。まだ良い方法があるかもしれませんが、解決してよかったです。
    あと、ボタンを用意して本体はそのままで変更するというやり方もとても興味深いです。できるかどうかわかりませんが、考えて試してみたいと思います。いろいろ教えてくれて感謝します。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.23%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる