187
183

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AngularJS+PHP+MySQLでCRUD入門

Last updated at Posted at 2014-03-22

AngularJS+PHP+MySQL

AngularJSとPHPを組み合わせたCRUDのサンプルです。なるべくシンプルになるように作ってみました。

スクリーンショット 2014-03-22 19.08.11.png

全ソースコード

https://github.com/naga3/angular-php-basic

以下のデータベースとテーブルを作成して、それ以下の3ファイルを入力してindex.htmlを開くと動きます。

SQL

CREATE DATABASE school;
USE school;
CREATE TABLE students(
  id SERIAL PRIMARY KEY,
  name VARCHAR(255),
  age INT,
  comment TEXT
);

学生テーブルがひとつだけのシンプルなDBです。

HTML

index.html
<!doctype html>
<html lang="ja" ng-app="app">
<head>
  <meta charset="utf-8">
  <title>名簿</title>
</head>
<body ng-controller="MainCtrl">
  <table border="1">
    <tr><th>ID</th><th>名前</th><th>年齢</th><th>コメント</th></tr>
    <tr ng-controller="DetailCtrl" ng-repeat="student in students">
      <td>{{student.id}}</td>
      <td><input ng-model="student.name"></td>
      <td><input ng-model="student.age"></td>
      <td><input ng-model="student.comment"></td>
      <td><button ng-click="update()">更新</button></td>
      <td><button ng-click="delete()">削除</button></td>
    </tr>
    <tr>
      <td>&nbsp;</td>
      <td><input ng-model="new_student.name"></td>
      <td><input ng-model="new_student.age"></td>
      <td><input ng-model="new_student.comment"></td>
      <td><button ng-click="add()">追加</button></td>
      <td>&nbsp;</td>
    </tr>
  </table>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js"></script>
  <script src="controller.js"></script>
</body>
</html>

JavaScript

controller.js
var app = angular.module('app', ['ngResource']);

app.controller('MainCtrl', function($scope, $resource, $window) {
  var Student = $resource('students.php', {id: '@id'});
  $scope.students = Student.query();
  $scope.add = function() {
    Student.save($scope.new_student, function() {
      alert("追加しました。");
      $window.location.reload();
    });
  };
});

app.controller('DetailCtrl', function($scope, $window) {
  $scope.update = function() {
    $scope.student.$save(function() {
      alert("更新しました。");
    });
  };
  $scope.delete = function(index) {
    $scope.student.$delete();
    alert("削除しました。");
    $window.location.reload();
  };
});

PHP

students.php
<?php
$pdo = new PDO('mysql:dbname=school', 'root');
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
  $st = $pdo->query("SELECT * FROM students");
  echo json_encode($st->fetchAll(PDO::FETCH_ASSOC));
  break;
case 'POST':
  $in = json_decode(file_get_contents('php://input'), true);
  if (isset($in['id'])) {
    $st = $pdo->prepare("UPDATE students SET name=:name,age=:age,comment=:comment WHERE id=:id");
  } else {
    $st = $pdo->prepare("INSERT INTO students(name,age,comment) VALUES(:name,:age,:comment)");
  }
  $st->execute($in);
  break;
case 'DELETE':
  $st = $pdo->prepare("DELETE FROM students WHERE id=?");
  $st->execute([$_GET['id']]);
  break;
}

MySQLのログインユーザーはroot、パスワードは無し(XAMPPのデフォルトの危ないやつ)を想定しています。

HTMLの解説

<html lang="ja" ng-app="app">

AngularJSはタグの入れ子構造がそのままスコープとして使うことが出来て直感的です。ng-appの付いている要素内がAngularJSの管轄となります。

<body ng-controller="MainCtrl">

body要素内をコントローラMainCtrlで制御します。

    <tr ng-controller="DetailCtrl" ng-repeat="student in students">

tr要素内をコントローラDetailCtrlで制御します。studentsはJavaScript側で設定した変数$scope.studentsを参照します。そこにstudentsテーブルの内容が入りますので要素を$scope.studentに代入しつつループします。

      <td>{{student.id}}</td>

