From b446afd93716ea0e5a388d1b929d14ef41c51035 Mon Sep 17 00:00:00 2001 From: NateC Date: Wed, 24 Feb 2016 17:13:25 -0600 Subject: [PATCH] Updating the Getting Started guide's documentation --- docs/en/tutorials/getting-started.rst | 262 ++++++++++++-------------- 1 file changed, 125 insertions(+), 137 deletions(-) diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 5cbcfb170..605b1a017 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -14,7 +14,7 @@ Guide Assumptions ----------------- This guide is designed for beginners that haven't worked with Doctrine ORM -before. There are some prerequesites for the tutorial that have to be +before. There are some prerequisites for the tutorial that have to be installed: - PHP (latest stable version) @@ -118,8 +118,8 @@ Add the following directories: Obtaining the EntityManager --------------------------- -Doctrine's public interface is the EntityManager, it provides the -access point to the complete lifecycle management of your entities +Doctrine's public interface is through the ``EntityManager``. This class +provides access points to the complete lifecycle management for your entities, and transforms entities from and back to persistence. You have to configure and create it to use your entities with Doctrine 2. I will show the configuration steps and then discuss them step by @@ -150,8 +150,8 @@ step: // obtaining the entity manager $entityManager = EntityManager::create($conn, $config); -The first require statement sets up the autoloading capabilities of Doctrine -using the Composer autoload. +The require_once statement sets up the class autoloading for Doctrine and +its dependencies using Composer's autoloader. The second block consists of the instantiation of the ORM ``Configuration`` object using the Setup helper. It assumes a bunch @@ -159,8 +159,8 @@ of defaults that you don't have to bother about for now. You can read up on the configuration details in the :doc:`reference chapter on configuration <../reference/configuration>`. -The third block shows the configuration options required to connect -to a database, in my case a file-based sqlite database. All the +The third block shows the configuration options required to connect to +a database. In this case, we'll use a file-based SQLite database. All the configuration options for all the shipped drivers are given in the `DBAL Configuration section of the manual `_. @@ -170,15 +170,10 @@ factory method. Generating the Database Schema ------------------------------ -Now that we have defined the Metadata mappings and bootstrapped the -EntityManager we want to generate the relational database schema -from it. Doctrine has a Command-Line Interface that allows you to -access the SchemaTool, a component that generates the required -tables to work with the metadata. - -For the command-line tool to work a cli-config.php file has to be -present in the project root directory, where you will execute the -doctrine command. Its a fairly simple file: +Doctrine has a command-line interface that allows you to access the SchemaTool, +a component that can generate a relational database schema based entirely on the +defined entity classes and their metadata. For this tool to work, a +cli-config.php file must exist in the project root directory: .. code-block:: php @@ -188,40 +183,38 @@ doctrine command. Its a fairly simple file: return \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($entityManager); -You can then change into your project directory and call the -Doctrine command-line tool: +Change into your project directory and call the Doctrine command-line tool: :: $ cd project/ $ vendor/bin/doctrine orm:schema-tool:create -At this point no entity metadata exists in `src` so you will see a message like -"No Metadata Classes to process." Don't worry, we'll create a Product entity and -corresponding metadata in the next section. +Since we haven't added any entity metadata in `src` yet, you'll see a message +stating "No Metadata Classes to process." In the next section, we'll create a +Product entity along with the corresponding metadata, and run this command again. -You should be aware that during the development process you'll periodically need -to update your database schema to be in sync with your Entities metadata. - -You can easily recreate the database: +Note that as you modify your entities' metadata during the development process, +you'll need to update your database schema to stay in sync with the metadata. +You can rasily recreate the database using the following commands: :: $ vendor/bin/doctrine orm:schema-tool:drop --force $ vendor/bin/doctrine orm:schema-tool:create -Or use the update functionality: +Or you can use the update functionality: :: $ vendor/bin/doctrine orm:schema-tool:update --force The updating of databases uses a Diff Algorithm for a given -Database Schema, a cornerstone of the ``Doctrine\DBAL`` package, +Database Schema. This is a cornerstone of the ``Doctrine\DBAL`` package, which can even be used without the Doctrine ORM package. -Starting with the Product -------------------------- +Starting with the Product Entity +-------------------------------- We start with the simplest entity, the Product. Create a ``src/Product.php`` file to contain the ``Product`` entity definition: @@ -257,9 +250,9 @@ entity definition: } } -Note that all fields are set to protected (not public) with a -mutator (getter and setter) defined for every field except $id. -The use of mutators allows Doctrine to hook into calls which +When creating entity classes, all of the fields should be protected or private +(not public), with getter and setter methods for each one (except $id). +The use of mutators allows Doctrine to hook into calls which manipulate the entities in ways that it could not if you just directly set the values with ``entity#field = foo;`` @@ -274,9 +267,10 @@ language. The metadata language describes how entities, their properties and references should be persisted and what constraints should be applied to them. -Metadata for entities are configured using a XML, YAML or Docblock Annotations. -This Getting Started Guide will show the mappings for all Mapping Drivers. -References in the text will be made to the XML mapping. +Metadata for an Entity can be configured using DocBlock annotations directly +in the Entity class itself, or in an external XML or YAML file. This Getting +Started guide will demonstrate metadata mappings using all three methods, +but you only need to choose one. .. configuration-block:: @@ -332,27 +326,28 @@ References in the text will be made to the XML mapping. The top-level ``entity`` definition tag specifies information about the class and table-name. The primitive type ``Product#name`` is defined as a ``field`` attribute. The ``id`` property is defined with -the ``id`` tag, this has a ``generator`` tag nested inside which -defines that the primary key generation mechanism automatically -uses the database platforms native id generation strategy (for -example AUTO INCREMENT in the case of MySql or Sequences in the +the ``id`` tag. It has a ``generator`` tag nested inside, which +specifies that the primary key generation mechanism should automatically +use the database platform's native id generation strategy (for +example, AUTO INCREMENT in the case of MySql, or Sequences in the case of PostgreSql and Oracle). -Now that we have defined our first entity, let's update the database: +Now that we have defined our first entity and its metadata, +let's update the database schema: :: $ vendor/bin/doctrine orm:schema-tool:update --force --dump-sql -Specifying both flags ``--force`` and ``--dump-sql`` prints and executes the DDL -statements. +Specifying both flags ``--force`` and ``--dump-sql`` will cause the DDL +statements to be executed and then printed to the screen. -Now create a new script that will insert products into the database: +Now, we'll create a new script to insert products into the database: .. code-block:: php require_once "bootstrap.php"; $newProductName = $argv[1]; @@ -372,22 +367,19 @@ Call this script from the command-line to see how new products are created: $ php create_product.php ORM $ php create_product.php DBAL -What is happening here? Using the ``Product`` is pretty standard OOP. +What is happening here? Using the ``Product`` class is pretty standard OOP. The interesting bits are the use of the ``EntityManager`` service. To -notify the EntityManager that a new entity should be inserted into the database -you have to call ``persist()``. To initiate a transaction to actually perform -the insertion, You have to explicitly call ``flush()`` on the ``EntityManager``. +notify the EntityManager that a new entity should be inserted into the database, +you have to call ``persist()``. To initiate a transaction to actually *perform* +the insertion, you have to explicitly call ``flush()`` on the ``EntityManager``. -This distinction between persist and flush is allows to aggregate all writes -(INSERT, UPDATE, DELETE) into one single transaction, which is executed when -flush is called. Using this approach the write-performance is significantly -better than in a scenario where updates are done for each entity in isolation. +This distinction between persist and flush is what allows the aggregation of +all database writes (INSERT, UPDATE, DELETE) into one single transaction, which +is executed when ``flush()`` is called. Using this approach, the write-performance +is significantly better than in a scenario in which writes are performed on +each entity in isolation. -Doctrine follows the UnitOfWork pattern which additionally detects all entities -that were fetched and have changed during the request. You don't have to keep track of -entities yourself, when Doctrine already knows about them. - -As a next step we want to fetch a list of all the Products. Let's create a +Next, we'll fetch a list of all the Products in the database. Let's create a new script for this: .. code-block:: php @@ -404,10 +396,10 @@ new script for this: } The ``EntityManager#getRepository()`` method can create a finder object (called -a repository) for every entity. It is provided by Doctrine and contains some -finder methods such as ``findAll()``. +a repository) for every type of entity. It is provided by Doctrine and contains +some finder methods like ``findAll()``. -Let's continue with displaying the name of a product based on its ID: +Let's continue by creating a script to display the name of a product based on its ID: .. code-block:: php @@ -425,9 +417,13 @@ Let's continue with displaying the name of a product based on its ID: echo sprintf("-%s\n", $product->getName()); -Updating a product name demonstrates the functionality UnitOfWork of pattern -discussed before. We only need to find a product entity and all changes to its -properties are written to the database: +Next we'll update a product's name, given its id. This simple example will +help demonstrate Doctrine's implementation of the UnitOfWork pattern. Doctrine +keeps track of all the entities that were retrieved from the Entity Manager, +and can detect when any of those entities' properties have been modified. +As a result, rather than needing to call ``persist($entity)`` for each individual +entity whose properties were changed, a single call to ``flush()`` at the end of a +request is sufficient to update the database for all of the modified entities. .. code-block:: php @@ -455,9 +451,8 @@ product name changed by calling the ``show_product.php`` script. Adding Bug and User Entities ---------------------------- -We continue with the bug tracker domain, by creating the missing classes -``Bug`` and ``User`` and putting them into ``src/Bug.php`` and -``src/User.php`` respectively. +We continue with the bug tracker example by creating the ``Bug`` and ``User`` +classes. We'll store them in ``src/Bug.php`` and ``src/User.php``, respectively. .. code-block:: php @@ -561,14 +556,15 @@ We continue with the bug tracker domain, by creating the missing classes } } -All of the properties discussed so far are simple string and integer values, -for example the id fields of the entities, their names, description, status and -change dates. Next we will model the dynamic relationships between the entities -by defining the references between entities. +All of the properties we've seen so far are of simple types (integer, string, +and datetime). But now, we'll add properties that will store objects of +specific *entity types* in order to model the relationships between different +entities. -References between objects are foreign keys in the database. You never have to -(and never should) work with the foreign keys directly, only with the objects -that represent the foreign key through their own identity. +At the database level, relationships between entities are represented by foreign +keys. But with Doctrine, you'll never have to (and never should) work with +the foreign keys directly. You should only work with objects that represent +foreign keys through their own identities. For every foreign key you either have a Doctrine ManyToOne or OneToOne association. On the inverse sides of these foreign keys you can have @@ -602,6 +598,7 @@ domain model to match the requirements: assignedToBug($this); $this->engineer = $engineer; } - public function setReporter($reporter) + public function setReporter(User $reporter) { $reporter->addReportedBug($this); $this->reporter = $reporter; @@ -717,15 +713,15 @@ the bi-directional reference: { // ... (previous code) - protected $reportedBugs = null; - protected $assignedBugs = null; + protected $reportedBugs; + protected $assignedBugs; - public function addReportedBug($bug) + public function addReportedBug(Bug $bug) { $this->reportedBugs[] = $bug; } - public function assignedToBug($bug) + public function assignedToBug(Bug $bug) { $this->assignedBugs[] = $bug; } @@ -741,7 +737,7 @@ You can see from ``User#addReportedBug()`` and ``User#assignedToBug()`` that using this method in userland alone would not add the Bug to the collection of the owning side in ``Bug#reporter`` or ``Bug#engineer``. Using these methods and -calling Doctrine for persistence would not update the collections +calling Doctrine for persistence would not update the Collections' representation in the database. Only using ``Bug#setEngineer()`` or ``Bug#setReporter()`` @@ -749,7 +745,7 @@ correctly saves the relation information. The ``Bug#reporter`` and ``Bug#engineer`` properties are Many-To-One relations, which point to a User. In a normalized -relational model the foreign key is saved on the Bug's table, hence +relational model, the foreign key is saved on the Bug's table, hence in our object-relation model the Bug is at the owning side of the relation. You should always make sure that the use-cases of your domain model should drive which side is an inverse or owning one in @@ -758,7 +754,7 @@ or an engineer is assigned to the bug, we don't want to update the User to persist the reference, but the Bug. This is the case with the Bug being at the owning side of the relation. -Bugs reference Products by an uni-directional ManyToMany relation in +Bugs reference Products by a uni-directional ManyToMany relation in the database that points from Bugs to Products. .. code-block:: php @@ -771,7 +767,7 @@ the database that points from Bugs to Products. protected $products = null; - public function assignToProduct($product) + public function assignToProduct(Product $product) { $this->products[] = $product; } @@ -783,7 +779,7 @@ the database that points from Bugs to Products. } We are now finished with the domain model given the requirements. -Lets add metadata mappings for the ``User`` and ``Bug`` as we did for +Lets add metadata mappings for the ``Bug`` entity, as we did for the ``Product`` before: .. configuration-block:: @@ -890,13 +886,13 @@ For the "created" field we have used the ``datetime`` type, which translates the YYYY-mm-dd HH:mm:ss database format into a PHP DateTime instance and back. -After the field definitions the two qualified references to the +After the field definitions, the two qualified references to the user entity are defined. They are created by the ``many-to-one`` tag. The class name of the related entity has to be specified with the ``target-entity`` attribute, which is enough information for the database mapper to access the foreign-table. Since ``reporter`` and ``engineer`` are on the owning side of a -bi-directional relation we also have to specify the ``inversed-by`` +bi-directional relation, we also have to specify the ``inversed-by`` attribute. They have to point to the field names on the inverse side of the relationship. We will see in the next example that the ``inversed-by`` attribute has a counterpart ``mapped-by`` which makes that @@ -907,7 +903,7 @@ holds all products where the specific bug occurs. Again you have to define the ``target-entity`` and ``field`` attributes on the ``many-to-many`` tag. -The last missing definition is that of the User entity: +Finally, we'll add metadata mappings for the ``User`` entity. .. configuration-block:: @@ -934,13 +930,13 @@ The last missing definition is that of the User entity: /** * @OneToMany(targetEntity="Bug", mappedBy="reporter") - * @var Bug[] + * @var Bug[] An ArrayCollection of Bug objects. **/ protected $reportedBugs = null; /** * @OneToMany(targetEntity="Bug", mappedBy="engineer") - * @var Bug[] + * @var Bug[] An ArrayCollection of Bug objects. **/ protected $assignedBugs = null; @@ -996,10 +992,7 @@ means the join details have already been defined on the owning side. Therefore we only have to specify the property on the Bug class that holds the owning sides. -This example has a fair overview of the most basic features of the -metadata definition language. - -Update your database running: +Update your database schema by running: :: $ vendor/bin/doctrine orm:schema-tool:update --force @@ -1008,7 +1001,8 @@ Update your database running: Implementing more Requirements ------------------------------ -For starters we need to create user entities: +So far, we've seen the most basic features of the metadata definition language. +To explore additional functionality, let's first create new ``User`` entities: .. code-block:: php @@ -1032,23 +1026,22 @@ Now call: $ php create_user.php beberlei -We now have the data to create a bug and the code for this scenario may look -like this: +We now have the necessary data to create a new Bug entity: .. code-block:: php require_once "bootstrap.php"; - $theReporterId = $argv[1]; - $theDefaultEngineerId = $argv[2]; + $reporterId = $argv[1]; + $engineerId = $argv[2]; $productIds = explode(",", $argv[3]); - $reporter = $entityManager->find("User", $theReporterId); - $engineer = $entityManager->find("User", $theDefaultEngineerId); + $reporter = $entityManager->find("User", $reporterId); + $engineer = $entityManager->find("User", $engineerId); if (!$reporter || !$engineer) { - echo "No reporter and/or engineer found for the input.\n"; + echo "No reporter and/or engineer found for the given id(s).\n"; exit(1); } @@ -1070,22 +1063,17 @@ like this: echo "Your new Bug Id: ".$bug->getId()."\n"; -Since we only have one user and product, probably with the ID of 1, we can call this script with: +Since we only have one user and product, probably with the ID of 1, we can +call this script as follows: :: php create_bug.php 1 1 1 -This is the first contact with the read API of the EntityManager, -showing that a call to ``EntityManager#find($name, $id)`` returns a -single instance of an entity queried by primary key. Besides this -we see the persist + flush pattern again to save the Bug into the -database. - -See how simple relating Bug, Reporter, Engineer and Products is -done by using the discussed methods in the "A first prototype" -section. The UnitOfWork will detect this relationship when flush is -called and relate them in the database appropriately. +See how simple it is to relate a Bug, Reporter, Engineer and Products? +Also recall that thanks to the UnitOfWork pattern, Doctrine will detect +these relations and update all of the modified entities in the database +automatically when ``flush()`` is called. Queries for Application Use-Cases --------------------------------- @@ -1094,7 +1082,7 @@ List of Bugs ~~~~~~~~~~~~ Using the previous examples we can fill up the database quite a -bit, however we now need to discuss how to query the underlying +bit. However, we now need to discuss how to query the underlying mapper for the required view representations. When opening the application, bugs can be paginated through a list-view, which is the first read-only use-case: @@ -1225,7 +1213,7 @@ write scenarios: .. code-block:: php require_once "bootstrap.php"; $theBugId = $argv[1]; @@ -1300,7 +1288,7 @@ and usage of bound parameters: .. code-block:: php require_once "bootstrap.php"; $theUserId = $argv[1]; @@ -1366,7 +1354,7 @@ should be able to close a bug. This looks like: .. code-block:: php require_once "bootstrap.php"; $theBugId = $argv[1];