PHP7 + Doctrine ODMでMongoDBを使う

  • 2
    Like
  • 0
    Comment

Symfony Advent Calendar 2016 8日目の記事です。


SymfonyでMongoDBを扱う場合、Doctrine ODM(Object Document Mapper)を使うと、ORMと同様のAPIでMongoDBを扱うことができます。

必要なもの

$ mongo --version
MongoDB shell version v3.4.0

$ php -v
PHP 7.1.0 (cli) (built: Dec  2 2016 11:32:42) ( NTS )

$ php -m | grep mongo
mongodb

インストール

Doctrine ODMは単独のライブラリとしても使えますが、ここではDoctrineMongoDBBundleを使って、Symfonyと一緒に使ってみます。

Symfonyのインストール方法については公式サイトを参照してください。
以下はSymfonyインストーラーを使った例です。

$ symfony new doctrine_odm
$ cd doctrine_odm
$ php bin/console --version
Symfony 3.2.0 (kernel: app, env: dev, debug: true)

次にDoctrineMongoDBBundleをインストールします。
PHP5の場合とPHP7の場合で手順が異なります。

PHP5の場合

composer.jsonに以下の2行を追加します。

{
    "require": {
        "doctrine/mongodb-odm": "^1.1",
        "doctrine/mongodb-odm-bundle": "^3.2"
    }
}

次に、composer updateを実行します。

PHP7の場合

composer.jsonに以下の3行を追加します。

{
    "require": {
        "alcaeus/mongo-php-adapter": "^1.0",
        "doctrine/mongodb-odm": "^1.1",
        "doctrine/mongodb-odm-bundle": "^3.2"
    }
}

MongoDBのドライバは、PHP5とPHP7で異なります。
PHP5のext-mongoは、PHP7では動作しません。
逆に、PHP7のext-mongodbもPHP5では使えません。

alcaeus/mongo-php-adapterは、PHP7のMongoDBドライバをPHP5のMongoDBドライバと同様のAPIで使えるようにするアダプタです。
Doctrine ODMはPHP5のMongoDBドライバを前提としているので、PHP7+Doctrine ODMの組み合わせで使うには、このアダプタが必要です。
本アダプタの注意点として、ネイティブの実装に比べてパフォーマンスが低いという問題が指摘されています(GitHub Issues)。

インストールにはcomposer update --ignore-platform-reqsを実行します。
--ignore-platform-reqsは、PHPのバージョンやPHP拡張等の依存関係チェックを無視するオプションです。
doctrine/mongodbのcomposer.jsonにはext-mongo(PHP5のMongoDBドライバ)が依存ライブラリとして指定されているため、このライブラリを使えないPHP7では、上記オプションを使用する必要があります。

Symfonyの設定

オートローダー設定

// app/autoload.php
use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver;
AnnotationDriver::registerAnnotationClasses();

AppKernelに登録

// app/AppKernel.php
public function registerBundles()
{
    $bundles = [
        // ...
        new Doctrine\Bundle\MongoDBBundle\DoctrineMongoDBBundle(),
    ];

    // ...
}

設定ファイル

# app/config/parameters.yml
parameters:
    mongodb_server: "mongodb://localhost:27017"
# app/config/config.yml
doctrine_mongodb:
    connections:
        default:
            server: "%mongodb_server%"
            options: {}
    default_database: test_database
    document_managers:
        default:
            auto_mapping: true

ドキュメントクラスの追加

$ mkdir src/AppBundle/Document
$ touch src/AppBundle/Document/Product.php

Product.phpの中身は以下。Doctrine ORMと同様、アノテーションでマッピング情報を追加します。

<?php

// src/AppBundle/Document/Product.php
namespace AppBundle\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;

/**
 * @MongoDB\Document
 */
class Product
{
    /**
     * @MongoDB\Id
     */
    protected $id;

    /**
     * @MongoDB\Field(type="string")
     */
    protected $name;

    /**
     * @MongoDB\Field(type="float")
     */
    protected $price;
}

以下のコマンドでgetter/setterを生成します。この辺もORMと同じですね。

$ php bin/console doctrine:mongodb:generate:documents AppBundle

実行テスト

src/AppBundle/Command/MongoDBTestCommand.php に、以下の内容のファイルを作成します。

<?php

namespace AppBundle\Command;

use AppBundle\Document\Product;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class MongoDBTestCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this
            ->setName('app:mongo-test')
            ->setDescription('...')
            ->addArgument('argument', InputArgument::OPTIONAL, 'Argument description')
            ->addOption('option', null, InputOption::VALUE_NONE, 'Option description')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // データの永続化
        $product = new Product();
        $product->setName('Foobar');
        $product->setPrice(19.99);

        $dm = $this->getContainer()->get('doctrine_mongodb')->getManager();
        $dm->persist($product);
        $dm->flush();

        // データ取得
        $product = $dm->getRepository('AppBundle:Product')
            ->findOneBy(['name' => 'Foobar']);

        if (!$product) {
            $output->writeln('Product not found');
        }

        var_dump($product->getName(), $product->getPrice());
    }
}

上記コマンドで実行確認できます。

$ php bin/console app:mongo-test

MongoDBクライアントでも結果を確認してみましょう。

$ mongo test_database
> db.Product.find();
{ "_id" : ObjectId("584559656adfe57b63216d51"), "name" : "Foobar", "price" : 19.99 }

上記手順で作成したプロジェクトはGitHubで公開しています。

まとめ

以上、Doctrine ODMの基本的な使い方です。基本的なAPIはORMと似ていますが、MongoDB独特のAPIを扱うこともできます。詳細は公式ドキュメントを参照してください。

This post is the No.8 article of Symfony Advent Calendar 2016