PHP での Cloud SQL for PostgreSQL の使用

PHP Bookshelf チュートリアルのこのパートでは、Google Cloud SQL for PostgreSQL で構造化データを作成、読み取り、更新、および削除する方法を解説します。

このページは複数ページからなるチュートリアルの一部です。最初から設定手順を確認するには、PHP Bookshelf アプリに移動してください。

Cloud SQL インスタンスとデータベースの作成

SQL プロキシのインストール

Cloud SQL Proxy をダウンロードしてインストールします。Cloud SQL Proxy を使用すると、ローカル実行時に Cloud SQL インスタンスに接続できます。

Linux 64 ビット

  1. プロキシをダウンロードします。
    wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O cloud_sql_proxy
    
  2. プロキシを実行できるようにします。
    chmod +x cloud_sql_proxy
    

Linux 32 ビット

  1. プロキシをダウンロードします。
    wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.386 -O cloud_sql_proxy
    
  2. プロキシを実行できるようにします。
    chmod +x cloud_sql_proxy
    

macOS 64 ビット

  1. プロキシをダウンロードします。
    curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.amd64
    
  2. プロキシを実行できるようにします。
    chmod +x cloud_sql_proxy
    

macOS 32 ビット

  1. プロキシをダウンロードします。
    curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.386
    
  2. プロキシを実行できるようにします。
    chmod +x cloud_sql_proxy
    

Windows 64 ビット

https://dl.google.com/cloudsql/cloud_sql_proxy_x64.exe を右クリックして、[名前を付けてリンク先を保存...] を選択し、名前を cloud_sql_proxy.exe に変更してプロキシをダウンロードします。

Windows 32 ビット

https://dl.google.com/cloudsql/cloud_sql_proxy_x86.exe を右クリックして、[名前を付けてリンク先を保存...] を選択し、名前を cloud_sql_proxy.exe に変更してプロキシをダウンロードします。
お使いのオペレーティング システムがここに含まれていない場合は、プロキシをソースからコンパイルすることもできます。

Cloud SQL インスタンスを作成する

  1. Cloud SQL for PostgreSQL のインスタンスを作成します。

    インスタンスに「library」といった名前を付けます。インスタンスを使用できるようになるまで数分かかることがあります。インスタンスの準備が整うと、インスタンス リストに表示されます。

  2. コマンドラインから Cloud SDK を使用して、次のコマンドを実行します。次のステップで使用するために、connectionName に示されている値をコピーします。
    gcloud sql instances describe [YOUR_INSTANCE_NAME]

    connectionName の値は、[PROJECT_NAME]:[REGION_NAME]:[INSTANCE_NAME] の形式になります。

Cloud SQL インスタンスを初期化する

  1. 前のステップの connectionName を使用して、Cloud SQL Proxy を開始します。

    Linux / Mac OS X

    ./cloud_sql_proxy -instances="[YOUR_INSTANCE_CONNECTION_NAME]"=tcp:5432

    Windows

    cloud_sql_proxy.exe -instances="[YOUR_INSTANCE_CONNECTION_NAME]"=tcp:5432

    [YOUR_INSTANCE_CONNECTION_NAME] は、前のステップで記録した connectionName の値で置き換えます。

    このステップで、ローカル パソコンから Cloud SQL インスタンスへのローカルテスト用接続が確立されます。ローカルでのアプリケーションのテストが終了するまで、Cloud SQL Proxy を実行させたままにしてください。

  2. 次に、新しい Cloud SQL ユーザーとデータベースを作成します。

    Console

    1. Cloud SQL インスタンス library 用に、GCP Console を使用して新しいデータベースを作成します。たとえば、bookshelf という名前を使用します。
    2. Cloud SQL インスタンス library 用に GCP Console を使用して新しいユーザーを作成します。

    POSTGRES クライアント

    1. 別のコマンドライン タブで、Postgres クライアントをインストールします。
      sudo apt-get install postgresql
      
    2. Postgres クライアント、または同様のプログラムを使用して、インスタンスに接続します。プロンプトが表示されたら、設定したルート パスワードを使用します。
      psql --host 127.0.0.1 --user postgres --password
      
    3. 次のコマンドを使用して、Cloud SQL データベース内に必要なデータベース、ユーザー、アクセス権限を作成します。[POSTGRES_USER][POSTGRES_PASSWORD] は、使用するユーザー名とパスワードに置き換えてください。
      CREATE DATABASE bookshelf;
      CREATE USER [POSTGRES_USER] WITH PASSWORD '[POSTGRES_PASSWORD]';
      GRANT ALL PRIVILEGES ON DATABASE bookshelf TO [POSTGRES_USER];
      GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO [POSTGRES_USER];
      

