FuelPHPでdoctrine2を使ってみた


仕事でどうしてもFuelPHPでOracleを扱う必要があり、
標準のORMでは扱うことが出来なかったため他の手段を探していた。

Symfonyで使われているdoctrine2が使えれば楽だなと思い少し試した話をメモしておく。

※ データベースのテーブルは作ってある前提

## doctrine2を使うためのライブラリ

調べてみると既にライブラリがあったのでそれを利用してみる。

https://github.com/aspendigital/fuel-doctrine2

## 構成

最初は以下の様な標準のディレクトリ構成にする。

```
yourdirectory/
	core/				# fuelphpのcore 
	packages/			# fuelphpのpackages
	app/				# fuelphpのapp
		bootstrap.php
		classes/
		config/
		views/
		vendor/			# この下にライブラリを置く
```

## インストール

composerでインストールが可能なので、まずcomposer.jsonを作る。

```json:composer.json
{
	"require": {
		"aspendigital/fuel-doctrine2": "dev-master"
	},
	"config": {
		"vendor-dir": "app/vendor"
	},
	"minimum-stability": "dev"
}
```

composerでinstallする。

```
$ curl -s 'http://getcomposer.org/installer' |php
$ php composer.phar install
```

ちゃんとインストールされれば app/vendor の下に色々置かれているはず。

## doctrine2を使うための設定

### autoload

何もしないと読み込まれず使えないので読み込まれるようにする。

```php:app/bootstrap.php
<?php

// Load in the Autoloader
require COREPATH.'classes'.DIRECTORY_SEPARATOR.'autoloader.php';
class_alias('Fuel\\Core\\Autoloader', 'Autoloader');

// Bootstrap the framework DO NOT edit this
require COREPATH.'bootstrap.php';

/**
 * ここから追加
 */
// app/vendor以下をautoloadする
require APPPATH.'vendor'.DIRECTORY_SEPARATOR.'autoload.php';

// EntityとRepositoryが属する\EntityネームスペースをFuelPHPのAutoloaderに追加
Autoloader::add_namespace('Entity', APPPATH.'classes'.DIRECTORY_SEPARATOR.'entity'.DIRECTORY_SEPARATOR);
/**
 * ここまで
 */

Autoloader::add_classes(array(
));

// Register the autoloader
Autoloader::register();

/**
 * Your environment.  Can be set to any of the following:
 *
 * Fuel::DEVELOPMENT
 * Fuel::TEST
 * Fuel::STAGING
 * Fuel::PRODUCTION
 */
Fuel::$env = (isset($_SERVER['FUEL_ENV']) ? $_SERVER['FUEL_ENV'] : Fuel::DEVELOPMENT);

// Initialize the framework with the config file.
Fuel::init('config.php');
```

### db

databaseの設定もやらないと使えない。

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

return array(
	'doctrine2' => array(
		'proxy_dir' => APPPATH.'classes'.DIRECTORY_SEPARATOR.'proxy',
		'proxy_namespace' => 'Proxy',
		'metadata_path' => APPPATH.'classes'.DIRECTORY_SEPARATOR.'entity',
	),
	'default' => array(
		'type' => 'pdo',
		'connection' => array(
			'dsn' => 'oci:dbname=yourdb',		// これはoracle用の設定
			'username' => 'yourdbusername',
			'password' => 'yourdbpasswd',
			'persistent' => false,
		),
		'identifier' => '',
		'table_prefix' => '',
		'charset' => 'utf8',
		'caching' => true,
		'profiling' => false,
	),
);
```

### view

EntityクラスがFuelPHPのセキュリティ機能に引っかかってしまうため、viewで扱うための設定をする。
都度追加するのはめんどくさいので基底クラスを作りそれを継承、継承しているものだけ許可をするようにする。

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

return array(
	// ...
	'security' => array(
		'whitelisted_classes' => array(
			'Fuel\\Core\\Response',
			'Fuel\\Core\\View',
			'Fuel\\Core\\ViewModel',
			'Closure',
			'Entity\\Base',				// これを追加する
		),
	),
	// ...
);
```

## エンティティを作る

定義をyamlなどで作ってコマンドで作れる。
...が、色々工夫しないと作れず、今回はお試しなので一旦手作業で作ることにする。

今回は簡単にキャンペーンのエンティティを作るだけにする。

```php:app/classes/entity/base.php
<?php

namespace Entity;

/**
 * Viewのセキュリティ機能を通過させる為に作る基底クラス
 */
class Base
{
}
```

```php:app/classes/entity/campaign.php
<?php

namespace Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @Entity(repositoryClass="Entity\CampaignRepository")
 * @Table(name="campaigns")
 */
class Campaign extends Base
{
	/**
	 * @Id
	 * @Column(type="integer")
	 * @GeneratedValue(strategy="AUTO")
	 */
	protected $id;

	/**
	 * @Column(type="string", length=512)
	 */
	protected $title;

	public function setId($id)
	{
		$this->id = $id;

		return $this;
	}

	public function setTitle($title)
	{
		$this->title = $title;

		return $this;
	}

	public function getId()
	{
		return $this->id;
	}

	public function getTitle()
	{
		return $this->title;
	}
}
```

リポジトリも作る。
メソッドはキャンペーン一覧を取得するだけ。

```php:app/classes/entity/campaignrepository.php
<?php

namespace Entity;

use Doctrine\ORM\EntityRepository;

class CampaignRepository extends EntityRepository
{
	public function getCampaignList()
	{
		$qb = $this->createQueryBuilder('c');
		return $qb->getQuery()->getResult();
	}
}
```

## Controllerから使ってみる

先ほど作ってリポジトリからキャンペーン一覧を取得するだけの簡単なものを作成。

```php:app/classes/controller/campaign.php
<?php

use \Fuel\Core\Controller;

class Controller_Campaign extends Controller
{
	public function action_list()
	{
		$em = \Fuel\Doctrine::manager();		
		$campaigns = $em->getRepository('Entity\Campaign')->getCampaignList();

		$this->theme
			->set_template('campaign/list.html.twig')
			->set(array(
				'campaigns' => $campaigns,
			));

		return \Response::forge($this->theme);
	}
}
```

## Viewから使う

Controllerにて取得したものをViewから使い表示する。

```twig:app/views/pc/campaign/list.html.twig
{% extends 'pc/layout.html.twig' %}

{% block contents %}
<h1>キャンペーン一覧</h1>

{% if campaigns is empty %}
<p>キャンペーンはありません</p>
{% else %}
<ul>
	{% for campaign in campaigns %}
		<li>{{ campaign.id }} : {{ campaign.title }}</li>
	{% endfor %}
</ul>
{% endif %}
{% endblock contents %}
```

## 終わり

基本のfetchできるかだけ試した。
insert,update,deleteも試したいし、paginationとかも試したい所だけど時間がなく断念。

以上。