Important

You are browsing the documentation for version 4.1 of OroCommerce, OroCRM and OroPlatform, which is no longer maintained. Read version 5.1 (the latest LTS version) of the Oro documentation to get up-to-date information.

See our Release Process documentation for more information on the currently supported and upcoming releases.

Create Entities

When working with OroPlatform, creating entities and mapping them to the database is no different from doing the same in a common Symfony application. Once you have created your bundle, create the entity classes you need in the bundle’s Entity namespace, add all the required properties, and add the required mapping annotations as usual.

A task is composed of a brief subject, a more verbose description, a due date, and a priority. Also, each task is identified by a unique identifier that is automatically generated by the database:

  1// src/AppBundle/Entity/Task.php
  2namespace AppBundle\Entity;
  3
  4use Doctrine\ORM\Mapping as ORM;
  5
  6/**
  7 * @ORM\Entity()
  8 * @ORM\Table(name="app_task")
  9 */
 10class Task
 11{
 12    /**
 13     * @ORM\Id()
 14     * @ORM\GeneratedValue(strategy="AUTO")
 15     * @ORM\Column(type="integer")
 16     *
 17     * @var int
 18     */
 19    private $id;
 20
 21    /**
 22     * @ORM\Column(type="string")
 23     *
 24     * @var string
 25     */
 26    private $subject;
 27
 28    /**
 29     * @ORM\Column(type="text")
 30     *
 31     * @var string
 32     */
 33    private $description;
 34
 35    /**
 36     * @ORM\Column(type="datetime", name="due_date")
 37     *
 38     * @var \DateTime
 39     */
 40    private $dueDate;
 41
 42    /**
 43     * @ORM\ManyToOne(targetEntity="Priority")
 44     * @ORM\JoinColumn(name="task_priority_id", onDelete="SET NULL")
 45     *
 46     * @var Priority
 47     */
 48    private $priority;
 49
 50    /**
 51     * Returns the id.
 52     *
 53     * @return int
 54     */
 55    public function getId()
 56    {
 57        return $this->id;
 58    }
 59
 60    /**
 61     * Returns the subject.
 62     *
 63     * @return string
 64     */
 65    public function getSubject()
 66    {
 67        return $this->subject;
 68    }
 69
 70    /**
 71     * Sets the subject.
 72     *
 73     * @param string $subject
 74     */
 75    public function setSubject($subject)
 76    {
 77        $this->subject = $subject;
 78    }
 79
 80    /**
 81     * Returns the description.
 82     *
 83     * @return string
 84     */
 85    public function getDescription()
 86    {
 87        return $this->description;
 88    }
 89
 90    /**
 91     * Sets the description.
 92     *
 93     * @param string $description
 94     */
 95    public function setDescription($description)
 96    {
 97        $this->description = $description;
 98    }
 99
100    /**
101     * Returns the due date.
102     *
103     * @return \DateTime
104     */
105    public function getDueDate()
106    {
107        return $this->dueDate;
108    }
109
110    /**
111     * Sets the due date.
112     *
113     * @param \DateTime $dueDate
114     */
115    public function setDueDate(\DateTime $dueDate)
116    {
117        $this->dueDate = $dueDate;
118    }
119
120    /**
121     * Returns the priority.
122     *
123     * @return Priority
124     */
125    public function getPriority()
126    {
127        return $this->priority;
128    }
129
130    /**
131     * Sets the priority.
132     *
133     * @param Priority $priority
134     */
135    public function setPriority(Priority $priority)
136    {
137        $this->priority = $priority;
138    }
139}

Users should be able to create and change priorities through the user interface, therefore, they are modeled as separate entities:

 1// src/AppBundle/Entity/Priority.php
 2namespace AppBundle\Entity;
 3
 4use Doctrine\ORM\Mapping as ORM;
 5
 6/**
 7 * @ORM\Entity()
 8 * @ORM\Table(name="app_task_priority")
 9 */
10class Priority
11{
12    /**
13     * @ORM\Id()
14     * @ORM\GeneratedValue(strategy="AUTO")
15     * @ORM\Column(type="integer")
16     *
17     * @var int
18     */
19    private $id;
20
21    /**
22     * @ORM\Column(type="string", unique=true)
23     *
24     * @var string
25     */
26    private $label;
27
28    /**
29     * Returns the priority id.
30     *
31     * @return int
32     */
33    public function getId()
34    {
35        return $this->id;
36    }
37
38    /**
39     * Returns the label.
40     *
41     * @return string
42     */
43    public function getLabel()
44    {
45        return $this->label;
46    }
47
48    /**
49     * Changes the priority label.
50     *
51     * @param string $label
52     */
53    public function setLabel($label)
54    {
55        $this->label = $label;
56    }
57}

After you have modeled your entities, you need to update the database schema. To update the schema, use the doctrine:schema:update command. Use the --dump-sql option first to make sure that Doctrine makes the expected changes:

$ php bin/console doctrine:schema:update --dump-sql

