Symfoware

Symfowareについての考察blog

Phalcon 3.4 チュートリアル Creating a Simple REST API

こちらのチュートリアルを試してみます。
Tutorial: Creating a Simple REST API

簡単なAPIを作りたいだけなのに、通常のphpフレームワークを使用すると
それなりのファイル数をサーバーにアップロードすることになります。

Phalconだと、php拡張なのでいちいちフレームワーク用のファイルを
アップロードする必要がないので、これは便利だと思います。



Defining the API



チュートリアルに従い、ロボット情報を取得・登録するAPIを作成します。
APIの仕様はこちら。

MethodURLAction
GET/api/robots登録されているロボットをすべて取得
GET/api/robots/search/Astro‘Astro’という名前のロボットを検索
GET/api/robots/2Primary Keyを指定してロボットを検索
POST/api/robots新しいロボットを追加
PUT/api/robots/2Primary Keyを指定してロボット情報を更新
DELETE/api/robots/2Primary Keyを指定してロボット情報を削除




Creating the Application



これから作成するアプリケーションのディレクトリ構成です。

974_01.png


以下の2ファイルだけの構成です。


index.php
models/Robots.php




まず、index.phpを編集。
Microというクラスを生成して、urlのrouteを設定します。


  1. <?php
  2. use Phalcon\Mvc\Micro;
  3. $app = new Micro();
  4. // 登録されているロボットをすべて取得
  5. $app->get(
  6.     '/api/robots',
  7.     function () {
  8.         // Operation to fetch all the robots
  9.     }
  10. );
  11. // {name}という名前のロボットを検索
  12. $app->get(
  13.     '/api/robots/search/{name}',
  14.     function ($name) {
  15.         // Operation to fetch robot with name $name
  16.     }
  17. );
  18. // Primary Keyを指定してロボットを検索
  19. $app->get(
  20.     '/api/robots/{id:[0-9]+}',
  21.     function ($id) {
  22.         // Operation to fetch robot with id $id
  23.     }
  24. );
  25. // 新しいロボットを追加
  26. $app->post(
  27.     '/api/robots',
  28.     function () {
  29.         // Operation to create a fresh robot
  30.     }
  31. );
  32. // Primary Keyを指定してロボット情報を更新
  33. $app->put(
  34.     '/api/robots/{id:[0-9]+}',
  35.     function ($id) {
  36.         // Operation to update a robot with id $id
  37.     }
  38. );
  39. // Primary Keyを指定してロボット情報を削除
  40. $app->delete(
  41.     '/api/robots/{id:[0-9]+}',
  42.     function ($id) {
  43.         // Operation to delete the robot with id $id
  44.     }
  45. );
  46. $app->handle();



「/api/robots/{id:[0-9]+}」のように、メソッドが受け取れる値を指定できるのは、
他のフレームワークでもよく見かけますね。




Creating a Model



models/Robots.phpを編集し、入力値の検証(validation)を設定します。

・models/Robots.php


  1. <?php
  2. namespace Store\Toys;
  3. use Phalcon\Mvc\Model;
  4. use Phalcon\Mvc\Model\Message;
  5. use Phalcon\Validation;
  6. use Phalcon\Validation\Validator\Uniqueness
  7. use Phalcon\Validation\Validator\InclusionIn;
  8. class Robots extends Model
  9. {
  10.     public function validation()
  11.     {
  12.         $validator = new Validation();
  13.         
  14.         // Type は「droid」「mechanical」または「virtual」
  15.         $validator->add(
  16.             "type",
  17.             new InclusionIn(
  18.                 [
  19.                     'message' => 'Type は「droid」「mechanical」または「virtual」で指定してください。',
  20.                     'domain' => [
  21.                         'droid',
  22.                         'mechanical',
  23.                         'virtual',
  24.                     ],
  25.                 ]
  26.             )
  27.         );
  28.         // Robot name は重複禁止
  29.         $validator->add(
  30.             'name',
  31.             new Uniqueness(
  32.                 [
  33.                     'field' => 'name',
  34.                     'message' => 'robot nameはユニークな値を指定してください。',
  35.                 ]
  36.             )
  37.         );
  38.         // Year は0以上
  39.         if ($this->year < 0) {
  40.             $this->appendMessage(
  41.                 new Message('yearは0以上の値を指定してください。')
  42.             );
  43.         }
  44.         // 検証チェック
  45.         if ($this->validationHasFailed() === true) {
  46.             return false;
  47.         }
  48.     }
  49. }




作成したモデルのロードと、データベースへの接続設定をindex.phpに追記します。

・index.php

追記した箇所の抜粋
※データベースはPostgreSQLを使用しています。


  1. <?php
  2. use Phalcon\Loader;
  3. use Phalcon\Mvc\Micro;
  4. use Phalcon\Di\FactoryDefault;
  5. use Phalcon\Db\Adapter\Pdo\Postgresql as DbAdapter;
  6. $loader = new Loader();
  7. $loader->registerNamespaces(
  8.     [
  9.         'Store\Toys' => __DIR__ . '/models/',
  10.     ]
  11. );
  12. $loader->register();
  13. $di = new FactoryDefault();
  14. // Set up the database service
  15. $di->set(
  16.     'db',
  17.     function () {
  18.         return new DbAdapter(
  19.             [
  20.                 'host'     => '127.0.0.1',
  21.                 'username' => 'pgadmin',
  22.                 'password' => 'P@ssw0rd',
  23.                 'dbname' => 'sample',
  24.             ]
  25.         );
  26.     }
  27. );
  28. // Create and bind the DI to the application
  29. $app = new Micro($di);






