SonataDoctrineORMAdminBundle

repository·4.x·Indexed 19 days ago

https://github.com/sonata-project/sonatadoctrineormadminbundle

An integration layer between Doctrine ORM and SonataAdminBundle that allows developers to manage Doctrine entities through a web-based administration interface. It enables the creation of automated CRUD interfaces for database-backed entities and provides features for auditing via EntityAuditBundle, custom datagrid filters, and template overrides for list and show views.

Tokens
14.8K
Snippets
44
Records
55
Agent score
65%

What's inside SonataDoctrineORMAdminBundle

  1. Overview of SonataDoctrineORMAdminBundle

    4.x
    SonataDoctrineORMAdminBundle is a bridge that integrates Doctrine ORM into the SonataAdminBundle. It allows developers to use Doctrine entities as the data source for Sonata's administration interface, enabling the creation of automated CRUD (Create, Read, Update, Delete) interfaces for database-backed entities.
  2. Configure child associations in an Admin class

    4.x

    When an Admin class manages a child entity (an entity that belongs to a parent), you can use the $parentAssociationMapping property to define the relationship.

    In the configureFormFields method, you can check $this->isChild() to determine if the current context is a child view. This allows you to conditionally add fields, such as a selector for the parent entity, only when creating a new child record rather than editing an existing one.

    final class CommentAdmin extends AbstractAdmin
    {
        protected $parentAssociationMapping = 'post';
    
        protected function configureFormFields(FormMapper $formMapper)
        {
            if (!$this->isChild()) {
                $formMapper->add('post', ModelType::class, [], ['edit' => 'list']);
            }
    
            $formMapper
                ->add('name')
                ->add('message');
        }
    }
  3. Configure One-to-Many relation fields (Collections)

    4.x

    To manage a One-to-Many relationship (e.g., a Gallery containing multiple Media items), use the Sonata\Form\Type\CollectionType.

    You can customize the collection behavior using these options in the field definition:

    • edit: Set to inline or standard. inline mode allows adding new rows directly.
    • inline: Set to table or standard. table displays fields in a table format.
    • sortable: Set to the name of the field (e.g., 'position') to enable drag-and-drop sorting.
    • limit: An integer that limits the number of elements that can be added. Once the limit is reached, the "Add new" button is hidden.

    Important: You must implement a setMedias method in your model and ensure cascading persistence is configured for the relationship.

    namespace Sonata\MediaBundle\Admin;
    
    use Sonata\AdminBundle\Admin\AbstractAdmin;
    use Sonata\AdminBundle\Form\FormMapper;
    use Sonata\Form\Type\CollectionType;
    
    final class GalleryAdmin extends AbstractAdmin
    {
        protected function configureFormFields(FormMapper $form): void
        {
            $form
                ->add('code')
                ->add('enabled')
                ->add('name')
                ->add('defaultFormat')
                ->add('galleryHasMedias', CollectionType::class, [
                        'by_reference' => false,
                    ],
                    [
                        'edit' => 'inline',
                        'inline' => 'table',
                        'sortable' => 'position',
                        'limit' => 3,
                    ]);
        }
    }
  4. Filter by sub-entity properties using dot notation

    4.x

    You can filter base entities by the properties of related entities using dot-separated notation (e.g., relation.property).

    Note: This only works when the path consists of entities. It does not work when traversing collections.

    protected function configureDatagridFilters(DatagridMapper $filter): void
    {
        $filter
            ->add('address.street')
            ->add('address.ZIPCode')
            ->add('address.town');
    }
  5. How auditing works with EntityAuditBundle

    4.x

    Auditing works by creating a mirroring table for every audited entity table, suffixed with _audit. These mirror tables contain all columns from the original entity plus two metadata fields:

    • rev: The global revision number from a central revisions table.
    • revtype: The type of operation that triggered the log entry ('INS' for insert, 'UPD' for update, or 'DEL' for delete).

    The global revision table tracks the id, timestamp, username, and a change comment. This mechanism allows you to version your application and view associations at specific points in time. The extension automatically hooks into the Doctrine SchemaTool generation process to create the necessary DDL statements for these audit tables.

  6. Understand the required routes for an Admin class

    4.x

    When creating an Admin class for a Doctrine entity in SonataAdmin, the bundle expects 6 specific routes to be defined to handle the standard CRUD lifecycle. These routes are:

    • list: Displays the list of entities.
    • create: The form for creating a new entity.
    • batch: Handles batch operations on multiple entities.
    • update: The logic for updating an existing entity.
    • edit: The form for editing an existing entity.
    • delete: The action for deleting an entity.

    Note: In standard configurations, the route information is automatically generated for you by the bundle, allowing you to proceed without manual routing configuration.

  7. Use ProxyQuery to extend Doctrine Query functionality

    4.x

    The ProxyQuery object wraps a standard Doctrine QueryBuilder to provide additional features and convenience methods required by Sonata Admin.

    Key enhancements include:

    • Direct Execution: Use the execute() method directly instead of calling getQuery()->getResult().
    • Sorting: Provides simplified setSortBy() and sort order options.
    • Optimized Joins: Automatically handles preselecting IDs on left join queries so that setMaxResults() (limit) is applied to the primary entity rather than the entire result set, simulating Doctrine 1 behavior.
    • Duplicate Prevention: By default, Sonata uses the DISTINCT SQL keyword when fetching identifiers for listings to prevent duplicates caused by joins. If you are certain no duplicates will occur and want to improve performance, you can disable this using setDistinct(false).
    use Sonata\AdminBundle\Datagrid\ORM\ProxyQuery;
    
    $queryBuilder = $this->em->createQueryBuilder();
    $queryBuilder->from('Post', 'p');
    
    $proxyQuery = new ProxyQuery($queryBuilder);
    $proxyQuery->leftJoin('p.tags', 't');
    $proxyQuery->setSortBy('name');
    $proxyQuery->setMaxResults(10);
    
    $results = $proxyQuery->execute();
  8. Handle object-based IDs (e.g., UUIDs) in SonataAdmin

    4.x

    If your entity uses an object for its ID (such as a Ramsey\\Uuid\" object) instead of a primitive type, the bundle will automatically resolve the string representation for URLs and rendering by calling $entity->getId()->__toString(). To support this, ensure your ID object implements the __toString()` method.

    use Doctrine
    oDB\\Types\\
    use Doctrine\ORM\Mapping as ORM;
    use Ramsey\Uuid\Doctrine\UuidOrderedTimeGenerator;
    use Ramsey\Uuid\UuidInterface;
    
    class Comment
    {
        #[ORM\Id]
        #[ORM\Column(type: Types::INTEGER)]
        #[ORM\GeneratedValue(strategy: 'CUSTOM')]
        #[ORM\CustomIdGenerator(class: UuidOrderedTimeGenerator::class)]
        private ?UuidInterface $id = null;
    
        // ...
    }
  9. Configure Many-to-One relation fields

    4.x

    When a model has a Many-to-One relationship (e.g., many Post entities linked to one User), you can choose between two primary Sonata types to handle the relation:

    1. Sonata\AdminBundle\Form\Type\ModelType: Displays the list in a select widget with an Add button to create a new related object.
    2. Sonata\AdminBundle\Form\Type\ModelListType: Displays the list in a searchable model where you can select and delete related objects.

    ModelListType Options:

    • btn_add: Custom label for the add button.
    • btn_list: Translation key for the list button.
    • btn_delete: Set to false to hide the delete button.
    • btn_edit: Custom label for the edit button (shows when a value is set).
    • btn_catalogue: Custom translation domain for the buttons.
    namespace Sonata\NewsBundle\Admin;
    
    use Sonata\AdminBundle\Admin\AbstractAdmin;
    use Sonata\AdminBundle\Form\FormMapper;
    use Sonata\AdminBundle\Form\Type\ModelListType;
    use Sonata\AdminBundle\Form\Type\ModelType;
    
    final class PostAdmin extends AbstractAdmin
    {
        protected function configureFormFields(FormMapper $form): void
        {
            $form
                ->with('General')
                    ->add('enabled', null, ['required' => false])
                    ->add('author', ModelListType::class, [
                        'btn_add'       => 'Add author',
                        'btn_list'      => 'button.list',
                        'btn_delete'    => false,
                        'btn_edit'      => 'Edit',
                        'btn_catalogue' => 'SonataNewsBundle',
                    ], [
                        'placeholder' => 'No author selected',
                    ])
                    ->add('title')
                    ->add('abstract')
                    ->add('content')
                ->end()
                ->with('Tags')
                    ->add('tags', ModelType::class, ['expanded' => true])
                ->end()
                ->with('Options', ['collapsed' => true])
                    ->add('commentsCloseAt')
                    ->add('commentsEnabled', null, ['required' => false])
                    ->add('commentsDefaultStatus', 'choice', [
                        'choices' => Comment::getStatusList()
                    ])
                ->end();
        }
    }
  10. Migrate ModelAutocompleteFilter to ModelFilter in version 4.1+

    4.x

    In version 4.1 and later, Sonata\DoctrineORMAdminBundle\Filter\ModelAutocompleteFilter is deprecated. To maintain compatibility and follow the new pattern, replace the direct use of ModelAutocompleteFilter with ModelFilter and specify the ModelAutocompleteType via the field_type option.

    Old way (Deprecated):

    ->add('foo', ModelAutocompleteFilter::class)

    New way:

    ->add('foo', ModelFilter::class, [
         'field_type' => ModelAutocompleteType::class,
    ])
  11. Define Doctrine entities for SonataAdmin

    4.x
    The SonataDoctrineORMAdminBundle interacts directly with your Doctrine entities. You can use any Doctrine metadata driver (Attributes, XML, YAML, etc.) to define your models. When defining entities, ensure you implement __toString() if you want a readable representation in the admin interface, or ensure the ID object implements __toString() if you are using object-based identifiers (like UUIDs).
  12. Define a custom CRUD controller

    4.x

    A CRUD controller in Sonata is a class that extends Sonata\AdminBundle\Controller\CRUDController. By default, you can use an empty class if you only need the standard CRUD actions. However, defining a custom controller allows you to add new actions or overwrite default behavior to suit your application's specific needs.

    If you do not declare a controller in your Admin configuration, the AdminBundle will automatically use the default CRUDController.

    // src/Tutorial/BlogBundle/Controller/CommentAdminController.php
    
    namespace Tutorial//BlogBundle/Controller;
    
    use Sonata\AdminBundle\Controller\CRUDController;
    
    final class CommentAdminController extends CRUDController
    {
        // You can add new actions or overwrite default ones here
    }