Files
planeMapper/.claude/skills/oro-dba/SKILL.md
T

9.8 KiB

name, description
name description
oro-dba OroCommerce database administration reference covering PostgreSQL configuration, Doctrine ORM mapping, migrations, entity extensions, schema management, indexing, performance optimization, and data management. Use when working with database schema, writing migrations, optimizing queries, configuring PostgreSQL, or troubleshooting database issues in OroCommerce.

OroCommerce DBA

Database administration and schema reference for OroCommerce. Docs: https://doc.oroinc.com/backend/entities/

Database Requirements

  • PostgreSQL >= 16.1 (only supported RDBMS)
  • uuid-ossp extension required: CREATE EXTENSION "uuid-ossp";
  • Doctrine ORM with Oro Doctrine Extensions for PG-specific functions

Doctrine ORM in Oro

Oro uses standard Doctrine ORM with PHP 8 attributes:

#[ORM\Entity]
#[ORM\Table(name: 'acme_example')]
#[ORM\Index(columns: ['status'], name: 'idx_acme_example_status')]
#[ORM\UniqueConstraint(columns: ['external_id'], name: 'uniq_acme_ext_id')]
class Example { }

Oro-specific: #[Config] attribute adds entity config metadata (ACL, ownership, audit, extend support). This is processed by EntityConfigBundle.

Migration System

Oro uses its own migration system (not Doctrine Migrations).

Migration Interface

use Oro\Bundle\MigrationBundle\Migration\Migration;

class AddStatusColumn implements Migration
{
    public function up(Schema $schema, QueryBag $queries): void
    {
        $table = $schema->getTable('acme_example');
        $table->addColumn('status', 'string', ['length' => 32, 'default' => 'new']);
        $table->addIndex(['status'], 'idx_acme_example_status');
    }
}

Installation Interface (Initial Schema)

use Oro\Bundle\MigrationBundle\Migration\Installation;

class AcmeExampleInstaller implements Installation
{
    public function getMigrationVersion(): string { return 'v1_0'; }

    public function up(Schema $schema, QueryBag $queries): void
    {
        $table = $schema->createTable('acme_example');
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        // ... all columns
        $table->setPrimaryKey(['id']);
    }
}

Data Migrations

Use QueryBag for INSERT/UPDATE/DELETE:

public function up(Schema $schema, QueryBag $queries): void
{
    $queries->addQuery("INSERT INTO oro_enum_value ...");
    $queries->addPreQuery("UPDATE ..."); // runs before schema changes
    $queries->addPostQuery("UPDATE ..."); // runs after schema changes
}

Migration Commands

php bin/console oro:migration:load              # run pending migrations
php bin/console oro:migration:dump              # generate diff migration
php bin/console oro:entity-extend:update-schema # update extended entity schema
php bin/console oro:entity-extend:update-schema --dry-run

Migration File Location

Migrations/Schema/
    v1_0/CreateExampleTable.php
    v1_1/AddStatusColumn.php
    v1_2/AddForeignKeys.php

Version directories must sort lexicographically (v1_0 < v1_1 < v1_10).

Multiple migration files per version directory are allowed and common:

Migrations/Schema/
    v1_13/
        AddNewFields.php
        RemoveObsoleteFields.php
        UpdateFieldLabels.php

Extending Oro Base Entities

Custom entities can extend Oro base entity classes (e.g., Transport for shipping/payment integrations):

#[ORM\Entity]
class FreightShippingSettings extends Transport
{
    #[ORM\ManyToMany(targetEntity: LocalizedFallbackValue::class, cascade: ['ALL'], orphanRemoval: true)]
    #[ORM\JoinTable(name: 'acme_freight_transport_label')]
    #[ORM\JoinColumn(name: 'transport_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
    #[ORM\InverseJoinColumn(name: 'localized_value_id', referencedColumnName: 'id', onDelete: 'CASCADE', unique: true)]
    private Collection $labels;
}

Doctrine Mapping Configuration

For app-level entities outside bundles, configure in config/doctrine.yml:

doctrine:
    orm:
        mappings:
            App:
                is_bundle: false
                type: attribute
                dir: '%kernel.project_dir%/src/Entity'
                prefix: 'App\Entity'
                alias: App

Bundle entities are auto-discovered -- no explicit mapping needed.

Entity Extension Schema

Extended fields use oro_options in column definitions:

$table->addColumn('custom_field', 'string', [
    'oro_options' => [
        'extend' => [
            'is_extend' => true,
            'owner' => ExtendScope::OWNER_CUSTOM,  // or OWNER_SYSTEM
            'nullable' => true,
        ],
        'entity' => ['label' => 'Custom Field'],
        'datagrid' => ['is_visible' => DatagridScope::IS_VISIBLE_TRUE],
    ]
]);

Adding Enum Fields via ExtendExtension

