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

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

応用2で作った「sortableアプリ」は1枚のphpファイルでいろんな動作を処理していましたが、 本来は機能ごとにファイルを分割することで、保守性だったりエラー時の条件切り分けに役立てることができます。

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

主なファイルの編集箇所は、ファイルの場所が変わったので、パス(ファイルの場所の指定)を変更がメインとなります。

config.php


<?php
/* データベース設定 */
define('DB_DNS', 'mysql:host=localhost; dbname=cri_sortable_gender; 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->bindParam(':ONAMAE', $_POST['inputName'], PDO::PARAM_STR);
    $stmt->bindParam(':GENDER',    $_POST['inputSex'],  PDO::PARAM_INT);
    $stmt->execute();
    /* ↓index.phpへのパスを指定し、処理が終わったらindex.phpに戻る */
    header('location: http://localhost:8001/marge/index.php');
  } 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();
    /* ↓index.phpへのパスを指定し、処理が終わったらindex.phpに戻る */
    header('location: http://localhost:8001/marge/index.php');
  } 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){
      var myNum  = $(this).data('num');
      var myLeft = (ui.offset.left - $('#drag-area').offset().left);
      var 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');  /* ←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="新メンバー名を入力">
    <?php
      $sql    = 'SELECT * FROM GENDER';
      $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="inputSex" value="'.$val['id'].'"' . $checked . '>'.$val['gender'].PHP_EOL;
      }
    ?>
    <input type="submit" value="登録">
  </form>
</div>
<div id="drag-area">
<?php
$sql = '
  SELECT
   t1.*
  FROM
    sortable AS t1
  LEFT JOIN `genders` ON t1.gender_id = gender.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'].'</span></p>'.PHP_EOL;
  echo '  </div>'.PHP_EOL;
}
?>
</div>
</div>
</body>
</html>
DB接続用のファイルを読み込ませます。
sort.jsを読み込みます。
新規登録時にformタグのaction属性にはデータの飛ばし先として、insert.phpへのパスを指定します。

index.phpにアクセスしてこのような表示になればOKです

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

PHPのクラスを理解するための予備知識

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

ほかにも今までの書き方と異なることもありますが、読み解けるようがんばりましょう。
さきほど作ってもらったphpですが早速Class化します。
最小限の情報でまずはClassをさわってみましょう。

このようなファイル構成になります。sortable2フォルダを複製してsortable3を作ります
クラス内では変数と関数の名称が変わります。
変数プロパティ または メンバー変数
関数メソッド または メンバーメソッド

①config.phpをClassで作ってみよう


<?php
  class Connect
  {
    /* 定数の宣言 */
    const DB_NAME ='cri_sortable_gender';
    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文のときに使用する 関数(メソッド) */
    public $sql; /* プロパティ */

    public function select($sql){
      $items = $this->pdo()->query($sql)->fetchAll(PDO::FETCH_ASSOC);
      return $items;
    }
  }
上記の内容をコピーして Connect.php という名前で保存しましょう。
クラスを書いているファイルの名前は、クラス名をつけます。
その際にControllerというフォルダを作ってその中に入れておきましょう。
▼解説

例
<?php
class クラス名{
  const 定数名 = '値';
  public $プロパティ;

  public function メソッド(){
    計算や式などの機能;
  }
}

クラスの名前をつける


class クラス名
{
 機能を記述
}

class Connect
{
classをつけてクラス名を設定します。必ず初めの文字は大文字で設定しましょう

定数を作る


const 定数名 ='値';

const DB_NAME ='cri_sortable_gender';
const HOST    ='localhost';
const UTF     ='utf8';
const USER    ='root';
const PASS    ='root';
constをつけて定数名に定数値を代入します。

DBへ接続する機能「pdo」を作る


public function メソッド名(){
  処理
}

public function pdo(){

}
アクセス修飾子しゅうしょくし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, オプション);
config.phpでもnew演算子でPDOクラスを使っていましたね。同じ手法で接続します。

DBへ接続したのち、データを取得する機能「select()」


public function select($sql){
select()というメソッドを作ります。引数として $sql を設定します。

$items = $this->pdo()->query($sql)->fetchAll(PDO::FETCH_ASSOC);
  ↓
  $this->pdo();
  $this->query($sql);
  $this->fetchAll(PDO::FETCH_ASSOC);
  をひとまとめにしました
pdo()メソッドは先ほど作成したDBに接続させるメソッドです。
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->bindParam(':LEFT',   $left, PDO::PARAM_INT);
      $stmt->bindParam(':TOP',    $top,  PDO::PARAM_INT);
      $stmt->bindParam(':NUMBER', $id,   PDO::PARAM_INT);
      $stmt->execute();
      return $stmt;
    }

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

▼解説

class 新しく作る子クラス名 extends 親クラス名
{

class AppController extends Connect
{
AppControllerという名前のクラスを作りますが、Connectクラスで作った pdo()メソッドを使いたいのでextends Connect とすることでConnectクラスを継承させます。

座標をアップデートさせる機能


public function update_sortable($sql, $left, $top, $id){
update_sortable()メソッドを作成します。引数は$sql, $left, $top, $idの4つです。

新規登録させる機能


public function insert_sortable($sql, $name, $gender){
insert_sortable()メソッドを作成します。引数は$sql, $name, $genderの3つです。

クラスを使う手順

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

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

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


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

    function koe() {
      $hello = $this->name.' さん '.$this->aisatsu;
      echo $hello;
    }
  }

$taro = new Sample();
$taro->name    = '下北太郎';
$taro->aisatsu = 'おはよう';
$taro->koe();
echo '
'; $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->bindParam(':ONAMAE', $_POST['inputName'], PDO::PARAM_STR);
    $stmt->bindParam(':GENDER',    $_POST['inputSex'],  PDO::PARAM_INT);
    $stmt->execute();  */

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

    header('location: http://localhost:8001/sortable3/index.php');
  } 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->bindParam(':ONAMAE', $_POST['inputName'], PDO::PARAM_STR);
$stmt->bindParam(':GENDER', $_POST['inputSex'],  PDO::PARAM_INT);
$stmt->execute();
これらはAppController.phpで機能を作ったので削除します。

$obj = new AppController();
$obj->insert_sortable($sql, $_POST['inputName'], $_POST['inputSex']);
AppControllerクラスからインスタンスを作成し、$objに代入します。
insert_sortable()メンバーメソッドを呼び出し、決められた引数を渡しました。

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


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

  require_once('./Controller/Connect.php');  /* 追加 */
  $obj = new Connect();  /* 追加 */
  ?>
  <!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 = $obj->select($sql);

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

  /*$result = $dbh->query($sql);  //削除 */
  /* 追加 */
  $result = $obj->select($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'].'</span></p>'.PHP_EOL;
    echo '  </div>'.PHP_EOL;
  }
  ?>
  </div>
  </div>
  </body>
  </html>
▼解説

require_once('./Controller/Connect.php');
$obj = new Connect();
DB接続用のConnect.phpを読み込んで、早速Connect()クラスからインスタンスを作成し、$objに代入しておきます。
性別のラジオボタン用のデータを取得します

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

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

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

/* 追加 */
$result = $obj->select($sql);
$objからselect()メンバメソッドを利用し、$resultに代入します。
これでClass化は完了です。config.phpはconfigフォルダごと削除してください。
powerd by