Symfony-GridFSファイルとORMエンティティを組み合わせます

以前の記事で、ファイルをGridFSにアップロードすることについて書きました。 そこで、 @MongoDB \ Fileとして注釈が付けられた$ fileプロパティを持つMongoDBドキュメントを作成しました。 私はODMドキュメントよりもORMエンティティを頻繁に使用するため、エンティティからドキュメントにアクセスする簡単な方法を探していました。



エンティティとドキュメントの間に直接的な接続を確立することは不可能であり、自分のタイプのフィールドを作成することは悪くないと考えました。 独自のフィールドタイプを定義することにより、ドキュメントリンクの保存方法を制御し、同時にエンティティプロパティを呼び出してこのリンクを返すことができます。



独自のフィールドタイプのクラスを作成する



タイプuploadの列を定義するUploadTypeクラスを作成することから始めましょう。



namespace Dennis\UploadBundle\Types; use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Platforms\AbstractPlatform; class UploadType extends Type { const UPLOAD = 'upload'; public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) { return $platform->getClobTypeDeclarationSQL($fieldDeclaration); } public function getName() { return self::UPLOAD; } public function requiresSQLCommentHint(AbstractPlatform $platform) { return true; } }
      
      





アップロードドキュメントへのリンクをリクエストするには、Doctrine ODM DocumentManagerがそのようなリンクを作成する必要があります。そのためにセッターを追加します。



 use Doctrine\ODM\MongoDB\DocumentManager; // ... private $dm; public function setDocumentManager(DocumentManager $dm) { $this->dm = $dm; }
      
      





アップロードドキュメントのIDのみがデータベースに保存されるようにするには、 convertToDatabaseValueメソッドをオーバーライドして、ドキュメントのIDのみを返すようにします。



 use Dennis\UploadBundle\Document\Upload; use Doctrine\DBAL\Types\ConversionException; // ... public function convertToDatabaseValue($value, AbstractPlatform $platform) { if (empty($value)) { return null; } if ($value instanceof Upload) { return $value->getId(); } throw ConversionException::conversionFailed($value, self::UPLOAD); }
      
      





エンティティがデータベースから受信した後、 Uploadドキュメントへのリンクを返すには、 convertToPHPValueメソッドをオーバーライドします。 以下に示すように、リンクの作成は、ドキュメントのクラスとIDをDocumentManagerクラスのgetReference()メソッドに渡すだけです。 なぜなら convertToDatabaseValueメソッドで、 アップロードドキュメントのIDを返すことにしました。すぐに使用できます。



 // ... public function convertToPHPValue($value, AbstractPlatform $platform) { if (empty($value)) { return null; } return $this->dm->getReference('Dennis\UploadBundle\Document\Upload', $value); }
      
      





DennisUploadBundle:Upload repositoryを使用してドキュメントを取得する代わりに、ドキュメントへのリンクを作成する大きな利点は、ドキュメントがデータベースから受信され、このドキュメントのフィールドが要求されたときにのみ初期化されることです。 DennisUploadBundle:Upload repositoryを使用してドキュメントを検索し、エンティティプロパティに割り当てると、ORM EntityManagerが返す各イメージエンティティに対してドキュメントインスタンスが作成されます。 つまり、100個のエンティティごとに同じ数のドキュメントが作成されますが、これはもちろん非効率的です。 リンクを作成すると、要求したときにのみドキュメントが作成されることが保証されます。



独自のフィールドタイプを登録する



UploadTypeUploadドキュメントを正しく変換できるようになったので、今度はSymfonyアプリケーションで使用します。 Matthias Nobackによるこの記事によると、Doctrineに新しい型を追加するのに最適な場所はバンドルコンストラクターで、ここでODM DocumentManagerの依存関係がUploadTypeにロードされます



 namespace Dennis\UploadBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; use Doctrine\DBAL\Types\Type; class DennisUploadBundle extends Bundle { public function __construct() { if (!Type::hasType('upload')) { Type::addType('upload', 'Dennis\UploadBundle\Types\UploadType'); } } public function boot() { $dm = $this->container->get('doctrine.odm.mongodb.document_manager'); /* @var $type \Dennis\UploadBundle\Types\UploadType */ $type = Type::getType('upload'); $type->setDocumentManager($dm); } }
      
      





UploadTypeを使用する



新しいアップロードフィールドの使用方法を示すために、まずイメージエンティティを作成します。



 namespace Acme\DemoBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity */ class Image { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @ORM\Column(type="string", length=100) */ protected $name; /** * @ORM\Column(type="upload") */ protected $image; public function getId() { return $this->id; } public function setName($name) { $this->name = $name; } public function getName() { return $this->name; } public function setImage($image) { $this->image = $image; } public function getImage() { return $this->image; } }
      
      





$ imageフィールドのアノテーション@ORM \ Columnを見ると、 UploadTypeタイプのタイプ名( “ upload” )をtypeパラメーターに追加するだけで十分であり、Doctrineは$ imageフィールドのベースを読み書きするときにUploadTypeクラスオブジェクトを使用します。



フォーム処理



Imageエンティティ用に作成されたフォームの処理は、エンティティベースのフォームとほぼ同じです。 唯一の追加は、ダウンロードしたファイルがGridFSに保存され、作成されたアップロードドキュメントがImageエンティティの$ imageフィールドに割り当てられることを確認する必要があることです。



 namespace Acme\DemoBundle\Controller; use Dennis\UploadBundle\Document\Upload; class ImageController extends Controller { public function newAction(Request $request) { // ... $form->bind($request); if ($form->isValid()) { /** @var $upload \Symfony\Component\HttpFoundation\File\UploadedFile */ $upload = $image->getImage(); $document = new Upload(); $document->setFile($upload->getPathname()); $document->setFilename($upload->getClientOriginalName()); $document->setMimeType($upload->getClientMimeType()); $dm = $this->get('doctrine.odm.mongodb.document_manager'); $dm->persist($document); $dm->flush(); $image->setImage($document); $em = $this->getDoctrine()->getManager(); $em->persist($image); $em->flush(); } } }
      
      





フォームを正常に保存した後、 画像テーブルを見ると、 アップロード文書のIDが画像フィールドに書き込まれているレコードが作成されていることがわかります。



データベースから画像を取得する



以下で説明するメソッドは、 前の記事の UploadControllerの showActionメソッドとほぼ同じです。 唯一の違いは、 AcmeDemoBundle:Imageリポジトリを使用してImageエンティティを取得し、次にgetImage()を呼び出すだけでUploadドキュメントを取得できることです。 繰り返しますが、アップロードドキュメントはMongoDBから抽出され、getImage()メソッドが呼び出されたときにのみ作成されます。



 /** * @Route("/{id}", name="image_show") */ public function showAction($id) { $image = $this->getDoctrine()->getManager() ->getRepository('AcmeDemoBundle:Image') ->find($id); $response = new Response(); $response->headers->set('Content-Type', $image->getImage()->getMimeType()); $response->setContent($image->getImage()->getFile()->getBytes()); return $response; }
      
      





それだけです! これで、 イメージエンティティのドキュメントのアップロードへのリンクを処理する独自のUploadTypeができました 。 独自のTypeクラスを作成するこのアプローチは、ODMドキュメントとORMエンティティを結合する簡単な方法を提供すると確信しています。



唯一の大きな欠点は、エンティティを保存する前にドキュメントを手動で保存(永続化)する必要があることです。 これは間違いなく、そのような組み合わせでこのような各コントローラーで繰り返したいことではありません。 次回の記事では、この問題を克服しようとします。



All Articles