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

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

ただいまの
回答率

89.99%

処理の関数切り分けについて

解決済

回答 5

投稿

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

newyee

score 151

PHPの学習の為、掲示板のサイトを作成致しました。
掲示板は、投稿されたコメント一覧及び、書き込みフォームを表示する「bbs_1.php」書き込みフォームより投稿されたコメントを処理する「write1.php」コメント一覧部分の削除フォームから、削除パスワードを入力された場合にコメントを削除する、「delete1.php」の3つのファイルで構成しております。ですが、学習の為、関数切り分けを行いたいと思い、データベース接続部分のPDOインスタンスを作成する部分「Db_connect.php」、及び、データベースの接続情報を定数として、定義した、「config.php」作成し、処理を切り分けてみました。

<?php 

require_once 'Db_connect.php';
require_once 'config.php';

  error_reporting(E_ALL);
  ini_set("display_errors",1);

  //1ページに表示されるコメントの数
  $num = 10;
  //ページ数が指定されている時
  $page = 0;
  if(isset($_GET['page']) && $_GET['page'] > 0){
    $page = intval($_GET['page']) -1;
  }

  try{

    $dbh = db_connect(DSN,DB_USERNAME,DB_PASSWORD);
    $stmt = $dbh->prepare("SELECT id,name,title,comment,created_at,password FROM post ORDER BY created_at DESC LIMIT
    :page,:num");
    //パラメーターを割り当て
    $page = $page * $num;

    $stmt->bindValue(':page',$page,PDO::PARAM_INT);
    $stmt->bindValue(':num',$num,PDO::PARAM_INT);
    $stmt->execute();
  }catch(PDOException $e){
    echo "エラー: " . $e->getMessage();
  }

?>

  <html>
    <head>
      <title>交流サイト:掲示板</title>
      <meta charset="utf-8">
    </head>
    <body>
      <h1>掲示板</h1>
      <form action="write1.php" method="post">
        <p>名前:<input type="text" name="name" value="<?php echo isset($_COOKIE['name']) ? $_COOKIE['name'] : '' ?>"></p>
        <p>タイトル:<input type="text" name="title" size="60"></p>
        <textarea name="comment"></textarea>
        <p>削除パスワード(数字4桁):<input type="text" name="pass">
        <input type="submit" name="submit" value="書き込む">
        <input type="hidden" name="token" value="<?php echo password_hash(session_id(),PASSWORD_DEFAULT); ?>">
      </form> 
      <hr>
    <?php 
      while($row = $stmt->fetch()):

        $title = $row['title'] ? $row['title'] : '(無題)';
      ?>
      <p>名前:<?php echo $row['name'] ?></p>
      <p>タイトル:<?php echo $title ?></p>
      <p><?php echo nl2br(htmlspecialchars($row['comment'],ENT_QUOTES,'UTF-8'),false) ?></p>
      <p><?php echo $row['created_at'] ?></p>
      <form action="delete1.php" method="post">
        <input type="hidden" name="id" value="<?php echo $row['id']; ?>">
        削除パスワード:<input type="password" name="pass">
        <input type="submit" value="削除">
        <input type="hidden" name="token" value="<?php echo password_hash(session_id(),PASSWORD_DEFAULT); ?>">
      </form>
    <?php 
      endwhile;
      //ページ数の表示
      try{
        $stmt = $dbh->prepare("SELECT COUNT(*) FROM post");
        $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
        $dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

        //クエリの実行
        $stmt->execute();

      }catch(PDOException $e){
        echo "エラー:" . $e->getMessage();
      }
      /**
       * コメントの件数を取得
       *fetchColumn関数で 最初のレコードを取り出し、最初の列「id」のデータを取り出す
       * idは降順に設定されているため、コメントの件数が分かる
       * */
      $comments = $stmt->fetchColumn();
      $max_page = ceil($comments / $num);
      echo '<p>';
      for($i = 1; $i <= $max_page; $i++){
        echo '<a href="bbs.php?page=' . $i .'">' . $i .
        '</a>&nbsp;'; 
      }
      echo '</p>';
      ?>

    </body>
  </html>


上記は、「bbs_1.php」になります。

<?php

require_once 'Db_connect.php';
require_once 'config.php';

error_reporting(E_ALL);
ini_set('display_errors', '1');

$err_message = [];

if(!empty($_POST)){

  $title = filter_input(INPUT_POST,'title',FILTER_SANITIZE_SPECIAL_CHARS);
  $token = filter_input(INPUT_POST,'token',FILTER_SANITIZE_SPECIAL_CHARS);

  if(!$name = filter_input(INPUT_POST,'name',FILTER_SANITIZE_SPECIAL_CHARS)){
    $err_message[] = '名前が入力されていません';
  }

  if(!$pass = filter_input(INPUT_POST,'pass',FILTER_SANITIZE_SPECIAL_CHARS)){
    $err_message[] = 'パスワードが入力されていません';
  }

  if(!$comment = filter_input(INPUT_POST,'comment',FILTER_SANITIZE_SPECIAL_CHARS)){
    $err_message[] = 'コメントが入力されていません';
  }

}


if(!preg_match("/^[0-9]{4}$/",$pass)){

  $err_message[] = 'パスワードを4文字で入力してください';

}

$pass = password_hash($pass,PASSWORD_DEFAULT);

if(count($err_message) === 0){
  //CSRF対策
  if(!password_verify(session_id(),$token)){
    header('Location:bbs_1.php');
    exit();
  }

  setcookie('name',$name,time() + 60 * 60 * 24 * 30);



  //データベースに接続


  try{

    $dbh = db_connect(DSN,DB_USERNAME,DB_PASSWORD); 
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
    $dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
    $stmt = $dbh->prepare("
      INSERT INTO post (name,title,comment,created_at,password)
      VALUES (:name, :title, :comment, now(), :pass)");
    $stmt->bindValue(':name',$name,PDO::PARAM_STR);
    $stmt->bindValue(':title',$title,PDO::PARAM_STR);
    $stmt->bindValue(':comment',$comment,PDO::PARAM_STR);
    $stmt->bindValue(':pass',$pass,PDO::PARAM_STR);
    $stmt->execute();

  }catch(PDOException $e){
    die ('エラー:' . $e->getMessage());
  }

}

?>
<html>
<head>
  <meta charset="utf-8">
  <title>Page Title</title>

</head>
<body>
  <?php 
    echo implode("<br />",$err_message);
  ?>
  <h4>投稿に成功しました。</h4>
  <p><a href="bbs.php">戻る</a>  

</body>
</html>


上記は「write1.php」になります。

<?php
  require_once 'config.php';
  require_once 'Db_connect.php';
  error_reporting(E_ALL);
  ini_set("display_errors",1);

  //エラーメッセージ
  $err_msg = [];

  if(!empty($_POST)){

    if(!$pass = filter_input(INPUT_POST,'pass',FILTER_SANITIZE_SPECIAL_CHARS)){

      $err_msg[] = 'パスワードが入力されていません。';

    }


    $token = filter_input(INPUT_POST,'token',FILTER_SANITIZE_SPECIAL_CHARS);

    //POSTされたIDを取得
    $id = filter_input(INPUT_POST,'id',FILTER_SANITIZE_SPECIAL_CHARS);
    $id = intval($id);

  }

  //CRLF対策
  if(!password_verify(session_id(),$token)){
    header('Location:bbs.php');
    exit();
  };

  if(count($err_msg) === 0){


      try{
        $dbh = db_connect(DSN,DB_USERNAME,DB_PASSWORD);
        $stmt = $dbh->prepare("
          SELECT password FROM post where id = :id 
        ");

        $stmt->bindValue(':id',$id,PDO::PARAM_INT);

        $stmt->execute();

        $db_pass = $stmt->fetch();

        if(!password_verify($pass,$db_pass['password'])){

          die('パスワードが違います');
        }

        $stmt = $dbh->prepare("
            DELETE FROM post WHERE id = :id 
          ");

        $stmt->bindValue(':id',$id,PDO::PARAM_INT);
        $stmt->execute();

      }catch(PDOException $e){

        die( "エラー:" . $e->getMessage());
      }

    header('Location: bbs_1.php');
    exit();

    } 

?>


上記は「delete1.php」になります。

<?php
define('DSN','mysql:host=localhost;dbname=online_bbs;charset=utf8');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', '12345');


上記は「config.php」になります。

<?php
function db_connect($dsn,$user,$password){
  $dbh = new PDO($dsn,$user,$password);
  $dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
  $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
  return $dbh;
}


上記は、「Db_connect.php」になります。

「bbs_1.php」「write1.php」「delete1.php」内で、他に関数切り分けした方が良いと思う箇所などがありましたら、ご助言頂きたいです。
よろしくお願いいたします。

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 5

+3

これはモデリングの話ですね。
PG100人に聞いたら100通りの回答されると思います。つまり正解はありません。

見た感じリファクタリングできる箇所はMVCモデル化とGoFデザインパターンの導入でしょう。

一例ですがこんな感じにモデリングした後、実際の書き込み処理などを実装していく感じになるでしょう

<?php
//C&V
// require_once(BBS_Model);

$bm = new BBS_Model();
if($bm->init() === false){
  http_response_code(400) ;
  exit();
}

$bm->exec($_GET,$_POST);

?><html>~省略~

<?= empty($bm->getError()) ? $bm->getMessage()['success_msg'] : $bm->getError() ?>

~省略~
</html>
<?php
require_once('Db_connect.php');
require_once('config.php');


class BBS_Model()
 private $conf;
 private $db;
 private $error_msg = [];
 private $msg = [];
 public function __construct(){
   $this->conf = new config();
 }
 public function init(){
   $this->db = new Db_connect($conf);//「php db GoFパターン」で検索
   if($this->db === false){
    return false;
   }
 }
 public function exec($get, $post){
   //メインロジック
   //エラー処理
   if(isset($post)){
     $this->error_msg = 'Error';
     return false;
   }
   $this->saveComment($name, $title,$comment,$pass);

   $this->msg['success_msg'] = '成功です';
 }
 public function getError(){
   return $this->error_msg;
 }
 public function getMessage(){
   return $this->msg;
 }

 private saveComment($name, $title,$comment,$pass){
Value(':name',$name,PDO::PARAM_STR);

  try{

~省略~
    $stmt->bindValue(':title',$title,PDO::PARAM_STR);
    $stmt->bindValue(':comment',$comment,PDO::PARAM_STR);
    $stmt->bindValue(':pass',$pass,PDO::PARAM_STR);
~省略~
    $this->db->execute($stmt);

  }catch(PDOException $e){
    die ('エラー:' . $e->getMessage());
  }

}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/01 23:56

    回答依頼いただいたんですが、こちらの回答の方針を支持いたしますので、このコメントをもって回答といたします。
    コメントとして方針を加えるとすれば、ビュー(描画するhtmlを書くソース)でselectとかいう文字をみたくないです。
    bbs.php(bbs_1.phpになってない箇所がいくつかある)と、write_1とdelete_1の関係がおかしい(writeは遷移しておわり、deleteはLocationで戻している)。コントローラがきちんと役割立てされてない感じ。

    キャンセル

  • 2019/04/02 12:16

    papinianusさんご返信ありがとうござます。
    ご指摘頂きました点につきまして、コードの方見直して、見たいと思います。

    キャンセル

  • 2019/04/02 12:41

    解決済みになっていますが
    >ご回答いただきました、「$this->db = new Db_connect($conf);/」こちらの部分なのですが、
    Db_connect.phpは自分のコードではクラスを記載しておりません。インスタンス化することはできるのでしょうか...?

    サンプルコード中のコメントにある通り、GoFのデザインパターン化する前提です。よって質問にあるコードのDb_connectではインスタンス化できません。
    DBの抽象化は多くの人が作ってるのでそれを参考にするのがベストでしょう

    キャンセル

checkベストアンサー

+2

学習のため、ってところが引っかかるのは私だけだろうか。
そして、まだオブジェクト指向のオの字も出てこないのも気になる。

なんのために分割するのかを見失ってますよね。

生産性を高めるために、
コードの再利用性を高めるために、
(もしかしたら)内部処理と見た目(見せ方)を分業するために分離する
ってのが本筋じゃないでしょうか。

その場合に、あなたがどういう観点で構造を変更すれば
生産性が高まり、コードの再利用性が高まるのか、
内部処理と見た目(見せ方)を分業できるのか、
っていう視点で考えればいいし、
分け方なんて人それぞれで場合にもよるのだから、
「こういう考え方に基づいてこういうふうにわけました」が正しく行われれば
それにツッコミの入れようはないはずです。

ちなみに、内部処理と見た目(見せ方)の分業について、
たった一人で開発する上では、一本のphpの中で
データベースの処理もhtmlやcssやjsのコードも全部書いちゃいますが、
規模が大きくなると分業することもあります。
分業を円滑にするためにフレームワークを駆使してみたり、
テンプレートエンジンを使ってみたりします。

と、前置きして、

Db_connect.php と config.php を分ける目的が、
システム全般的な設定事項を config.php にまとめてしまうのであれば分けててもいいし、
単にDB接続事項だけであれば Db_connect.php にまとめてしまった方が一行で済む利便性もあります。
分ければ、処理を起こす都度2行ずつ書くのを忘れなければいいのだし。
分けてあれば、ローカルのテスト用の設定とデプロイ先の設定をファイル差し替えで分けることもできます。
目的に沿っていれば、どっちでもいいんです。

でも、せっかくなら、 config.php に設定ごとを追い出しているのだとしたら、
環境依存的な事柄もそこに含めるべきで、
例えばデバッグ用の設定と思われる

  error_reporting(E_ALL);
  ini_set("display_errors",1);


も追い出すと良いです。サーバーにデプロイしたときに不要(というか見直しする箇所)なので。

それと、今回投稿やコメントの更新処理が含まれていませんが、
更新処理をもし組み込んだとすると、
削除処理と更新処理で共通する処理というのも出てきます。
(書き換え用のパスワードが同じかどうかなど検証処理が)
そういうときどうするかも考えてみてください。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/01 18:01

    config.php への追い出しについて、
    各アクション単位のphpファイルを、デプロイ時にデバッグ向け設定箇所を改変しなくてもいいように、
    config.php 一箇所で済ませたほうが楽って観点です。
    怖いのは修正漏れなので。

    キャンセル

  • 2019/04/01 18:06

    分け方を、単に今回の掲示板だけで考えるのか、
    今後も類似の何かを開発する前提での再利用を考えるか、
    など視点や立ち位置によって変わってしまうので、
    namdaさんのいうデザインパターンは一度触れておいたほうがいいでしょう。

    キャンセル

  • 2019/04/01 18:11

    ご返信ありがとうございます。
    >config.php への追い出しについて
    教えて頂き、ありがとうございます。
    namdaさんのご回答も参考にさせて頂きたいと思います。

    キャンセル

+2

タグ「オブジェクト指向」は今回どのように関係するのでしょうか。
PDOはphpから提供されている機能ですがそれを「オブジェクト指向」と置いているのでしょうか。

正直なところ、この手のものはやろうと思えば幾らでもできてしまうものなので、質問としてあげるには不適当の内容と思います。
「その幾らでもできてしまうところを教えて」と言われると質問ではなくなります。
「こうしなければならないを教えて」となるともはや質問ではなくなります。仕事だと現場のコーディングルールに従うわけですし、現場のルールであればおいそれと公の場に出すわけにはいきません。
趣味としたら好きなようにやったら良いだけです。

要件通り動いていることが大前提。
もしリファクタリングなのであれば前と同じ操作で同じ結果が出ていればOK。

「どこまでもできる、キリがない」ので、どこまでやるかは自分が決めることです。

リファクタリングとしても何を機能切り出しされたのか見えないのは問題ですね。
基本は「同様の処理は関数化(引数で切り分ける)」
「ファットコントローラーになるなら外出し」
「役割分担、一定の仕事をきちんと与えて似た仕事を重複させない」
ですが、その観点で「自分では限界までやった」のでしょうか?

ほとんど1枚ペラのプログラムと変わらないですね。「他にある?」と聞く段階ではなさそうです。
9割程度できていて1割なんとかならないか、なら分かりますが、1割も出来てなさそうに見えます。
ツッコミどころが多すぎてどこから突っ込むべきか、というか、もっと質問する前に考えられることだらけで突っ込む気もおきない、という感じです。
(質問者さんのこれまでの経緯から、この程度で止まってしまってることが残念でならないです。そういった意味で「出来そうなところの1割も」という意味で書いてます。)

いっそ、ほぼ0から作り直してみては。
今あるものをなんとかしようというのではなく、部品作っておいて使う形に。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/04 11:41

    「改行」も「改行コード」「改行タグ(html)」と最低でも2種類あるのでできればそこも正しく表現したほうが良いですね。
    phpも全てブラウザで参照されるわけではないですし、php自体はどこから参照されるか知らないわけですから。
    あとはコードを見やすいように更新しておいてください。
    数日後とか自分で見たときにどこまでが1パターンなのか分からなくなりますからね。

    そのあたり、下記のような記事はかなり参考になるし、耳が痛いところを突いています。
    https://qiita.com/rana_kualu/items/95f0c8be51e8665015d5
    https://qiita.com/tadsan/items/fb496e450fc27c8c4494

    キャンセル

  • 2019/04/04 13:25

    helloworldの修正したコードの方、githubにアップロードしておきました。
    qiitaの記事のリンク、どうもです。
    今、記事の方、読ませて頂いていますが、かなり勉強になりますね。

    キャンセル

  • 2019/04/04 13:33

    >githubにアップロードしておきました。

    後程確認します。

    >今、記事の方、読ませて頂いていますが、かなり勉強になりますね。

    「戒める」なので全て逆だったり対応されてなかったりした場合にどうなるかを考えながら読むと考える力も身に付きますし、身を引き締めるキッカケにもなります。
    「今の自分にあてはまるものがないか(逆をやってしまっていることはないか)」振り返りながらどうぞ。

    キャンセル

+2

一度設計作業をちゃんとしてみれば?
掲示板であれば、設計サンプルはいくらでも入手できるし、設計作業ができなければ、そもそもコレ以上の規模でまともなコーディングができるわけないので。

タマゴとニワトリだけど、クラス図がそれなりの精度で書ければ、オブジェクト指向でリファクタリングするのも容易です。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/02 14:14

    ご回答ありがとうございます。
    正直な所、自分としましては、「設計」というものも良く分かっていませんでした...
    掲示板の設計サンプル、調べてみたいと思います。

    キャンセル

+1

こんにちは。

回答ではなく助言です。これで伝わってくれれば良いのですが。
「プログラムの分割」と言ってまずイメージできるものというと、一連の処理をぶつ切りにして、それぞれを分けて管理するやり方が挙がりますね。これを便宜的に「縦分割」と表現してみます。流れる一連の処理をフェーズ毎に切り分けた状態をイメージしてください。これに対して、オブジェクト指向は「横分割」であると言えます。縦横の表現でイメージできるでしょうか、横分割は言い換えると「処理の階層化」を行う構造化という意味です。この2つを混同して「切り分け」と表現してしまうのは、何も分かっていないと言われても仕方ありません。縦分割がいわゆる工場のラインを工程別に分けたものであるとすると、横分割は、小さなツールを「使う」小さなツール、を「使って作られた」大きなツール、を「使って」プログラムを作るものです。この積み重なる「階層化」のイメージを持って、今一度自分のプログラムを見直してみてください。どうですか?階層化できそうですか?自分にはできそうにありません。一度コードを捨てて一から考え直した方が良い、と言っていたのは、そういうことです。一から、考え直してみるのはどうでしょうか。鍵は「階層化」と「小さなツール」です。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/02 12:21

    ご回答ありがとうございます。
    抽象的な捉え方になってしまうかもしれないのですが、横分割というのは、「処理の階層化」、縦分割というのは、一連の処理をフェーズ事に切り分ける、といった考え方で理解できました。
    他のご回答者様のご回答なども、参考にさせて頂き、もう少し勉強していきたいと思います。。。

    キャンセル

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

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