LoginSignup
3
0

More than 3 years have passed since last update.

ストアドプロシージャを jQuery + PHPで呼び出してみた

Posted at

はじめに

・トランザクション処理をストアドプロシージャにまとめることでソースをすっきりさせたい衝動にかられ、ストアドプロシージャを呼び出してみた。
・いままでクライアント側にSQLをベタ打ちしていてセキュリティーの問題がやばいみたいなので...

開発環境

・サーバー構成
 WindowsServr2012R2
 Apache + SQLServer2012 + PHP
 
・クライアント構成
 JavaScript + jQuery

実装コード

・クライアントからのAPIの呼び出し

classDatabase.js
//=========================================
// Database クラス
// データベースに関する命令をまとめる
//=========================================
const DatabaseAsyncOn = true;
const DatabaseAsyncOff = false;

function Database(accountName, userName, async) {
    this.accountName = "dev"; // accountName;
    this.userName = "dev"; // userName;
    this.async = false; // async;
}
Database.prototype = {
    //=========================================
    // SELECT メソッド(prepareを使用したもの)
    // DBからSQLを使ってデータをGETする
    //=========================================
    getProcedures: function(mappingName, prepare) {
        var returnData = null;
        $.ajax({
            type: "POST",
            url: "../../common/php/getProcedures.php",
            data: {
                "accountName": this.accountName,
                "userName": this.userName,
                "mappingName": mappingName,
                "prepare[]": prepare
            },
            dataType: "json",
            async: this.async,
            // 成功したときの処理
            success: function(data) {
                returnData = data;
                console.log('\n\nmappingName: ' + mappingName);
                console.log('parametter: ' + prepare);
                console.log('data: ' + data);
            },
            // エラー処理
            error: function(data) {
                returnData = false;
                console.log('\n\nmappingName: ' + mappingName);
                console.log('parametter: ' + prepare);
                console.log('data: ' + data);
            }
        });
        return returnData;
    }
};
main.js
    // APIの呼び出し
    // マッピング:200402
    self.midokuMember = self.database.getProcedures(
            'mapping200402',[
                self.companyID,
                eventNo,
                today
            ]
    );

サーバー側
 ストアドプロシージャ呼び出し用のAPI

setProcedures.php
<?php
  $accountName = $_POST["accountName"];
  $userName    = $_POST["userName"];
  $mappingName = $_POST["mappingName"];
  $prepare     = $_POST["prepare"];
  $org_timezone = date_default_timezone_get();
  date_default_timezone_set('Asia/Tokyo');
  $dsn = 'sqlsrv:server=【IPアドレス】;database=' . $accountName;
  $password = '【DB接続パスワード】';
  try {
    $dbh = new PDO($dsn, $userName, $password);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE , PDO::FETCH_ASSOC);
    //////////////////////////////////////////////
    // セット処理
    //////////////////////////////////////////////
    // ステートメント準備
    $stringPrepare = "{CALL ".$mappingName."(";
    foreach ($prepare as $key => $value) {
      # code...
      $stringPrepare = $stringPrepare."?,";
    }
    if (count($prepare) != 0) {
      $stringPrepare = substr($stringPrepare, 0, -1);   // 最後尾の文字を削除する
    }
    $stringPrepare = $stringPrepare.")}";
    error_log($stringPrepare);
    $stmt = $dbh->prepare($stringPrepare);
    for ($i=0; $i < count($prepare); $i++) {
      # code...
      $stmt->bindParam($i+1, $prepare[$i], PDO::PARAM_STR);
    }
    $stmt -> execute();
    echo "\nPDO::errorInfo():\n";
    print_r($dbh->errorInfo());
    $dbh = null;
    date_default_timezone_set($org_timezone);
  }
  catch (PDOException $e) {
    echo 'Error : ',  $e->getMessage(), "\n";
    die('MSSQL Serer Connect Error');
  }
?>

呼び出し元のストアドプロシージャ

mapping200409.sql
USE [dev]
GO
/****** Object:  StoredProcedure [dbo].[mapping200402]    Script Date: 2018/12/26 12:10:09 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[mapping200402]
  @companyID nvarchar(4),
  @eventNo int,
  @today nvarchar(10)
AS
BEGIN TRY
    BEGIN TRANSACTION        --トランザクションの開始
        BEGIN
            -- マッピング2:未読者の取得
            SELECT MK_smartPhone.companyID,
                   MK_smartPhone.staffID,
                   MK_staff.name
            FROM   MK_smartPhone INNER JOIN MK_staff ON 
                   MK_smartPhone.companyID = MK_staff.companyID AND 
                   MK_smartPhone.staffID = MK_staff.staffID LEFT OUTER JOIN
                   MK_history ON 
                   MK_smartPhone.companyID = MK_history.companyID AND 
                   MK_smartPhone.staffID = dbo.MK_history.staffID
            WHERE  (MK_smartPhone.name <> N'card') AND 
                   ((MK_history.category = 2) AND 
                   (MK_history.flag = 0) AND 
                   (MK_history.companyID = @companyID) AND 
                   (MK_history.externNo = @eventNo) AND 
                   (MK_staff.retireDate > @today OR
                    MK_staff.retireDate IS NULL OR
                    MK_staff.retireDate = '') OR
                   (MK_history.flag IS NULL))
            GROUP BY MK_smartPhone.companyID,
                     MK_smartPhone.staffID, 
                     MK_staff.name, 
                     MK_history.externNo
            ORDER BY MK_smartPhone.staffID          
        END
    COMMIT TRANSACTION       --トランザクションを確定
END TRY


--例外処理
BEGIN CATCH
    ROLLBACK TRANSACTION     --トランザクションを取り消し
    PRINT ERROR_MESSAGE()    --エラー内容を戻す
    PRINT 'ROLLBACK TRANSACTION'
END CATCH

RETURN

まとめ

・SQLを別ファイルに分離してソースの読みやすさ管理のしやすさの向上が図れた。
・言語ごとに得意分野があるのでそれぞれ適材適所で使用していきたい。※違う言語が紛れているとソースが読みづらくなる

 下記の様な使い分けがよいと改めて思った。
①JavaScript・・・画面周り、APIの呼び出し(ビジネスロジックは書かない)
②PHP・・・ビジネスロジックだけを書く
③ストアドプロシージャ・・・トランザクションの処理(ビジネスロジックは書かない)

3
0
0

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
3
0