diff --git a/docs/odm-ng/reference/annotations-reference.rst b/docs/odm-ng/reference/annotations-reference.rst new file mode 100644 index 0000000000..fb94016f56 --- /dev/null +++ b/docs/odm-ng/reference/annotations-reference.rst @@ -0,0 +1,1067 @@ +Annotations Reference +===================== + +In this chapter a reference of every Doctrine 2 ODM Annotation is +given with short explanations on their context and usage. + +@AlsoLoad +--------- + +Specify one or more MongoDB fields to use for loading data if the original field +does not exist. + +.. code-block:: php + + firstName, $this->lastName) = explode(' ', $name); + } + +For additional information on using `@AlsoLoad`_, see +:doc:`Migrations `. + +@ChangeTrackingPolicy +--------------------- + +This annotation is used to change the change tracking policy for a document: + +.. code-block:: php + + `. + +@DefaultDiscriminatorValue +-------------------------- + +This annotation can be used when using `@DiscriminatorField`_. It will be used +as a fallback value if a document has no discriminator field set. This must +correspond to a value from the configured discriminator map. + +.. code-block:: php + + ` hierarchy. +It takes a string as its only argument, which specifies the database field to +store a class name or key (if a discriminator map is used). ODM uses this field +during hydration to select the instantiation class. + +.. code-block:: php + + ` hierarchy. +It takes an array as its only argument, which maps keys to class names. The +class names may be fully qualified or relative to the current namespace. When +a document is persisted to the database, its class name key will be stored in +the discriminator field instead of the |FQCN|. + +.. code-block:: php + + ` query method to populate the property with the +calculated distance value. + +.. code-block:: php + + dm->createQuery('City') + ->geoNear(50, 60) + ->limit(1) + ->getQuery() + ->getSingleResult(); + echo $city->distance; + +@Document +--------- + +Required annotation to mark a PHP class as a document, whose peristence will be +managed by ODM. + +Optional attributes: + +- + db - By default, the document manager will use the MongoDB database defined + in the configuration, but this option may be used to override the database + for a particular document class. +- + collection - By default, the collection name is derived from the document's + class name, but this option may be used to override that behavior. +- + repositoryClass - Specifies a custom repository class to use. +- + indexes - Specifies an array of indexes for this document. +- + writeConcern - Specifies the write concern for this document that overwrites + the default write concern specified in the configuration. It does not overwrite + a write concern given as :ref:`option ` to the ``flush`` + method when committing your documents. + +.. code-block:: php + + amount = (float) $amount; + } + //... + } + + /** @Document(db="finance", collection="wallets") */ + class Wallet + { + /** @EmbedOne(targetDocument="Money") */ + private $money; + + public function setMoney(Money $money) + { + $this->money = $money; + } + //... + } + //... + $wallet = new Wallet(); + $wallet->setMoney(new Money(34.39)); + $dm->persist($wallet); + $dm->flush(); + +Unlike normal documents, embedded documents cannot specify their own database or +collection. That said, a single embedded document class may be used with +multiple document classes, and even other embedded documents! + +Optional attributes: + +- + indexes - Specifies an array of indexes for this embedded document, to be + included in the schemas of any embedding documents. + +@Field +------ + +Marks an annotated instance variable for persistence. Values for this field will +be saved to and loaded from the document store as part of the document class' +lifecycle. + +Optional attributes: + +- + type - Name of the ODM type, which will determine the value's representation + in PHP and BSON (i.e. MongoDB). See :ref:`doctrine_mapping_types` for a list + of types. Defaults to "string". +- + name - By default, the property name is used for the field name in MongoDB; + however, this option may be used to specify a database field name. +- + nullable - By default, ODM will ``$unset`` fields in MongoDB if the PHP value + is null. Specify true for this option to force ODM to store a null value in + the database instead of unsetting the field. + +Examples: + +.. code-block:: php + + ` attribute. + +.. code-block:: php + + `_. +ODM allows mapped field names (i.e. PHP property names) to be used when defining +``keys``. + +.. code-block:: php + + `. ``SINGLE_COLLECTION`` and +``COLLECTION_PER_CLASS`` are currently supported. + +Examples: + +.. code-block:: php + + ` for additional information. + +.. code-block:: php + + ` and +:ref:`reference-many ` collections in separate write operations, +which do not bump the document version. Users employing document versioning are +encouraged to use the :ref:`atomicSet ` or +:ref:`atomicSetArray ` strategies for such collections, which +will ensure that collections are updated in the same write operation as the +versioned document. + +.. _BSON specification: http://bsonspec.org/spec.html +.. _DBRef: https://docs.mongodb.com/manual/reference/database-references/#dbrefs +.. _geoNear command: https://docs.mongodb.com/manual/reference/command/geoNear/ +.. _GridFS: https://docs.mongodb.com/manual/core/gridfs/ +.. _MongoGridFSFile: http://php.net/manual/en/class.mongogridfsfile.php +.. _MongoId: http://php.net/manual/en/class.mongoid.php +.. |FQCN| raw:: html + FQCN diff --git a/docs/odm-ng/reference/introduction.rst b/docs/odm-ng/reference/introduction.rst new file mode 100644 index 0000000000..543e288045 --- /dev/null +++ b/docs/odm-ng/reference/introduction.rst @@ -0,0 +1,498 @@ +Introduction +============ + +Doctrine MongoDB Object Document Mapper is built for PHP 5.3.0+ and +provides transparent persistence for PHP objects to the popular `MongoDB`_ database by `10gen`_. + +Features Overview +----------------- + +- Transparent persistence. +- Map one or many embedded documents. +- Map one or many referenced documents. +- Create references between documents in different databases. +- Map documents with Annotations, XML, YAML or plain old PHP code. +- Documents can be stored on the `MongoGridFS `_. +- Collection per class(concrete) and single collection inheritance supported. +- Map your Doctrine 2 ORM Entities to the ODM and use mixed data stores. +- Inserts are performed using `MongoCollection::batchInsert() `_ +- Updates are performed using atomic operators. + +Here is a quick example of some PHP object documents that demonstrates a few of the features: + +.. code-block:: php + + id; } + + public function getChanges() { return $this->changes; } + public function incrementChanges() { $this->changes++; } + + public function getNotes() { return $this->notes; } + public function addNote($note) { $this->notes[] = $note; } + + public function getName() { return $this->name; } + public function setName($name) { $this->name = $name; } + + public function getSalary() { return $this->salary; } + public function setSalary($salary) { $this->salary = (int) $salary; } + + public function getStarted() { return $this->started; } + public function setStarted(DateTime $started) { $this->started = $started; } + + public function getLeft() { return $this->left; } + public function setLeft(DateTime $left) { $this->left = $left; } + + public function getAddress() { return $this->address; } + public function setAddress(Address $address) { $this->address = $address; } + } + + /** @ODM\Document */ + class Employee extends BaseEmployee + { + /** @ODM\ReferenceOne(targetDocument="Documents\Manager") */ + private $manager; + + public function getManager() { return $this->manager; } + public function setManager(Manager $manager) { $this->manager = $manager; } + } + + /** @ODM\Document */ + class Manager extends BaseEmployee + { + /** @ODM\ReferenceMany(targetDocument="Documents\Project") */ + private $projects; + + public __construct() { $this->projects = new ArrayCollection(); } + + public function getProjects() { return $this->projects; } + public function addProject(Project $project) { $this->projects[] = $project; } + } + + /** @ODM\EmbeddedDocument */ + class Address + { + /** @ODM\Field(type="string") */ + private $address; + + /** @ODM\Field(type="string") */ + private $city; + + /** @ODM\Field(type="string") */ + private $state; + + /** @ODM\Field(type="string") */ + private $zipcode; + + public function getAddress() { return $this->address; } + public function setAddress($address) { $this->address = $address; } + + public function getCity() { return $this->city; } + public function setCity($city) { $this->city = $city; } + + public function getState() { return $this->state; } + public function setState($state) { $this->state = $state; } + + public function getZipcode() { return $this->zipcode; } + public function setZipcode($zipcode) { $this->zipcode = $zipcode; } + } + + /** @ODM\Document */ + class Project + { + /** @ODM\Id */ + private $id; + + /** @ODM\Field(type="string") */ + private $name; + + public function __construct($name) { $this->name = $name; } + + public function getId() { return $this->id; } + + public function getName() { return $this->name; } + public function setName($name) { $this->name = $name; } + } + +Now those objects can be used just like you weren't using any +persistence layer at all and can be persisted transparently by +Doctrine: + +.. code-block:: php + + setName('Employee'); + $employee->setSalary(50000); + $employee->setStarted(new DateTime()); + + $address = new Address(); + $address->setAddress('555 Doctrine Rd.'); + $address->setCity('Nashville'); + $address->setState('TN'); + $address->setZipcode('37209'); + $employee->setAddress($address); + + $project = new Project('New Project'); + $manager = new Manager(); + $manager->setName('Manager'); + $manager->setSalary(100000); + $manager->setStarted(new DateTime()); + + $dm->persist($employee); + $dm->persist($address); + $dm->persist($project); + $dm->persist($manager); + $dm->flush(); + +The above would insert the following: + +:: + + Array + ( + [000000004b0a33690000000001c304c6] => Array + ( + [name] => New Project + ) + + ) + Array + ( + [000000004b0a33660000000001c304c6] => Array + ( + [changes] => 0 + [notes] => Array + ( + ) + + [name] => Manager + [salary] => 100000 + [started] => MongoDate Object + ( + [sec] => 1275265048 + [usec] => 0 + ) + + [projects] => Array + ( + [0] => Array + ( + [$ref] => projects + [$id] => 4c0300188ead0e947a000000 + [$db] => my_db + ) + + ) + + ) + + ) + Array + ( + [000000004b0a336a0000000001c304c6] => Array + ( + [changes] => 0 + [notes] => Array + ( + ) + + [name] => Employee + [salary] => 50000 + [started] => MongoDate Object + ( + [sec] => 1275265048 + [usec] => 0 + ) + + [address] => Array + ( + [address] => 555 Doctrine Rd. + [city] => Nashville + [state] => TN + [zipcode] => 37209 + ) + + ) + + ) + +If we update a property and call ``->flush()`` again we'll get an +efficient update query using the atomic operators: + +.. code-block:: php + + setSalary(200000); + $manager->addNote('Gave user 100k a year raise'); + $manager->incrementChanges(2); + $manager->addProject($newProject); + + $dm->persist($newProject); + $dm->flush(); + +The above could would produce an update that looks something like +this: + +:: + + Array + ( + [$inc] => Array + ( + [changes] => 2 + ) + + [$pushAll] => Array + ( + [notes] => Array + ( + [0] => Gave user 100k a year raise + ) + + [projects] => Array + ( + [0] => Array + ( + [$ref] => projects + [$id] => 4c0310718ead0e767e030000 + [$db] => my_db + ) + + ) + + ) + + [$set] => Array + ( + [salary] => 200000 + ) + + ) + +This is a simple example, but it demonstrates well that you can +transparently persist PHP objects while still utilizing the +atomic operators for updating documents! Continue reading to learn +how to get the Doctrine MongoDB Object Document Mapper setup and +running! + +Setup +----- + +Before we can begin, we'll need to install the Doctrine MongoDB ODM library and +its dependencies. The easiest way to do this is with `Composer`_: + +:: + + $ composer require "doctrine/mongodb-odm" + +Once ODM and its dependencies have been downloaded, we can begin by creating a +``bootstrap.php`` file in our project's root directory, where Composer's +``vendor/`` directory also resides. Let's start by importing some of the classes +we'll use: + +.. code-block:: php + + add('Documents', __DIR__); + +Ultimately, our application will utilize ODM through its ``DocumentManager`` +class. Before we can instantiate a ``DocumentManager``, we need to construct the +``Connection`` and ``Configuration`` objects required by its factory method: + +.. code-block:: php + + setProxyDir(__DIR__ . '/Proxies'); + $config->setProxyNamespace('Proxies'); + $config->setHydratorDir(__DIR__ . '/Hydrators'); + $config->setHydratorNamespace('Hydrators'); + $config->setDefaultDB('doctrine_odm'); + +The easiest way to define mappings for our document classes is with annotations. +We'll need to specify an annotation driver in our configuration (with one or +more paths) and register the annotations for the driver: + +.. code-block:: php + + setMetadataDriverImpl(AnnotationDriver::create(__DIR__ . '/Documents')); + + $loader = require_once('path/to/vendor/autoload.php'); + + AnnotationRegistry::registerLoader([$loader, 'loadClass']); + +At this point, we have everything necessary to construct a ``DocumentManager``: + +.. code-block:: php + + add('Documents', __DIR__); + + AnnotationRegistry::registerLoader([$loader, 'loadClass']); + + $connection = new Connection(); + + $config = new Configuration(); + $config->setProxyDir(__DIR__ . '/Proxies'); + $config->setProxyNamespace('Proxies'); + $config->setHydratorDir(__DIR__ . '/Hydrators'); + $config->setHydratorNamespace('Hydrators'); + $config->setDefaultDB('doctrine_odm'); + $config->setMetadataDriverImpl(AnnotationDriver::create(__DIR__ . '/Documents')); + + $dm = DocumentManager::create($connection, $config); + +That is it! Your ``DocumentManager`` instance is ready to be used! + +Using PHP 7 +----------- + +You can use Doctrine MongoDB ODM with PHP 7, but there are a few extra steps during +the installation. Since the legacy driver (referred to as ``ext-mongo``) is not +available on PHP 7, you will need the new driver (``ext-mongodb``) installed and +use a polyfill to provide the API of the legacy driver. + +To do this, you have to require ``alcaeus/mongo-php-adapter`` before adding a composer +dependency to ODM. To do this, run the following command: + +:: + + $ composer require "alcaeus/mongo-php-adapter" + +Next, manually add a ``provide`` section to your ``composer.json``: + +.. code-block:: json + + "provide": { + "ext-mongo": "1.6.14" + } + +This section needs to be added to work around a composer issue with libraries +providing platform packages (such as ``ext-mongo``). Now, you may install ODM as +described above: + +:: + + $ composer require "doctrine/mongodb-odm" + +.. _MongoDB: https://www.mongodb.com/ +.. _10gen: http://www.10gen.com +.. _Composer: http://getcomposer.org/ diff --git a/docs/odm-ng/tutorials/getting-started.rst b/docs/odm-ng/tutorials/getting-started.rst new file mode 100644 index 0000000000..c4077700d6 --- /dev/null +++ b/docs/odm-ng/tutorials/getting-started.rst @@ -0,0 +1,304 @@ +Getting Started +=============== + +Doctrine is a project that aims to handle the persistence of your +domain model in a non-interfering way. Non-relational or no-sql +databases like MongoDB give you flexibility of building data store +around your object model and not vise versa. You can read more on the +initial configuration and setup in :doc:`Introduction to MongoDB Object +Document Mapper <../reference/introduction>`. This section will give you a basic +overview of what could be accomplished using Doctrine MongoDB ODM. + +Example Model: Simple Blog +-------------------------- + +To create the simplest example, let’s assume the following in a simple blog web application: + +- Blog has a user. +- Blog user can make blog posts + +A first prototype +----------------- + +For the above mentioned example, something as simple as this could be modeled with plain PHP classes. +First define the ``User`` document: + +.. code-block:: php + + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: yaml + + Documents\User: + fields: + id: + type: id + id: true + name: + type: string + email: + type: string + referenceMany: + posts: + targetDocument: Documents\BlogPost + cascade: all + + Documents\BlogPost: + fields: + id: + type: id + id: true + title: + type: string + body: + type: string + createdAt: + type: date + +That’s it, we have our models, and we can save and retrieve them. Now +all we need to do is to properly instantiate the ``DocumentManager`` +instance. Read more about setting up the Doctrine MongoDB ODM in the +:doc:`Introduction to MongoDB Object Document Mapper <../reference/introduction>`: + +.. code-block:: php + + setProxyDir('/path/to/generate/proxies'); + $config->setProxyNamespace('Proxies'); + $config->setHydratorDir('/path/to/generate/hydrators'); + $config->setHydratorNamespace('Hydrators'); + $config->setMetadataDriverImpl(AnnotationDriver::create('/path/to/document/classes')); + + $dm = DocumentManager::create(new Connection(), $config); + +Usage +----- + +Here is how you would use your models now: + +.. code-block:: php + + setName('Bulat S.'); + $user->setEmail('email@example.com'); + + // tell Doctrine 2 to save $user on the next flush() + $dm->persist($user); + + // create blog post + $post = new BlogPost(); + $post->setTitle('My First Blog Post'); + $post->setBody('MongoDB + Doctrine 2 ODM = awesomeness!'); + $post->setCreatedAt(new DateTime()); + + $user->addPost($post); + + // store everything to MongoDB + $dm->flush(); + +.. note:: + + Note that you do not need to explicitly call persist on the ``$post`` because the operation + will cascade on to the reference automatically. + +Now if you did everything correctly, you should have those two objects +stored in MongoDB in correct collections and databases. You can use the +`php-mongodb-admin project, hosted on github`_ to look at your +``BlogPost`` collection, where you will see only one document: + +:: + + Array + ( + [_id] => 4bec5869fdc212081d000000 + [title] => My First Blog Post + [body] => MongoDB + Doctrine 2 ODM = awesomeness! + [createdAt] => MongoDate Object + ( + [sec] => 1273723200 + [usec] => 0 + ) + ) + +And the ``User`` collection would consist of the following: + +:: + + Array + ( + [_id] => 4bec5869fdc212081d010000 + [name] => Bulat S. + [email] => email@example.com + [posts] => Array + ( + [0] => Array + ( + [$ref] => blog_posts + [$id] => 4bec5869fdc212081d000000 + [$db] => test_database + ) + ) + ) + +You can retrieve the user later by its identifier: + +.. code-block:: php + + find('User', $userId); + +Or you can find the user by name even: + +.. code-block:: php + + getRepository('User')->findOneByName('Bulat S.'); + +If you want to iterate over the posts the user references it is as easy as the following: + +.. code-block:: php + + getPosts(); + foreach ($posts as $post) { + } + +You will notice that working with objects is nothing magical and you only have access to the properties, +getters and setters that you have defined yourself so the semantics are very clear. You can continue +reading about the MongoDB in the :doc:`Introduction to MongoDB Object Document Mapper <../reference/introduction>`. + +.. _php-mongodb-admin project, hosted on github: http://github.com/jwage/php-mongodb-admin