Retrieving Data



データベースを検索する処理を追記します。

・index.php

変更箇所の抜粋


  1. <?php
  2. // 登録されているロボットをすべて取得
  3. $app->get(
  4.     '/api/robots',
  5.     function () use ($app) {
  6.         $phql = 'SELECT * FROM Store\Toys\Robots ORDER BY name';
  7.         $robots = $app->modelsManager->executeQuery($phql);
  8.         $data = [];
  9.         foreach ($robots as $robot) {
  10.             $data[] = [
  11.                 'id' => $robot->id,
  12.                 'name' => $robot->name,
  13.             ];
  14.         }
  15.         echo json_encode($data);
  16.     }
  17. );
  18. // {name}という名前のロボットを検索
  19. $app->get(
  20.     '/api/robots/search/{name}',
  21.     function ($name) use ($app) {
  22.         $phql = 'SELECT * FROM Store\Toys\Robots WHERE name LIKE :name: ORDER BY name';
  23.         $robots = $app->modelsManager->executeQuery(
  24.             $phql,
  25.             [
  26.                 'name' => '%' . $name . '%'
  27.             ]
  28.         );
  29.         $data = [];
  30.         foreach ($robots as $robot) {
  31.             $data[] = [
  32.                 'id' => $robot->id,
  33.                 'name' => $robot->name,
  34.             ];
  35.         }
  36.         echo json_encode($data);
  37.     }
  38. );
  39. // Primary Keyを指定してロボットを検索
  40. $app->get(
  41.     '/api/robots/{id:[0-9]+}',
  42.     function ($id) use ($app) {
  43.         $phql = 'SELECT * FROM Store\Toys\Robots WHERE id = :id:';
  44.         $robot = $app->modelsManager->executeQuery(
  45.             $phql,
  46.             [
  47.                 'id' => $id,
  48.             ]
  49.         )->getFirst();
  50.         
  51.         // Create a response
  52.         $response = new Response();
  53.         if ($robot === false) {
  54.             $response->setJsonContent(
  55.                 [
  56.                     'status' => 'NOT-FOUND'
  57.                 ]
  58.             );
  59.         } else {
  60.             $response->setJsonContent(
  61.                 [
  62.                     'status' => 'FOUND',
  63.                     'data' => [
  64.                         'id' => $robot->id,
  65.                         'name' => $robot->name
  66.                     ]
  67.                 ]
  68.             );
  69.         }
  70.         return $response;
  71.     }
  72. );



PHQLという独自記法が使われています。
FROMにテーブル名ではなく「Store\Toys\Robots」の用にモデル名を指定するのがポイントでしょうか。


Inserting Data



データの登録箇所の抜粋です。

・index.php


  1. <?php
  2. use Phalcon\Http\Response;
  3. // 新しいロボットを追加
  4. $app->post(
  5.     '/api/robots',
  6.     function () use ($app) {
  7.         $robot = $app->request->getJsonRawBody();
  8.         $phql = 'INSERT INTO Store\Toys\Robots (name, type, year) VALUES (:name:, :type:, :year:)';
  9.         $status = $app->modelsManager->executeQuery(
  10.             $phql,
  11.             [
  12.                 'name' => $robot->name,
  13.                 'type' => $robot->type,
  14.                 'year' => $robot->year,
  15.             ]
  16.         );
  17.         // Create a response
  18.         $response = new Response();
  19.         // Check if the insertion was successful
  20.         if ($status->success() === true) {
  21.             // Change the HTTP status
  22.             $response->setStatusCode(201, 'Created');
  23.             $robot->id = $status->getModel()->id;
  24.             $response->setJsonContent(
  25.                 [
  26.                     'status' => 'OK',
  27.                     'data' => $robot,
  28.                 ]
  29.             );
  30.         } else {
  31.             // Change the HTTP status
  32.             $response->setStatusCode(409, 'Conflict');
  33.             // Send errors to the client
  34.             $errors = [];
  35.             foreach ($status->getMessages() as $message) {
  36.                 $errors[] = $message->getMessage();
  37.             }
  38.             $response->setJsonContent(
  39.                 [
  40.                     'status' => 'ERROR',
  41.                     'messages' => $errors,
  42.                 ]
  43.             );
  44.         }
  45.         return $response;
  46.     }
  47. );






Updating Data



データの更新箇所の抜粋です。


  1. <?php
  2. use Phalcon\Http\Response;
  3. // Primary Keyを指定してロボット情報を更新
  4. $app->put(
  5.     '/api/robots/{id:[0-9]+}',
  6.     function ($id) use ($app) {
  7.         $robot = $app->request->getJsonRawBody();
  8.         $phql = 'UPDATE Store\Toys\Robots SET name = :name:, type = :type:, year = :year: WHERE id = :id:';
  9.         $status = $app->modelsManager->executeQuery(
  10.             $phql,
  11.             [
  12.                 'id' => $id,
  13.                 'name' => $robot->name,
  14.                 'type' => $robot->type,
  15.                 'year' => $robot->year,
  16.             ]
  17.         );
  18.         // Create a response
  19.         $response = new Response();
  20.         // Check if the insertion was successful
  21.         if ($status->success() === true) {
  22.             $response->setJsonContent(
  23.                 [
  24.                     'status' => 'OK'
  25.                 ]
  26.             );
  27.         } else {
  28.             // Change the HTTP status
  29.             $response->setStatusCode(409, 'Conflict');
  30.             $errors = [];
  31.             foreach ($status->getMessages() as $message) {
  32.                 $errors[] = $message->getMessage();
  33.             }
  34.             $response->setJsonContent(
  35.                 [
  36.                     'status' => 'ERROR',
  37.                     'messages' => $errors,
  38.                 ]
  39.             );
  40.         }
  41.         return $response;
  42.     }
  43. );





