あーる学習帳

自分が勉強したことや気になることなど、99%自分用です。コードを書いてるのでPCから閲覧を推奨。

「PHPしっかり入門教室」を終えて

先ほど、今まで取り組んでいた「PHPしっかり入門教室」におけるPHPの学習を一通り終えることができました。

www.shoeisha.co.jp

r-de-aru.hateblo.jp

独学状態でこの本に3週間強取り組んでいたので、やり切った達成感がかなりあります。やってよかった。

現在の仕事で使うことはないのですが、今後もPHPの学習を続けてサービスを1つ作ってみたいと思っているところです。イカトドンでそういう話題を振ってみたところいろんなアイディアをいただき、その中でも「Twitterにアップされている画像や動画を検索する」というサービスを作ってみたいと考えています。

 

PHPを学習している間に困ったのは、仕事のVB6みたいに実行しながらコンパイルすることがどうしてもできない点です。サーバーに出されたエラーから推測してああでもないこうでもない…とちまちまデバッグしていくのはどうしても時間がかかるので、仕事で慣れている「エラー箇所でプログラムが止まって、その部分にカーソルがある状態でソースを確認・修正できる」という機能のありがたみがわかりました。

 

明日からは上記のサービスをゆっくり作っていこうと思いますが、これまでのようにコードをまるごと載せるようなことができるかどうかはわかりません。今までもだいぶグレーだったと思ってますし…。例として使えそうな箇所を抜き出して載せたりはするかも。

とりあえずは明日から以下の2つを進めていこうと思っております。

  • 仕様を作成し、サービスの基礎となるHTMLやPHPの作成
  • TwitterAPIなどの勉強

とりあえず今夜は達成感に浸ります!

PHPまとめ⑤:会員専用ページを作る、会員登録システムの完成

これで最後なので!!勢いよく!!やります!!

 

今回は以下のファイルを作成・編集する。

f:id:R_de_aru:20180709204633p:plain

member.php

  • ログイン済みか確認し、会員データを取得する 

logout.php

  • ログアウト後はログイン画面にリダイレクトさせる 

db_helper.php

  • 登録されている全会員データを取得する関数を作る 

member_view.php

  • 自身のデータと会員一覧を表示する 

まずはコントローラであるmember.phpを作成する。会員登録済みの会員名も表示させる。ひとまず、ログインしているかどうかを確認する機能を作成する。

<?php
 require_once('config.php');
 require_once('./helper/db_helper.php');
 require_once('./helper/extra\helper.php');

 session_start();

 //ログイン中かどうか確認
 if(empty($_SESSION['member'])){
  header('Location:'.SITE_URL.'/login.php');
  exit();
 }

 //クライアントの会員データを取得
 $member=$_SESSION['member'];
 $dbh=get_db_connect();
 $members=array();
 //全会員データを取得する

 include_once('./views/member_view.php');

ログイン状態の確認はempty()で行い、ログイン中ならばクライアントの配列データを$memberに取得する。

全会員データの取得は関数で行う。以下のコードをdb_helper.phpに追加。

