symfony 1.4 メモ（Doctrine）

1. [symfony 1.4 メモ（アクション）](https://qiita.com/shotets/items/dd56c98d397a42fa4f50)
2. [symfony 1.4 メモ（context）](https://qiita.com/shotets/items/ffa0baa3f930d8992dcb)
3. [symfony 1.4 メモ（モデル）](https://qiita.com/shotets/items/535204c3e7ad1decdab4)
4. [symfony 1.4 メモ（schemaの書き方）](https://qiita.com/shotets/items/f55d9e625c78bb8f05e6)
5. **symfony 1.4 メモ（Doctrine）**
6. [symfony 1.4 メモ（フォーム）](https://qiita.com/shotets/items/8343167452d42d67b93e)
7. [symfony 1.4 メモ（コマンド）](https://qiita.com/shotets/items/82d1f9f8d00f25e65197)
8. [symfony 1.4 メモ（ユーティリティ）](https://qiita.com/shotets/items/ca32b99e43badfe53f98)

## WHERE句

### WHERE句
```php
where('u.username = ?', 'jwage')
```

### AND WHERE句
whereは前のwhere句をキャンセルしてしまうので、こちらの方がベター

```php
andWhere('u.username = ?', 'jwage')
```

### AND IN
こちらは前の句をキャンセルしない。andWhereInと同じ。

```php
whereIn('u.id', array(1, 2, 3))
```

```php
andWhereIn('u.id', array(1, 2, 3))
```

### OR IN
```php
orWhereIn('u.id', array(1, 2, 3))
```

### AND NOT IN
```php
whereNotIn('u.id', array(1, 2, 3))
```

```php
andWhereNotIn('u.id', array(1, 2, 3))
```

### OR NOT IN
```php
orWhereNotIn('u.id', array(1, 2, 3))
```

### OR
```php
orWhere('u.username = ?', 'jwage')
```


## FROM, JOIN

### FROM
スペースを空けて別名を指定できる。カンマ区切りで複数指定できる。モデルでリレーションが定義されていれば簡単にJOINできる。

```php
from('User u, u.Phonenumber p')
```

### LEFT JOIN
```php
leftJoin('u.Phonenumber p')
```

### INNER JOIN
```php
innerJoin('u.Profile p')
```

### JOIN
```php
addFrom('u.Phonenumber p')
```


## SELECT

```php
select('u.id, u.username, COUNT(p.id) as num_phonenumbers')

addSelect('u.email_address')

distinct($flag = true)
```


## その他
```php
groupBy('u.id')

addGroupBy('u.username')

having('num_phonenumbers > 0')

addHaving('u.username = ?', 'jwage')

orderBy('u.username')

addOrderBy('u.is_active = ?', 1)

limit(20)

offset(5)
```


## 単一オブジェクトの取得

### プライマリーキーでの取得
```php
$account = AccountTable::getInstance()->find( $id );
$account = Doctrine::getTable('Account')->find( $id );
```

### プライマリーキー以外での取得
```php
$account = AccountTable::getInstance()->findOneBy( 'login_id', $login_id );
$account = Doctrine::getTable('Account')->findOneBy( 'login_id', $login_id );
```

### 複数条件での取得
```php
$account = AccountTable::getInstance()->findOneByLoginIdAndStatus($login_id, "on");
$account = Doctrine::getTable("Account")->findOneByLoginIdAndStatus($login_id, "on");

$account = AccountTable::getInstance()->findOneBy(array('login_id' => $login_id, 'status' => "on")); 
$account = Doctrine::getTable("Account")->findOneBy(array('login_id' => $login_id, 'status' => "on")); 

$q = AccountTable::getInstance()->createQuery("a")
	->where('a.login_id = ?', $login_id)
	->andWhere('a.status = ?', "on");
$account = $q->fetchOne();

$q = Doctrine_Query::create()
	->from('Account a')
	->where('a.created_at > ?', date('Y-m-d H:i:s', time() - 86400 * 30));
$account = $q->fetchOne();
```

### 連想配列での取得
```php
$account = $q->fetchOne()->toArray();
```

### Tableクラス内での取得
```php
class AccountTable extends Doctrine_Table
{
	public static function getObjectById( $id )
	{
		return self::getInstance()->find( $id );
	}
}
```

## 複数オブジェクトの取得

### プライマリーキーでの取得
```php
$accounts = AccountTable::getInstance()->find( array(1, 2) );
$accounts = Doctrine::getTable('Account')->find( array(1, 2) );
```

### プライマリーキー以外での取得
```php
$accounts = AccountTable::getInstance()->findBy( 'status', "on" );
$accounts = Doctrine::getTable('Account')->findBy( 'status', "on" );
```

### 並び順の指定
```php
$accounts = AccountTable::getInstance()->findBy( array( 'status' => "on" ), array('label' => 'ASC') );
$accounts = Doctrine::getTable('Account')->findBy( array( 'status' => "on" ), array('label' => 'ASC') );

$q = AccountTable::getInstance()->createQuery("a")
	->where('a.status = ?', "on")
	->orderBy('a.label', 'ASC');
$accounts = $q->execute();

$q = Doctrine_Query::create()
	->from('Account a')
	->where('a.status = ?', "on")
	->orderBy('a.label', 'ASC');
$accounts = $q->execute();
```

### 連想配列で取得したい場合
```php
$accounts = $q->fetchArray();
```

### 全て取得
```php
$accounts = $q->fetchAll();
```

### プライマリーキーが配列のキーとなる連想配列を取得したい場合
```php
// 戻り値はキーがid、値がラベルの連想配列となる。
$accounts = $q->select("a.id id, a.label label")->execute()->toKeyValueArray('id', 'label');

// こちらはオブジェクト
$user_list = Doctrine_Query::create()->from('User u INDEXBY u.id')->execute();
```

## オブジェクトの更新

```php
// プロパティを変更
$user->username = 'jwage';

//リレーション（hasOne）の値も変更可能
$user->Profile->name = 'Jonathan H. Wage';

//リレーション（hasMany）も
$user->Phonenumbers[]->phonenumber = '6155139185';

//多対多のリレーションも可能
$tag = new Tag;
$tag->name = 'doctrine';
$user->Tags[] = $tag;

// saveで更新
$user->save();
```

多対多の追加処理をモデルクラスのメソッドに入れておけば、もっとシンプルに書ける

```php:Tag.class.php
class Tag extends BaseTag
{
	public function setName($name)
	{
		$tag = Doctrine::getTable('Tag')->findOneByName($name);
		if ($tag)
		{
			$this->assignIdentifier($tag->identifier());
		}
		else
		{
			$this->_set('name', $name);
		}
	}
}
```

```php
$blogPost->Tags[]->name = 'doctrine';
$blogPost->save();
```


## オブジェクトの削除

### オブジェクトを取得してから削除する場合
オブジェクトが既に生成されている場合はこちらが有効。

```php
$user = Doctrine::getTable('User')->find(1);
$user->delete();
```

### deleteクエリで実行する方法
オブジェクトが生成されていない場合は、オブジェクトを取得するクエリを生成する上記方法よりこちらの方がクエリが1回で済むため効率的。

```php
$deleted = Doctrine_Query::create()
	->delete()
	->from('User u')
	->where('u.id = ?', 1)
	->execute();
```


## 生SQLの実行

```php
$query = "SELECT * from something";
$rs = Doctrine_Manager::getInstance()->getCurrentConnection()->fetchAssoc($query);
```

適当なテーブルからコネクタを取得することも可能

```php
$table_user = Doctrine::getTable('User');
$con = $table_user->getConnection();

$sql = 'UPDATE aaaaa SET status = ?, updated_at = ? WHERE id = ? ';
foreach( $ids as $id )
{
	try
	{
		// prepare メソッドで SQL エスケープすることも可能です
		$stmt = $con->prepare($sql);
		$flag = $stmt->execute( array( 2, $date, $id ) );
		// 開放しないと次の処理でエラーが発生することがある。
		$stmt->closeCursor();
		$stmt = NULL;
	}
	catch (Exception $e)
	{
		// 例外処理
	}
}
```

```php
// 基本
$statement = $conn->executeQuery('SELECT * FROM users WHERE id = ?', array(1));
$user = $statement->fetch(); // array( "id" => "1", "name" => "bob" )

// update
$count = $conn->executeUpdate('UPDATE users SET name = ? WHERE id = ?', array('smith', 1));
echo $count; // 1

// update 2
$conn->update('users', array('name' => 'smith'), array('id' => 1));

// insert
$conn->insert('users', array('id' => 2, 'name' => 'mike'));

// delete
$conn->delete('users', array('id' => 1));

// select 複数
$results = $conn->fetchAll('SELECT * FROM users');
/*
array
(
	array( "id" => "1", "name" => "bob" ),
	array( "id" => "2", "name" => "mike" )
);

// select 一件
$user = $conn->fetchAssoc('SELECT * FROM users WHERE id = ?', array(1));
// array( "id" => "1", "name" => "bob" )

// select カラム指定
$name = $conn->fetchColumn('SELECT name FROM users WHERE id = ?', array(1));
echo $name; // "bob"
```

:xx という形式のキーを使って連想配列で指定することも可能。複数に同じ指定をしたい場合はこっちがいいかも。

```php
$sql = 'UPDATE aaaaa SET status = :status, updated_at = :date WHERE id = :id ';
$con->execute( $sql, array( ':status' => 'on', ':date' => $date, ':id' => $id ) );
```

第三引数でパラメータの型を「配列」と指定してあげれば、IN句に配列を渡すことが出来る。
（他のメソッドでもだいたい最後の引数で指定できるみたい）

```php
$conn->fetchAll(
    'select * from users WHERE id IN (?)',
    array( array(1, 2) ),
    array( \Doctrine\DBAL\Connection::PARAM_INT_ARRAY )
);
```


## サブクエリー

```php
$params = $query->getParams();
$q2 = $query->createSubquery()->select('CONCAT(\',\', GROUP_CONCAT( IF(( rt.event_inside_id = r.id  ),1,0)), \',\') as subj')
			->from('LabelsEventInsideType lt')->leftJoin('lt.RelationEventInsideType rt');
$query->select('r.id, (' . $q2->getDql() . ") scnt")->addOrderBy('scnt ' . $sort[1]);
$query->setParams( $params );
```

```php
$params = $query->getParams();
$q2 = $query->createSubquery()->select('CONCAT(\',\', GROUP_CONCAT(DISTINCT (CASE WHEN ae.contract_status = "contract" then 3 WHEN ae.contract_status = "temporary" then 2 WHEN ae.contract_status = "my" then 1 END)), \',\') as subj')->from('AssignEvent ae')->where('ae.supply_id = r.supply_id');
$query->select('r.id, (' . $q2->getDql().") scnt")->addOrderBy('scnt ' . $sort[1]);
$query->setParams( $params );
```


## 特殊なOrderBy

```php
// 値が NULL のものは後ろになるように
$query->addOrderBy("COALESCE( t.".$sort[0] . ',99999999) ' . $sort[1]);

// concat で文字連結してソート
$query->select('r.id, concat(' . implode(", ",$column ) .") cnt")->addOrderBy('cnt ' . $sort[1]);

// 複数条件を数値化して連結し、ソート
$query->select('r.id, concat(IF(( t.info_public_status = 1 ),1,0), IF(( t.signage_public_status = 1 ),1,0)) pstatus')->addOrderBy('pstatus ' . $sort[1]);
```