If the command displays unexpected information, double-check the configured mapping information and rerun the command.

When everything is displayed as expected, update the database schema by passing the --force option:

$ php bin/console doctrine:schema:update --force

Tip

Doctrine caches mapping metadata. If the doctrine:schema:update command does not recognize your changes to the entity mapping, clear the metadata cache manually and update the schema again:

# clear the metadata cache
$ php bin/console doctrine:cache:clear-metadata

# check the schema change queries to be executed
$ php bin/console doctrine:schema:update --dump-sql

# apply the schema changes to the database
$ php bin/console doctrine:schema:update --force

Caution

Do not use the doctrine:schema:update command with your production database. Instead, create migrations to update the schema of your database. You can read more about using migrations in the Update Database Schema section. To run migrations and emulate complete migration process, use the oro:platform:update command.

Doctrine Entities

Define Entities

You can define entities the same way as in typical Symfony applications. For example, use the annotations provided by Doctrine (you can also use the YAML or XML configuration format):

 1// src/Acme/DemoBundle/Entity/Hotel.php
 2namespace Acme\DemoBundle\Entity;
 3
 4use Doctrine\ORM\Mapping as ORM;
 5
 6/**
 7 * @ORM\Entity
 8 * @ORM\Table(name="acme_hotel")
 9 */
10class Hotel
11{
12    /**
13     * @ORM\Id
14     * @ORM\Column(type="integer")
15     * @ORM\GeneratedValue(strategy="AUTO")
16     */
17    private $id;
18
19    /**
20     * @ORM\Column(type="string", length=255)
21     */
22    private $name;
23
24    public function getId()
25    {
26        return $this->id;
27    }
28
29    public function getName()
30    {
31        return $this->name;
32    }
33
34    public function setName($name)
35    {
36        $this->name = $name;
37    }
38}

You can create a class that represents a particular model from your domain and add the getter and setter methods to access and modify the data in your application. Next, add mapping information to tell Doctrine how the data is mapped to your database.

Update Database Schema

Once the models are ready, update the database to reflect the changes you have made. Use migrations as a mechanism to extend your model iteratively. Migrations allow you to version your database schema. Every time you modify your model, you create a new migration that reflects the changes for this particular schema version.

However, Doctrine’s migration mechanism only works well on the application level. It is not capable of handling different schema versions per bundle, which means that you cannot use them in a modular architecture. Luckily, you can use the features provided by the OroMigrationBundle to create separate migrations for each bundle.

Organizing migrations is relatively simple if you follow the basic conventions below:

  • Place all migrations under the Migrations/Schema/ directory of your bundle.

  • In this directory, create one subdirectory per schema version.

  • Create as many migration classes as necessary inside a particular schema version directory (see the example below).

Note

The names of the schema version directories are compared to each other using PHP’s version_compare function. Therefore, it is a good practice to name them following the v1_0, v2_0 pattern.

When migration to a particular schema version is performed, all migration classes from the corresponding directory are evaluated. Then, the contents of their up() method is executed. A class is treated as a migration class when it implements the Oro\Bundle\MigrationBundle\Migration\Migration interface.

For example, the migration class for the Hotel entity looks is illustrated below:

 1// src/Acme/DemoBundle/Migrations/Schema/v1_0/Hotel.php
 2namespace Acme\DemoBundle\Migrations\Schema\v1_0;
 3
 4use Doctrine\DBAL\Schema\Schema;
 5use Oro\Bundle\MigrationBundle\Migration\Migration;
 6use Oro\Bundle\MigrationBundle\Migration\QueryBag;
 7
 8class Hotel implements Migration
 9{
10    public function up(Schema $schema, QueryBag $queries)
11    {
12        $table = $schema->createTable('acme_hotel');
13        $table->addColumn('id', 'integer', ['autoincrement' => true]);
14        $table->addColumn('name', 'string', ['length' => 255]);
15        $table->setPrimaryKey(['id']);
16        $table->addIndex(['name'], 'hotel_name_idx', []);
17    }
18}

Note

Entity metadata in the PHP entity classes (annotations) should match exactly what the schema migration is doing. If you create a migration that modifies the type, length or another property of an existing entity field, please remember to make the same change in the PHP entity class annotations.

You can modify the database using the interface the Doctrine DBAL offers with its Schema class, and you can also execute queries directly using the QueryBag, if necessary.

Queries executed using the QueryBag, are divided into two groups: use the addPreQuery() to add a query that is executed before the schema changes from the migration class are performed. Queries scheduled with the addPostQuery() method are executed after the schema is modified.

To load and apply migrations to the existing database schema, execute the oro:migration:load command:

$ php bin/console oro:migration:load --force

This command checks for present migration versions that are currently not reflected in the existing database schema and executes all missing migrations sequentially in ascending order.

Tip

You can use the --dry-run option to see what is going to be executed and you can use the --bundles option to perform migrations only for a subset of all available bundles (use --exclude for a bundle blacklist instead). You can also get more information about each query with the --show-queries option.