作成日: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つです。
- /config/config.php
- /function/insert.php
- /function/update.php
- /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();
  }
}
また、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();
  }
}
また、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処理====================================== */
  });
});
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>  
  <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="新メンバー名を入力">
  ・
  ・
  ・
- DB接続用のファイルconfig.phpを読み込ませます。
- ドラッグ機能のsort.jsを読み込みます。
- 新規登録formタグのaction属性にはデータの飛ばし先として、insert.phpへのパスを指定します。
  <?php
  require_once('./config/config.php');
  ?>
  <!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);
        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
  ';
  $stmt = $dbh->query($sql);
  foreach ($stmt as $result){
    echo '  <div class="drag gender'.$result['gender_id'].'" data-num="'.$result['id'].'" style="left:'.$result['left_x'].'px; top:'.$result['top_y'].'px;">'.PHP_EOL;
    echo '    <p><span class="name">'.$result['id'].' '.$result['name'].' ('.$result['gender'].')</span></p>'.PHP_EOL;
    echo '  </div>'.PHP_EOL;
  }
  ?>
  </div>
  </div>
  </body>
  </html>

切り出しただけなので、同じ表示になるはずです。
これで準備は完了です。
#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, $pass, array(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;
  }
}
クラスのファイル名は、クラス名をつけます。
その際にControllerというフォルダを作って、その中に入れておきましょう。Controller/Connect.php
クラス内では変数と関数の名称が変わります。
  
クラスの名前は必ず、初めの文字を大文字で設定します。
アクセス修飾子のpublicを使って、作ったpdoメソッドがどこからでもアクセスできるようにしておきます。
※publicを指定しなくても初期設定がpublicとなりますが、明示しておくことが好ましいです。
  
SelectDataというクラスを作りますが、DB接続の機能であるpdoメソッドつまり、pdo()も使いたいので extendsをつかってDB接続クラスConnectを引き継ぎ(継承し)ます。
query()メソッドで$sql変数に入っているSQL文をDBで実行します
fetchAll()メソッドで連想配列データにして、$items変数に代入します。
| 変数 | → | プロパティ | 
|---|---|---|
| 関数 | メソッド | 
<?php
class クラス名{
  const 定数名 = '値';
  public $プロパティ;
  public function メソッド(){
    計算や式などの機能;
  }
}
クラスの名前は必ず、初めの文字を大文字で設定します。
定数を作る
const 定数名 ='値';
const DB_NAME ='cri_sortable';
const HOST    ='localhost';
const UTF     ='utf8';
const USER    ='root';
const PASS    ='root';
DBへ接続する機能「pdo」を作る
public function メソッド名(){
  機能や処理
}
アクセス修飾子のpublicを使って、作ったpdoメソッドがどこからでもアクセスできるようにしておきます。
※publicを指定しなくても初期設定がpublicとなりますが、明示しておくことが好ましいです。
| public | クラスの内外、どこからでもアクセス可能 | 
|---|---|
| protected | クラス自身と継承クラスからアクセス可能 | 
| private | 同じクラス内だけアクセス可能 | 
/* config.phpの場合 */
$dbh = new PDO(DB_DNS, DB_USER, DB_PASSWORD);
/* Connect.phpの場合 */
$pdo = new PDO($dsn, $user, $pass, オプション);
DBへ接続したのち、データを取得する機能「select()」
接続することとは別の機能になりますので、新しくクラスを作って分けます。SelectDataというクラスを作りますが、DB接続の機能であるpdoメソッドつまり、pdo()も使いたいので extendsをつかってDB接続クラスConnectを引き継ぎ(継承し)ます。
class SelectData extends Connect
{
  private $sql;
  public function select($sql){
    $items = $this->pdo()->query($sql)->fetchAll(PDO::FETCH_ASSOC);
    return $items;
  }
}
$items = $this->pdo()->query($sql)->fetchAll(PDO::FETCH_ASSOC);
  ↓
  $this->pdo();
  $this->query($sql);
  $this->fetchAll(PDO::FETCH_ASSOC);
  をひとまとめに書いたものです。
query()メソッドで$sql変数に入っているSQL文をDBで実行します
fetchAll()メソッドで連想配列データにして、$items変数に代入します。
更新機能と新規登録を別のクラスファイルとして作成します。
<?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という名前で保存しましょう
別のクラスの機能を利用するために、クラスを継承(引き継ぎ)させることができます。
継承させる 親クラス:Connect
Connectクラスで作ったpdo()メソッドを使いたいので、extends ConnectとすることでConnectクラスを継承させます。
class `新しく作る子クラス名` extends `親クラス名`
{
↓
class AppController extends Connect
{
継承させる 親クラス:Connect
Connectクラスで作ったpdo()メソッドを使いたいので、extends ConnectとすることでConnectクラスを継承させます。
座標をアップデートさせる機能
public function update_sortable($sql, $left, $top, $id){
新規登録させる機能
public function insert_sortable($sql, $name, $gender){
クラスを使う手順
先程作ったクラスを使ってプログラムします。 
例
<?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 '
';
/* ② */
$jiro = new Sample();
$jiro->name = '下北二郎';
$jiro->aisatsu = 'おはよう';
$jiro->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');
$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']);
メソッド側で引数を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');
$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']);
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();
性別のラジオボタン用のデータを取得します
/* 削除 */
$stmt   = $dbh->query($sql);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
/* 追加 */
$result = $select->select($sql);
drag-area内のデータを取得します
/* 削除 */
$result = $dbh->query($sql);
/* 追加 */
$result = $select->select($sql);
これでクラス化は完了です。config.phpはconfigフォルダごと削除してください。
 入門
入門 基礎
基礎 応用
応用 実践フロントエンド
実践フロントエンド 実践バックエンド
実践バックエンド デザイン
デザイン キッズ
キッズ