Deleting Data



最後にデータの削除です。


  1. <?php
  2. use Phalcon\Http\Response;
  3. // Primary Keyを指定してロボット情報を削除
  4. $app->delete(
  5.     '/api/robots/{id:[0-9]+}',
  6.     function ($id) use ($app) {
  7.         $phql = 'DELETE FROM Store\Toys\Robots WHERE id = :id:';
  8.         $status = $app->modelsManager->executeQuery(
  9.             $phql,
  10.             [
  11.                 'id' => $id,
  12.             ]
  13.         );
  14.         // Create a response
  15.         $response = new Response();
  16.         if ($status->success() === true) {
  17.             $response->setJsonContent(
  18.                 [
  19.                     'status' => 'OK'
  20.                 ]
  21.             );
  22.         } else {
  23.             // Change the HTTP status
  24.             $response->setStatusCode(409, 'Conflict');
  25.             $errors = [];
  26.             foreach ($status->getMessages() as $message) {
  27.                 $errors[] = $message->getMessage();
  28.             }
  29.             $response->setJsonContent(
  30.                 [
  31.                     'status' => 'ERROR',
  32.                     'messages' => $errors,
  33.                 ]
  34.             );
  35.         }
  36.         return $response;
  37.     }
  38. );





index.php



最終的なindex.phpはこのようになりました。


  1. <?php
  2. use Phalcon\Loader;
  3. use Phalcon\Mvc\Micro;
  4. use Phalcon\Http\Response;
  5. use Phalcon\Di\FactoryDefault;
  6. use Phalcon\Db\Adapter\Pdo\Postgresql as DbAdapter;
  7. $loader = new Loader();
  8. $loader->registerNamespaces(
  9.     [
  10.         'Store\Toys' => __DIR__ . '/models/',
  11.     ]
  12. );
  13. $loader->register();
  14. $di = new FactoryDefault();
  15. // Set up the database service
  16. $di->set(
  17.     'db',
  18.     function () {
  19.         return new DbAdapter(
  20.             [
  21.                 'host'     => '127.0.0.1',
  22.                 'username' => 'pgadmin',
  23.                 'password' => 'P@ssw0rd',
  24.                 'dbname' => 'sample',
  25.             ]
  26.         );
  27.     }
  28. );
  29. // Create and bind the DI to the application
  30. $app = new Micro($di);
  31. // 登録されているロボットをすべて取得
  32. $app->get(
  33.     '/api/robots',
  34.     function () use ($app) {
  35.         $phql = 'SELECT * FROM Store\Toys\Robots ORDER BY name';
  36.         $robots = $app->modelsManager->executeQuery($phql);
  37.         $data = [];
  38.         foreach ($robots as $robot) {
  39.             $data[] = [
  40.                 'id' => $robot->id,
  41.                 'name' => $robot->name,
  42.             ];
  43.         }
  44.         echo json_encode($data);
  45.     }
  46. );
  47. // {name}という名前のロボットを検索
  48. $app->get(
  49.     '/api/robots/search/{name}',
  50.     function ($name) use ($app) {
  51.         $phql = 'SELECT * FROM Store\Toys\Robots WHERE name LIKE :name: ORDER BY name';
  52.         $robots = $app->modelsManager->executeQuery(
  53.             $phql,
  54.             [
  55.                 'name' => '%' . $name . '%'
  56.             ]
  57.         );
  58.         $data = [];
  59.         foreach ($robots as $robot) {
  60.             $data[] = [
  61.                 'id' => $robot->id,
  62.                 'name' => $robot->name,
  63.             ];
  64.         }
  65.         echo json_encode($data);
  66.     }
  67. );
  68. // Primary Keyを指定してロボットを検索
  69. $app->get(
  70.     '/api/robots/{id:[0-9]+}',
  71.     function ($id) use ($app) {
  72.         $phql = 'SELECT * FROM Store\Toys\Robots WHERE id = :id:';
  73.         $robot = $app->modelsManager->executeQuery(
  74.             $phql,
  75.             [
  76.                 'id' => $id,
  77.             ]
  78.         )->getFirst();
  79.         // Create a response
  80.         $response = new Response();
  81.         if ($robot === false) {
  82.             $response->setJsonContent(
  83.                 [
  84.                     'status' => 'NOT-FOUND'
  85.                 ]
  86.             );
  87.         } else {
  88.             $response->setJsonContent(
  89.                 [
  90.                     'status' => 'FOUND',
  91.                     'data' => [
  92.                         'id' => $robot->id,
  93.                         'name' => $robot->name
  94.                     ]
  95.                 ]
  96.             );
  97.         }
  98.         return $response;
  99.     }
  100. );
  101. // 新しいロボットを追加
  102. $app->post(
  103.     '/api/robots',
  104.     function () use ($app) {
  105.         $robot = $app->request->getJsonRawBody();
  106.         $phql = 'INSERT INTO Store\Toys\Robots (name, type, year) VALUES (:name:, :type:, :year:)';
  107.         $status = $app->modelsManager->executeQuery(
  108.             $phql,
  109.             [
  110.                 'name' => $robot->name,
  111.                 'type' => $robot->type,
  112.                 'year' => $robot->year,
  113.             ]
  114.         );
  115.         // Create a response
  116.         $response = new Response();
  117.         // Check if the insertion was successful
  118.         if ($status->success() === true) {
  119.             // Change the HTTP status
  120.             $response->setStatusCode(201, 'Created');
  121.             $robot->id = $status->getModel()->id;
  122.             $response->setJsonContent(
  123.                 [
  124.                     'status' => 'OK',
  125.                     'data' => $robot,
  126.                 ]
  127.             );
  128.         } else {
  129.             // Change the HTTP status
  130.             $response->setStatusCode(409, 'Conflict');
  131.             // Send errors to the client
  132.             $errors = [];
  133.             foreach ($status->getMessages() as $message) {
  134.                 $errors[] = $message->getMessage();
  135.             }
  136.             $response->setJsonContent(
  137.                 [
  138.                     'status' => 'ERROR',
  139.                     'messages' => $errors,
  140.                 ]
  141.             );
  142.         }
  143.         return $response;
  144.     }
  145. );
  146. // Primary Keyを指定してロボット情報を更新
  147. $app->put(
  148.     '/api/robots/{id:[0-9]+}',
  149.     function ($id) use ($app) {
  150.         $robot = $app->request->getJsonRawBody();
  151.         $phql = 'UPDATE Store\Toys\Robots SET name = :name:, type = :type:, year = :year: WHERE id = :id:';
  152.         $status = $app->modelsManager->executeQuery(
  153.             $phql,
  154.             [
  155.                 'id' => $id,
  156.                 'name' => $robot->name,
  157.                 'type' => $robot->type,
  158.                 'year' => $robot->year,
  159.             ]
  160.         );
  161.         // Create a response
  162.         $response = new Response();
  163.         // Check if the insertion was successful
  164.         if ($status->success() === true) {
  165.             $response->setJsonContent(
  166.                 [
  167.                     'status' => 'OK'
  168.                 ]
  169.             );
  170.         } else {
  171.             // Change the HTTP status
  172.             $response->setStatusCode(409, 'Conflict');
  173.             $errors = [];
  174.             foreach ($status->getMessages() as $message) {
  175.                 $errors[] = $message->getMessage();
  176.             }
  177.             $response->setJsonContent(
  178.                 [
  179.                     'status' => 'ERROR',
  180.                     'messages' => $errors,
  181.                 ]
  182.             );
  183.         }
  184.         return $response;
  185.     }
  186. );
  187. // Primary Keyを指定してロボット情報を削除
  188. $app->delete(
  189.     '/api/robots/{id:[0-9]+}',
  190.     function ($id) use ($app) {
  191.         $phql = 'DELETE FROM Store\Toys\Robots WHERE id = :id:';
  192.         $status = $app->modelsManager->executeQuery(
  193.             $phql,
  194.             [
  195.                 'id' => $id,
  196.             ]
  197.         );
  198.         // Create a response
  199.         $response = new Response();
  200.         if ($status->success() === true) {
  201.             $response->setJsonContent(
  202.                 [
  203.                     'status' => 'OK'
  204.                 ]
  205.             );
  206.         } else {
  207.             // Change the HTTP status
  208.             $response->setStatusCode(409, 'Conflict');
  209.             $errors = [];
  210.             foreach ($status->getMessages() as $message) {
  211.                 $errors[] = $message->getMessage();
  212.             }
  213.             $response->setJsonContent(
  214.                 [
  215.                     'status' => 'ERROR',
  216.                     'messages' => $errors,
  217.                 ]
  218.             );
  219.         }
  220.         return $response;
  221.     }
  222. );
  223. $app->handle();






