Phalconチュートリアル Basic,REST, Vokuro, Performance, Invoを攻略!!!!

攻略って書いてるけど、攻略しきれていない部分もたくさん・・・
お許しください、、、。

#phalconチュートリアル-Basic-
phalconのチュートリアルを学習してみます。

こちらを元に勉強しました。
[phalconチュートリアル-Basic編-](https://docs.phalconphp.com/en/3.2/tutorial-base)

macでxamppの上に置いてやっています

##ファイル構造

xamppと一緒に使いたいので、htdocsの中にファイルを置いています。

```
┗ tutorial
   ┣ app
   ┇ ┣ controllers
   ┇ ┇ ┣ IndexController.php
   ┇ ┇ ┗ SignupController.php
   ┇ ┣ models
   ┇ ┇ ┗ Users.php
   ┇ ┗ views
   ┗ public
      ┣ css
      ┣ img
      ┣ js
      ┗ index.php
```
#チュートリアルのコードの意味を徹底解明！ ~「Hello」を出力するまで~

###まずはindex.php

一度全体を見せたあと少しずつ解剖していきます。

```public/index.php

<?php

use Phalcon\Loader; //①オートローダー使うよ！って宣言
use Phalcon\Mvc\View;　 //ビューを使用するために必要な宣言
use Phalcon\Mvc\Application;　 //MVCの煩雑な処理はこれが行なっているらしい
use Phalcon\Di\FactoryDefault; //Phalconに付属しているコンポーネントのほとんどを登録するための宣言
use Phalcon\Mvc\Url as UrlProvider; //URLを作成するために必要な宣言

// Define some absolute path constants to aid in locating resources
define('BASE_PATH', dirname(__DIR__));　//
define('APP_PATH', BASE_PATH . '/app');

// Register an autoloader
$loader = new Loader();

$loader->registerDirs(
    [
        APP_PATH . '/controllers/',
        APP_PATH . '/models/',
    ]
);

$loader->register();

// Create a DI
$di = new FactoryDefault();

// Setup the view component
$di->set(
    'view',
    function () {
        $view = new View();
        $view->setViewsDir(APP_PATH . '/views/');
        return $view;
    }
);

// Setup a base URI
$di->set(
    'url',
    function () {
        $url = new UrlProvider();
        $url->setBaseUri('/');
        return $url;
    }
);

$application = new Application($di);

try {
    // Handle the request
    $response = $application->handle();

    $response->send();
} catch (\Exception $e) {
    echo 'Exception: ', $e->getMessage();
}

```

###解剖します

```public/index.php

<?php

use Phalcon\Loader; //オートローダー使うよ！って宣言
use Phalcon\Mvc\View;　 //ビューを使用するために必要な宣言
use Phalcon\Mvc\Application;　 //MVCの煩雑な処理はこれが行なっているらしい
use Phalcon\Di\FactoryDefault; //Phalconに付属しているコンポーネントのほとんどを登録するための宣言
use Phalcon\Mvc\Url as UrlProvider; //URLを作成するために必要な宣言

```

オートローダーってなんぞや？

>Phalcon\Loader とは、PSR-4 に準拠したオートロードの機能を提供するためのクラスです。
これを利用することによって、Phalcon は Composer に依存しないオートロードを実装することが出来ます。

だそうです。
このサイトが分かりやすい
[What about Phalcon\Loader](https://qiita.com/irihit/items/de9227ab6751c6e2f331)

あとは、宣言なので、ふーんっていう感じでいいのかな？

```index.php
// Define some absolute path constants to aid in locating resources
define('BASE_PATH', dirname(__DIR__));　//
define('APP_PATH', BASE_PATH . '/app');
```

define('A', 'B');　で、AをBと定義するという用法ですね。

dirname(\__DIR__)は、そのファイルが存在するディレクトリなので、
この場合は、<strong>BASE_PATH = そのファイルが存在するディレクトリ　</strong>と定義したということですね。
同様にAPP\_PATHもパスを定義しています。

参考：[自動的に定義される定数](http://php.net/manual/ja/language.constants.predefined.php)


```
// Register an autoloader
$loader = new Loader(); //オートローダーを作る

$loader->registerDirs(
    [
        APP_PATH . '/controllers/',
        APP_PATH . '/models/',
    ]
);

$loader->register(); //上で定義したオートローダーを登録
```

これは、オートローダーを作って、ディレクトリを登録しておきます。
ここでは、controllersとmodelsを登録します。


```
// Create a DI
$di = new FactoryDefault();
```

DIを作ります。
DIは、phlconの特徴の一つでもあるので、知っておきましょう！！
[猿でも分かる! Dependency Injection: 依存性の注入](https://qiita.com/hshimo/items/1136087e1c6e5c5b0d9f)

```
// Setup the view component
$di->set(
    'view',
    function () {
        $view = new View(); //新しいviewを作る
        $view->setViewsDir(APP_PATH . '/views/'); //パスを「/views/」に指定する
        return $view; //変数viewを返します
    }
);
```

Viewの部分を作っていきます。htmlとかがある場所ですね。
viewをセットして、第二引数の関数の中に実行したいことを記します。

```
// Setup a base URI
$di->set(
    'url',
    function () {
        $url = new UrlProvider();
        $url->setBaseUri('/');
        return $url;
    }
);
```

ベースとなるURIを作ります。
[URLとURIは何が違うの？ どちらが正しい呼び方？](https://webtan.impress.co.jp/e/2010/03/09/7539)

urlをセットして、第二引数に実行したいことを記します。
変数urlに、新しいUrlProviderを作って、パスは現在置で良いので、「/」としているのでしょう。
変数urlを返します。


```
$application = new Application($di);

try {
    // Handle the request
    $response = $application->handle(); 

    $response->send();
} catch (\Exception $e) {
    echo 'Exception: ', $e->getMessage();
}
```

applicationの存在意義がわからない・・・そこで調べてみると、このようなことが書かれていました。難しい。

>PhalconでのMVCの操作をオーケストレーションする背後にあるすべてのハードワークは、通常、Phalcon \ Mvc \ Applicationによって行われます。このコンポーネントは、必要なすべての複雑なオペレーションをバックグラウンドでカプセル化し、必要なすべてのコンポーネントをインスタンス化し、MVCパターンを必要に応じて動作させるためにプロジェクトと統合します。

applicationに一度放り込んで、複雑な処理をするということかな？

tryは例外処理を記述する場所です。
まずは、何か実行することを記します。

何かを行うときは、handleを使うそうで、この場合、responseを使いますよっていうことかな？
applicationを代入しているので、resoponseを返すということは、applicationを実行するっていうことかな。

例外が発生したら、エラーのメッセージを返すようになっていますね。


ちなみに
例外処理の記述はこう書くそうです

```
try {
    // 例外が発生する可能性のあるコード
} catch (Exception $e) {
    // 例外が発生した場合に行う処理
}
```


###次に、コントローラーを見ていきます。

```app/controllers/IndexController.php
<?php

use Phalcon\Mvc\Controller; //コントローラーを使うために必要なやつ

class IndexController extends Controller
{
    public function indexAction()
    {

    }
}
```

Controllerを継承して、IndexControllerというクラスを作ります。
indexActionは、「index」というのviewを参照するので、index.phtmlという名前にしておきます。
ちなみに、indexControllerは、indexフォルダを参照しますので、indexフォルダのindex.htmlを参照するということになります。

indexというフォルダの中に、index.phtmlを作りましょう。

###index/phtmlを作る

```app / views / index / index.phtml
<?php echo "<h1>Hello!</h1>";
```

コントローラーから、viewが参照されたので、helloを出力するプログラムを書きます。
そうすると、xamppでローカルホストに行くと、Hello!と表示されるでしょう。


##サインアップフォームの設計の部分
###index.phtmlを書き換える

サインアップフォームの場合は、Hello!の下にsign up here!という文字を作ってみましょう！

```app / views / index / index.phtml
<?php

echo "<h1>Hello!</h1>";

echo PHP_EOL; //改行

echo PHP_EOL; //改行

echo $this->tag->linkTo(
    "signup",
    "Sign Up Here!"
);
```

tag->linkTO　でリンクを生成します。
signupに行くリンクを生成します(ブラウザには、「」Sign Up Here!)と表示される。

つまり、通常のhtmlなら、こんな感じになっているということです。

```
<h1>Hello!</h1>

<a href="/signup">Sign Up Here!</a>
```

##SignupControllerを作る

```app/controllers/SignupController.php
<?php

use Phalcon\Mvc\Controller;

class SignupController extends Controller
{
    public function indexAction()
    {

    }
}
```

SignupControllerは、signupフォルダを参照します。
Controllerを継承して、SignupControllerというクラスを作り、
indexActionでindex.phtmlを参照します。

###signup用のindex.phtmlを作る

```app/views/signup/index.phtml
<h2>Sign up using this form</h2>

<?php echo $this->tag->form("signup/register"); ?> //formタグを作って、signupコントローラーのregisterActionに飛ぶように設定

    <p>
        <label for="name">Name</label> //「Name」と表示されるラベルを作る
        <?php echo $this->tag->textField("name"); ?> //テキストを書くフィールドを作る
    </p>

    <p>
        <label for="email">E-Mail</label>　　//「E-Mail」と表示されるラベルを作る
        <?php echo $this->tag->textField("email"); ?> //テキストを書くフィールドを作る
    </p>

    <p>
        <?php echo $this->tag->submitButton("Register"); ?> //Registerボタンを作る
    </p>

</form>
```

##モデルを作る

###まずデータベースの設定から


ターミナルで以下のコマンドを打ちます。これで、ターミナルからxamppのphpmyadminに入れます。
この後パスワードを入力してください。
(ここは、チュートリアルにはありません。)

```
/Applications/XAMPP/xamppfiles/bin/mysql -u root -p
```


データベースを作ります。ここでは、phalcon_testという名前のデータベースを作ってみました。
ターミナルで下記のコマンドを打ってください。
(ここはチュートリアルにありません。)

```
create database phalcon_test;
```

このデータベースを使うことを宣言します。

```
use phalcon_test;
```


次にテーブルを作ります。
これはチュートリアルのやつですね。
ターミナルに打ち込みます。

```
CREATE TABLE `users` (
    `id`    int(10)     unsigned NOT NULL AUTO_INCREMENT,
    `name`  varchar(70)          NOT NULL,
    `email` varchar(70)          NOT NULL,

    PRIMARY KEY (`id`)
);
```


###モデルを作ります

```app/models/Users.php
<?php

use Phalcon\Mvc\Model; //モデルを使います！という宣言

class Users extends Model　//Modelクラスを継承して、Usersというクラスを作成(Usersテーブルを参照という意味になります)
{
    public $id; 
    public $name;
    public $email;
}
```

モデルの働きを確認すると、、、

>モデルは、主に、対応するデータベーステーブルとの相互作用のルールを管理するために使用されます。

なるほど。
データベーステーブルの情報を書いていけばいいんですね。

ちなみに、publicを使うと、どこからでも読み取り・変更できるようになるらしい・・・。publicがいまいち分からんな。。。


###データベースとの接続
チュートリアルにもある以下のコードをindex.phpに追記します。

```public/index.php

use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;

// Setup the database service
$di->set(
    'db',　  //DIにdbをセット
    function () {
        return new DbAdapter(
            [
                'host'     => 'localhost', //xamppでやっているのでローカルホストを指定
                'username' => 'root', //phpmyadminにログインするときのユーザー名
                'password' => 'secret',　//phpmyadminにログインするときのパスワード
                'dbname'   => 'phalcon_test',  //他のデータベース名にした人は、そのデータベース名を指定
            ]
        );
    }
);
```

###モデルを使用したデータの保存

```app/controllers/SignupController.php
<?php

use Phalcon\Mvc\Controller; //コントローラを使います！

class SignupController extends Controller　//Controllerを継承して、　SignuoControllerクラスを作成(signupフォルダを参照するようになります)
{
    public function indexAction() //index.phtmlを実行
    {

    }

    public function registerAction() register //register.phtmlがあれば、それを実行するが、ないので、以下のコードを実行
    {
        $user = new Users();  //usersを作成

        // Store and check for errors
        $success = $user->save(   //saveはレコードの作成、更新ができる
            $this->request->getPost(),　  //requestを$_POST[$name]と$_POST[$email]で返す
            [
                "name",
                "email",
            ]
        );

        if ($success) {  //もし$successがあれば(さっきのがうまくいったら)
            echo "Thanks for registering!"; //Thanks for registering!と表示
        } else {  //もし$successがなければ(さっきのがうまくいかなかったら)
            echo "Sorry, the following problems were generated: ";　 //Sorry, the following problems were generated:と表示

            $messages = $user->getMessages(); //生成されたメッセージは、getMessages()メソッドで取得できる、それを$messagesに代入

            foreach ($messages as $message) {
                echo $message->getMessage(), "<br/>"; //データを表示
            }
        }

        $this->view->disable(); //register.phtmlがあれば、それを表示しちゃうので、viewの機能を遮断しました。
    }
}
```

中に全部書いてしまった笑
終わりです。



#phalconチュートリアル-REST-

phalconのチュートリアル「REST」を解剖しながらみていきます。

macでXAMPPの上で動かしています。

[チュートリアル-Creating a Simple REST API編-](https://docs.phalconphp.com/en/3.2/tutorial-rest)


全てを解剖した上で、知っておくべき知識がやっとわかったので、
以下の内容をコンプリートしてから先に進みましょう笑笑

でも、まだよくわからないことだらけなので、わからない部分はごめんなさい、笑


[Phalconモデルまとめ(１)データ取得の基本](http://ryo511.blog.jp/archives/37644913.html)
[よく見るHTTPステータスコード一覧とその意味を理解する](https://www.seohacks.net/blog/seo-tech/1147/)
[HTTPについて](http://www.fellow-ship.com/tech/httperr.html)
[クラスPhalcon \ Http \ Response](https://docs.phalconphp.com/en/latest/api/Phalcon_Http_Response)　⇦レスポンスの部分
[PhalconのHTTPリクエスト](http://w.builwing.info/2015/04/16/phalcon%E3%81%AEhttp%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88/)


##ファイル構造
ファイル構造はこんな感じです。チュートリアルと一緒

```
my-rest-api/
    models/
        Robots.php
    index.php
    .htaccess
```


##.htaccessを作る
これは良く分からんので、コピペで。

```my-rest-api/.htaccess
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^((?s).*)$ index.php?_url=/$1 [QSA,L]
</IfModule>
```

##index.phpを作る

分解していきます。

```my-rest-api/index.php
<?php

use Phalcon\Loader;　//オートローダー使うよ！って宣言
use Phalcon\Mvc\Micro;　 //マイクロフレームワークのようなアプリケーションを作成する
use Phalcon\Di\FactoryDefault; //Phalconに付属しているコンポーネントのほとんどを登録するための宣言
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql; //mysqlを使う

// Use Loader() to autoload our model
$loader = new Loader(); //オートローダーを作る

$loader->registerNamespaces( //名前空間を定義する
    [
        'Store\Toys' => __DIR__ . '/models/', //store\Toysを定義。「/models/」以下のディレクトリをロードする
    ]
);

$loader->register();　//上で定義したオートローダーを登録

$di = new FactoryDefault(); //DIを作る

// Set up the database service
$di->set(
    'db',  //データベースを定義
    function () {
        return new PdoMysql(
            [
                'host'     => 'localhost', //XAMPPならlocalhost
                'username' => 'root',  //使用するデータベースの名前を記す
                'password' => 'secret',  //使用するデータベースのパスワードを記す
                'dbname'   => 'robotics', //使用するデータベースの名前を記す
            ]
        );
    }
);

// Create and bind the DI to the application
$app = new Micro($di); //appというマイクロを作って??DIと結びつける

```

```my-rest-api/index.php
// Retrieves all robots
//データの検索の定義
$app->get(
    '/api/robots', //検索する際は「/api/robots」を使う！
    function () use ($app) {
        $phql = 'SELECT * FROM Store\Toys\Robots ORDER BY name'; //Store\Toys\Robotsから、全ての情報を取り出す(名前順に)という作業を変数phqlに代入

        $robots = $app->modelsManager->executeQuery($phql); //さっきのphqlを実行します。

        $data = []; //空のdataを作ります。

        foreach ($robots as $robot) { 
            $data[] = [
                'id'   => $robot->id, //全てのrobotのidとnameをdataに格納します
                'name' => $robot->name, 
            ];
        }

        echo json_encode($data); //dataをjson形式にして表示
    }
);
```
JSON形式ってなんやったっけ・・・

>要するに「JavaScriptの中でオブジェクトを記述する書式」のことです。

>JSONというのは「データを表現するための記法（≒文法）」です。

らしいです。笑



```my-rest-api/index.php
// Searches for robots with $name in their name
$app->get(
    '/api/robots/search/{name}', 「名前」で検索するのは「/api/robots/serch/{name}」
    function ($name) use ($app) {  
        $phql = 'SELECT * FROM Store\Toys\Robots WHERE name LIKE :name: ORDER BY name';　//Store\Toys\Robotsから、全ての情報を取り出す(名前順に)という作業を変数phqlに代入

        $robots = $app->modelsManager->executeQuery( //先ほどのphqlを実行
            $phql,
            [
                'name' => '%' . $name . '%'  //なんじゃこりゃ？？
            ]
        );

        $data = []; //空のデータを作成

        foreach ($robots as $robot) {
            $data[] = [
                'id'   => $robot->id,　　//さっき抽出したやつのidとnameをdataに格納します
                'name' => $robot->name,
            ];
        }

        echo json_encode($data);　//dataをjson形式にして表示
    }
);
```

```my-rest-api/index.php
use Phalcon\Http\Response;

// Retrieves robots based on primary key
$app->get(
    '/api/robots/{id:[0-9]+}',　//主キーで検索するのは「/api/robots/{id:[0-9]+}」を使う
    function ($id) use ($app) {
        $phql = 'SELECT * FROM Store\Toys\Robots WHERE id = :id:'; //「Store\Toys\Robots」から全てのIDを取り出す。

        $robot = $app->modelsManager->executeQuery( //先ほどのphqlを実行
            $phql,
            [
                'id' => $id, //idを表示？代入？
            ]
        )->getFirst(); //最初のレコードを取得

        // Create a response
        $response = new Response();　responseを作る

        if ($robot === false) { //もし変数robotがなかったら
            $response->setJsonContent( 
                [
                    'status' => 'NOT-FOUND' //ステータスを404 NOT FOUNDにする
                ]
            );
        } else {
            $response->setJsonContent( 
                [
                    'status' => 'FOUND', 
                    'data'   => [
                        'id'   => $robot->id, //dataにidとnameを格納
                        'name' => $robot->name
                    ]
                ]
            );
        }

        return $response; responseを返す
    }
);

```

```my-rest-api/index.php
// Adds a new robot 新しいのを入れる
$app->post(
    '/api/robots',　POSTには「/api/robots」を使う
    function () use ($app) {
        $robot = $app->request->getJsonRawBody(); //POSTのbodyを取得する機能を$robotに持たせます

        $phql = 'INSERT INTO Store\Toys\Robots (name, type, year) VALUES (:name:, :type:, :year:)'; //phqlは、「Store\Toys\Robots」に「name」「type」「year」を追加

        $status = $app->modelsManager->executeQuery(
            $phql,
            [
                'name' => $robot->name, //phqlを実行し、nameに名前をtypeにタイプを、yearに年を代入
                'type' => $robot->type,
                'year' => $robot->year,
            ]
        );

        // Create a response
        $response = new Response(); //responseを作る

        // Check if the insertion was successful
        if ($status->success() === true) {　　//successはフラッシュメッセージ
            // Change the HTTP status
            $response->setStatusCode(201, 'Created'); //ステータスのコード設定　404のNOTFOUNDみたいな感じで、201のCreatedを設定しているのだろう。

            $robot->id = $status->getModel()->id;　//$robotに先ほどのstatusの現在のモデル状況からidを参照します。

            $response->setJsonContent( //HTTPレスポンスの本文を作成する
                [
                    'status' => 'OK',  //statusがokの場合はリクエストがなんの問題もなく受理されます
                    'data'   => $robot, //dataに先ほどのrobotを代入
                ]
            );
        } else {
            // Change the HTTP status
            $response->setStatusCode(409, 'Conflict');　//ステータスコードを409のConflictに設定

            // Send errors to the client
            $errors = []; //エラーメッセージを入れるようの空箱を用意

            foreach ($status->getMessages() as $message) { //ステータスのエラーメッセージを取得
                $errors[] = $message->getMessage(); // errorsに格納
            }

            $response->setJsonContent(　//HTTPレスポンスの本文を作成する
                [
                    'status'   => 'ERROR',//ステータスをエラーにして
                    'messages' => $errors, //エラーメッセージをmessagesに代入
                ]
            );
        }

        return $response; responseを返す
    }
);
```



```my-rest-api/index.php
// Updates robots based on primary key アップデート
$app->put(
    '/api/robots/{id:[0-9]+}', 
    function ($id) use ($app) {
        $robot = $app->request->getJsonRawBody(); //POSTのbodyを取得する機能を$robotに持たせます

        $phql = 'UPDATE Store\Toys\Robots SET name = :name:, type = :type:, year = :year: WHERE id = :id:'; //それぞれアップデート

        $status = $app->modelsManager->executeQuery( //先ほどのphqlを実行
            $phql,
            [
                'id'   => $id, //それぞれ代入
                'name' => $robot->name,
                'type' => $robot->type,
                'year' => $robot->year,
            ]
        );

        // Create a response
        $response = new Response(); //レスポンスを作成

        // Check if the insertion was successful
        if ($status->success() === true) {
            $response->setJsonContent( //ステータスがうまくいったら
                [
                    'status' => 'OK' //ステータスをOKにする
                ]
            );
        } else {
            // Change the HTTP status
            $response->setStatusCode(409, 'Conflict'); //うまくいかなければ、409のConflictにする

            $errors = []; //エラーを格納する空箱を用意

            foreach ($status->getMessages() as $message) { //エラーメッセージを取得して、
                $errors[] = $message->getMessage(); //errorsに代入
            }

            $response->setJsonContent(　//HTTPレスポンスの本文を作成する
                [
                    'status'   => 'ERROR', //ステータスをエラーにして
                    'messages' => $errors, //エラーメッセージを表示？
                ]
            );
        }

        return $response;　//レスポンスを返す
    }
);
```

```my-rest-api/index.php
// Deletes robots based on primary key 削除
$app->delete(
    '/api/robots/{id:[0-9]+}',
    function ($id) use ($app) {
        $phql = 'DELETE FROM Store\Toys\Robots WHERE id = :id:'; //削除なので、DELETEがありますね

        $status = $app->modelsManager->executeQuery( //先ほどのphqlを実行
            $phql,
            [
                'id' => $id, //idを表示？？
            ]
        );

        // Create a response
        $response = new Response();

        if ($status->success() === true) { //うまくいったら
            $response->setJsonContent( //HTTPレスポンスの本文を作成する
                [
                    'status' => 'OK' //ステータスをOKにする
                ]
            );
        } else { //うまくいかなければ
            // Change the HTTP status
            $response->setStatusCode(409, 'Conflict'); //ステータスを409のConflictにする

            $errors = [];　//エラーを格納する空箱を作って

            foreach ($status->getMessages() as $message) { //エラーメッセージを取得し
                $errors[] = $message->getMessage(); //errorsに代入
            }

            $response->setJsonContent(　//HTTPレスポンスの本文を作成する
                [
                    'status'   => 'ERROR', //ステータスをERRORにして
                    'messages' => $errors, //エラーメッセージを表示？
                ]
            );
        }

        return $response; //responseを返す
    }
);
```


#phalconチュートリアル-Vokuro-


phalconのチュートリアルを学習してみます。

こちらを元に勉強しました。

[phalconチュートリアル-Vokuro編-](https://docs.phalconphp.com/id/3.3/tutorial-vokuro)

macでxamppの上に置いてやっています

#githubからクローンする

ターミナルでhtdocsがあるところまでいってください。そしてクローンします。
僕は、macでxamppの上に置きたいので、このようなコードになります

```
cd /Applications/XAMPP/xamppfiles/htdocs
git clone https://github.com/phalcon/vokuro.git
```

すると、テンプレが出来上がりました。

次にvokuroに移動して、composerをインストールします
そのあとに、composer installをすればいいらしい

```
cd vokuro 
curl -sS -k https://getcomposer.org/installer | php
composer install
```

#チュートリアル通りにするとエラーに・・・解決策見つけた！！

チュートリアルでは、これにアクセスせよと書かれていますが・・・
http://localhost/vokuro


エラーが出ました。

`Warning: Phalcon\Mvc\View\Engine\Volt\Compiler::compileFile`

でも、解決策見つけましたよ。

エラーに、index.php on line 41がどうのこうの書いてましたけど、そんなん関係ない！！！
cacheフォルダにアクセス権がないためにエラーが発生しているらしいです。

解決策はこれ
ターミナルで以下のコマンドを打ってください
chmod -R 777のあとは、cacheまでのパスを書いてくださいね。

```
chmod -R 777 /Applications/XAMPP/xamppfiles/htdocs/vokuro/cache
```

これで、cacheのアクセス権限はクリア！
もう一度見てみると、いけてました！よかった！


##あとはカリキュラム通り

Vökuróは、Swiftという言語を使って、登録ユーザーのサインアップを確認するための電子メールを送信しているそう。
 sends emails to confirm the sign up of registered users using Swift

composer.jsonはこうなるそうです。
実際に見たら、若干違うけど、ほとんど同じ。

```vokuro/composer.json
{
    "require" : {
        "php" : ">=5.5.0",
        "ext-phalcon" : ">=3.0.0",
        "swiftmailer/swiftmailer" : "^5.4",
        "amazonwebservices/aws-sdk-for-php" : "~1.0"
    }
}
```

app / config / loader.phpには、最後に自動読み込み用の設定があるそう。
うん、確かにあった。

```app/config/loader.php
<?php

// ...

// Use composer autoloader to load vendor classes
require_once BASE_PATH . "/vendor/autoload.php";
```

VökuróはINVOとは違って、コントローラとモデルに名前空間を使っているそう。
ちょっと初心者の僕には理解できない・・・どういうことなんだろう、どういう違いがあるんだろう。

```app/config/loader.php
<?php

use Phalcon\Loader;

$loader = new Loader();

$loader->registerNamespaces(
    [
        "Vokuro\\Models"      => $config->application->modelsDir,
        "Vokuro\\Controllers" => $config->application->controllersDir,
        "Vokuro\\Forms"       => $config->application->formsDir,
        "Vokuro"              => $config->application->libraryDir,
    ]
);

$loader->register();

// ...
```

registerDirectories()を使う代わりに、registerNamespaces()を使うらしい
全ての名前空間？はapp / config / config.phpで定義されたディレクトリを指すらしい。
例えば、Vokuro\Controllersはapp/controllersを指すらしい。

でも、config.phpにそんな記述ないぞ・・・？
まあいっか、

```app/config/config.php
<?php

namespace Vokuro\Controllers;

class AboutController extends ControllerBase
{
    // ...
}
```

create an accountをクリックしたら、エラーが出ました。

(未解決ですが一旦終わります・・・)


#phalconチュートリアル-Performance-

phalconのチュートリアルを学習してみます。

こちらを元に勉強しました。

[phalconチュートリアル学習記録 -Performance編-](https://docs.phalconphp.com/en/3.2/performance)

MacでXAMPPを使用しています。

このチュートリアルでは、パフォーマンスの向上について述べられています。

##xdebugを入れる



1.インストールする
ターミナルで以下を入力

```
brew install homebrew/php/php70-xdebug
```

そしたら、こんなのが出てきました

```
/usr/local/Cellar/php70-xdebug/2.6.0: 4 files, 225.6KB
```

これ、どこなん笑

```
cd /usr/local/Cellar/
```

これで行ったら、無事にありました。

/usr/local/Cellar/php70-xdebug/2.6.0の中に、xdebung.soがあるのを確認してください。

2. php.iniを編集

php.iniの最後の方に以下のコードを付け足します。

```
[xdebug]
zend_extension=/usr/local/Cellar/php70-xdebug/2.6.0/xdebug.so //xdebug.soまでのパス
xdebug.remote_enable=1
xdebug.profiler_enable = On
xdebug.profiler_output_dir = /tmp
xdebug.trace_output_dir = /tmp
```

3. phpinfoをみる

apacheを再起動して、phpinfo()を覗いてください。
Command+Fで検索かけて、「xdebug」が見つかったらオッケーです。


##webgrindを入れる

インストールしてから3時間くらい経過してこの記事を書いているんですが、
どうやって、インストールしたっけ・・・笑

多分、このサイトからかな？
まあ、このサイトでインストールできるみたいなので、ここからしましょう
[webgrind](https://code.google.com/archive/p/webgrind/)


これをXAMPPのhtdocs上にインストールしました。

そして、localhost/webgrindにアクセスしすると・・・

できたっ！！



#phalconチュートリアル-Invo-

phalconのチュートリアルを学習してみます。

こちらを元に勉強しました。
[phalconチュートリアル-Invo-](https://qiita.com/n_oshiumi/items/08e9cd1521a780983e5e)

macでxamppの上に置いてやっています


ターミナルで以下のようにコマンドを打つ
XAMPPの上におきたいからファイル移動しただけなので、好きなところにクローンしてください。

```
cd /Applications/XAMPP/xamppfiles/htdocs
git clone https://github.com/phalcon/invo.git
```

すると、テンプレ的なフォルダたちが出てきます。


http://localhost/invo
これにアクセスすると・・・
エラー出ました。。

-Vökuró編-の時と同じ対処法をやってみます。
cacheまでのパスを書いてください

```
chmod -R 777 /Applications/XAMPP/xamppfiles/htdocs/invo/cache
```

できた、、、けど、cssが適用されていない、笑

まあ良いか、、、あとで考えよう笑

構成からみていきます

```public/index.php
<?php

use Phalcon\Config\Adapter\Ini as ConfigIni;

// ...

// Read the configuration
$config = new ConfigIni(
    APP_PATH . 'app/config/config.ini' //設定ファイルを読み込みます
);

```


configでは、データベースやアプリケーションの登録をしているらしいです。

```app/config/config.ini
[database]
host     = localhost //XAMPPならローカルホストで
username = root //自分のユーザーネームを入れます。「root」の人はそのままで
password = secret //自分のパスワードを入れます
name     = invo //phpmyadminでinvoというデータベースを作っておきましょう

[application]
controllersDir = app/controllers/
modelsDir      = app/models/
viewsDir       = app/views/
pluginsDir     = app/plugins/
formsDir       = app/forms/
libraryDir     = app/library/
baseUri        = /invo/
```

オートローダーの部分を書いていきます

```public/index.php
<?php

/**
 * Auto-loader configuration
 */
require APP_PATH . 'app/config/loader.php'; 

$loader = new Phalcon\Loader(); //オートローダー使うよって宣言します

// We're a registering a set of directories taken from the configuration file
$loader->registerDirs( 
    [
        APP_PATH . $config->application->controllersDir, //設定ファイルで定義されたディレクトリを登録
        APP_PATH . $config->application->pluginsDir,
        APP_PATH . $config->application->libraryDir,
        APP_PATH . $config->application->modelsDir,
        APP_PATH . $config->application->formsDir,
    ]
);

$loader->register(); //登録します
```


これから、サービスの登録に入っていきます。

```app/config/services.php
<?php

/**
 * Load application services
 */
require APP_PATH . 'app/config/services.php'; //パスを設定します

use Phalcon\Mvc\Url as UrlProvider; //urlを使う

// ...

/**
 * The URL component is used to generate all kind of URLs in the application
 */
$di->set(
    'url', //urlのDIをセットします
    function () use ($config) {
        $url = new UrlProvider(); //新しく作成

        $url->setBaseUri(
            $config->application->baseUri //vonfigフォルダのアプリケーションの部分のbaseUriを参照
        );

        return $url; //urlを返す
    }
);
```


これから、リクエストの処理についてやります
これは、index.phpの最後まで行ったら、Phalcon\Mvc\Applicationが実行されて、アプリケーションが実行されるっていう意味らしい・・・

```public/index.php
<?php

use Phalcon\Mvc\Application;

// ...

$application = new Application($di); //アプリケーションでDIを使う

$response = $application->handle(); //$applicationを操作します

$response->send(); //
```


セッションを使いたい場合

```
<?php

use Phalcon\Session\Adapter\Files as Session;

// ...

// Start the session the first time a component requests the session service
$di->set(
    'session',
    function () {
        $session = new Session(); //新しいsessionを作って

        $session->start(); //セッション開始

        return $session; //セッション終了
    } 
);
```

セッションを使用する前に、データベースに接続する必要があります。

```public/index.php
<?php

use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;

// ...

// Database connection is created based on parameters defined in the configuration file
$di->set(
    'db',
    function () use ($config) {
        return new DbAdapter(
            [
                'host'     => $config->database->localhost, //XAMPPなたlocalhost
                'username' => $config->database->username, //ユーザー名
                'password' => $config->database->password, //パスワード
                'dbname'   => $config->database->invo, //データベース名
            ]
        );
    }
);
```

app/views/session/index.voltはログイン情報を要求します。

```app/views/session/index.volt
{{ form('session/start') }}
    <fieldset>
        <div>
            <label for='email'>
                Username/Email //　「Username/Email」というラベルを用意
            </label>

            <div>
                {{ text_field('email') }} //emailを打つスペースを用意
            </div>
        </div>

        <div>
            <label for='password'> //「password」というラベルを表示
                Password
            </label>

            <div>
                {{ password_field('password') }} //passwordを打つスペースを用意
            </div>
        </div>

        <div>
            {{ submit_button('Login') }} //「Login」というボタンを用意し、ボタンを押したらsubmit
        </div>
    </fieldset>
{{ endForm() }}
```

app/controllers/SessionController.phpの「SessionController」の中の「startAction」は、データベースに有効なユーザーのチェックなど、フォームに入力されたデータを検証する作業を記すことができる

>わかりやすくするためにsha1を使用してデータベースにパスワードハッシュを格納しましたが、このアルゴリズムは実際のアプリケーションではお勧めできません。代わりにbcryptを使用してください。

ちなみに、bindは[Phalconモデルまとめ(１)データ取得の基本](http://ryo511.blog.jp/archives/37644913.html)に書いてあります。

ちなみに、flashには「success」「error」「notice」「warning」があるそう
参考サイトはこちら→ [Phalconのフラッシュメッセージ](http://w.builwing.info/2015/04/20/phalcon%E3%81%AE%E3%83%95%E3%83%A9%E3%83%83%E3%82%B7%E3%83%A5%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8/)に書いてあります。

```app/controllers/SessionController.php
<?php

class SessionController extends ControllerBase //ControllerBAseを継承して、SessionControllerを作る
{
    // ...

    private function _registerSession($user)
    {
        $this->session->set(
            'auth',
            [
                'id'   => $user->id, //sessionを使って、idとnameを変数に格納
                'name' => $user->name,
            ]
        );
    }

    /**
     * This action authenticate and logs a user into the application
     * ユーザー認証をして、ログインする
     */
    public function startAction()
    {
        if ($this->request->isPost()) { //POSTでデータを送る
            // Get the data from the user
            $email    = $this->request->getPost('email'); //emailを取得
            $password = $this->request->getPost('password'); //passwordを取得

            // Find the user in the database
            $user = Users::findFirst(
                [
                    "(email = :email: OR username = :email:) AND password = :password: AND active = 'Y'", //データベースに同じemail or username　、passwordがあるか、activeかを調べる。
                    'bind' => [ //bindはこのコードの上のリンクをご覧ください。
                        'email'    => $email,
                        'password' => sha1($password), //これではsha1の方が伝えやすいから使っているが、bcryptを推奨しているらしい
                    ]
                ]
            );

            if ($user !== false) { //データベースにユーザーが存在しなかったら・・・
                $this->_registerSession($user); //フォームが表示されているアクションにユーザーを再度戻す
                $this->flash->success( //うまくいったら...  (フラッシュについてはこのコードのを参考に)
                    'Welcome ' . $user->name
                );

                // 'invoices' controller if the user is valid ユーザーが有効ならinvoicesコントローラーにいく
                return $this->dispatcher->forward( //転送
                    [
                        'controller' => 'invoices', //invoicesコントローラーの
                        'action'     => 'index', //indexActionに
                    ]
                );
            }

            $this->flash->error( //エラーをだす
                'Wrong email/password'
            );
        }

        // Forward to the login form again　もう一回ログインフォームへ
        return $this->dispatcher->forward( //転送
            [
                'controller' => 'session',　//sessionコントローラーの
                'action'     => 'index', //indexActionに
            ]
        );
    }
}
```


バックエンドのセキュリティ

>バックエンドは、登録ユーザーのみがアクセスできるプライベートエリアです。したがって、登録されたユーザーだけがこれらのコントローラにアクセスできることを確認する必要があります。

Dispatcherというコンポーネントを使用し、次に、適切なコントローラをロードし、対応するアクションメソッドを実行します。

```
<?php

use Phalcon\Mvc\Dispatcher;

// ...

/**
 * MVC dispatcher
 */
$di->set(
    'dispatcher',
    function () {
        // ...

        $dispatcher = new Dispatcher(); //新しいdispatcherを生成する

        return $dispatcher; //返す
    }
);
```

EventsManagerという新しいコンポーネントを使用して、イベント管理をする
イベントについてのまとめ記事は参考になります⇨[Phalcon アクション周りのイベントとかメモ](https://qiita.com/nise_nabe/items/87aac65fffc192c1b0f1)


```
<?php

use Phalcon\Mvc\Dispatcher;
use Phalcon\Events\Manager as EventsManager;

$di->set(
    'dispatcher',
    function () {
        // Create an events manager
        $eventsManager = new EventsManager(); //イベントマネージャーを生成

        // Listen for events produced in the dispatcher using the Security plugin セキュリティー・プラグインを使用してディスパッチャーで生成されたイベントをリッスンします。

        $eventsManager->attach( //プラグインはattachで処理を埋め込むように使うらしい
            'dispatch:beforeExecuteRoute',　//ディスパッチループが回るごとに一回でアクションがあったら実行　
            new SecurityPlugin()
        );

        // Handle exceptions and not-found exceptions using NotFoundPlugin 
NotFoundPluginを使用して例外や未発見の例外を処理する
        $eventsManager->attach(
            'dispatch:beforeException',  //例外が投げられると、beforeExceptionが投げられ実行される
            new NotFoundPlugin()
        );

        $dispatcher = new Dispatcher(); //dispatcherを生成する

        // Assign the events manager to the dispatcher イベントマネージャをディスパッチャに割り当てます。
        $dispatcher->setEventsManager($eventsManager);

        return $dispatcher;
    }
);
```


ちなみに、SecurityPluginは、app/plugins/SecurityPlugin.phpにあります。
ACLについてはこちら　⇨ [Phalconのアクセス制御リストACL](http://w.builwing.info/2015/04/22/phalcon%E3%81%AE%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E5%88%B6%E5%BE%A1%E3%83%AA%E3%82%B9%E3%83%88acl/)

```app/plugins/SecurityPlugin.php
<?php

use Phalcon\Acl;
use Phalcon\Events\Event;
use Phalcon\Mvc\User\Plugin;
use Phalcon\Mvc\Dispatcher;

class SecurityPlugin extends Plugin
{
    // ...

    public function beforeExecuteRoute(Event $event, Dispatcher $dispatcher)
    {
        // Check whether the 'auth' variable exists in session to define the active role　アクティブな役割を定義するために 'auth'変数がセッションに存在するかどうかを確認します
        $auth = $this->session->get('auth');

        if (!$auth) {
            $role = 'Guests'; //$authがなければ変数roleに、「Guest」を代入する
        } else {
            $role = 'Users';　//$authがあれば変数roleに、「Users」を代入する
        }

        // Take the active controller/action from the dispatcher　ディスパッチャからアクティブコントローラ/アクションを取得する

        $controller = $dispatcher->getControllerName();//コントローラ名を取得して代入
        $action     = $dispatcher->getActionName(); //アクション名を取得して代入

        // Obtain the ACL list　ACLリストを取得する
        $acl = $this->getAcl(); //ACL生成

        // Check if the Role have access to the controller (resource)　役割($role)がコントローラ（リソース）にアクセスできるかどうかを確認する
        $allowed = $acl->isAllowed($role, $controller, $action);

        if (!$allowed) { 
            // If he doesn't have access forward him to the index controller　インデックスコントローラーにアクセスする権利がなかったら
            $this->flash->error( //エラーメッセージを出す
                "You don't have access to this module"
            );

            $dispatcher->forward(　//転送
                [
                    'controller' => 'index', //indexコントローラーの
                    'action'     => 'index', //indexActionに
                ]
            );

            // Returning 'false' we tell to the dispatcher to stop the current operation　　「false」を返すと、現在の操作を停止するようにディスパッチャに指示します。
            return false;
        }
    }
}
```

ACLリストの取得
$this->getAcl()でACLを取得

```
<?php

use Phalcon\Acl;
use Phalcon\Acl\Role;
use Phalcon\Acl\Adapter\Memory as AclList;

// Create the ACL
$acl = new AclList(); //acl生成

// The default action is DENY access　デフォルトのアクションはDENYアクセスです
$acl->setDefaultAction(
    Acl::DENY
);

// Register two roles, Users is registered users　2つの役割を登録します。ユーザーは登録済みのユーザーです
// and guests are users without a defined identity　ゲストは特定のIDがないユーザーです　
$roles = [
    'users'  => new Role('Users'),
    'guests' => new Role('Guests'),
]; 

foreach ($roles as $role) {
    $acl->addRole($role);
}
```

>各領域のリソースをそれぞれ定義します。コントローラ名はリソースであり、そのアクションはリソースへのアクセスです。各領域のリソースをそれぞれ定義します。コントローラ名はリソースであり、そのアクションはリソースへのアクセスです。

```
<?php

use Phalcon\Acl\Resource;

// ...

// Private area resources (backend) プライベートエリアリソース（バックエンド）
$privateResources = [
    'companies'    => ['index', 'search', 'new', 'edit', 'save', 'create', 'delete'],
    'products'     => ['index', 'search', 'new', 'edit', 'save', 'create', 'delete'],
    'producttypes' => ['index', 'search', 'new', 'edit', 'save', 'create', 'delete'],
    'invoices'     => ['index', 'profile'],
];

foreach ($privateResources as $resourceName => $actions) {
    $acl->addResource(
        new Resource($resourceName), //resourceNameを追加
        $actions
    );
}

// Public area resources (frontend)　公共エリアのリソース（フロントエンド）
$publicResources = [
    'index'    => ['index'],
    'about'    => ['index'],
    'register' => ['index'],
    'errors'   => ['show404', 'show500'],
    'session'  => ['index', 'register', 'start', 'end'],
    'contact'  => ['index', 'send'],
];

foreach ($publicResources as $resourceName => $actions) {
    $acl->addResource(
        new Resource($resourceName), //resourceNameを追加
        $actions
    );
}
```
>ACLは現在、既存のコントローラとその関連するアクションについて認識しています。ロールUsersは、フロントエンドとバックエンドの両方のすべてのリソースにアクセスできます。役割Guestsは公共エリアへのアクセスのみを持っています：

```
<?php

// Grant access to public areas to both users and guests ユーザーとゲストの両方にパブリックエリアへのアクセスを許可する
foreach ($roles as $role) {
    foreach ($publicResources as $resource => $actions) {
        $acl->allow( //アクセスを許可する
            $role->getName(), //名前を取得
            $resource, 
            '*'
        );
    }
}

// Grant access to private area only to role Users 役割のUserのみにプライベートエリアへのアクセスを許可する
foreach ($privateResources as $resource => $actions) {
    foreach ($actions as $action) {
        $acl->allow(
            'Users',
            $resource,
            $action
        );
    }
}
```

CRUDの操作

>これは、Phalconがフォーム、バリデーション、ページネーターなどを使用して簡単に行える非常に一般的なタスクです。
INVO（企業、製品、製品の種類）のデータを操作するほとんどのオプションは、基本的で共通のCRUD（作成、読み取り、更新、削除）を使用して開発されました。各CRUDには、次のファイルが含まれています。

```
invo/
    app/
        controllers/
            ProductsController.php
        models/
            Products.php
        forms/
            ProductsForm.php
        views/
            products/
                edit.volt
                index.volt
                new.volt
                search.volt
```
検索フォーム
>すべてのCRUDは検索フォームから始まります。このフォームは、テーブルにある各フィールド（製品）を表示し、ユーザーが任意のフィールドの検索条件を作成できるようにします。

```invo/app/controllers/productsController.php
<?php

/**
 * The start action, it shows the 'search' view
 */
public function indexAction()
{
    $this->persistent->searchParams = null; //searchparamsをnullにする

    $this->view->form = new ProductsForm(); //新しいformを作る
}
```

ProductsFormのインスタンスがビューに渡されます。
次に扱うフォームについての前提知識として、これは知っておいた方がいいかもしれません
[フロントエンド：フォーム Phalconフレームワーク](https://docs.phalconphp.com/en/3.3/forms)

あとで出てくるPhalconのバリデーションについてはこちら
[Phalconモデルまとめ(4) バリデーションとイベント](http://ryo511.blog.jp/archives/37921445.html)

バリデーションのNumericalityについて詳しい内容はこちら
[Class Phalcon\Validation\Validator\Numericality](https://olddocs.phalconphp.com/en/3.0.0/api/Phalcon_Validation_Validator_Numericality.html)

```app/forms/ProductsForm.php
<?php

use Phalcon\Forms\Form;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Element\Hidden;
use Phalcon\Forms\Element\Select;
use Phalcon\Validation\Validator\Email;
use Phalcon\Validation\Validator\PresenceOf;
use Phalcon\Validation\Validator\Numericality;

class ProductsForm extends Form //Formを継承して、ProductsFromクラスを作成
{
    /**
     * Initialize the products form 初期化
     */
    public function initialize($entity = null, $options = []) //initializeという関数を作成($entituy=null, $options=[]は初期値)
    {
        if (!isset($options['edit'])) { //変数optionsにeditが入っていなければ
            $element = new Text('id'); //変数elementに'id'という1行のテキストを入れるボックス
            $element->setLabel('Id'); //変数elementに'Id'というラベルを代入
            $this->add($element); // で、実際に追加
        } else { //もし変数optionsにeditが入っていれば
            $this->add(new Hidden('id'));  //表示はしないが、データを送りたいからhiddenで追加
        }

        $name = new Text('name'); //変数nameに'name'という1行のテキストボックスを代入
        $name->setLabel('Name'); //変数nameに'Name'というラベルを代入
        $name->setFilters( //データを検証する前にフィルターをかける
            [
                'striptags', //HTMLタグも残しておく (『<p>あ</p>』を普通に表示すると『あ』になるが、これを設定るすと『<p>あ</p>』で出力する)
                'string', //文字列として出力？
            ]
        );
        $name->addValidators( //バリデーションを付け足す
            [
                new PresenceOf( //フィールドにNOT NULL制約が付与されているときに、null値をINSERT/UPDATEしようとした際に発生
                    [
                        'message' => 'Name is required', //メッセージを表示
                    ]
                )
            ]
        );
        $this->add($name); //$nameを表示

        $type = new Select( //セレクトタグを生成??
            'profilesId',
            ProductTypes::find(), //'product_typesからのデータを格納
            [
                'using'      => [
                    'id',
                    'name',
                ],
                'useEmpty'   => true,
                'emptyText'  => '...',
                'emptyValue' => '',
            ]
        );

        $this->add($type); //変数typeを表示

        $price = new Text('price'); //変数priceに1行のテキストボックスを代入
        $price->setLabel('Price'); //変数priceに'Price'というラベルを追加
        $price->setFilters(
            [
                'float', //float型にするってことかな？
            ]
        );
        $price->addValidators( //バリデーションをつける
            [
                new PresenceOf( //フィールドにNOT NULL制約が付与されているときに、null値をINSERT/UPDATEしようとした際に発生
                    [
                        'message' => 'Price is required', //メッセージを表示
                    ]
                ),
                new Numericality( //有効な数字を確認する
                    [
                        'message' => 'Price is required', //メッセージを表示
                    ]
                ),
            ]
        );
        $this->add($price);//$priceを追加
    }
}
```

フォームはだいたいこの構造らしいです
知っといた方がいいやつですね

```
<?php

// Create the element
$name = new Text('name');

// Set its label
$name->setLabel('Name');

// Before validating the element apply these filters
$name->setFilters(
    [
        'striptags',
        'string',
    ]
);

// Apply this validators
$name->addValidators(
    [
        new PresenceOf(
            [
                'message' => 'Name is required',
            ]
        )
    ]
);

// Add the element to the form
$this->add($name);
```

検索の実行

>searchアクションは、2つの動作があります。POST経由でアクセスすると、フォームから送信されたデータに基づいて検索が実行されますが、GET経由でアクセスすると、現在のページがページネーターに移動します。HTTPメソッドを区別するために、Requestコンポーネントを使用してチェックします。

```
<?php

/**
 * Execute the 'search' based on the criteria sent from the 'index'　「インデックス」から送信された基準に基づいて「検索」を実行します。
 * Returning a paginator for the results　結果のページ数を返す
 */
public function searchAction() //search.voltを実行
{
    if ($this->request->isPost()) { //requestがpostだったら
        // Create the query conditions  クエリ条件を作成する
    } else {
        // Paginate using the existing conditions 既存の条件を使用して改ページする
    }

    // ...
}
```

>Phalcon \ Mvc \ Model \ Criteriaの助けを借りて、フォームから送信されたデータ型と値に基づいて、検索条件をインテリジェントに作成できます

以下で使う、criteriaについてはこちら
[Class Phalcon\Mvc\Model\Criteria](https://olddocs.phalconphp.com/en/3.0.0/api/Phalcon_Mvc_Model_Criteria.html)

```
<?php
$query = Criteria::fromInput(  //_POSTのような入力配列に基づいてPhalcon\Mvc\Model\Criteriaを構築する
    $this->di, //diを参照
    'Products',
    $this->request->getPost() //POSTのデータを返す
);
```

>生成されたパラメータをコントローラのセッションバッグに格納します。
セッションバッグは、セッションサービスを使用する要求の間に持続するコントローラ内の特殊な属性です。アクセスされると、この属性は各コントローラーで独立したPhalcon \ Session \ Bagインスタンスを挿入します。

セッションバッグは、セッションのデータを名前空間で分別するのに役立つ
[Class Phalcon\Session\Bag](https://docs.phalconphp.com/en/3.3/api/Phalcon_Session_Bag)

```
$this->persistent->searchParams = $query->getParams();
```

>次に、構築されたパラメータに基づいてクエリを実行します。

```
<?php

$products = Products::find($parameters); //Productsの「$parameters」を照会しているのかな？

if (count($products) === 0) { //$productsが0だったら
    $this->flash->notice( //メッセージを表示
        'The search did not found any products'
    );

    return $this->dispatcher->forward( //forwardは「転送」的な感じ、
        [
            'controller' => 'products', //productsコントローラーの
            'action'     => 'index', //indexActionに
        ]
    );
}
```

>検索で商品が返されない場合は、ユーザーを再度インデックス操作に転送します。返された検索結果をふりかえってみましょう。その後、それらを簡単にナビゲートするためにページ設定を作成します。

Paginationについても見ておきましょう。
[Pagination](https://olddocs.phalconphp.com/en/3.0.0/reference/pagination.html)

```
<?php

use Phalcon\Paginator\Adapter\Model as Paginator;

// ...

$paginator = new Paginator( //Paginatorを生成(表示する情報量を制限するときに使う)
    [
        'data'  => $products,   // 
        'limit' => 5,           // 5ページまで
        'page'  => $numberPage, // アクティブページ
    ]
);

// Get active page in the paginator
$page = $paginator->getPaginate(); //アクティブページを代入
```

>最後に、返されたページを渡して表示します：

```
<?php

$this->view->page = $page;
```


app/models/Products.phpを見てみる
belongsToがかかれてあるやつ
[Phalconモデルまとめ(2)リレーション](http://ryo511.blog.jp/archives/37687385.html)

reusableがかかれている
[ORMでのキャッシュ](https://olddocs.phalconphp.com/en/3.0.0/reference/models-cache.html)


```app/models/Products.php
<?php

use Phalcon\Mvc\Model;

/**
 * Products
 */
class Products extends Model //Modelを継承して、Productsクラスを作る
{
    // ...

    /**
     * Products initializer
     */
    public function initialize() //リクエストごとに一回呼び出され、ORMを使って初期化
    {
        $this->belongsTo( //belongsToは多対1のときに使う　関連するレコードのモデルを返す
            'product_types_id', 
            'ProductTypes',
            'id',
            [
                'reusable' => true, //同じデータが何回も照会されるのを防ぐ
            ]
        );
    }

    // ...
}
```

Productsにある「product_types_id」というローカル属性は、「ProductTypes」モデルに対して、1対多の関係性を持つ
この関係を定義することによって、以下を使用して製品タイプの名前にアクセスできます。

```
<td>{{ product.productTypes.name }}</td>
<td>{{ '%.2f'|format(product.price) }}</td>
```

普通のphpだと

```
<?php echo sprintf('%.2f', $product->price) ?>
```

レコードの作成と更新

「new」と「edit」のビューから、「create」,[save]アクションにデータが送られる

>作成のケースでは、送信されたデータを回復し、新しいProductsインスタンスに割り当てます。

```
<?php

/**
 * Creates a product based on the data entered in the 'new' action 「new」アクションで入力されたデータに基づいて製品を作成します。
 */
public function createAction() 
{
    if (!$this->request->isPost()) { //もしPOSTされていなかったら
        return $this->dispatcher->forward( //転送
            [
                'controller' => 'products', //productsコントローラーの
                'action'     => 'index', //indexActionに
            ]
        );
    }

    $form = new ProductsForm(); //formを生成

    $product = new Products(); //新しいproductsインスタンスを生成

    $product->id               = $this->request->getPost('id', 'int'); //それぞれ取得して代入
    $product->product_types_id = $this->request->getPost('product_types_id', 'int');
    $product->name             = $this->request->getPost('name', 'striptags');
    $product->price            = $this->request->getPost('price', 'double');
    $product->active           = $this->request->getPost('active');

    // ...
}
```

データが代入される前に、(プロダクトフォームで定義したフィルター「以下のフォーム」)フィルタリングされる

```
<?php

// ...

$name = new Text('name');

$name->setLabel('Name');

// Filters for name
$name->setFilters(
    [
        'striptags',
        'string',
    ]
);

// Validators for name
$name->addValidators(
    [
        new PresenceOf(
            [
                'message' => 'Name is required',
            ]
        )
    ]
);

$this->add($name);
```

>最後に、フォームから検証メッセージが返されない場合は、製品インスタンスを保存できます。

```
<?php

// ...

if ($product->save() === false) { //saveがミスったら
    $messages = $product->getMessages(); //メッセージを取得して

    foreach ($messages as $message) {
        $this->flash->error($message); //エラーを表示
    }

    return $this->dispatcher->forward( //転送
        [
            'controller' => 'products', //productsコントローラーの
            'action'     => 'new', //newActionに
        ]
    );
}

$form->clear(); //フォームをクリアする

$this->flash->success( //成功のメッセージを出す
    'Product was created successfully'
);

return $this->dispatcher->forward( //転送
    [
        'controller' => 'products', //productsコントローラーの
        'action'     => 'index', //indexActionに
    ]
);
```


>さて、製品を更新する場合は、まず、編集されたレコードに現在あるデータをユーザーに提示する必要があります。

```
<?php

/**
 * Edits a product based on its id
 */
public function editAction($id)
{
    if (!$this->request->isPost()) { //POSTされなかったら
        $product = Products::findFirstById($id); //最初のID

        if (!$product) { //productsが入っていなければ
            $this->flash->error( //エラーを出す
                'Product was not found'
            );

            return $this->dispatcher->forward( //転送
                [
                    'controller' => 'products', //productsコントローラーの
                    'action'     => 'index', //indexActionに
                ]
            );
        }

        $this->view->form = new ProductsForm( //新しいフォームを作って
            $product,
            [
                'edit' => true,
            ]
        );
    }
}
```

>見つかったデータは、最初のパラメータとしてモデルを渡すことによってフォームにバインドされます。これにより、ユーザーは任意の値を変更してから、saveアクションを実行してデータベースに戻すことができます。

以下出現するバリデーション「isValid()」について
[Phalcon でモデルにフォームの内容を保存する](https://qiita.com/nise_nabe/items/b61b629fd5099435a0d9#formisvalid-%E3%82%92%E4%BD%BF%E3%81%86)

```
<?php

/**
 * Updates a product based on the data entered in the 'edit' action
 */
public function saveAction()
{
    if (!$this->request->isPost()) { //POSTされなかったら
        return $this->dispatcher->forward(　//転送
            [
                'controller' => 'products', //productsコントローラーの
                'action'     => 'index', //indexActionに
            ]
        );
    }

    $id = $this->request->getPost('id', 'int'); //idをint型で取得？

    $product = Products::findFirstById($id); //$idの最初のやつを検索

    if (!$product) { //$productsがなければ
        $this->flash->error( //エラーを出す
            'Product does not exist'
        );

        return $this->dispatcher->forward( //転送
            [
                'controller' => 'products', //productsコントローラーの
                'action'     => 'index', //indexActionに
            ]
        );
    }

    $form = new ProductsForm(); //新しいフォームを作る

    $data = $this->request->getPost(); //データを受け取る

    if (!$form->isValid($data, $product)) { //バリデーションを使う
        $messages = $form->getMessages(); //メッセージを代入

        foreach ($messages as $message) {
            $this->flash->error($message); //エラーメッセージを出力
        }

        return $this->dispatcher->forward( //転送
            [
                'controller' => 'products', //productsコントローラーの
                'action'     => 'new', //newActionに
            ]
        );
    }

    if ($product->save() === false) { //saveがミスったら
        $messages = $product->getMessages(); //メッセージを受け取る

        foreach ($messages as $message) { 
            $this->flash->error($message); //エラーメッセージを表示
        }

        return $this->dispatcher->forward( //転送
            [
                'controller' => 'products', //productsコントローラーの
                'action'     => 'new', //newActionに
            ]
        );
    }

    $form->clear(); //フォームをクリアして

    $this->flash->success( //成功のメッセージを表示
        'Product was updated successfully'
    );

    return $this->dispatcher->forward( //転送
        [
            'controller' => 'products', //productsコントローラーの
            'action'     => 'index', //newActionに
        ]
    );
}
```

ユーザーコンポーネント

アプリケーションのすべてのUI要素とビジュアルスタイルは、主にBootstrapを通じて実現されています。アプリケーションの状態に応じてナビゲーションバーなどの一部の要素が変更されます。たとえば、右上のコーナーでは、ユーザーがアプリケーションにログインしている場合にリンクLog in/Sign UpがLog outに変更されます。

コンポーネントはこうやって書いていくよっていう紹介なのかな？

[Phalconのビューコンポーネント](http://w.builwing.info/2015/04/05/phalcon%E3%81%AE%E3%83%93%E3%83%A5%E3%83%BC%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88/)

```app/library/Elements.php
<?php

use Phalcon\Mvc\User\Component; //コンポーネントを使う

class Elements extends Component //コンポーネントクラスを拡張して、Elementsクラスを作る
{
    public function getMenu() 
    {
        // Register a user component ユーザーの登録のコンポーネント
$di->set(
    'elements',
    function () {
        return new Elements();
    }
);
    }

    public function getTabs()
    {
        // ...
    }
}
```

>ビュー内のコントローラ、プラグイン、またはコンポーネントとして、このコンポーネントは、コンテナに登録されているサービスにアクセスし、以前に登録したサービスと同じ名前の属性にアクセスするだけでアクセスできます。

```
<div class='navbar navbar-fixed-top'>
    <div class='navbar-inner'>
        <div class='container'>
            <a class='btn btn-navbar' data-toggle='collapse' data-target='.nav-collapse'>
                <span class='icon-bar'></span>
                <span class='icon-bar'></span>
                <span class='icon-bar'></span>
            </a>

            <a class='brand' href='#'>INVO</a>

            {{ elements.getMenu() }}  //⇦この部分
        </div>
    </div>
</div>

<div class='container'>
    {{ content() }}

    <hr>

    <footer>
        <p>&copy; Company 2017</p>
    </footer>
</div>
```

動的にタイトルを変更する

>あるオプションと別のオプションを参照すると、現在作業している場所を示すタイトルが動的に変更されます。これは、各コントローラーのイニシャライザーで実現されます。

```
<?php

class ProductsController extends ControllerBase //ControllerBaseクラスを継承して、ProductsControllerクラスを作る
{
    public function initialize()
    {
        // Set the document title
        $this->tag->setTitle( //titleをセット
            'Manage your product types'
        );

        parent::initialize();　//これが実行されると、タイトルに「Manage your product types」が追加
    }

    // ...
}
```


終わった・・・・。