php軽量フレームワークslimでちょっとしたアプリケーションを作る

よくある、ちょっとしたものを作るつもりでフルスタックな感じになっていった記録をサンプル的に書き留めておきます
サンプル機能の内容に脈絡は無いです

## 注意

この記事はslim version2の記事です。3はPSR-7準拠でrequest,responseの書き方がガラッと変わっています。
(個人的にはその辺はフレームワーク側で穏便してくれないかと思うのですが...)

## 概要

- マイクロフレームワークのslimを使用
- DIはよくわからないのでpimpleはサービスのコンテナのような形で使用
- ormはlaravelに使われているEloquentを単体で使用
- monolog 個人的にはちょっとしたものでも開発にロガーは必須
- mailerはswiftmailer
- テンプレートエンジンは個人的趣味でtwig

## 雑感

slimは勝手にテンプレートをレンダリングしたり
余計なことをしないので
$app->get(..  )
で色々試すのが楽です。
思ったことをすぐ試せるのがなによりという時もある

## コード

composer.jsonはこんな感じ


```json:composer.json
{
    "require": {
        "slim/slim": "2.*",
        "pimple/pimple": "*",
        "illuminate/database": "4.0.*",
        "monolog/monolog": "@stable",
        "swiftmailer/swiftmailer": "@stable",
        "twig/twig": "1.*"
    }
}
```

やっぱりormは楽
以下Eloquentのクラス

```php:User.php
<?php
class User extends Illuminate\Database\Eloquent\Model
{
	protected $table = 'user';
	//デフォルトでは多重代入不可なのでホワイトリストで指定する
	protected $fillable = ['email'];

}
```

Eloquentはtableに'created_at','updated_at'カラムが必要

メールのテンプレ

```mail.txt

変数を展開するんよ
{{ email }}

スピリチュアルやね

```


```php:index.php
#php5.5で動作

<?php
require ('path/to/vendor/autoload.php');

use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Events\Dispatcher;
use Illuminate\Container\Container;

//定義したモデルのクラス読み込み
require ('path/to/User.php');

session_start();

$app = new \Slim\Slim();

//コンテナの初期化
$app->c = new \Pimple\Container();

//db
$app->c['db'] = function() {
	return new Capsule;
};
//困ったときは普通にpdoを使いましょう
$app->c['pdo'] = function() use($app) {
	return $app->c['db']->manager->connection()->getPdo();
};

//logger setting
$app->c['log'] = function() {
	$log = new Monolog\Logger('applog');
	$log->pushHandler(new Monolog\Handler\StreamHandler('path/to/logs/application.log', Monolog\Logger::WARNING));
	return $log;
};

// mailer settings
// 外部のSMTPサーバーを使用
$app->c['smtp'] = function() {
	return Swift_SmtpTransport::newInstance('mail.example.com', 587)
		->setUsername('username')
		->setPassword('password');
};

$app->c['mailer'] = function($app) {
	return Swift_Mailer::newInstance($app['smtp']);
};

$app->c['message'] = Swift_Message::newInstance();

$app->c['mail.subject'] = 'テスト件名';

$app->c['mail.from'] = 'from@example.com';

$app->c['mail.from_name'] = 'example';

//テンプレートエンジン初期化
$app->c['twig'] = function() {
	$loader = new Twig_Loader_Filesystem('path/to/tmpl');
	return new Twig_Environment($loader);
};

//before filterでdb初期化(ここでやる必要は無いかも)
$app->hook('slim.before', function() use($app) {
	$db = $app->c['db'];
	//init
	$config = [
		'driver' => 'mysql',
		'host' => 'localhost',
		'database' => 'dbname',
		'username' => 'dbuser',
		'password' => 'dbpass',
		'charset' => 'utf8',
		'collation' => 'utf8_unicode_ci',
		'prefix' => '',
		//全てのエラー発生時に例外を投げる
		'options' => [
			PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
			PDO::ATTR_EMULATE_PREPARES => false,
		]
	];
	$db->addConnection($config);
	$db->setAsGlobal();
	$db->setEventDispatcher(new Dispatcher(new Container));
	$db->bootEloquent();

});

$app->get('/user', function() use($app) {
	$users = User::all();
	var_dump($users);
	//whereの時はgetしないとダメ
	$users_active = User::where('active', '=', 1)->get();
	var_dump($users);
	//テンプレートをレンダリング
	echo $app->c['twig']->render('user.html',[
		'users' => $users
	]);
});

$app->get('/user/add', function() {
	$apply = User::create([
		'email' => 'rin@example.com'
		]);
	//挿入したレコードのID
	echo $user->id;
});
//メール送信
$app->get('/mail', function() use($app) {
	$email = 'hanayo@example.com';
	$mailer = $app->c['mailer'];
	$message = $app->c['message'];
	$message
		->setSubject($app->c['mail.subject'])
		->setFrom([
			$app->c['mail.from'] => $app->c['mail.from_name']
		])
		->setTo([$email])
		//twigでレンダリング
		->setBody($app->c['twig']->render('mail.txt', [
				'email' => $email,
			])
		);

	//
	if (!$mailer->send($message, $failers)) {
		//ログに記録
		$app->c['log']->addWarning('failed to sending mail to: ' . $email);

	}
});

//pdoでの生のSQL発行、トランザクションや例外処理などのサンプル
$app->post('/complex', function() use($app){
	$email = $app->request->post('email');
	$pdo = $app->c['pdo'];
	$pdo->beginTransaction();
	try{
		$stmt = $pdo->prepare("INSERT INTO user(email, column1) VALUES(:email, ([なにか難しいサブクエリ]))");
		$stmt->execute([
			":email" => $email
			]);
		$user_id = $pdo->lastInsertId();

		//さらに何か複雑な処理
        if([何かのエラー]) {
            throw new Exception('エラー');
        }
		
		//コミット
		$pdo->commit();
		//jsonでレスポンスを返す
		header('Content-Type: application/json; charset=utf-8');
		echo json_encode(['status' => 'ok']);

	} catch(PDOException $e1) { //dbの例外処理
		$app->c['log']->addWarning('fail to insert db:' . $e1->getMessage());
		$app->c['log']->addWarning('failed email is : ' . $email);
		//ロールバック
		$pdo->rollback();
		header('Content-Type: application/json; charset=utf-8');
		echo json_encode(['status' => 'ng']);
		exit;
               
	} catch(Exception $e2) { //その他例外処理
		$app->c['log']->addWarning('fail some process :' . $e2->getMessage());
		$app->c['log']->addWarning('failed email is : ' . $email);
		$pdo->rollback();
		header('Content-Type: application/json; charset=utf-8');
		echo json_encode(['status' => 'ng']);	}
});

$app->run();
```