For select/multiselect fields on Oro core entities, use addEnumField:

use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtension;
use Oro\Bundle\EntityExtendBundle\Migration\Extension\ExtendExtensionAwareInterface;

class AddFrequencyEnum implements Migration, ExtendExtensionAwareInterface
{
    protected ExtendExtension $extendExtension;

    public function setExtendExtension(ExtendExtension $extendExtension): void
    {
        $this->extendExtension = $extendExtension;
    }

    public function up(Schema $schema, QueryBag $queries): void
    {
        $this->extendExtension->addEnumField(
            $schema,
            $schema->getTable('oro_product'),
            'frequency',                       // field name
            'acme_product_frequency',        // enum code (must be unique)
            false,                             // is_multiple (false = select, true = multiselect)
            false,                             // is_public
            [
                'extend' => ['owner' => ExtendScope::OWNER_CUSTOM],
                'entity' => ['label' => 'Frequency']
            ]
        );
    }
}

Populate enum values via data migration:

use Oro\Bundle\EntityExtendBundle\Migration\Fixture\AbstractEnumFixture;

class LoadFrequencyEnumValues extends AbstractEnumFixture
{
    protected function getData(): array
    {
        return [
            '50hz' => false,  // value => is_default
            '60hz' => true,
            'variable' => false,
        ];
    }

    protected function getEnumCode(): string
    {
        return 'acme_product_frequency';
    }
}

Datagrid Visibility in oro_options

Value Effect
DatagridScope::IS_VISIBLE_TRUE Shown by default
DatagridScope::IS_VISIBLE_HIDDEN Available but hidden by default
DatagridScope::IS_VISIBLE_FALSE Not available in datagrid

Extended entity schema is managed separately:

  • Proxy classes generated in cache
  • oro:entity-extend:update-schema applies extend changes
  • oro:entity-extend:cache:clear rebuilds proxies

Table Naming Conventions

Scope Pattern Example
Custom entities acme_{bundle}_{entity} acme_cpn_customer_part_number
Oro core oro_{entity} oro_order
Extend fields auto-generated columns on existing table custom_field on oro_order
Enum tables oro_enum_{code} oro_enum_order_status

Indexing Guidance

When to Add Indexes

  • Columns used in WHERE clauses
  • Foreign key columns
  • Columns used in ORDER BY
  • Columns used in datagrid filters/sorters

Oro Datagrid Query Patterns

Datagrids generate ORM queries. The data_name in sorters and filters maps directly to query aliases. Ensure underlying columns are indexed.

Composite Indexes

$table->addIndex(['organization_id', 'status'], 'idx_org_status');

Key Oro Tables

Table Purpose
oro_user Admin/back-office users
oro_customer Commerce customers (organizations)
oro_customer_user Commerce storefront users
oro_order Orders
oro_product Products
oro_category Product categories
oro_shopping_list Shopping lists
oro_checkout Checkout sessions
oro_price_list Price lists
oro_workflow_item Workflow state
oro_message_queue DBAL message queue
oro_entity_config Entity configuration metadata
oro_migration Migration version tracking

Performance Considerations

PostgreSQL Tuning

  • shared_buffers: 25% of RAM
  • effective_cache_size: 75% of RAM
  • work_mem: 4-16MB (depends on concurrent queries)
  • maintenance_work_mem: 256MB-1GB for migrations/vacuum
  • random_page_cost: 1.1 for SSD
  • Enable pg_stat_statements for query monitoring

Oro-Specific Performance

  • Extend entity cache warmup is CPU-intensive; run during deployment
  • Migration load can be slow on large schemas; use maintenance windows
  • DBAL MQ polls every 1s by default; tune polling_interval or use RabbitMQ
  • Elasticsearch offloads search from PostgreSQL (EE)
  • Redis caching reduces DB query load

Doctrine Query Hints

Oro uses Doctrine query hints for:

  • HINT_PRECISE_ORDER_BY -- deterministic ordering
  • Custom walker hints for ACL filtering
  • Translation hints for localized queries

Backup and Recovery

Standard PostgreSQL tools apply:

pg_dump -Fc dbname > backup.dump          # custom format backup
pg_restore -d dbname backup.dump          # restore
pg_dump --schema-only dbname > schema.sql # schema only

Always back up before oro:migration:load or oro:entity-extend:update-schema.

Data Fixtures

Seed data in Migrations/Data/ORM/, demo data in Migrations/Data/Demo/ORM/. Fixtures implement FixtureInterface. Use OrderedFixtureInterface for ordering. Load: php bin/console oro:migration:data:load (--fixtures-type=demo for demo). Preview: --dry-run. Scope: --bundles=BundleName.

Data Audit Tables

When dataaudit is enabled, changes tracked in oro_audit and oro_audit_field. Each entry stores: entity class, entity ID, action, user, timestamp, version. Segment filters can query audit data for historical analysis.