二度忘れた事を三度忘れないようにする

しがないフリーランスIT系エンジニア

Phalconを使う際に最初にすることリスト

どのようなサービスであっても個人的にほぼ必須と思っている設定をつらつら書き留める記事です。
こんなのもあるといいYOってのがあれば教えてください。

環境
CentOS 7
Phalcon 3.0.1
PHP 7.0.10

名前空間(Namespace)

使わない人はとことん使わないイメージですが、個人的にはあまりデメリットを感じないので必ず利用するようにしてます。

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

$loader = new \Phalcon\Loader();

$loader->registerNamespaces([
        'Appname\Controllers' => $config->application->controllersDir,
        'Appname\Models' => $config->application->modelsDir
]);

$loader->register();

ローダに名前空間ディレクトリの関係を定義します。「'Appname' => 'app/'」みたいにするとコントローラとかモデルも同的に定義・読み込みするらしいのですが、少々面倒でも細かく定義するようにしています。サブモジュールとかで階層重なるのを防げたりするので。

// app/config/services.php
// 以下を追記(ルーティング設定により不要
$di->set('dispatcher', function () {
    $dispatcher = new Dispatcher();
    $dispatcher->setDefaultNamespace('Appname\Controllers');
    return $dispatcher;
});

デフォルトのnamespaceをここで指定すると、次のルーティング設定時にnamespaceの指定を省略できます。逆に異なるnamespaceを多用するのであればあまりメリットはでないかもしれません。

ルーティング

ルーティングの実装方法はいくつか用意されていますが、極力読みやすく厳しく設定できると思っている方法を使ってます。といっても公式で紹介されている方法ですが。あと正規表現は適当です。

// app/config/router.php
<?php

use Phalcon\Mvc\Router;
use Phalcon\Mvc\Router\Group as RouterGroup;

// デフォルトの動作を無効化
$router = new Router(false);
// 末尾のスラッシュを自動的に取り除く
$router->removeExtraSlashes(true);

// デフォルトルート
$router->add('/', ["controller" => "index", "action" => "index"]);
// 404のパス設定
$router->notFound(["controller" => "index", "action" => "route404"]);

// ここから
$blog = new RouterGroup(['controller' => 'blog']);
$blog->setPrefix('/blog');
$blog->addGet('', ['action' => 'index']);
$blog->addGet('/edit/{id}', ['action' => 'edit']);
$blog->addPost('/edit', ['action' => 'create']);
$router->mount($blog);
// ここまでを1セット

return $router;
// app/config/services.php
// 以下を追記
$di->setShared('router', function () {
    $config = $this->getConfig();
    require $config->application->routerPath; // configにパスを書いています
    return $router;
});

Groupを使った時のルート指定は「''」とすることで定義可能。
なお、わざと1行で書くようにして見通しをよくしてます。コーディング規約的におかしいとか言われた事もありますが、ルーティング定義はPHPで書かれているとはいえ、個人的には見易さを重視します。
あと、書き方が冗長だ、こうすれば動的に定義できてスッキリする、というのはわかっていますが、明示的に設定した以外の動作を取って欲しくない(特にチームで開発している時)ので、パスやメソッドとか細かく書いてます。

Viewの無効化(View不要時)

APIとか作る際にはViewを無効にするだけで、標準のMVCを使うようにしています。これは学習コストの問題だけなので効率的か、といわれると効率的ではないでしょう。

// app/controllers/ControllerBase.php
    public function initialize()
    {
        $this->view->disable();
    }

Viewに処理が到達するまでに実行されればいいので、どこに記述しても問題はないです。APIを作っている時は初期化時に実行させれば記述漏れとかなくなるので、親コントローラのinitializeに記述していることが多いです。

ログ出力

とりあえず実装して、足りない部分はそれぞれのコントローラやモデルで実装します。

// app/config/services.php
// 以下を追記
use Phalcon\Logger;
use Phalcon\Logger\Adapter\File as FileAdapter;
$di->setShared('logger', function () {
    $config = $this->getConfig();
    $logger = new FileAdapter($config->application->applogPath, ['mode' => 'w']);
    return $logger;
});

手っ取り早いのでファイル出力のロガーを実装。syslogやらもあるので必要に応じて変更する。

Exception

エラーはExceptionで処理する事が多かったので、どこのExceptionでも一箇所でコントロール出来るようにします。個人的には設計しやすいのでよく使います。(主要部分のみコード記載)

// app/config/services.php
// 以下のように編集
use Phalcon\Mvc\Dispatcher as MvcDispatcher;
use Phalcon\Dispatcher;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Mvc\Dispatcher\Exception as DispatchException;
use Appname\Controllers\ExceptionModule;
$di->setShared('dispatcher', function () {
    $dispatcher = new MvcDispatcher();
    $dispatcher->setDefaultNamespace('Appname\Controllers');

    $eventsManager = new EventsManager();

    // Attach a listener
    $eventsManager->attach("dispatch:beforeException", new ExceptionModule());

    $dispatcher->setEventsManager($eventsManager);

    return $dispatcher;
});
// app/controllers/ExceptionModule.php
<?php
namespace Appname\Controllers;

use Phalcon\Events\Event;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\Dispatcher\Exception as DispatchException;

class ExceptionModule
{
    public function beforeException(Event $event, Dispatcher $dispatcher, $exception)
    {
        // Handle 404 exceptions
        if ($exception instanceof DispatchException) {
            $dispatcher->forward(array(
                'controller' => 'index',
                'action'     => 'show404'
            ));
            return false;
        }

        // Handle other exceptions
        $dispatcher->forward(array(
            'controller' => 'index',
            'action'     => 'show503'
        ));

        return false;
    }
}

DIのところでbeforeExceptionを独自のものを指定する。いちおう公式に載っている方法ですが、Exception処理部分を別ファイルにして管理しやすいようにしています。(以前、docでもnewして外部ファイルで管理する方法をもうちょっと細かくコード付きで書いてたような記憶がある)
基本的にはinstanceofのif文を必要なだけ追記してException毎に処理を分けていきます。開発時は最初にログ出力処理を書いてデバッグしやすくしたりしてます。