Creating database



データベースにテーブルを作成します。
今回は、既に作成済のsampleデータベースにテーブルを作成しました。
※PostgreSQL用にアレンジしています。


  1. CREATE TABLE robots (
  2. id serial NOT NULL,
  3. name varchar(200) NOT NULL,
  4. type varchar(200) NOT NULL,
  5. year smallint NOT NULL,
  6. PRIMARY KEY (id)
  7. );
  8. -- 初期データ登録
  9. INSERT INTO robots (name, type, year) VALUES ('Robotina', 'virtual', 1980);
  10. INSERT INTO robots (name, type, year) VALUES ('Astro Boy', 'virtual', 1981);
  11. INSERT INTO robots (name, type, year) VALUES ('Terminator', 'virtual', 1982);





Testing our Application



curlコマンドでAPIをテストしてみます。

・登録されているロボットをすべて取得


$ curl -i -X GET http://192.168.1.102/api/robots
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 09 Mar 2019 11:47:58 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive

[{"id":2,"name":"Astro Boy"},{"id":1,"name":"Robotina"},{"id":3,"name":"Terminator"}]




・{name}という名前のロボットを検索


$ curl -i -X GET http://192.168.1.102/api/robots/search/Astro
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 09 Mar 2019 11:50:50 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive

[{"id":2,"name":"Astro Boy"}]




・Primary Keyを指定してロボットを検索


$ curl -i -X GET http://192.168.1.102/api/robots/3
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 09 Mar 2019 11:51:34 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive

{"status":"FOUND","data":{"id":3,"name":"Terminator"}}




・新しいロボットを追加


$ curl -i -X POST -d '{"name":"C-3PO","type":"droid","year":1977}' http://192.168.1.102/api/robots
HTTP/1.1 201 Created
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 09 Mar 2019 11:52:31 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive

{"status":"OK","data":{"name":"C-3PO","type":"droid","year":1977,"id":"4"}}




・同じ名前のロボットを追加


