Symfonyのお勉強3 ~データベース接続~

ページ作成が完了したので最後にデータベースと接続してみたい

様々な方法でデータベースに接続できるのだが、今回はPDOを使用して接続しようと考えた

## データベース接続
```
try {
    $sql = new \PDO ('mysql:dbname=データベース名; host=サーバアドレス;port=ポート番号; charset=utf8', 'データベースのユーザ名', 'データベースのパスワード');
    echo '接続に成功しました。';
} catch (PDOException $e) {
    echo "接続エラー:{$e->getMessage()}";
}
$sql = null;
```
try/catch文で接続したらecho文で成功したと表示し、エラーを検知したらエラーメッセージを表示するようにした
データベースはmysqladminを使用し、ユーザ名とパスワードもmysqladminのログイン時に使用するものを使う
アドレスはコマンド上でサーバーを動かす際に表示されるアドレスを使用
ポート番号はxamppのmysqlのポート番号を使用

それを組み込んだ結果がこちら
![image.png](https://qiita-image-store.s3.amazonaws.com/0/206600/6293c594-53ed-96e9-0b09-f2c625ef3d20.png)
正しく接続されたことが確認できた

##リスト表示
次にデータベースに格納されているデータをリスト表示する

今回使用するテーブル
![image.png](https://qiita-image-store.s3.amazonaws.com/0/206600/72694f64-f978-2496-c0b9-d58094932736.png)

```
//1
$stmt = $sql->query("select * from テーブル名");
//2
$count = 0;
$array[] = "";
//3
foreach ($stmt as $row) {
    $array[$count] = 'Task: ' . $row['カラム１'] . '   Time: ' . $row['カラム２'];
    $count++;
}
$sql = null;
```
### 1.sql文
select文を変数に格納する

### 2.初期値
変数と配列に初期値を設定する
配列arrayは複数データを格納するために使用
変数countは配列に格納する際、どこに格納するかを指定するために使用

### 3.データ取得
データベースからデータを複数取得するためforeach文を使い、データの数だけ繰り返し配列に格納する

![image.png](https://qiita-image-store.s3.amazonaws.com/0/206600/7a5c18e0-952d-8f88-8bbe-7f79a39bcb35.png)

実際にテーブルと全く同じデータを取得し表示できたことが確認できた

##データの追加
次にデータをデータベースに追加する

###ここで問題
テキストフィールドやボタンを作成し、そこから取得しようとしたのだが
GUIではもちろんPHPでは作成できない
PHPのファイルでPHPのタグの外にコードを書けばできるかもしれないがそれではviewの意味がない
ではさきほどのtwigファイルに書けばいいのではないか...
技術不足か調べ方が悪いのか、それかもともと方法がないのかわからないが、できない！！

そのため今回はformを作成しその上からテキストフィールドやボタンを作成しようと考えた

###formの設定
まずデータの保存を行うためにTaskクラスというものを作成しなければならない
![まいける.png](https://qiita-image-store.s3.amazonaws.com/0/206600/9d280a0c-6a8a-bcf6-68da-464e03091b6e.png)

またTask.phpに処理を定義する

```php:Task.php
namespace AppBundle\Entity;

class Task
{
    protected $task;
    protected $dueDate;

    public function getTask()
    {
        return $this->task;
    }

    public function setTask($task)
    {
        $this->task = $task;
    }

    public function getDueDate()
    {
        return $this->dueDate;
    }

    public function setDueDate(\DateTime $dueDate = null)
    {
        $this->dueDate = $dueDate;
    }
}
```

```php:TestController.php
//1
$task = new Task();
//2
$form = $this->createFormBuilder($task)
    ->setMethod('GET')
    ->add('task', TextType::class)
    ->add('dueDate', DateType::class)
    ->add('save', SubmitType::class, array('label' => 'Create Task'))
    ->getForm();

//3
$form->handleRequest($request);

//4
if ($form->isSubmitted() && $form->isValid()) {
   
}
```

###　1.タスクの生成
新しいタスクを生成する
その際にテキストフィールドにセットしておきたい場合は
先ほどのタスクからTask.phpから呼び出すことで可能となる

```
$task->setTask('Write a blog post');
$task->setDueDate(new \DateTime('tomorrow'));
```

### 2.form作成
createFormBuilderを使用しformを作成する
setmethodではメソッドの属性を設定する（使用しなくてもできる場合がある）
addではフォームにテキストフィールドやボタンを追加する
TextTypeではテキストフィールド
DateTypeは時間をプルダウン形式で選択できる
SubmitTypeはボタンを追加する

### 3.Request
フォームが送信されたか認識するもの
これによりプロパティに書き込み検証を行う

### 4.処理
値がsubmitできた時にif文の中に入るためこの中にデータベースに追加する処理などを書く

##表示
最後にControllerからフォームをViewに飛ばし、Viewで表示を行う

```php:Test.php
return $this->render('default/new.html.twig', array(
    'form' => $form->createView(),
));

```

```twig:test.html.twig
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/206600/abe1597e-46f7-9abb-a5f6-5f451d113f0f.png)

## これでようやく...
フォームを作成できたのでようやくデータベースに追加する作業を行う

### データベース追加
項目ごとに別々にsubmitしたので項目ごと取得し、先ほどのif文の中に追加処理を書き込んでいく

```
//5
$taskdata = $form->get('task')->getViewData();
$duedate = $form->get('dueDate')->getViewData();
$due = $duedate['year'].'-'.$duedate['month'].'-'.$duedate['day'];

if ($form->isSubmitted() && $form->isValid()) {
    //6
    $insert = $sql->prepare("insert into symfony.task(task, datetime) VALUES (:task,:due)");
    $insert->bindParam(':task',$taskdata);
    $insert->bindValue(':due',$due);
    $insert->execute();
}
```

### 5.データ取得
先ほどsubmitしたデータを項目ごとに取得する
duedateは取得した時、年月日をまとめて取得してくるため
データベースに格納できるような形に直す

### 6.データの追加
insert文を変数に格納し、execute()で実際にデータベースに追加する
bindParam、bindValueの詳しい説明はこちらで
[PDOでINSERTする（PHPでMySQLにデータ挿入）](https://qiita.com/tabo_purify/items/0a69fd48018c4ebfd2f2)

## 実際に実装してみた結果
![image.png](https://qiita-image-store.s3.amazonaws.com/0/206600/655c3082-4cf3-5432-2f7b-c398497eea36.png)

↓

![image.png](https://qiita-image-store.s3.amazonaws.com/0/206600/cdad61ee-9a04-a111-a6eb-7cdb0bed9e45.png)

```php:GenusController.php
<?php
namespace AppBundle\Controller;

use AppBundle\Entity\Task;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\DateType;

/**
 *
 */
class GenusController extends Controller
{
    /**
     * @Route("/genus/")
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
     */

    public function showAction(Request $request)
    {
        try {
            $sql = new \PDO ('mysql:dbname=symfony; host=127.0.0.1;port=3306; charset=utf8', 'root', '');
            echo '接続に成功しました。';
        } catch (PDOException $e) {
            echo "接続エラー:{$e->getMessage()}";
        }

        $task = new Task();

        $form = $this->createFormBuilder($task)
            //getとpostの違いで苦戦
            ->setMethod('GET')
            ->add('task', TextType::class)
            ->add('dueDate', DateType::class)
            ->add('save', SubmitType::class, array('label' => 'Create Task'))
            ->getForm();

        $form->handleRequest($request);

        $taskdata = $form->get('task')->getViewData();
        $duedate = $form->get('dueDate')->getViewData();
        $due = $duedate['year'].'-'.$duedate['month'].'-'.$duedate['day'];

        if ($form->isSubmitted() && $form->isValid()) {
            $insert = $sql->prepare("insert into symfony.task(task, datetime) VALUES (:task,:due)");
            $insert->bindParam(':task',$taskdata);
            $insert->bindValue(':due',$due);
            $insert->execute();
        }

        $stmt = $sql->query("select * from symfony.task");
        $count = 0;
        $array[] = "";
        foreach ($stmt as $row) {
            $array[$count] = 'Task: ' . $row['task'] . '   Time: ' . $row['datetime'];
            $count++;
        }

        $sql = null;

        return $this->render('genus/show.html.twig', [
            'arrays' => $array,
            'form' => $form->createView(),
            'task' => $taskdata,
            'duedate' => $due
        ]);
    }
}

?>
```

```php:Task.php
<?php
namespace AppBundle\Entity;

class Task
{
    protected $task;
    protected $dueDate;

    public function getTask()
    {
        return $this->task;
    }

    public function setTask($task)
    {
        $this->task = $task;
    }

    public function getDueDate()
    {
        return $this->dueDate;
    }

    public function setDueDate(\DateTime $dueDate = null)
    {
        $this->dueDate = $dueDate;
    }
}

?>
```

```twig:show.html.twig
{% extends 'base.html.twig' %}

{% block title %}Symfonyフレームワーク{% endblock %}

{% block body %}
    <ul>
        {% for array in arrays %}
            <li>{{ array }}</li>
        {% endfor %}
    </ul>
{% endblock %}
{% block form %}
    {{ form_start(form) }}
    {{ form_widget(form) }}
    {{ form_end(form) }}
{% endblock %}
```

```twig:base.html.twig
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>{% block title %}Welcome!{% endblock %}</title>
    {% block stylesheets %}{% endblock %}
    <link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}"/>
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
<form>
    {% block form %}{% endblock %}
</form>
</html>
```


無事実装することに成功した！！！


## 実装してみて
今回簡易的にフレームワークを使用したのだが、まず日本語の資料が少ない！
そのため読み取る力や検索力が必要だと感じた。
また書いてある通りに進めていっても上手くいかなかったり、英語が分からなかったり資料で言っているものが自分のプログラムに存在しなかったりと大変だった....
またフォームから値を取得する際にフォームに作成したメソッドがpopメソッドになっており上手く取得できない状態が続き、直すのに時間がかかりました。
他にもPDOではなくducrightというsymfony独自？のやり方があったのだが全く分からない！！
あまりに理解できないことが多いため少し使いづらさがあったのだが、色々処理をまとめられるため、コードを見るとここで何を行っているかわかりやすい。
phpの知識が少しでもあったから良かったが、phpの知識がない状態だと少しきついかもしれない。