設定の構成

  1. getting-started-php/2-structured-data ディレクトリに移動して、settings.yml.dist ファイルをコピーします。

    cp config/settings.yml.dist config/settings.yml
    
  2. 編集のため config/settings.yml を開きます。

  3. YOUR_PROJECT_ID は、実際のプロジェクト ID に置き換えます。

  4. bookshelf_backend の値を postgres に設定します。

  5. cloudsql_connection_namecloudsql_database_namecloudsql_usercloudsql_password、および cloudsql_port の値を、Cloud SQL インスタンスに適切な値に設定します。postgres を使用しているため、ポートには 5432 を使用する必要があります。次に例を示します。

    cloudsql_connection_name: your_project_name:your_region:your_instance
    cloudsql_database_name: bookshelf
    cloudsql_user: phpapp
    cloudsql_password: password
    cloudsql_port: 5432
    
  6. settings.yml を保存して閉じます。

また、デプロイする前に app.yaml を更新する必要もあります。

  1. 編集用に app.yaml を開きます。

  2. beta_settingscloud_sql_instances の行のコメントを外します。cloud_sql_instances の値を、config/settings.ymlcloudsql_connection_name に使用した値に設定します。your_project_name:your_region:your_instance の形式にする必要があります。

  3. app.yaml を保存して閉じます。

依存関係のインストール

2-structured-data ディレクトリで、次のコマンドを入力します。

composer install

ローカルマシンでのアプリの実行

  1. ローカル ウェブサーバーを起動します。

    php -S localhost:8000 -t web
    
  2. ウェブブラウザで、次のアドレスを入力します。

    http://localhost:8000

これで、このアプリのウェブページを表示し、書籍の追加、編集、削除を行うことができます。