$ curl -i -X POST -d '{"name":"C-3PO","type":"droid","year":1977}' http://192.168.1.102/api/robots
HTTP/1.1 201 Created
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 09 Mar 2019 11:54:42 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive

{"status":"OK","data":{"name":"C-3PO","type":"droid","year":1977,"id":"6"}}



エラーになるはずが追加できてしまいました。
PostgreSQLに変更した影響かもしれません。


・Primary Keyを指定してロボット情報を更新


$ curl -i -X PUT -d '{"name":"ASIMO","type":"humanoid","year":2000}' http://192.168.1.102/api/robots/2
$ curl -i -X GET http://192.168.1.102/api/robots/2
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 09 Mar 2019 11:56:37 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive

{"status":"FOUND","data":{"id":2,"name":"ASIMO"}}




・Primary Keyを指定してロボット情報を削除


$ curl -i -X DELETE http://192.168.1.102/api/robots/6
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 09 Mar 2019 11:57:46 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive

{"status":"OK"}




テーマ:プログラミング - ジャンル:コンピュータ

  1. 2019/03/09(土) 21:03:45|
  2. PHP
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Phalcon 3.4 チュートリアル Basic 3

Phalcon 3.4 チュートリアルの続きを試してみます。
Phalcon 3.4 チュートリアル Basic 2

6.Tutorials - basic
https://docs.phalconphp.com/3.4/en/tutorial-base


Ubuntu



Phalcon 3 + PostgreSQL 11で動作確認を行いたかったのですが、
FreeBSDの環境では、Phalcon 3がPostgreSQL 5.5のクライアントライブラリに
依存関係があるようで、PostgreSQL 11と共存できませんでした。

FreeBSDでのテストは諦めて、Ubuntu Server 18.04にPhalcon 3.4の環境を構築。
PHPフレームワーク Phalcon 3 をUbuntu Server 18.04 + nginxで動作させる

PostgreSQL 11をインストール。
PostgreSQL 11.1をUbuntu 18.04へインストールし、外部から接続する

この環境に変更して、チュートリアルの続きを試してみます。



Setting a Database Connection



PostgreSQLにユーザー登録用のテーブル「users」を作成したところからの続きです。

データベースへの接続設定をpublic/index.phpに記載します。

・public/index.php

追加箇所の抜粋


  1. use Phalcon\Db\Adapter\Pdo\Postgresql as DbAdapter;
  2. // Setup the database service
  3. $di->set(
  4.     'db',
  5.     function () {
  6.         return new DbAdapter(
  7.             [
  8.                 'host'     => '127.0.0.1',
  9.                 'username' => 'pgadmin',
  10.                 'password' => 'P@ssw0rd',
  11.                 'dbname' => 'sample',
  12.             ]
  13.         );
  14.     }
  15. );



index.php全体ではこのようになっています。


  1. <?php
  2. use Phalcon\Loader;
  3. use Phalcon\Mvc\View;
  4. use Phalcon\Mvc\Application;
  5. use Phalcon\Di\FactoryDefault;
  6. use Phalcon\Mvc\Url as UrlProvider;
  7. use Phalcon\Db\Adapter\Pdo\Postgresql as DbAdapter;
  8. // Define some absolute path constants to aid in locating resources
  9. define('BASE_PATH', dirname(__DIR__));
  10. define('APP_PATH', BASE_PATH . '/app');
  11. // Register an autoloader
  12. $loader = new Loader();
  13. $loader->registerDirs(
  14.     [
  15.         APP_PATH . '/controllers/',
  16.         APP_PATH . '/models/',
  17.     ]
  18. );
  19. $loader->register();
  20. // Create a DI
  21. $di = new FactoryDefault();
  22. // Setup the view component
  23. $di->set(
  24.     'view',
  25.     function () {
  26.         $view = new View();
  27.         $view->setViewsDir(APP_PATH . '/views/');
  28.         return $view;
  29.     }
  30. );
  31. // Setup a base URI
  32. $di->set(
  33.     'url',
  34.     function () {
  35.         $url = new UrlProvider();
  36.         $url->setBaseUri('/');
  37.         return $url;
  38.     }
  39. );
  40. // Setup the database service
  41. $di->set(
  42.     'db',
  43.     function () {
  44.         return new DbAdapter(
  45.             [
  46.                 'host'     => '127.0.0.1',
  47.                 'username' => 'pgadmin',
  48.                 'password' => 'P@ssw0rd',
  49.                 'dbname' => 'sample',
  50.             ]
  51.         );
  52.     }
  53. );
  54. $application = new Application($di);
  55. try {
  56.     // Handle the request
  57.     $response = $application->handle();
  58.     $response->send();
  59. } catch (\Exception $e) {
  60.     echo 'Exception: ', $e->getMessage();
  61. }





Storing data using models



postされたデータをデータベースに登録する処理を追加します。

・app/controllers/SignupController.php


  1. <?php
  2. use Phalcon\Mvc\Controller;
  3. class SignupController extends Controller
  4. {
  5.     public function indexAction()
  6.     {
  7.     }
  8.     public function registerAction()
  9.     {
  10.         $user = new Users();
  11.         // Store and check for errors
  12.         $success = $user->save(
  13.             $this->request->getPost(),
  14.             [
  15.                 "name",
  16.                 "email",
  17.             ]
  18.         );
  19.         if ($success) {
  20.             echo "Thanks for registering!";
  21.         } else {
  22.             echo "Sorry, the following problems were generated: ";
  23.             $messages = $user->getMessages();
  24.             foreach ($messages as $message) {
  25.                 echo $message->getMessage(), "<br/>";
  26.             }
  27.         }
  28.         $this->view->disable();
  29.     }
  30. }