function select_members($dbh){
 $sql="select name from members";
 $stmt=$dbh->prepare($sql);
 $stmt->execute();
 while($row=$stmt->fetch(PDO::FETCH_ASSOC)){
  $data[]=$row;//二次元配列として格納
 }

全会員データを取得するSQLを作成し、複数の会員データは二次元配列として取得できるようにしておく。これをコントローラに加える。

//全会員データを取得する
$members=select_members($dbh); 

 全会員データを関数を使って$membersに入れておく。

会員専用ページのビューファイルとなるmember_view.phpを作成する。

<!DOCTYPE html>
<html>
 <head>
  <meta charset="UTF-8">
  <title>会員専用ページ</title>
 </head>
 <body>
  <p>
   こんにちは、<?php echo html_escape($member['name']); ?>さん。
   Email:<?php echo html_escape($member['email']); ?>
   <a href="logout.php">ログアウト</a>
  </p>
  <h1>会員専用ページ</h1>
  <hr width="300px" align="left">
  <p style="font-size:small">こちらはログイン後の画面です</p>
  <h2>会員一覧</h2>
  <ul>
  <?php foreach($members as $member): ?>
   <li><?php echo stml_escape($member['name']); ?></li>
  <?php endforeach; ?>
 </body>
</html> 

 クライアント個人のデータと会員一覧が表示されるページになった。

ログアウトの仕組みを作成し、ログアウト後はlogin.phpにリダイレクトする。

<?php
 require_once('config.php');
 session_start();

 $session_name=session_name();
 $_SESSION=array();
 if(isset($_COKKIE[$session_name])===true){
  setcookie($session_name,'',time()-3600);
 }
 session_destroy();
 header('Location:'.SITE_URL.'login.php');

これでこの章で扱うすべてのファイルが完成。あとは実際にテストを行い、動作の確認を行う。

 

案の定ものすごい数のエラーが…。

うまくMySQLにログインできていないだとか、シンタックスエラーだとか、シンタックスエラーだとか、returnを忘れているとか、コロンとセミコロンを間違えてるとか…。

そんなとき、以下のサイトでよさそうなコードを発見。ファイルの先頭に以下のコードを加えておくとすべてのエラーが見つけられるとのこと。

teratail.com

error_reporting(E_ALL);

ini_set('dusplay_errors'.'1'); 

 

デバッグを重ね、なんとか形になりました。

f:id:R_de_aru:20180709222308p:plain

f:id:R_de_aru:20180709222319p:plain

f:id:R_de_aru:20180709222331p:plain

長かった…!!この本で作るPHPの作品はこれですべてになります。

お疲れ様でした!!

PHPまとめ④:ログインの仕組みを作る

今回は以下のファイルを作成する。

f:id:R_de_aru:20180709194511p:plain

login.php

  • メールアドレスとパスワードを確認し、ログインする仕組みを作る 

login_view.php

  • 会員登録後に飛んでくる画面であり、ログインボタンを備える 

db_helper.php

  • 入力されたメールアドレスとパスワードがデータベースのものと一致するかを調べる関数を作る

まずはコントローラであるlogin.phpを作成する。実はsignup.phpを一部流用できる。

<?php
 //必要ファイル読み込み
 require_once('config.php');
 require_once('./helpers/db_helper.php');
 require_once('./helpers/extra_helper.php');

 session_start();

 //既にログイン済みだった場合、member.phpへリダイレクト

 if($_SERVER['REQUEST_METHOD']==='POST'){
  //$name=get_post('name');
  $email=get_post('email');
  $password=get_post('password');
  $dbh=get_db_connection(); //データベース接続
  $errs=array();

  //バリデーション
  // if(!check_words($name,50)){
   // $errs['name']='お名前欄は必ず50文字以内で入力してください(必須項目)。';
  // }
  if(!filter_var($email,FILTER_VARIDATE_EMAIL)){
   $errs['email']='メールアドレスの形式が正しくありません。';
  }elseif(email_exists($dbh,$email)){
   $errs['email']='このメールアドレスは既に登録されています。';
  }elseif(!check_words($email,100)){
   $errs['email']='メールアドレスは100文字以内で入力してください(必須項目)。';
  }
  if(!check_words($password,50)){
   $errs['password']='パスワードは50文字以内で入力してください(必須項目)。';
  }

  //エラーがなければデータ挿入
  // if(empty($errs)){
   // if(insert_member_data($dbh,$name,$email,$password)){
    // header('Location:'.SITE_URL.'login.php');
    // exit();
   // }
   // $errs['password']='登録に失敗しました。';
  // }

  //メールアドレスとパスワードが一致するか検証

  //ログインする
}

include_once('./views/login_view.php'); //ビューファイル読み込み

コメントアウトしてある箇所はsignup.phpにはあったが今回は使わない箇所(次からは消しておく)。これから制作していく箇所にコメントを入れている。

 ログイン用のビューファイルも作成しておく。

<!DOCTYPE html>
 <html>
  <head>
   <meta charset="UTF-8">
   <title>ログイン画面</title>
  </head>
  <body>
   <h1>ログイン</h1>
   <form action="" method="POST">
    <p>メールアドレス:<input type="text" name="email"><?php echo html_escape($errs['email']); ?></p>
    <p>パスワード:<input type="password" name="password"><?php echo html_escape($errs['password']); ?></p>
    <p><input type="submit" value="ログイン"></p>
    <p><a href="index.php">新規登録</a>
   </form>
  </body>
 </html> 

 メールアドレスとパスワードだけでログインするシンプルな画面になる。

次に、db_helper.phpに以下の関数を追加する。メールアドレスとパスワードが一致した場合、会員データを配列で返す。

function select_member($dbh,$email,$password){
 $sql="select * from members where email=:email limit 1";
 $stmt=$dbh->prepare($sql);
 $stmt->bindvalue(':email',$email,PDO::PARAM_STR);
 $stmt->execute();
 if($stmt->rowCount()>0){
  $data=$stmt->fetch(PDO::FETCH_ASSOC):
  if(password_verify($password,$data['password'])){
   return $data; //会員データを返す
  }else{
   return false;
  }
 }else{
  return false;
 }

 この関数では、まずメールアドレスの一致する会員データを引き出すSQLを作る。同じメールアドレスは存在しないようになっているが、「LIMIT 1」で1件だけ取得することを明記する。password_verify()を使って入力されたパスワードとデータベースに登録されたパスワードが一致するかを調べる。第1引数は入力値、第2引数はすでにハッシュ化されてデータベースに登録されていたパスワードとなる。一致したらtrueが返ってくる。

この関数をlogin.phpのバリデーションに加える。

//バリデーション
if(!filter_var($email,FILTER_VARIDATE_EMAIL)){
 $errs['email']='メールアドレスの形式が正しくありません。';
}elseif(email_exists($dbh,$email)){
 $errs['email']='このメールアドレスは既に登録されています。';
}elseif(!check_words($email,100)){
 $errs['email']='メールアドレスは100文字以内で入力してください(必須項目)。';
}
if(!check_words($password,50)){
 $errs['password']='パスワードは50文字以内で入力してください(必須項目)。';
}elseif(!$member=select_member($dbh,$email,$password)){
 //メールアドレスとパスワードが一致するか検証
 $errs['password']='メールアドレスとパスワードが正しくありません。'

 これでパスワードの一致を確認できるようになった。

ログインに関わる仕組みを作る。バリデーション後のログインと、ログインしていた場合のリダイレクトである。login.phpに以下のコードを追加。

 <?php
 //必要ファイル読み込み
 require_once('config.php');
 require_once('./helpers/db_helper.php');
 require_once('./helpers/extra_helper.php');

 session_start();
 //既にログイン済みだった場合、member.phpへリダイレクト
 if(!empty($_SESSION['member'])){
  header('Location:'.SITE_URL.'/member.php');
  exit();
 }

 //バリデーション

 //中略

 //ログインする
 if(empty($errs)){
  session_regenerate_id(true);//セッションIDの変更
  $_SESSION['member']=$member;//ログイン
  header('Location:'.SITE_URL.'/member.php');//会員ページへリダイレクト
 }
}

include_once('./views/login_view.php'); //ビューファイル読み込み

バリデーション側について、session_regenerate_id(true)でセッションIDの切り替えを行う。セッションがスタートしてからログインするまでの間にセッションIDが盗まれていた場合を想定し、セッションIDはログインの直前に変更しなければならない。ログインの仕組みそのものは$_SESSION['member']=$memberだけで終わる。

これ以降、$_SESSION['member']が存在するかどうかを確かめることでログイン状態を確認するようになる。ログインが終わったら会員ページへリダイレクトする。ログインが済んだ後にログインページに行くのもおかしな挙動となるので、session_start()直後にログイン済みのクライアントは会員ページへリダイレクトさせる。empty()を使って$_SESSION['member']が空であるかどうかを調べる。

以上でログインの仕組みは完成となる。

PHPまとめ③:会員登録の仕組みを作る

第13章-03

今回編集するのは以下の3ファイル。

f:id:R_de_aru:20180708181807p:plain

singup.php

  • POSTデータを取得し、バリデーション、登録までを行う 

signup_view.php

  •  会員登録画面。入力値のエラーも出力する

db_helper.php

メールアドレスのバリデーションやデータ挿入機能を作る

 「メールアドレスは重複させない」「パスワードは暗号化する」の2つを必ず守ること。

 

まずはコントローラであるsignup.phpを作成する。

// signup.php

<?php
 //必要ファイル読み込み
 require_once('config.php');
 require_once('./helper/db_helper.php');
 require_once('./helper/extra_helper.php');

 if($_SERVER['REQUEST_METHOD']==='POST'){
  $name=get_post('name');
  $email=get_post('email');
  $password=get_post('password');

  $dbh=get_db_connection(); //データベース接続
  $errs=array();

  //入力値バリデーション
  //エラーがなければデータ挿入
 }

 include_once('./views/signup_view.php'); //ビューファイル読み込み

必要ファイルを先に読み込むことで、それ以降定数や関数が使用できるようになる。

 

次に入力フォームを作る。デザインは極力排除する。

//signup_view.php

<html>
 <head>
  <meta charset="UTF-8">
  <title>新規ユーザー登録</title>
 </head>
 <body>
  <h1>新規ユーザー登録</h1>
  <form action="signup.php" method="POST">
   <p>お名前:<input type="text" name="name"><?php echo $errs['name']; ?></p>
   <p>メールアドレス:<input type="text" name="email"><?php echo $errs['email']; ?></p>
   <p>パスワード:<input type="password" name="password"><?php echo $errs['password']; ?></p>
   <input type="submit" value="登録する">
  </form>
 </body>
</html>

 

メールアドレスの重複を調べる関数をextra_helper.phpに作成する。

まず、入力するメールアドレスが何件存在しているかを調べる。取得する値は0か1なので、カウント後の値が0より大きければtrueを返すようにする。

//db_helper.php

<?php

 //中略

  function email_exists($dbh,$email){
  $sql="select count(id) from members where email=:email";
  $stmt=$dbh->prepare($sql);
  $stmt->bindvalue(':email',$email,PDO::PARAM_STR);
  $stmt->execute();
  $count=$stmt->fetch(PDO::FETCH_ASSOC); //結果を配列で取得
  if($count['count(id)']>0){ //件数を取得
   return true;
  }else{
   return false;
  }
 }

count(id)を取得する。PDOでは$stmt->rowCount()というコードを使うことで行数を取得できるが、これはすべてのデータベースで動作が保証されない。

次に、入力データを挿入するための関数を作る。データベースへの挿入前にパスワードは暗号化しなければならない。

 //db_helper.php

<?php

//中略

function insert_member_data($dbh,$name,$email,$password){
 $password=password_hash($password,PASSWORD_DRFAULT); //パスワード暗号化
 $data=data('Y-m-d H:i:s');
 $sql="insert into members (name,email,password,created) values (:name,;email,:password,'{$data}')";
 $stmt->bindvalue(':name',$name,PDO::PARAM_STR);
 $stmt->bindvalue(':email',$email,PDO::PARAM_STR);
 $stmt->bindvalue(':password',$password,PDO::PARAM_STR);
 if($stmt->execute()){
  return true;
 }else{
  return false;
 }
}

 password_hash()はPHPで用意された暗号化用の組み込み関数である。password_verify()を使えば、暗号化後のパスワードと入力したパスワードが一致するか確認できる。

入力値を検証するコードをsignup.phpに加える。

//signup.php

<?php

 //中略

 //入力値バリデーション
 if(!check_words($name,50)){
  $errs['name']='お名前欄は必ず50文字以内で入力してください(必須項目)。';
 }
 if(!filter_var($email,FILTER_VARIDATE_EMAIL)){
  $errs['email']='メールアドレスの形式が正しくありません。';
 }elseif(email_exists($dbh,$email)){
  $errs['email']='このメールアドレスは既に登録されています。';
 }elseif(!check_words($email,100)){
  $errs['email']='メールアドレスは100文字以内で入力してください(必須項目)。'
 }
 if(!check_words($password,50)){
  $errs['password']='パスワードは50文字以内で入力してください(必須項目)。'
 } 

 エラー文はinputごとに別々に表示する。$errsにキー名を設定し、エラーがあった場合に代入する。filter_var($email,FILTER_VARIDATE_EMAIL)は組み込み関数で、メールアドレスの形式に一致しなかった場合にfalseが返ってくる。

最後に、データ挿入コードを追加する。

//signup.php

<?php

 //中略

 //エラーがなければデータ挿入
 if(empty($errs)){
  if(insert_member_data($dbh,$name,$email,$password)){ //データ挿入
   header('Location:'.SITE_URL.'login.php'); //ログイン画面へ移動
   exit();
  }
  $errs['password']='登録に失敗しました。';
 }

自作した関数を使ってデータを挿入する。このまま同じ画面には留まらせず、ログイン画面に遷移するようにする。

signup.phpをindex.phpへ名前変更してテスト。というか、必ずindex.phpを開こうとするけどmain.phpとかsignup.phpとかに変更できないんだろうか…。

PHPまとめ②:設定ファイルや関数ファイルを用意する

第13章-02

設定ファイルを作っていく。ここで設定した値はすべて他のファイルに反映される。これにより、後から修正ファイルが必要になった際は効率よく変更することが可能となる。今回は以下のような値を設定する。

//config.php

<?php
define('DSN','mysql:dbname=user;host=localhost;charset=utf8');
define('DB_USER','root');
define('DB_PASSWORD','');
define('SITE_URL','http://192.168.33.11:8000');

//E_NOTICE以外のエラーをすべて出力

error_reporting(R_ALl & ~E_NOTICE);

//セッションの設定
session_set_cookie_param(1440,'/'); 

変数にはスコープといって、参照できる範囲に制限があった。これでは後に変更する可能性がある値が複数のファイルに書かれていた場合、修正が非常に難しくなる。define()を使って定数として定義しておくことにより、すべてのファイルで同じ値を参照できるようになる。例えば一度定義した「DSN」は文字列として扱うのではなく、echo DSN;という形でシングルクォーテーションなしで使用する。

error_reporting()では、エラーの出力をコントロールできる。開発時はすべてのエラーを出力し、公開時にはerror_reporting(0)としてエラーを出力しないといった設定が可能。

session_set_cookie_param()は第1引数がセッションの有効期限、第2引数が有効範囲となる。第2引数を「/」としておけば全範囲でセッションが有効になる。

 

次にhelperファイルを作成する。データベース関連はdb_helper.phpに、それ以外はextra_helper.phpに書いておく。

//db_helper.php

<?php
 function get_db_connect(){
  try{
   $dsn=DSN;
   $user=DB_USER;
   $password=DB_PASSWORD;
   $dbh=new PDO($dsn,$user,$password);
  } catch(PDOException $e){
   echo ($e->getMessage());
   die();
  }
 $dbh->setAttribute(PDO::ATTR_ERRMODE,ERRMODE_EXCEPTION);
 return $dbh;

 // extra_helper.php

<?php
 function html_escape($word){
  return htmlspecialchars($word,ENT_QUOTE,'UTF-8');
 }

 function get_post($key){
  if(isset($_POST['key'])){
   $var=trim($_POST['key']);
   return $var;
  }
 }

 function check_word($word,$length){
  if(mb_strlen($word)===0){
   return false;
  }elseif(mb_strlen($word)>$length){
   return false;
  }else{
   return true;
  }
 }

 今後必要な関数があれば順次追加していく。

PHPまとめ①:ログイン認証と会員専用ページの構成

今回からは現在使っている教材のまとめとして、ログイン認証と会員専用ページの仕組みを作成する。本では簡易的ではあるが実務に沿って…とあるので、今回もその流れでいきます。

 

第13章-01

要件定義をする。今回はログイン機能だけを作るので以下のようになる。

  • emailとパスワードを入力して会員登録できる
  • ログイン後は他の会員の名前を検索できる
  • 同じemailで再度登録することはできない

この他、実際の定義書には「運用要件」というものもあり、「データは毎日バックアップし、1年間分保管する」など実際の運用者用の決まりを定めたものもある。

次に画面をイメージする。図そのものは本の図を使うので割愛。会員登録画面、ログイン画面、ログイン後の会員専用画面を作成する。

テーブルの構造を本に沿って作成する。今回はuserデータベースに「members」というテーブルを作成して管理する。

f:id:R_de_aru:20180708171640p:plain

今回初めてパスワードをMySQLで管理する。パスワードについてはデータベースが攻撃されたことを想定し、そのままの形でデータを挿入するのではなく変化させるので長さを255にしておく必要がある。

簡単なログイン認証の仕組みと流れは以下のようになる。

  1. 会員登録時はすでにメールアドレスが登録されていないか調べる必要がある。データ挿入前にデータベースからデータを引き出して調べるバリデーションが必要となる。
  2. セキュリティ面では、登録時からパスワードの扱いには注意を払う必要がある。入力されたパスワードを暗号化してからデータ挿入する。
  3. ログイン画面からはメールアドレスとパスワードの入力後、メールアドレスが登録されているか、パスワードは一致しているかを確認する。
  4. メールアドレスとパスワードが一致したら、セッションに本人のデータを格納する。
  5. セッションが存在する場合、ログイン中であると判断して会員専用のアクセスを許可する。ログインしていない場合はheader()でログインページに飛ばす。

最後に、ファイル構成を確認する。

f:id:R_de_aru:20180708173958p:plain

「13」、「views」はフォルダ、他の拡張子phpのファイルを作っていく。

config.phpにはWebサービスに関わる設定のみを書く。こうすることで、データベースやドメインを変更したときに1か所だけ書き直せばすべてのファイルに反映するようになる。helperというのは関数ファイルのこと。実際の制作では関数を1つのfunctions.phpにまとめるということはせず、役割ごとに細分化していく。今回はデータベース関連をdb_helper.phpに、それいがいをextra_helper.phpにまとめていく。

HTML関連はviewファイルとして管理する。ログアウトしたときは強制的にログインページへ飛ぶので、必要なのは会員登録ページ、ログインページ、メンバー専用ページの3つになる。処理の流れを書き込むファイルをコントローラと呼んだが、コントローラとビューの対応が明確になっていたほうがわかりやすい。コントローラであるmember.phpのビューファイルはmember_view.phpにするなど、名前をそろえておくとよい。

PHP実習:ショッピングカートを作ろう

この本も終わりが見えてきました…!!

 

第12章-03 実習

今回は通販サイトで使われるようなショッピングカートを作る。

制作の流れは以下の通り。

  1. 商品情報を表示するphpファイルを作る
  2. カートに入れたデータはセッションのキーを商品IDに設定し、個数を代入する
  3. カートの中身を見るためのphpファイル、カートの中身を一括削除するためのphpファイルをそれぞれ用意する

要件定義は以下の通り。

  • カートに追加した商品には「追加ボタン」の代わりに「追加済み」と表示する
  • カートページには「変更」と「削除」の2種類のボタンを用意し、それぞれのボタンに対する処理を分岐させる
  • カートで表示する商品はIDではなく商品名にする

では、商品一覧画面を作っていく。

<?php
 session_start();
 //POSTデータをカート用のセッションに保存
?>
<html>
 <body>
  <h1>商品一覧</h1>
  <a href="cart.php">カートを見る</a>
  <table style="text-align=center">
   <tr><th>商品</th><th>数量</th><th>ボタン<th></tr>
   <form action="" method="POST">
   <tr>
    <td>業務用デスク</td>
    <td>
     <select name="num">
      <?php for($i=1;$i<10;$i++): ?>
       <option value="<?php echo $i; ?>"><?php echo $i; ?></option>
      <?php endfor; ?>
     </select>
    </td>
    <td>
     <input type="hidden" name="product" value="desk_01">
     <?php if(isset($cart['desk_01'])===true): ?>
      <p>追加済み</p>
     <?php else: ?>
      <input type="submit" value="カートに入れる">
     <?php endif; ?>
    </td>
   </tr>
  </form>

  <form action="" method="POST">
   <tr>
    <td>快適いす</td>
    <td>
     <select name="num">
      <?php for($i=1;$i<10;$i++): ?>
       <option value="<?php echo $i; ?>"><?php echo $i; ?></option>
      <?php endfor; ?>
     </select>
    </td>
    <td>
     <input type="hidden" name="product" value="chair_07">
     <?php if(isset($cart['chair_07'])===true): ?>
      <p>追加済み</p>
     <?php else: ?>
      <input type="submit" value="カートに入れる">
     <?php endif; ?>
    </td>
   </tr>
  </form>

  </table>
  </body>
</html>

 「カートに入れる」ボタンが2つ出てくるが、飛び先は同じページとする。一度決定した個数を変更するのはcart.phpで行うものとする。商品のセレクトボックスをforを使って組み立てる。optionのvalueは実際に渡される値、<option></option>の間は表示される値である。今回はどちらも$iを出力する。$cart['desk_01']は商品をカートに入れた場合のみ存在するキーなので、今回はこのキーをisset()でチェックして商品を追加済みかどうか判定していく。

次に、「カートに入れる」を押した後の商品追加の機能を作成する。

 <?php
 session_start();
 //POSTデータをカート用のセッションに保存
 if($_SERVER['REQUEST_METHOD']==='POST'){
  $product=$_POST['product'];
  $num=$_POST['num'];
  $_SESSION['cart'][$product]=$num; //セッションにデータを格納
 }
 $cart=array();
 if(isset($_SESSION['cart'])){
  $cart=$_SESSION['cart'];
 }
 var_dump($cart);
?>

 POSTデータとして$productと$numの2つが渡されているので取得しておく。$_SESSION['cart'][$product]=$numのタイミングで$_SESSION['cart']['desk_01']などのキーが新たに出来上がり、個数が値として格納される。実はこの1行こそ、カートシステムの根幹ともいえる部分である。セッションに値を格納した後で$cartにセッションの配列データを入れておく。これで商品一覧画面は完成。

次に、カートの中身の一覧画面を作成する。

<?php
 session_start();
 $cart=array();
 if(isset($_SESION['cart'])){
  $cart=$_SESSION['cart'];
  }
 var_dump($_SESSION);
?>
<html>
 <body>
  <h1>ショッピングカート</h1>
  <p><a href="index.php">商品一覧へ</a><p>
  <p><a href="delete.php">カートをすべて空に</a></p>
  <table style="text-align=center">
   <tr><th>商品</th><th>個数</th><th>数量</th><th>変更ボタン</th><th>削除ボタン<th><tr>
   <?php foreach($cart as $key=>$var): ?>
    <tr>
     <td>
      <?php
       switch($key){
        case 'desk_01':
         echo '業務用デスク';
         break;
        case 'chair_07':
         echo '快適いす';
         break;
       }
      ?>
     </td>
     <td>
      <?php echo $var; ?>
     </td>
     <form action="" method="POST">
      <td>
       <select name="num">
       <?php for($i=1;$i<10;$i++): ?>
        <option value="<?php echo $i;?>"><?php echo $i; ?></option>
       <?php endfor; ?>
       </select>
      </td>
      <td>
       <input type="hidden" name="kind" value="change">
       <input type="hidden" name="product" value="<?php echo $key;?>">
       <input type="submit" value="変更">
      </td>
     </form>
     <form action="" method="POST">
      <td>
       <input type="hidden" name="kind" value="delete">
       <input type="hidden" name="product" value="<?php echo $key;?>">
       <input type="submit" value="削除">
      </td>
     </form>
    </tr>
   <?php endforeach; ?>
  </table>
 </body>
</html> 

 cart.phpではまずカートデータの取得を行う。セッションデータに$_SESSION['cart']が存在していないこともあるので、取得用の変数$cartを配列として初期化した後、isset($_SESSION['cart'])でキーの存在を確認している。カートデータには商品IDをキー、個数を値とした配列が格納されている。通常商品IDはデータベース挿入用の値として用いられるが、今回はswitch()を使って正式な商品名を表示させている。実際には商品名もデータベースに登録しておいて、そこからデータを引っ張ってくるのが一般的。今回は<form>が複数あるので、hiddenを使って処理の内容(変更or削除)と商品IDを渡す。

hiddenでkindと名付けた値を取得し、その後の処理を分岐させる仕組みを作る。

cart.phpの最初のphp部分を以下のように書き換え。

 <?php
 session_start();
 $cart=array();

 if($_SERVER['REQUEST_METHOD']==='POST'){
  $product=$_POST['product'];
  $kind=$_POST['kind'];
  if($kind==='change'){
   $num=$_POST['num'];
   $_SESSION['cart'][$product]=$num;
  } elseif($kind==='delete'){
   unset($_SESSION['cart'][$product]);
  }
 }

 if(isset($_SESION['cart'])){
  $cart=$_SESSION['cart'];
 }
 var_dump($_SESSION);
?>

$kindには各種ボタンの役割(change or delete)が格納される。まずif文でそれぞれの処理を分岐させる。変更ボタンを押した場合は単純に既存のキーの値を上書きし、削除ボタンではunset()でキーの値そのものを削除する。

最後に、カートの中身を一括削除するdelete.phpを作成する。

<?php
 session_start();
 $session_name=session_name();
 $_SESSION=array();
 if(isset($_COOKIE[$session_name])===true){
  setcookie($session_name,'',time()-3600);
 }
 session_destroy();
 header('Location:cart.php');
 exit; 

header()では、「Location:」の後にURLを指定することで強制的にページをジャンプさせることができる。その後ろのプログラムが実行されないよう、exitをつけて明示的に処理を終了させる。このような処理のことを「リダイレクト」という。

これで完成。

f:id:R_de_aru:20180708164617p:plain

f:id:R_de_aru:20180708164631p:plain

f:id:R_de_aru:20180708164643p:plain

セッションを用いて各ページに商品情報を受け渡ししている。

今回はここまで。次回からはいよいよ本のまとめ。