doctrine and nosql
DESCRIPTION
International PHP Conference Spring 2011 talk on Doctrine projects NoSQL libraries for CouchDB and MongoDB.TRANSCRIPT
Doctrine NoSQL
Benjamin Eberlei (SimpleThings GmbH)
About me
Benjamin EberleiWorking at SimpleThings GmbH
http://www.simplethings.deOpen Source contributor
Doctrine2, Symfony2(Zeta Components, PHPUnit, ...)
Twitter @beberleiBlog http://www.whitewashing.de
The Doctrine Project
www.doctrine-project.org
The Doctrine Project is the home of a selectedset of PHP libraries primarily focused onproviding persistence services and relatedfunctionality.
Doctrine Subprojects
DBAL and ORMDocument Mapper (MongoDB, CouchDB, PHPCR)AnnotationsXMLWhat is next?
Doctrine Philosophy
Separate Persistence and Model
Doctrine Philosophy
Similar look and feel
Doctrine Philosophy
Embrace Differences
Why NoSQL Mapper?
Schemaless storage allows:
Arbitrary associationsEmbedded objectsLists and Associative Arrays
No duplicate schema-maintenance!
Doctrine NoSQL History
MongoDB Mapper early 2010 (OpenSky)CouchDB Mapper started in October 2010 (Liip)PHPCR ODM started in early 2011 (Liip)APIs heavily inspired from ORM
SQL and NoSQL Similarities
Extracted common persistence interfacesCovering roughly 10-20% of the use-cases
Simple Finder MethodsInsert/Update/DeleteMetadata API
Support for Annotations/XML/YAML/PHP Mapping
Persistence Interfaces<?phpinterface ObjectManager{ function find($class, $id); function getReference($class, $id); function persist($object); function remove($object); function flush();
function getClassMetadata($class); function getRepository($class);}
Persistence Interfaces<?phpinterface ObjectRepository{ function find($id); function findAll(); function findBy(array $criteria, $orderBy = null, $limit = null, $offset = null ) function findOneBy(array $criteria);}
Sample Document<?php/** @Document */class Message{ /** @Id */ public $id; /** @Field(type="string") */ public $text;}
$message = new Message();$message->setText("Hello World!");
NoSQL benefits<?php/** @Document */class Product{ /** other fields */ /** @Field(type="array") */ public $attributes; /** @Field(type="array") */ public $translations;}
$product->attributes["isbn"] = "A-B-C-D";$product->translations["de"]["name"] = "Ein Produkt";
Working with Objects 1
Creating a new document:
<?php/** @var $dm DocumentManager */$message = new Message();$message->setText("I am new!");
$dm->persist($message);$dm->flush();
echo "ID: " . $message->getId();
Working with Objects 2
Find and update document:
<?php/** @var $dm DocumentManager */$message = $dm->find("Message", 1);$message->setText("New Message");$dm->flush();
Working with Objects 3
Find and remove documents:
<?php/** @var $dm DocumentManager */$repository = $dm->getRepository("User");$criteria = array("status" => "inactive");$users = $repository->findBy($criteria);
foreach ($users AS $user) { $dm->remove($user);}$dm->flush();
Persistence API Use-Cases
Focus on "in memory" object workflowsSpecialized reusable ModulesSymfony2:
User ManagementCommentAdmin Generatorslichess.org
Associations in NoSQL
Pros
Embedded DocumentsReferences between arbitrary types
Cons
No referential integrityNo support for transactions
Association Mappings<?php/** @Document */class Blog{ /** @ReferenceMany */ private $articles; /** @ReferenceOne(targetDocument="User") */ private $owner;}
Association keys<?php$id = "1";$articleSlug = "hello-world";
$blog = $dm->find("Blog", $id);$blog->articles[$articleSlug]->getHeadline();
Embedded Mappings<?php/** @Document */class User{ /** @EmbedMany */ private $phonenumbers; /** @EmbedOne(targetDocument="Address") */ private $address;}
CouchDB and Doctrine
JSON DatastorageHTTP/REST APIMVCC, eventually consistent (Conflicts)ReplicationAttachmentsViews and Map/Reduce in JavascriptCouchDB LuceneDoctrine CouchDB 1.0 Alpha 1
JSON Document{ "_id": "716104ac33c797b12d50c0a6483f1661", "_rev": "1-32db404b78f130fd8f7575905859e19b", "doctrine_metadata": { "type": "MyProject.Document.Message", "associations": { "user": "055fe8a3ab06c3998d27b6d99f5a9bdd" } }, "message": "I am a message"}
Document Version
Implement Optimistic-Locking
<?phpclass Article{ /** @Version */ private $version;}
$article = $dm->find( "Article", $id, $expectedVersion);
Attachments
CouchDB supports Attachments to documentsDoctrine converts into Attachment objectLazy Load binary data from the serverStream support planned
<?phpclass Article{ /** @Attachments */ public $attachments = array();}
Attachments 2<?phpuse Doctrine\CouchDB\Attachment;
$article = $dm->find("Article", 1);$data = $article->attachments["teaser.jpg"]->getContent();
$a = Attachment::createFromBase64data($data, "image/jpg");$article->attachments["author.jpg"] = $a;
$dm->flush();
Views
Doctrine CouchDB maps filesystem to design document:
application/ couchdb/ views/ username/ map.js reduce.js
Use javascript syntax highlighting in your IDE/Editor.
Views 2<?phpuse Doctrine\CouchDB\View\FileFolderDesignDocument;
$path = "/path/application/couchdb";$designDoc = new FileFolderDesignDocument($path);
/* @doc $couch CouchClient */$docName = "myapp";$couch->createDesignDoc($docName, $designDoc);
Query Views<?php/* @var $dm DocumentManager */$query = $dm->createQuery("myapp", "username");$result = $query->setStartKey("b") ->setEndKey("c") ->setLimit(10) ->setSkip(10) ->includeDocs(true) ->execute();
Using include docs creates PHP instances for you.
Lucene Queries
Support for the CouchDB Lucene extension:
<?php$query = $dm->createLuceneQuery("lucenedoc", "users");$result = $query->setQuery('"John Galt" OR "John Wayne"') ->setLimit(10) ->setSkip(10) ->includeDocs(true) ->execute();
MongoDB and Doctrine
Indexing and on the flyqueriesVery fastIn-Place Updates²GridFS, GeolocationShardingDoctrine MongoDB 1.0 Beta2
Complex Associations<?phpclass User{ /** * @ReferenceMany( * targetDocument="Comment", * mappedBy="blogPost", * sort={"date"="desc"}, * limit=5) */ private $last5Comments;}
Query API<?php$qb = $dm->createQueryBuilder('User') ->field('groups') ->all(array('Group 1', 'Group 2')) ->sort("username", "asc") ->limit(10) ->skip(10) ->execute();
Map/Reduce<?php$qb = $dm->createQueryBuilder('Documents\User') ->field('type')->equals('sale') ->map('function() { emit(this.user.$id, 1); }') ->reduce('function(k, vals) { var sum = 0; for (var i in vals) { sum += vals[i]; } return sum; }');
Geospatial Queries<?php/** @Document @Index(keys={"coordinates"="2d"}) */class City{ /** @EmbedOne(targetDocument="Coordinates") */ public $coordinates; /** @Distance */ public $distance;}class Coordinates{ public $lat; public $long;}
Geospatial Queries 2
Execute a Geospatial query and find locations near a point:
<?php/* @var $dm DocumentManager */$cities = $dm->createQuery('City') ->field('coordinates')->near(50, 60) ->execute();
Eventual Migration
Handle simple and complex schema refactorings
<?php/** @Document */class Person{ public $id;
public $name; // old
/** @AlsoLoad("name") */ public $fullName;}
More of Doctrine MongoDB
Support for TreesSupport for Files in MongoGridFSCapped CollectionsTailable Cursors
PHPCR ODM
PHPCR: Port of the Java Content Repository APIJackalope: Access to Apache Jackrabbit in PHPDoctrine PHPCR ODM: PHP objects from PHP Content Repositories
Object to XML Mapper
Convert objects to XML documents and back using metadata
<?php$user = new User();$user->setFirstName('John');$user->setLastName('Doe');$user->setAddress(new Address('123 Street', 'New Haven'));$user->addContact(new CustomerContact('[email protected]'));
$xml = $marshaller->marshalToString($user);$user = $marshaller->unmarshalFromString($xml);
Using Doctrine 2.0.x ORM?
Please checkout 2.1 BETA1
Backwards compatible!You win a present if you can prove otherwise.
Thank you!
Rate this talk:
http://joind.in/talk/view/3515