オブジェクト$scope.studentのプロパティidをこの場にバインドしています。JavaScript側でidの値を変更すると自動的に反映されます。

      <td><input ng-model="student.name"></td>
      <td><input ng-model="student.age"></td>
      <td><input ng-model="student.comment"></td>

テキストフィールドの内容とオブジェクト$scope.studentのプロパティを双方向バインドしています。テキストフィールドの内容を変更するとプロパティの値が変わるのはもちろん、JavaScript側でプロパティの値を変更しても自動的にテキストフィールドの内容が変わります。

      <td><button ng-click="update()">更新</button></td>
      <td><button ng-click="delete()">削除</button></td>

ng-clickでボタンをクリックしたときに呼び出される関数を指定します。

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js"></script>

angular.min.jsがAngularJS本体、angular-resource.min.jsはAngularJSにngResourceという機能を追加します。これはREST APIによりバックエンドとのやりとりをサポートしてくれます。

コントローラJavaScriptの解説

var app = angular.module('app', ['ngResource']);

モジュール宣言です。ngResourceを使えるようにしています。

app.controller('MainCtrl', function($scope, $resource, $window) {

HTML側でbody要素に割り当てたコントローラMainCtrlの中身を定義しています。
$scopeによってコントローラが定義された場所にスコープを割り当て、データバインディングを可能にします。
$resourceでngResourceの機能をここから呼び出せます。

  var Student = $resource('students.php', {id: '@id'});

REST APIを呼び出すURLをstudents.phpとします。{id: '@id'}の部分はAPI側に渡されるデフォルトのキーと値です。この設定では渡されるオブジェクトにidというキーがあったら自動的にそれが付加されます。

  $scope.students = Student.query();

queryメソッドはAPI側をGETで呼び出し配列で結果を受け取ります。ここではstudents.phpからstudentsテーブルのレコード全てをJSONで貰い、オブジェクト$scope.studentsに格納します。このstudentsプロパティはHTML側のMainCtrl配下のstudentsとバインディングされます。

  $scope.add = function() {
    Student.save($scope.new_student, function() {
      alert("追加しました。");
      $window.location.reload();
    });
  };

追加ボタンを押したときは、saveメソッドによりPOSTでPHPを呼び出し、レコードを挿入してリロードしています。

app.controller('DetailCtrl', function($scope, $window) {

HTML側でtr要素に割り当てたコントローラDetailCtrlの中身を定義しています。

  $scope.update = function() {
    $scope.student.$save(function() {
      alert("更新しました。");
    });
  };

更新ボタンを押したときは、該当の行をDBに保存します。$scope.student.$save()$resource('students.php').save($scope.student)とほぼ同じです。saveメソッドによりPOSTでPHPを呼び出し、レコードを更新します。

  $scope.delete = function() {
    $scope.student.$delete();
    alert("削除しました。");
    $window.location.reload();
  };

削除ボタンを押したときは、該当の行をDBから削除しリロードします。$scope.student.$delete()$resource('students.php').delete({id: $scope.student.id})とほぼ同じです。deleteメソッドによりDELETEでPHPを呼び出し、レコードを削除します。

PHPの解説

バックエンド側は完全にAPI提供に徹するのがナウい作りだそうなので、それに従います。

switch ($_SERVER['REQUEST_METHOD']) {

メソッドの種類で分岐します。

case 'GET':

$resourcequeryメソッドが呼ばれた場合はここに来ます。レコード一覧を返しています。

case 'POST':

$resourcesaveメソッドが呼ばれた場合はここに来ます。

  $in = json_decode(file_get_contents('php://input'), true);

saveメソッドの場合は渡されたパラメータがリクエストボディにJSON形式で入るので配列として取得します。

  if (isset($in['id'])) {

idが設定されている場合はUPDATE、設定されていない場合はINSERTに分岐します。

case 'DELETE':

JavaScript側からdeleteメソッドが呼ばれた場合はここに来ます。指定されたidのレコードを削除しています。

まとめ

  • AngularJSを使うと驚くほどスッキリとデータバインディングの仕組みが記述できることが分かりました。
  • エラーチェックは全くやっていません。PHP側でステータスコードを返すのがRESTの作法だと思います。正常の場合もレスポンスの内容がないときは204を返したほうが良いのかも知れません。
187
183
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
187
183

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?