App Engine フレキシブル環境へのアプリのデプロイ

  1. サンプルアプリをデプロイします。

    gcloud app deploy
    
  2. ウェブブラウザで、次のアドレスを入力します。[YOUR_PROJECT_ID] は実際のプロジェクト ID で置き換えます。

    https://[YOUR_PROJECT_ID].appspot.com
    

    アプリを更新する場合は、最初にデプロイしたときと同じコマンドを使って、更新バージョンをデプロイできます。デプロイを行うと、アプリの新しいバージョンが作成され、それがデフォルトのバージョンに設定されます。古いバージョンはそのまま残り、関連付けられた VM インスタンスも同様に残ります。すべてのアプリ バージョンと VM インスタンスが課金対象のリソースとなるのでご注意ください。

    アプリのデフォルト以外のバージョンを削除することで、コストを削減できます。

    アプリのバージョンを削除する手順は次のとおりです。

    1. GCP Console の [App Engine のバージョン] ページに移動します。

      [バージョン] ページに移動

    2. 削除したい、デフォルト以外のアプリのバージョンの横にあるチェックボックスをクリックします。
    3. ページ上部にある [削除] ボタンをクリックし、アプリのバージョンを削除します。

    課金対象のリソースをクリーンアップする方法の詳細については、このチュートリアルの最後のステップにあるクリーンアップ セクションを参照してください。

    アプリケーションの構造

    この図は、アプリケーションを構成するコンポーネントと、コンポーネント間の関係を示しています。

    Bookshelf App のデプロイと構造

    コードについて

    以前のステップで settings.yml を編集して bookshelf_backend の値を postgres に設定しました。これにより、アプリは src/DataModel/Sql.php で定義された Sql クラスを読み込みます。Sql クラスは PDO API をラップし、Cloud SQL データベースに書籍情報を格納します。

    controllers.php にある次のコードが GET '/books' ルートのハンドラを定義し、登録しています。$model 変数は Sql クラスのインスタンスです。$model->listBooks メソッドは、書籍の配列とカーソルを含む配列を返します。その後、list.html.twig テンプレートに従って Twig テンプレート エンジンが書籍のリストを生成します。

    $app->get('/books/', function (Request $request) use ($app) {
        /** @var DataModelInterface $model */
        $model = $app['bookshelf.model'];
        /** @var Twig_Environment $twig */
        $twig = $app['twig'];
        $token = $request->query->get('page_token');
        $bookList = $model->listBooks($app['bookshelf.page_size'], $token);
    
        return $twig->render('list.html.twig', array(
            'books' => $bookList['books'],
            'next_page_token' => $bookList['cursor'],
        ));
    });

    次の Twig テンプレートは、Cloud SQL データベースから取得した書籍のリストを生成するテンプレートです。このテンプレートは books という名前の配列変数を受信します。配列内の書籍ごとに、タイトルと著者名を表示します。また、テンプレートが受信する next_page_token 変数は、[More] ボタンを表示するかどうかを決定します。

    {% for book in books %}
    <div class="media">
      <a href="/books/{{book.id}}">
        <div class="media-left">
          <img src="http://placekitten.com/g/128/192">
        </div>
        <div class="media-body">
          <h4>{{book.title}}</h4>
          <p>{{book.author}}</p>
        </div>
      </a>
    </div>
    {% else %}
    <p>No books found</p>
    {% endfor %}

    このコードは、GET '/books/{id}' ルートのハンドラを定義して登録します。{id} は、個々の書籍の ID です。ハンドラは、$model->read メソッドを呼び出し、指定された書籍の情報を Cloud SQL から取得します。Twig テンプレート エンジンは、view.html.twig テンプレートに従って書籍の情報を表示します。

    $app->get('/books/{id}', function ($id) use ($app) {
        /** @var DataModelInterface $model */
        $model = $app['bookshelf.model'];
        $book = $model->read($id);
        if (!$book) {
            return new Response('', Response::HTTP_NOT_FOUND);
        }
        /** @var Twig_Environment $twig */
        $twig = $app['twig'];
    
        return $twig->render('view.html.twig', array('book' => $book));
    });

    view.html.twig テンプレートは、book という名前の変数を受信し、書籍のタイトル、出版日、著者名、説明を表示します。

    <div class="media">
      <div class="media-body">
        <h4 class="book-title">
          {{book.title}}
          <small>{{book.published_date}}</small>
        </h4>
        <h5 class="book-author">By {{book.author|default('Unknown', True)}}</h5>
        <p class="book-description">{{book.description}}</p>
      </div>
    </div>

    ユーザーが [Add book] をクリックすると、GET /books/add のハンドラは、タイトル、著者名などの書籍情報を入力するフォームを表示します。ユーザーが [Save] をクリックすると、POST /books/add のハンドラがリクエストから新しい書籍情報を取得し、$model->create を呼び出して書籍を Cloud SQL に格納します。

    $app->get('/books/add', function () use ($app) {
        /** @var Twig_Environment $twig */
        $twig = $app['twig'];
    
        return $twig->render('form.html.twig', array(
            'action' => 'Add',
            'book' => array(),
        ));
    });
    
    $app->post('/books/add', function (Request $request) use ($app) {
        /** @var DataModelInterface $model */
        $model = $app['bookshelf.model'];
        $book = $request->request->all();
        $id = $model->create($book);
    
        return $app->redirect("/books/$id");
    });

    次のテンプレートは、書籍情報の入力用フォームです。

    {% extends "base.html.twig" %}
    
    {% block content %}
    <h3>{{action}} book</h3>
    
    <form method="POST" enctype="multipart/form-data">
    
      <div class="form-group">
        <label for="title">Title</label>
        <input type="text" name="title" id="title" value="{{book.title}}" class="form-control"/>
      </div>
    
      <div class="form-group">
        <label for="author">Author</label>
        <input type="text" name="author" id="author" value="{{book.author}}" class="form-control"/>
      </div>
    
      <div class="form-group">
        <label for="published_date">Date Published</label>
        <input type="text" name="published_date" id="published_date" value="{{book.published_date}}" class="form-control"/>
      </div>
    
      <div class="form-group">
        <label for="description">Description</label>
        <textarea name="description" id="description" class="form-control">{{book.description}}</textarea>
      </div>
    
      <button id="submit" type="submit" class="btn btn-success">Save</button>
    </form>
    
    {% endblock %}

    このサンプルコードには、書籍情報を個別に編集または削除するハンドラも追加されています。

    $app->get('/books/{id}/edit', function ($id) use ($app) {
        /** @var DataModelInterface $model */
        $model = $app['bookshelf.model'];
        $book = $model->read($id);
        if (!$book) {
            return new Response('', Response::HTTP_NOT_FOUND);
        }
        /** @var Twig_Environment $twig */
        $twig = $app['twig'];
    
        return $twig->render('form.html.twig', array(
            'action' => 'Edit',
            'book' => $book,
        ));
    });
    
    $app->post('/books/{id}/edit', function (Request $request, $id) use ($app) {
        $book = $request->request->all();
        $book['id'] = $id;
        /** @var DataModelInterface $model */
        $model = $app['bookshelf.model'];
        if (!$model->read($id)) {
            return new Response('', Response::HTTP_NOT_FOUND);
        }
        if ($model->update($book)) {
            return $app->redirect("/books/$id");
        }
    
        return new Response('Could not update book');
    });
    $app->post('/books/{id}/delete', function ($id) use ($app) {
        /** @var DataModelInterface $model */
        $model = $app['bookshelf.model'];
        $book = $model->read($id);
        if ($book) {
            $model->delete($id);
    
            return $app->redirect('/books/', Response::HTTP_SEE_OTHER);
        }
    
        return new Response('', Response::HTTP_NOT_FOUND);
    });
このページは役立ちましたか?評価をお願いいたします。