作成日:2021/04/12 更新日:2022/07/04

#11 PHPでClassクラスを理解するための準備

以前作ったPHPプログラムをファイル分けしましょう

応用2で作った「sortableアプリ」は1枚のphpファイルでいろんな動作を処理していましたが、 本来は機能ごとにファイルを分割することで、保守性(あとで手直ししたりすること)などに役立てることができます。

前回作ったアプリケーションファイル[8001-sortable]を複製し[sortable2]というフォルダを作り、[8001-sortable]フォルダの中に入れます。
「sortable2」の中で、それぞれ機能ごとにファイルを切り分けましょう。ファイルの内容は以下のとおりです。


localhost:8001/sortable2/でアクセスします。
index.phpファイルの編集箇所
読み込むファイルが増えたので、それぞれのファイルの読み込みを設定しましょう。
index.phpから切り出して新たに作るファイルは4つです。
  1. /config/config.php
  2. /function/insert.php
  3. /function/update.php
  4. /js/sort.js

config.php(データベースに接続する機能)

<?php
/* データベース設定 */
define('DB_DNS''mysql:host=localhost; dbname=cri_sortable; charset=utf8');
define('DB_USER''root');
define('DB_PASSWORD''root');

/* データベースへ接続
======================================================== */
try {
  $dbh new PDO(DB_DNS, DB_USER, DB_PASSWORD);
  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
catch (PDOException $e){
    echo $e->getMessage();
    exit;
}
内容はそのままです

insert.php(データベースに新規登録する機能)

<?php
require_once('../config/config.php');  /* DB接続用のファイルを読み込む */

/* 新規氏名+性別をデータベースへ登録 */
if(!empty($_POST['inputName'])){
  try{
    $sql '
            INSERT INTO sortable(
              name,
              gender_id
            )
            VALUES(
              :ONAMAE,
              :GENDER
            )
            ';
    $stmt $dbh->prepare($sql);
    $stmt->bindValue(':ONAMAE'$_POST['inputName'],   PDO::PARAM_STR);
    $stmt->bindValue(':GENDER'$_POST['inputGender'], PDO::PARAM_INT);
    $stmt->execute();

    /* ↓一つ前のページのパスを指定し、処理が終わったらそこに戻る */
    header('location:'.$_SERVER["HTTP_REFERER"]);
  } catch (PDOException $e) {
    echo $e->getMessage();
  }
}
require_once()関数でDB接続させるconfig.phpファイルを読み込みます。
また、INSERT処理が終わったらheader()関数でindex.phpファイルに戻ってページを表示させます。

update.php(データベースの情報を変える機能)

<?php
require_once('../config/config.php');  /* DB接続用のファイルを読み込む */

/* Ajaxを経由してPOST受信した「座標」の変数をDBに登録 */
if(!empty($_POST['left'])){
  try{
    $sql  '
            UPDATE
              sortable
            SET
              left_x = :LEFT,
              top_y  = :TOP
            WHERE
              id = :NUMBER
            ';
    $stmt $dbh->prepare($sql);
    $stmt->bindValue(':LEFT'  , (int)$_POST['left'], PDO::PARAM_INT);
    $stmt->bindValue(':TOP'   , (int)$_POST['top'],  PDO::PARAM_INT);
    $stmt->bindValue(':NUMBER', (int)$_POST['id'],   PDO::PARAM_INT);
    $stmt->execute();

    /* ↓一つ前のページのパスを指定し、処理が終わったらそこに戻る */
    header('location:'.$_SERVER["HTTP_REFERER"]);
  } catch (PDOException $e) {
    echo $e->getMessage();
  }
}
insert.php同様、require_once()関数でファイルを読み込みます。
また、UPDATE処理が終わったらindex.phpファイルに戻ってページを表示させます。

sort.js(要素を動かす機能)

/*
 * DRAGGABLE
 =========================== */
$(function(){
  $('.drag').draggable({
    containment:'#drag-area',
    cursor:'move',
    opacity:0.6,
    scroll:true,
    zIndex:10,
    /* ==========STOP処理====================================== */
    stop:function(event, ui){
      let myNum  = $(this).data('num');
      let myLeft = (ui.offset.left - $('#drag-area').offset().left);
      let myTop  = (ui.offset.top  - $('#drag-area').offset().top);
      /* ==========AJAX通信================= */
      $.ajax({
        type:'POST',
        url :'./function/update.php',  /* ←update.phpファイルに飛ばします */
        data: {
          id  :myNum,
          left:myLeft,
          top :myTop
        }
      }).done(function(){
         /*console.log('成功');*/
      }).fail(function(XMLHttpRequest, textStatus, errorThrown){
         console.log(XMLHttpRequest.status);
         console.log(textStatus);
         console.log(errorThrown);
      });
      /* ==========/AJAX通信================= */

    }
    /* ==========/STOP処理====================================== */
  });
});
$.ajax()メソッドのurl引数には、データをとばす先であるupdate.phpのパスを指定します。

index.php(ファイルをリンクさせます)

<?php
require_once('./config/config.php');  /* 01 ←DB接続用のファイルを読み込む */
?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Sortable_Comp</title>
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
  <script src="js/sort.js"></script>  <!-- 02 ←sort.jsを読み込ませる -->
  <link href="css/style.css" rel="stylesheet">
</head>
<body>
<div id="wrapper">
<div id="input_form">
  <form action="./function/insert.php" method="POST">  <!-- 03 ←登録用のinsert.phpへpostする -->
    <input type="text" name="inputName" placeholder="新メンバー名を入力">
  ・
  ・
  ・
  1. DB接続用のファイルconfig.phpを読み込ませます。
  2. ドラッグ機能のsort.jsを読み込みます。
  3. 新規登録formタグのaction属性にはデータの飛ばし先として、insert.phpへのパスを指定します。
すべてのindex.php ▼
localhost:8001/sortable2/でアクセスします。

切り出しただけなので、同じ表示になるはずです。
ファイルを機能別に分け、役割ごとにフォルダに入れ、index.phpに読み込むことができました。
これで準備は完了です。

#12 PHPアプリケーションをクラス化してみよう

クラスを理解するための「オブジェクト指向プログラミング」

処理する機能をひとまとめににしたものを Classクラス と呼びます。
クラスを使ってプログラムを作る考え方を オブジェクト指向しこうプログラミング と呼びます。
大人数で、大規模なアプリケーションを作るときはクラス化しておくとたくさんのメリットがあります。

オブジェクト指向プログラミングとは「ある1つの処理は、データや処理をまとめたもの = “オブジェクト”で構成され、複数のオブジェクトを使って全体のプログラミングする考え方」という意味合いで理解しておけば大丈夫です。
最初はオブジェクト指向を理解するのは難しいです。作ったファイルを書き換えることで、なんとなく雰囲気や、書き方をつかめてもらえたら良いでしょう。

さきほど作ってもらった[sortable2]をクラス化していきましょう。
[sortable2]フォルダを複製して[sortable3]を作り編集していきます。


このようなファイル構成になります。

のちほどlocalhost:8001/sortable3/でアクセスします。

①DBに接続する機能config.phpをクラス化してみよう

<?php
/* データベースに接続するクラス */
class Connect
{
  /* プロパティ(定数)の宣言 */
  const DB_NAME ='cri_sortable';
  const HOST    ='localhost';
  const UTF     ='utf8';
  const USER    ='root';
  const PASS    ='root';

  /* データベースに接続する メソッド(関数) */
  public function pdo(){
    $dsn  "mysql:dbname=" .self::DB_NAME. "; host=" .self::HOST. "; charset=" .self::UTF;
    $user self::USER;
    $pass self::PASS;
    try{
      $pdo new PDO($dsn$user$passarray(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES '.SELF::UTF));
    }catch(Exception $e){
      echo 'エラー '.$e->getMessage;
      die();
    }
    return $pdo;
  }
}

/* SELECT文のときに使用するクラス */
class SelectData extends Connect
{
  /* プロパティ(変数)の宣言 */
  private $sql;

  /* データベースに接続しテーブルデータを取得する メソッド(関数) */
  public function select($sql){
    $items $this->pdo()->query($sql)->fetchAll(PDO::FETCH_ASSOC);
    return $items;
  }
}
上記の内容をコピーして Connect.php という名前で保存しましょう。
クラスのファイル名は、クラス名をつけます。
その際にControllerというフォルダを作って、その中に入れておきましょう。Controller/Connect.php
▼解説

更新機能と新規登録を別のクラスファイルとして作成します。

<?php
class AppController extends Connect
{
  /* 座標を登録 */
  public function update_sortable($sql$left$top$id){
    $stmt $this->pdo()->prepare($sql);
    $stmt->bindValue(':LEFT',   $left, PDO::PARAM_INT);
    $stmt->bindValue(':TOP',    $top,  PDO::PARAM_INT);
    $stmt->bindValue(':NUMBER'$id,   PDO::PARAM_INT);
    $stmt->execute();
    return $stmt;
  }

  /* 新規登録 */
  public function insert_sortable($sql$name$gender){
    $stmt $this->pdo()->prepare($sql);
    $stmt->bindValue(':ONAMAE'$name,   PDO::PARAM_STR);
    $stmt->bindValue(':GENDER'$gender, PDO::PARAM_INT);
    $stmt->execute();
    return $stmt;
  }
}
座標をアップデートさせる機能と、新規登録させる機能をまとめています。
上記の内容をAppController.phpという名前で保存しましょう

▼解説

クラスを使う手順

先程作ったクラスを使ってプログラムします。
クラスをnewしてインスタンスを作成し、インスタンスからクラス内で書いているメソッドを呼び出したり、プロパティ(変数)に値を代入します。

<?php
/* クラスファイルの読み込み */
require_once('読み込みたいファイルのパス');

require_once('./config/file.php');

/* クラスからインスタンスを生成 */
$インスタンス = new クラス();

$pdo new Connect();

/* クラス内のメソッドなどに実際使うデータを代入 */
$インスタンス->プロパティ = '値';

$pdo->prop = '100'

/* クラス内のメソッドの実行 */
$インスタンス->メソッド();

$select->select($sql);


<?php
class Sample
{
  public $name;
  public $aisatsu;

  public function koe({
    echo $this->name.' さん '.$this->aisatsu;
  }
}
/* ① */
$taro new Sample();
$taro->name = '下北太郎';     /* インスタンス->プロパティ (プロパティに値を代入) */
$taro->aisatsu = 'おはよう';
$taro->koe();                /* インスタンス->メソッド */

echo '<br>';

/* ② */
$jiro new Sample();
$jiro->name = '下北二郎';
$jiro->aisatsu = 'おはよう';
$jiro->koe();
実行した結果は
下北太郎 さん おはよう
下北二郎 さん バイバイ
となります。
クラスをインスタンス化し、クラス外でプロパティに名前などの値を代入して、クラス内のメソッド koe() の機能を使って文字を出力しています。
メソッド内の $this はインスタンス $taro の場合は $taro、インスタンス $jiro の場合は $jiro のこととなります。
また、プロパティ(変数)は $ マークをつけない点も注意が必要です。

②update.phpをクラス化に対応させます

<?php
/* 削除 */
/* require_once('../config/config.php'); */

/* 追加 */
require_once('../Controller/Connect.php');
require_once('../Controller/AppController.php');

/* Ajaxを経由してPOSTで受信した「座標」連想配列をDBに登録 ② */
if(!empty($_POST['left'])){
  try{
    $sql  '
            UPDATE sortable
            SET
              `left_x` = :LEFT,
              `top_y`  = :TOP
            WHERE
              `id` = :NUMBER
            ';
    /* 削除する
    $stmt = $dbh->prepare($sql);
    $stmt->bindValue(':LEFT',   (int)$_POST['left'], PDO::PARAM_INT);
    $stmt->bindValue(':TOP',    (int)$_POST['top'],  PDO::PARAM_INT);
    $stmt->bindValue(':NUMBER', (int)$_POST['id'],   PDO::PARAM_INT);
    $stmt->execute();  */

    /* 追加 */
    $obj new AppController();
    $obj->update_sortable($sql$_POST['left'], $_POST['top'], $_POST['id']);

  } catch (PDOException $e) {
    echo $e->getMessage();
  }
}
▼解説
require_once('../Controller/Connect.php');
require_once('../Controller/AppController.php');
DB接続用のクラスファイルConnect.phpと、更新機能のAppController.phpを両方読み込みます。
$stmt $dbh->prepare($sql);
$stmt->bindValue(':LEFT',   (int)$_POST['left'], PDO::PARAM_INT);
$stmt->bindValue(':TOP',    (int)$_POST['top'],  PDO::PARAM_INT);
$stmt->bindValue(':NUMBER', (int)$_POST['id'],   PDO::PARAM_INT);
$stmt->execute();
これらはAppController.phpで同様の機能を作ったので削除します。
$obj new AppController();
new演算子を使って、new AppController(); でAppControllerクラスをインスタンス化(実体化)し、$obj変数に代入します。
インスタンス化された変数には、クラスがコピーされ同じ機能の内容が入っていると考えてください。
$obj->update_sortable($sql$_POST['left'], $_POST['top'], $_POST['id']);
クラスと同じ内容が入っている$objから、メソッドupdate_sortableを使いたいので、 アロー演算子->を使って呼び出します。
メソッド側で引数を4つ指定したので、ここでも4つのデータを引数に渡しましょう。

③insert.phpをクラス化に対応させます

<?php
/* 追加 */
/* require_once('../config/config.php');  */
/* 追加 */
require_once('../Controller/Connect.php');
require_once('../Controller/AppController.php');

/* POST受信した「名前」「性別」「地域」連想配列をDBに登録 ① */
if(!empty($_POST['inputName'])){
  try{
    $sql '
            INSERT INTO sortable(
              name,
              gender_id
            )
            VALUES(
              :ONAMAE,
              :GENDER
            )
            ';
    /* 削除する
    $stmt = $dbh->prepare($sql);
    $stmt->bindValue(':ONAMAE', $_POST['inputName'], PDO::PARAM_STR);
    $stmt->bindValue(':GENDER',    $_POST['inputGender'],  PDO::PARAM_INT);
    $stmt->execute();  */

    /*追加*/
    $obj new AppController();
    $obj->insert_sortable($sql$_POST['inputName'], $_POST['inputGender']);

    header('location:'.$_SERVER["HTTP_REFERER"]);
  } catch (PDOException $e) {
    echo $e->getMessage();
  }
}
▼解説
require_once('../Controller/Connect.php');
require_once('../Controller/AppController.php');
DB接続用のConnect.phpと、更新機能のAppController.phpを両方読み込みます。
$stmt $dbh->prepare($sql);
$stmt->bindValue(':ONAMAE'$_POST['inputName'], PDO::PARAM_STR);
$stmt->bindValue(':GENDER'$_POST['inputGender'],  PDO::PARAM_INT);
$stmt->execute();
これらはAppController.phpで機能を作ったので削除します。
$obj new AppController();
$obj->insert_sortable($sql$_POST['inputName'], $_POST['inputGender']);
AppControllerクラスからインスタンスを作成し、$objに代入します。
insert_sortable()メソッドを呼び出し、決められた引数を渡しました。

④index.phpをクラス化に対応させます

  <?php
  /* 削除 */
  /* require_once('./config/config.php'); */

  /* 追加 */
  require_once('./Controller/Connect.php');
  $select new SelectData();
  ?>
  <!DOCTYPE html>
  <html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>Sortable_Comp</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
    <script src="js/sort.js"></script>
    <link href="css/style.css" rel="stylesheet">
  </head>
  <body>
  <div id="wrapper">
  <div id="input_form">
    <form action="./function/insert.php" method="POST">
      <input type="text" name="inputName" placeholder="新メンバー名を入力">
      <?php
        $sql 'SELECT * FROM genders';

        /* 削除
        $stmt   = $dbh->query($sql);
        $result = $stmt->fetchAll(PDO::FETCH_ASSOC);  */

        /* 追加 */
        $result $select->select($sql);

        foreach ($result as $val) {
          $checked = ($val['id'] == 1) ? ' checked="checked"' : '';
          echo '  <input type="radio" name="inputGender" value="'.$val['id'].'"' . $checked '>'.$val['gender'].PHP_EOL;
        }
      ?>
      <input type="submit" value="登録">
    </form>
  </div>
  <div id="drag-area">
  <?php
  $sql '
          SELECT
            t1.*,
            genders.gender
          FROM
            sortable AS t1
          LEFT JOIN `genders` ON t1.gender_id = genders.id
        ';

  /*$result = $dbh->query($sql);  //削除 */
  /* 追加 */
  $result $select->select($sql);

  foreach ($result as $val){
    echo '  <div class="drag gender'.$val['gender_id'].'" data-num="'.$val['id'].'" style="left:'.$val['left_x'].'px; top:'.$val['top_y'].'px;">'.PHP_EOL;
    echo '    <p><span class="name">'.$val['id'].' '.$val['name'].' ('.$val['gender'].')</span></p>'.PHP_EOL;
    echo '  </div>'.PHP_EOL;
  }
  ?>
  </div>
  </div>
  </body>
  </html>
▼解説
require_once('./Controller/Connect.php');
$select new SelectData();
DB接続用のConnect.phpを読み込んで、SelectData()クラスからインスタンスを作成し、変数に代入しておきます。
性別のラジオボタン用のデータを取得します
/* 削除 */
$stmt   $dbh->query($sql);
$result $stmt->fetchAll(PDO::FETCH_ASSOC);

/* 追加 */
$result $select->select($sql);
$selectからselect()メソッドを利用し、$resultに代入します。
drag-area内のデータを取得します
/* 削除 */
$result $dbh->query($sql);

/* 追加 */
$result $select->select($sql);
$selectからselect()メソッドを利用し、$resultに代入します。

これでクラス化は完了です。config.phpはconfigフォルダごと削除してください。