diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index de33c4545..2a16ad4a2 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -66,17 +66,17 @@ Bug Tracker domain model from the documentation. Reading their documentation we can extract the requirements: -- A Bugs has a description, creation date, status, reporter and +- A Bug has a description, creation date, status, reporter and engineer -- A bug can occur on different products (platforms) -- Products have a name. -- Bug Reporter and Engineers are both Users of the System. -- A user can create new bugs. -- The assigned engineer can close a bug. -- A user can see all his reported or assigned bugs. +- A Bug can occur on different Products (platforms) +- A Product has a name. +- Bug reporters and engineers are both Users of the system. +- A User can create new Bugs. +- The assigned engineer can close a Bug. +- A User can see all his reported or assigned Bugs. - Bugs can be paginated through a list-view. -Setup Project +Project Setup ------------- Create a new empty folder for this tutorial project, for example @@ -170,9 +170,9 @@ factory method. Generating the Database Schema ------------------------------ -Now that we have defined the Metadata Mappings and bootstrapped the +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 +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. @@ -223,9 +223,8 @@ which can even be used without the Doctrine ORM package. Starting with the Product ------------------------- -We start with the Product entity requirements, because it is the most simple one -to get started. Create a ``src/Product.php`` file and put the ``Product`` -entity definition in there: +We start with the simplest entity, the Product. Create a ``src/Product.php`` file to contain the ``Product`` +entity definition: .. code-block:: php @@ -258,10 +257,16 @@ entity definition in there: } } -Note how the properties have getter and setter methods defined except -``$id``. To access data from entities Doctrine 2 uses the Reflection API, so it -is possible for Doctrine to access the value of ``$id``. You don't have to -take Doctrine into account when designing access to the state of your objects. +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 +manipulate the entities in ways that it could not if you just +directly set the values with ``entity#field = foo;`` + +The id field has no setter since, generally speaking, your code +should not set this value since it represents a database id value. +(Note that Doctrine itself can still set the value using the +Reflection API instead of a defined setter function) The next step for persistence with Doctrine is to describe the structure of the ``Product`` entity to Doctrine using a metadata @@ -325,15 +330,15 @@ References in the text will be made to the XML mapping. type: string The top-level ``entity`` definition tag specifies information about -the class and table-name. The primitive type ``Product::$name`` is -defined as ``field`` attributes. The Id property is defined with -the ``id`` tag. The id has a ``generator`` tag nested inside which +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 +uses the database platforms native id generation strategy (for example AUTO INCREMENT in the case of MySql or Sequences in the -case of PostgreSql and Oracle. +case of PostgreSql and Oracle). -You have to update the database now, because we have a first Entity now: +Now that we have defined our first entity, lets update the database: :: @@ -360,7 +365,7 @@ Now create a new script that will insert products into the database: echo "Created Product with ID " . $product->getId() . "\n"; -Call this script from the command line to see how new products are created: +Call this script from the command-line to see how new products are created: :: @@ -382,7 +387,7 @@ 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 +As a next step we want to fetch a list of all the Products. Let's create a new script for this: .. code-block:: php @@ -399,7 +404,7 @@ new script for this: } The ``EntityManager#getRepository()`` method can create a finder object (called -repository) for every entity. It is provided by Doctrine and contains some +a repository) for every entity. It is provided by Doctrine and contains some finder methods such as ``findAll()``. Let's continue with displaying the name of a product based on its ID: @@ -558,12 +563,12 @@ 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. With just the scalar values this model cannot describe the dynamics that we want. We -want to model references between entities. +change dates. Next we will model the dynamic relationships between the entities +by defining the references between entities. References between objects are foreign keys in the database. You never have to -work with the foreign keys directly, only with objects that represent the -foreign key through their own identity. +(and never should) work with the foreign keys directly, only with the objects +that represent the foreign key through their own identity. For every foreign key you either have a Doctrine ManyToOne or OneToOne association. On the inverse sides of these foreign keys you can have @@ -732,20 +737,20 @@ methods are only used for ensuring consistency of the references. This approach is my personal preference, you can choose whatever method to make this work. -You can see from ``User::addReportedBug()`` and -``User::assignedToBug()`` that using this method in userland alone +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 +``Bug#reporter`` or ``Bug#engineer``. Using these methods and calling Doctrine for persistence would not update the collections representation in the database. -Only using ``Bug::setEngineer()`` or ``Bug::setReporter()`` +Only using ``Bug#setEngineer()`` or ``Bug#setReporter()`` correctly saves the relation information. We also set both collection instance variables to protected, however with PHP 5.3's new features Doctrine is still able to use Reflection to set and get values from protected and private properties. -The ``Bug::$reporter`` and ``Bug::$engineer`` properties are +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 in our object-relation model the Bug is at the owning side of the @@ -781,8 +786,8 @@ the database that points from Bugs to Products. } We are now finished with the domain model given the requirements. -Now we continue adding metadata mappings for the ``User`` and ``Bug`` -as we did for the ``Product`` before: +Lets add metadata mappings for the ``User`` and ``Bug`` as we did for +the ``Product`` before: .. configuration-block:: .. code-block:: php @@ -884,11 +889,9 @@ as we did for the ``Product`` before: Here we have the entity, id and primitive type definitions. -The column names are used from the Zend\_Db\_Table examples and -have different names than the properties on the Bug class. -Additionally for the "created" field it is specified that it is of -the Type "DATETIME", which translates the YYYY-mm-dd HH:mm:ss -Database format into a PHP DateTime instance and back. +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 user entity are defined. They are created by the ``many-to-one`` @@ -902,14 +905,10 @@ side of the relationship. We will see in the next example that the ``inversed-by attribute has a counterpart ``mapped-by`` which makes that the inverse side. -The last missing property is the ``Bug::$products`` collection. It -holds all products where the specific bug is occurring in. Again +The last definition is for the ``Bug#products`` collection. It +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. Furthermore you have to specify the -details of the many-to-many join-table and its foreign key columns. -The definition is rather complex, however relying on the XML -auto-completion I got it working easily, although I forget the -schema details all the time. +on the ``many-to-many`` tag. The last missing definition is that of the User entity: @@ -1132,7 +1131,7 @@ The console output of this script is then: .. note:: - **Dql is not Sql** + **DQL is not SQL** You may wonder why we start writing SQL at the beginning of this use-case. Don't we use an ORM to get rid of all the endless @@ -1145,6 +1144,7 @@ The console output of this script is then: of Entity-Class and property. Using the Metadata we defined before it allows for very short distinctive and powerful queries. + An important reason why DQL is favourable to the Query API of most ORMs is its similarity to SQL. The DQL language allows query constructs that most ORMs don't, GROUP BY even with HAVING, @@ -1154,30 +1154,31 @@ The console output of this script is then: throw your ORM into the dumpster, because it doesn't support some the more powerful SQL concepts. - Besides handwriting DQL you can however also use the - ``QueryBuilder`` retrieved by calling - ``$entityManager->createQueryBuilder()`` which is a Query Object - around the DQL language. - As a last resort you can however also use Native SQL and a - description of the result set to retrieve entities from the - database. DQL boils down to a Native SQL statement and a - ``ResultSetMapping`` instance itself. Using Native SQL you could - even use stored procedures for data retrieval, or make use of - advanced non-portable database queries like PostgreSql's recursive - queries. + Instead of handwriting DQL you can use the ``QueryBuilder`` retrieved + by calling ``$entityManager->createQueryBuilder()``. There are more + details about this in the relevant part of the documentation. + + + As a last resort you can still use Native SQL and a description of the + result set to retrieve entities from the database. DQL boils down to a + Native SQL statement and a ``ResultSetMapping`` instance itself. Using + Native SQL you could even use stored procedures for data retrieval, or + make use of advanced non-portable database queries like PostgreSql's + recursive queries. Array Hydration of the Bug List ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In the previous use-case we retrieved the result as their +In the previous use-case we retrieved the results as their respective object instances. We are not limited to retrieving objects only from Doctrine however. For a simple list view like the previous one we only need read access to our entities and can switch the hydration from objects to simple PHP arrays instead. -This can obviously yield considerable performance benefits for -read-only requests. + +Hydration can be an expensive process so only retrieving what you need can +yield considerable performance benefits for read-only requests. Implementing the same list view as before using array hydration we can rewrite our code: @@ -1231,7 +1232,7 @@ write scenarios: echo "Bug: ".$bug->getDescription()."\n"; echo "Engineer: ".$bug->getEngineer()->getName()."\n"; -The output of the engineers name is fetched from the database! What is happening? +The output of the engineer’s name is fetched from the database! What is happening? Since we only retrieved the bug by primary key both the engineer and reporter are not immediately loaded from the database but are replaced by LazyLoading @@ -1277,6 +1278,14 @@ The call prints: Bug: Something does not work! Engineer: beberlei +.. warning:: + + Lazy loading additional data can be very convenient but the additional + queries create an overhead. If you know that certain fields will always + (or usually) be required by the query then you will get better performance + by explicitly retrieving them all in the first query. + + Dashboard of the User ---------------------