Usersモデルのオブジェクトを生成し、postデータを保存(save)します。
データベースのnot null制約を参照し、自動的に必須入力のvalidationを実行。
save時のエラーメッセージには、必須入力の項目名が設定されています。


動作を確認するため、http://[サーバーIP]/signupを表示し「Registor」ボタンをクリックしてみます。

973_01.png

この時、「Exception: could not find driver」というエラーが発生する場合は、
phpのPostgreSQL接続ライブラリがインストールされていません。

PHP 7.2からPostgreSQL 11に接続する
こちらを参考に、php7.2-pgsqlをインストールします。


$ sudo apt-get install php7.2-pgsql



データベースに正しく接続できている場合は、以下のエラーが表示されます。


Sorry, the following problems were generated: name is required
email is required



973_02.png


適当にユーザー名、メールアドレスを入力して「Registor」をクリック。
ちゃんと登録できました。

973_03.png



List of users



登録したユーザー情報をindexページで表示してみます。
コントローラーで、Users::find()を実行。
ビューに設定してやります。

・app/controllers/IndexController.php


  1. <?php
  2. use Phalcon\Mvc\Controller;
  3. class IndexController extends Controller
  4. {
  5.     public function indexAction()
  6.     {
  7.         $this->view->users = Users::find();
  8.     }
  9. }




ビューファイルに、users情報を表示する処理を追加。

・views/index/index.phtml


  1. <?php
  2. echo "<h1>Hello!</h1>";
  3. echo PHP_EOL;
  4. echo PHP_EOL;
  5. echo $this->tag->linkTo(
  6.     '/signup',
  7.     'Sign Up Here!'
  8. );
  9. if ($users->count() > 0) {
  10.     ?>
  11.     <table class="table table-bordered table-hover">
  12.         <thead class="thead-light">
  13.         <tr>
  14.             <th>#</th>
  15.             <th>Name</th>
  16.             <th>Email</th>
  17.         </tr>
  18.         </thead>
  19.         <tfoot>
  20.         <tr>
  21.             <td colspan="3">Users quantity: <?php echo $users->count(); ?></td>
  22.         </tr>
  23.         </tfoot>
  24.         <tbody>
  25.         <?php foreach ($users as $user) { ?>
  26.             <tr>
  27.                 <td><?php echo $user->id; ?></td>
  28.                 <td><?php echo $user->name; ?></td>
  29.                 <td><?php echo $user->email; ?></td>
  30.             </tr>
  31.         <?php } ?>
  32.         </tbody>
  33.     </table>
  34.     <?php
  35. }



日本語も問題なく表示されますね。

973_04.png



Adding Style



最後にbootstrapを使用して色付けします。

・views/index/index.phtml


  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>Phalcon Tutorial</title>
  6.     <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
  7. </head>
  8. <body>
  9. <div class="container">
  10. <?php
  11. echo "<h1>Hello!</h1>";
  12. echo PHP_EOL;
  13. echo PHP_EOL;
  14. echo $this->tag->linkTo(
  15.     '/signup',
  16.     'Sign Up Here!'
  17. );
  18. if ($users->count() > 0) {
  19.     ?>
  20.     <table class="table table-bordered table-hover">
  21.         <thead class="thead-light">
  22.         <tr>
  23.             <th>#</th>
  24.             <th>Name</th>
  25.             <th>Email</th>
  26.         </tr>
  27.         </thead>
  28.         <tfoot>
  29.         <tr>
  30.             <td colspan="3">Users quantity: <?php echo $users->count(); ?></td>
  31.         </tr>
  32.         </tfoot>
  33.         <tbody>
  34.         <?php foreach ($users as $user) { ?>
  35.             <tr>
  36.                 <td><?php echo $user->id; ?></td>
  37.                 <td><?php echo $user->name; ?></td>
  38.                 <td><?php echo $user->email; ?></td>
  39.             </tr>
  40.         <?php } ?>
  41.         </tbody>
  42.     </table>
  43.     <?php
  44. }
  45. ?>
  46. </div>
  47. </body>
  48. </html>



973_05.png


色々ありましたが、一通り基本のチュートリアルはクリアできました。

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2019/03/09(土) 19:09:56|
  2. PHP
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

PHPフレームワーク Phalcon 3 をUbuntu Server 18.04 + nginxで動作させる

PHPフレームワーク Phalcon
https://phalconphp.com/ja/

Ubuntu Server 18.04にインストールしてみます。

Phalconのインストール



こちらを参考にしました。
https://phalconphp.com/en/download/linux

スクリプトを実行。


$ curl -s "https://packagecloud.io/install/repositories/phalcon/stable/script.deb.sh" | sudo bash



実行したら、インストール可能なphalconパッケージを検索してみます。


$ apt search phalcon
Sorting... Done
Full Text Search... Done
php7.0-phalcon/bionic 3.4.3-1+php7.0 amd64
High performance PHP framework

php7.0-phalcon-dbgsym/bionic 3.4.3-1+php7.0 amd64
Debug symbols for php7.0-phalcon

php7.1-phalcon/bionic 3.4.3-1+php7.1 amd64
High performance PHP framework

php7.1-phalcon-dbgsym/bionic 3.4.3-1+php7.1 amd64
Debug symbols for php7.1-phalcon

php7.2-phalcon/bionic 3.4.3-1+php7.2 amd64
High performance PHP framework

php7.2-phalcon-dbgsym/bionic 3.4.3-1+php7.2 amd64
Debug symbols for php7.2-phalcon




