LoginSignup
2
3

More than 5 years have passed since last update.

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

Posted at

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

phalconチュートリアル-Basic-

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

こちらを元に勉強しました。
phalconチュートリアル-Basic編-

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

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

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_)は、そのファイルが存在するディレクトリなので、
この場合は、BASE_PATH = そのファイルが存在するディレクトリ と定義したということですね。
同様にAPP_PATHもパスを定義しています。

参考:自動的に定義される定数

// 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: 依存性の注入

// 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は何が違うの? どちらが正しい呼び方?

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 "

Hello!

";

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編-

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

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

Phalconモデルまとめ(1)データ取得の基本
よく見るHTTPステータスコード一覧とその意味を理解する
HTTPについて
クラスPhalcon \ Http \ Response ⇦レスポンスの部分
PhalconのHTTPリクエスト

ファイル構造

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

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編-

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編-

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があるのを確認してください。

  1. 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
  1. phpinfoをみる

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

webgrindを入れる

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

多分、このサイトからかな?
まあ、このサイトでインストールできるみたいなので、ここからしましょう
webgrind

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

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

できたっ!!

phalconチュートリアル-Invo-

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

こちらを元に勉強しました。
phalconチュートリアル-Invo-

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モデルまとめ(1)データ取得の基本に書いてあります。

ちなみに、flashには「success」「error」「notice」「warning」があるそう
参考サイトはこちら→ Phalconのフラッシュメッセージに書いてあります。

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 アクション周りのイベントとかメモ

<?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

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フレームワーク

あとで出てくるPhalconのバリデーションについてはこちら
Phalconモデルまとめ(4) バリデーションとイベント

バリデーションのNumericalityについて詳しい内容はこちら
Class Phalcon\Validation\Validator\Numericality

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

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

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

セッションバッグは、セッションのデータを名前空間で分別するのに役立つ
Class 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

<?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)リレーション

reusableがかかれている
ORMでのキャッシュ

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 でモデルにフォームの内容を保存する

<?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のビューコンポーネント

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」が追加
    }

    // ...
}

終わった・・・・。

2
3
0

Register as a new user and use Qiita more conveniently

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