2011年12月6日火曜日

アノテーションを使用するときはインポートすること!

@Route や @Template のアノテーションを利用するときは、

// these import the "@Route" and "@Template" annotations
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

のように、FrameworkExtraBundle の Configuration\Route と Configuration\Template をインポートすること!!

2011年12月5日月曜日

Actionの戻り値について

Symfony2.0 では、Controller の Action の戻り値は、Response オブジェクト。
ってのはまぁ、良いとして、Response オブジェクトの作成方法についてちょっと自分なりにメモしておく。
(間違ってたらコメントください(´・ω・`))

基本的に以下の3パターン。
( http://symfony.com/doc/current/book/controller.htmlより引用 )

① 自分でレスポンスオブジェクトを作成
use Symfony\Component\HttpFoundation\Response;

public function helloAction()
{
    return new Response('Hello world!');
}
リダイレクトする場合は、このパターン


②render関数を Action 内で使用
自分でテンプレートを指定して、レンダリングを行う。render() の戻り値が Response オブジェクトなので、これをそのまま Action の戻り値とする。
use Symfony\Component\HttpFoundation\Response;

public function helloAction()
{
    return $this->render('AcmeHelloBundle:Welcome:hello.html.twig', array('name' => $name));
}


③ テンプレートに渡すパラメータをActionの戻り値にして、Responce オブジェクト作成は フレームワークにやらせる
自分でテンプレートを指定して、レンダリングを行う。render() の戻り値が Response オブジェクトなので、これをそのまま Action の戻り値とする。
use Symfony\Component\HttpFoundation\Response;

public function helloAction()
{
// array('name' => $name) が 内部で AcmeHelloBundle:Welcome:hello.html.twig に渡され、Response オブジェクトが自動で作成される
    return array('name' => $name)
}


多分こんな感じ。

Custom Repository Class

Doctrineでは、Entityというものを使うけど、これは基本的にデータを保持するためだけのクラス。
で、実際に find などの様にデータベース上のデータをフェッチするには、Repository を使用する。

こんな感じ。(http://docs.symfony.gr.jp/symfony2/book/doctrine.htmlより引用)
$product = $this->getDoctrine()
       -$gt;getRepository('AcmeStoreBundle:Product')
       -$gt;find($id);


...で、このgetRepositoryを見てみると、

/**
 * Gets the repository for an entity class.
 *
 * @param string $entityName The name of the entity.
 * @return EntityRepository The repository class.
 */
public function getRepository($entityName)
{
    $entityName = ltrim($entityName, '\\');
    if (isset($this->repositories[$entityName])) {
        return $this->repositories[$entityName];
    }

    $metadata = $this->getClassMetadata($entityName);
    $customRepositoryClassName = $metadata->customRepositoryClassName;

    if ($customRepositoryClassName !== null) {
        $repository = new $customRepositoryClassName($this, $metadata);
    } else {
        $repository = new EntityRepository($this, $metadata);
    }

    $this->repositories[$entityName] = $repository;

    return $repository;
}

の様になっている。

で、ここで注目するのは

if ($customRepositoryClassName !== null) {
    $repository = new $customRepositoryClassName($this, $metadata);
} else {
    $repository = new EntityRepository($this, $metadata);
}

のとこ。つまりカスタムリポジトリクラスがあれば、それを使用するけど、そうじゃなきゃEntityRepositoryを使うよーってことですね。

ってことで、自分で Repositoryを作成するには、EntityRepositoryを継承して独自にカスタマイズすればOK。
データ取得とかはコントローラでなくてモデル側に書いたほうが良いので、そういうのは独自のリポジトリを用意してそこで処理をさせたほうが綺麗!

ってことらしいです。

2011年10月20日木曜日

Doctrine - コマンドメモ

① データベースの作成
php app/console doctrine:database:create

② Entity Class の作成
php app/console doctrine:generate:entity --entity="AcmeStoreBundle:Product" --fields="name:string(255) price:float description:text"
ちなみに、Doctrineで扱えるフィールドタイプについては、http://docs.symfony.gr.jp/symfony2/book/doctrine.html#book-doctrine-field-typesや、http://www.doctrine-project.org/docs/orm/2.0/en/reference/basic-mapping.html#doctrine-mapping-types に載ってあります。



③ テーブル/スキーマの作成
php app/console doctrine:schema:update --force


④ Entity Class の更新
php app/console doctrine:generate:entities Acme

2011年10月17日月曜日

Model (Doctrine) について②

Model (Doctrine) について① の続きです。

詳しくは、Databases and Doctrine ("The Model")を参照してください。

オブジェクトの永続化


INSERTするときは以下のように、Entityに値をセットし、EntituManagerのpersist()を呼び、最後にflush() を実行すれば良い。

use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;

public function createAction()
{
    // Productインスタンスの作成
    $product = new Product();
    // 値をセット
    $product->setName('A Foo Bar');
    $product->setPrice('19.99');
    $product->setDescription('Lorem ipsum dolor');
    // EntityManager オブジェクトの取得
    $em = $this->getDoctrine()->getEntityManager();
    // データの保存
    $em->persist($product);
    $em->flush();

    return new Response('Created product id '.$product->getId());
}


オブジェクトのフェッチ


Doctrineオブジェクトからフェッチ対象のリポジトリを取得 (getRepository) する。
そのリポジトリオブジェクトに対し、 find, findAll, findBy などでデータを取得できる。

$product = $this->getDoctrine()
    ->getRepository('AcmeStoreBundle:Product')
    ->find($id);

いろんな取得方法がある。

// IDからデータを取得
$product = $repository->find($id);

// 指定したカラムの値からデータを1つ取得
$product = $repository->findOneById($id);
$product = $repository->findOneByName('foo');

// 同様に、指定したカラムの値からデータを取得 (複数)
$products = $repository->findByPrice(19.99);

// 細かい検索条件を付ける場合は以下 (findOneByは1つ、findByは複数取得)
$product = $repository->findOneBy(array('name' => 'foo', 'price' => 19.99));
$product = $repository->findBy(
    array('name' => 'foo'),
    array('price' => 'ASC')
);

// 全て取得
$products = $repository->findAll();

// 自分でクエリーを生成することも簡単
$em = $this->getDoctrine()->getEntityManager();
$query = $em->createQuery(
    'SELECT p FROM AcmeStoreBundle:Product p WHERE p.price > :price ORDER BY p.price ASC'
)->setParameter('price', '19.99');

$products = $query->getResult();

// クエリービルダーを使用することもできる (createQueryBuilder)
$repository = $this->getDoctrine()
    ->getRepository('AcmeStoreBundle:Product');

$query = $repository->createQueryBuilder('p')
    ->where('p.price > :price')
    ->setParameter('price', '19.99')
    ->orderBy('p.price', 'ASC')
    ->getQuery();

$products = $query->getResult();



オブジェクトのアップデートと削除


データをアップデート処理と削除処理について。
ちなみに、update, delete, どちらも最後に flush() を呼ぶ必要があります。

public function updateAction($id)
{
    // データを取得
    $em = $this->getDoctrine()->getEntityManager();
    $product = $em->getRepository('AcmeStoreBundle:Product')->find($id);
    if (!$product) {
        throw $this->createNotFoundException('No product found for id '.$id);
    }
    
    // データに値をセット
    $product->setName('New product name!');
    // データを更新
    // $productの中身を変更して、そのまま flush() すれば更新される。(参照渡しでエンティティを取得しているっぽいね。)
    // よって、persist() とかいちいち呼ぶ必要が無いです。
    $em->flush();

    // 削除はこうやる
    $em->remove($product);
    $em->flush();

    return $this->redirect($this->generateUrl('homepage'));
}


カスタムなリポジトリクラスの作成


前述のように、自分でクエリーを作成 (createQuery) する場合を考える。そのとき、毎回同じコードをコントローラに記述する事になってしまうと煩雑になってしまう。そんなときは、自分でカスタムなリポジトリクラスを作成することで解決できる。

まず、ProductRepository を使いますよーと、アノテーションで記載する。
// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="Acme\StoreBundle\Repository\ProductRepository")
 */
class Product
{
    //...
}

....で、以下のコマンドを実行する

php app/console doctrine:generate:entities Acme

そうすると、リポジトリクラスが作成される(以下)
そこにfindAllOrderedByNameなどのカスタムなプログラムを書いてやる。これでOK。

// src/Acme/StoreBundle/Repository/ProductRepository.php
namespace Acme\StoreBundle\Repository;

use Doctrine\ORM\EntityRepository;

class ProductRepository extends EntityRepository
{
    public function findAllOrderedByName()
    {
        return $this->getEntityManager()
            ->createQuery('SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC')
            ->getResult();
    }
}

Model (Doctrine) について①

Doctrine は, PHPのORM (Object-relational mapping : オブジェクト指向言語におけるオブジェクトと、MySQLやPostgreSQLの様な、リレーショナルデータベースにおけるレコードとを対照させるもの) で、Symfonyで利用されている。

詳しくは、Databases and Doctrine ("The Model")を参照してください。

データベースの情報を設定

まず、データベースに接続するためには, app/config/parameters.ini でホストなどを指定する。

[parameters]
    database_driver="pdo_mysql"
    database_host="localhost"
    database_name="hoge"
    database_user="moge"
    database_password="piyo"

以下のコマンドを叩けば、app/config/parameters.ini の情報に基づいてデータベースを作成してくれる。
php app/console doctrine:database:create

Entity Class の作成

エンティティを、src/Acme/StoreBundle/Entity/ のような場所 (〇〇Bundle/Entity下) に作成する。(このクラス自体は、データを格納するだけ)

まず、以下のコマンドを実行する。
php app/console doctrine:generate:entity --entity="AcmeStoreBundle:Product" --fields="name:string(255) price:float description:text"

すると、ORM情報をアノテーションとして設定されているエンティティクラスが作成される (以下)。
また、Getter と Setter も用意してくれます。

// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\DemoBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Acme\DemoBundle\Entity\Product
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Product
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string $name
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;

    /**
     * @var float $price
     *
     * @ORM\Column(name="price", type="float")
     */
    private $price;

    /**
     * @var text $description
     *
     * @ORM\Column(name="description", type="text")
     */
    private $description;


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set price
     *
     * @param float $price
     */
    public function setPrice($price)
    {
        $this->price = $price;
    }

    /**
     * Get price
     *
     * @return float 
     */
    public function getPrice()
    {
        return $this->price;
    }

    /**
     * Set description
     *
     * @param text $description
     */
    public function setDescription($description)
    {
        $this->description = $description;
    }

    /**
     * Get description
     *
     * @return text 
     */
    public function getDescription()
    {
        return $this->description;
    }
}

ORM情報をYAML等のコンフィグファイルで設定したい場合は、--format=yml のオプションを加えることで可能。(その場合のコンフィグファイルは、src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml となる)

ちなみに、SQLのキーワードとなってる名前をフィールドにつけようとすると、エラーとなるらしい。その場合は、
/** @Column(name="`number`", type="integer") */
private $number;
のようにエスケープが必要らしいです。(Reserved SQL keywords documentation より)


テーブル/スキーマの作成

次の以下のコマンドを実行すれば、自動的にテーブル/スキーマの作成を行なってくれる。
php app/console doctrine:schema:update --force

とりあえず、今回はココまでっ!!

2011年10月14日金曜日

Twigに関するメモ

Twigに関して、少しメモを。

  • テンプレートの継承が可能 (blocklタグ)
  • {{ ... }}: 変数や式の結果を出力, {{ ... }} で出力されるものは、デフォルトでエスケープしてくれる
  • {% ... %}: for ループや if 文等の、テンプレートのロジックを制御するタグ
  • {# ... #} コメントを記述できる
  • {{ path('_demo_hello' }} のようにして、routing configrationに基づいてパスを記述できる
  • url 関数は絶対 URL を生成
  • 画像やcssなどを読み込む際は、{{ asset('css/blog.css') }} などとすることで、アセットを簡単に扱える

Share

Twitter Delicious Facebook Digg Stumbleupon Favorites More