ドキュメントには「php5-phalcon」とありますが、流石になくなっているようです。
最新版の「php7.2-phalcon」をインストール。


$ sudo apt install php7.2-phalcon



これでphp7.2もインストールされます。
ただ、php-fpmはインストールされなかったので、手動で追加インストールしました。


$ sudo apt install php7.2-fpm





nginxのインストールと設定



nginxをインストール。


$ sudo apt install nginx



こちらを参考に設定を変更します。
https://docs.phalconphp.com/3.4/en/webserver-setup


$ sudo vi /etc/nginx/sites-available/default



設定はこのようになりました。


  1. server {
  2.         listen 80 default_server;
  3.         listen [::]:80 default_server;
  4.         root /var/www/html;
  5.         # Add index.php to the list if you are using PHP
  6.         index index.php index.html index.htm index.nginx-debian.html;
  7.         server_name _;
  8.         location / {
  9.                 # First attempt to serve request as file, then
  10.                 # as directory, then fall back to displaying a 404.
  11.                 #try_files $uri $uri/ =404;
  12.                 try_files $uri $uri/ /index.php?_url=$uri&$args;
  13.         }
  14.         # pass PHP scripts to FastCGI server
  15.         location ~ [^/]\.php(/|$) {
  16.                 fastcgi_pass unix:/run/php/php7.2-fpm.sock;
  17.                 include snippets/fastcgi-php.conf;
  18.                 include fastcgi_params;
  19.                 if (!-f $document_root$fastcgi_script_name) {
  20.                         return 404;
  21.                 }
  22.                 fastcgi_param PATH_INFO     $fastcgi_path_info;
  23.                 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  24.         }
  25. }




設定を編集したらnginxをリロード


$ sudo service nginx reload



動作確認用のphpプログラムを作成します。


$ sudo vi /var/www/html/info.php



内容は以下の通り。


  1. <?php print_r(get_loaded_extensions());



http://[サーバーIP]/info.phpを表示してみます。

972_01.png

ちゃんとphalconがロードされていることが確認できました。

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2019/03/09(土) 18:17:31|
  2. PHP
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Phalcon 3.4 チュートリアル Basic 2

Phalcon 3.4 チュートリアルの続きを試してみます。
Phalcon 3.4 チュートリアル Basic 1

6.Tutorials - basic
https://docs.phalconphp.com/3.4/en/tutorial-base


nginx.conf



このチュートリアルを始める前にnginx.confを変更しました。

Phalcon 3.4 チュートリアル Basic 1
こちらで作成した「public/index.php」
このindex.phpファイルがあるディレクトリをdocument_rootに変更しています。


server {
    root /usr/local/www/public/tutorial/public;
}






Designing a sign-up form



前回作成した「index.phtml」に手を加え、「signup」というリンクを追加してみます。

・app/views/index/index.phtml


  1. <?php
  2. echo "<h1>Hello!</h1>";
  3. echo PHP_EOL;
  4. echo PHP_EOL;
  5. echo $this->tag->linkTo(
  6.     'signup',
  7.     'Sign Up Here!'
  8. );




ブラウザで、http://[サーバーIP]/を表示。

971_01.png


出力されているhtmlは以下のとおりです。
/signupへのリンクに変換されていますね。


  1. <h1>Hello!</h1>
  2. <a href="/signup">Sign Up Here!</a>



ユーティリティークラス「Phalcon\Tag」により、「$this->tag」でリンクが生成できました。
tagクラスの詳細はこちら
View Helpers (Tags)


リンクをクリックすると当然404エラーになります。
リンク先のクラスを作成していきます。
「signup」というリンクなので、命名ルールに従いコントローラーのクラス名は
「SignupController」となります。

・app/controllers/SignupController.php


  1. <?php
  2. use Phalcon\Mvc\Controller;
  3. class SignupController extends Controller
  4. {
  5.     public function indexAction()
  6.     {
  7.     }
  8. }




コントローラーに対応するviewは、命名ルールに従い
「signup/index.phtml」に作成します。

・app/views/signup/index.phtml


  1. <h2>Sign up using this form</h2>
  2. <?php echo $this->tag->form("signup/register"); ?>
  3.     <p>
  4.         <label for="name">Name</label>
  5.         <?php echo $this->tag->textField("name"); ?>
  6.     </p>
  7.     <p>
  8.         <label for="email">E-Mail</label>
  9.         <?php echo $this->tag->textField("email"); ?>
  10.     </p>
  11.     <p>
  12.         <?php echo $this->tag->submitButton("Register"); ?>
  13.     </p>
  14. </form>



signupページが作成できました。

971_02.png

出力されたhtmlは以下のとおり。


  1. <h2>Sign up using this form</h2>
  2. <form action="/signup/register" method="post">
  3.     <p>
  4.         <label for="name">Name</label>
  5.         <input type="text" id="name" name="name" />    </p>
  6.     <p>
  7.         <label for="email">E-Mail</label>
  8.         <input type="text" id="email" name="email" />    </p>
  9.     <p>
  10.         <input type="submit" value="Register" />    </p>
  11. </form>




「Register」ボタンをクリックすると以下のエラーが表示されます。


Exception: Action 'register' was not found on handler 'signup'



SignupControllerにメソッドを追加し、signup/registerページを作成します。

・app/controllers/SignupController.php


  1. <?php
  2. use Phalcon\Mvc\Controller;
  3. class SignupController extends Controller
  4. {
  5.     public function indexAction()
  6.     {
  7.     }
  8.     // このメソッドを追加
  9.     public function registerAction()
  10.     {
  11.     }
  12. }




これで「Register」ボタンをクリックした時、エラーではなく真っ白なページが表示されるようになりました。
「Name」と「E-Mail」をデータベースに保存するためのModelを作成していきます。


Creating a Model



データを保存するためのテーブル「users」をデータベースに作成します。
今回、データベースにはPostgreSQLを使用することにしました。
こちらでインストールしたものです。
FreeBSD 12にPostgreSQL 11.1をインストールし、外部接続を許可する

チュートリアルのcreate文をPostgreSQL用に読み替えます。


  1. CREATE TABLE users (
  2. id serial NOT NULL,
  3. name varchar(70) NOT NULL,
  4. email varchar(70) NOT NULL,
  5. PRIMARY KEY (id)
  6. );



sampleというデータベースに上記のusersテーブルを作成しておきました。

続いて、このテーブルに対応するモデルを作成します。
app/modelsというディレクトリを新規作成。
Users.phpを作成します。

・app/models/Users.php


  1. <?php
  2. use Phalcon\Mvc\Model;
  3. class Users extends Model
  4. {
  5.     public $id;
  6.     public $name;
  7.     public $email;
  8. }




続いて、データベースへの接続設定ですが...ここで引っかかったので次の機会に。

Phalcon 3.4 チュートリアル Basic 3

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2019/03/07(木) 23:42:27|
  2. PHP
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Phalcon 3.4 チュートリアル Basic 1

Phalcon 3.4をFreeBSD 12にインストールしました。
PHPフレームワーク Phalcon 3 をFreeBSD 12 + nginxで動かす

使い方が全くわからないので、チュートリアルを写経してみます。
こちらの
https://docs.phalconphp.com/3.4/en/

6.Tutorials - basicを試していきます。
https://docs.phalconphp.com/3.4/en/tutorial-base


構成ディレクトリの基本形



例に習い「tutorial」というアプリケーションを作成していきます。

PHPフレームワーク Phalcon 3 をFreeBSD 12 + nginxで動かす
こちらでwebサーバーのルートディレクトリを
「/usr/local/www/public」にしました。

ここに「tutorial」というディレクトリを作成します。
「tutorial」ディレクトリの中に、さらに「app」と「public」というディレクトリも作成しておきます。

この時点でのディレクトリはこのようになりました。

953_01.png



Autoloaders



public/index.phpファイルを作成します。
これがアプリケーション実行の起点になります。

・public/index.php


  1. <?php
  2. use Phalcon\Loader;
  3. use Phalcon\Mvc\View;
  4. use Phalcon\Mvc\Application;
  5. use Phalcon\Di\FactoryDefault;
  6. use Phalcon\Mvc\Url as UrlProvider;
  7. // Define some absolute path constants to aid in locating resources
  8. define('BASE_PATH', dirname(__DIR__));
  9. define('APP_PATH', BASE_PATH . '/app');
  10. // Register an autoloader
  11. $loader = new Loader();
  12. $loader->registerDirs(
  13.     [
  14.         APP_PATH . '/controllers/',
  15.         APP_PATH . '/models/',
  16.     ]
  17. );
  18. $loader->register();
  19. // Create a DI
  20. $di = new FactoryDefault();
  21. // Setup the view component
  22. $di->set(
  23.     'view',
  24.     function () {
  25.         $view = new View();
  26.         $view->setViewsDir(APP_PATH . '/views/');
  27.         return $view;
  28.     }
  29. );
  30. // Setup a base URI
  31. $di->set(
  32.     'url',
  33.     function () {
  34.         $url = new UrlProvider();
  35.         $url->setBaseUri('/');
  36.         return $url;
  37.     }
  38. );
  39. $application = new Application($di);
  40. try {
  41.     // Handle the request
  42.     $response = $application->handle();
  43.     $response->send();
  44. } catch (\Exception $e) {
  45.     echo 'Exception: ', $e->getMessage();
  46. }



設定値の意味はおいおい調べていくことにして、「そういうものだ」と割り切りました。
ちゃんと動くか確認したい。




Creating a Controller



appディレクトリの中に「controllers」というディレクトリを作成。
「IndexController.php」というファイルを作成します。

・app/controllers/IndexController.php


  1. <?php
  2. use Phalcon\Mvc\Controller;
  3. class IndexController extends Controller
  4. {
  5.     public function indexAction()
  6.     {
  7.         echo '<h1>Hello!</h1>';
  8.     }
  9. }



ディレクトリの構成はこのようになるはずです。

953_02.png


ブラウザでhttp://[サーバーIP]/tutorial/public/を表示すると、こんな表示になりました。

953_03.png

ちゃんとControllerが呼び出せているようです。



Sending output to a view



ビューの処理を別ファイルに切り分けます。
app/views/indexディレクトリを作成。
index.phtmlファイルを作成します。

・app/views/index/index.phtml


  1. <?php echo "<h1>Hello! Views</h1>";




IndexControllerでechoしていた箇所は削除し、特に処理は記載しません。

・app/controllers/IndexController.php


  1. <?php
  2. use Phalcon\Mvc\Controller;
  3. class IndexController extends Controller
  4. {
  5.     public function indexAction()
  6.     {
  7.         
  8.     }
  9. }



ブラウザをリロードしてみます。

953_04.png

urlでビューファイルが自動的に関連付けられるようですね。

続きはこちら。
Phalcon 3.4 チュートリアル Basic 2

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2019/02/21(木) 22:55:28|
  2. PHP
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
次のページ