瀏覽代碼

arch docs

Tiago Garcia 11 年之前
父節點
當前提交
c29e4db98e
共有 1 個文件被更改,包括 209 次插入119 次删除
  1. 209 119
      Resources/doc/reference/architecture.rst

+ 209 - 119
Resources/doc/reference/architecture.rst

@@ -1,161 +1,251 @@
 Architecture
 ============
 
-The architecture of the SonataAdminBundle is primarily inspired by the Django Admin
+The architecture of the ``SonataAdminBundle`` is primarily inspired by the Django Admin
 Project, which is truly a great project. More information can be found at the
 `Django Project Website`_.
 
+If you followed the instructions on the :doc:`getting_started` page, you should by
+now have an ``Admin`` class and an ``Admin`` service. In this chapter, we'll discuss more in
+depth how it works.
+
 The Admin Class
 ---------------
 
-The ``Admin`` class represents the CRUD definition for a specific model. It
-contains all the configuration necessary to display a rich CRUD interface for
-the entity.
-
-Within the admin class, the following information can be defined:
+The ``Admin`` class maps a specific model to the rich CRUD interface provided by
+``SonataAdminBundle``. In other words, using your ``Admin`` classes, you can configure
+what is shown by ``SonataAdminBundle`` in each CRUD action for the associated model.
+By now you've seen 3 of those actions in the ``getting started`` page: list, 
+filter and form (for creation/edition). However, a fully configured ``Admin`` class
+can define more actions:
 
 * ``list``: The fields displayed in the list table;
 * ``filter``: The fields available for filtering the list;
-* ``form``: The fields used to edit the entity;
+* ``form``: The fields used to create/edit the entity;
 * ``show``: The fields used to show the entity;
 * Batch actions: Actions that can be performed on a group of entities
   (e.g. bulk delete)
 
-If a field is associated with another entity (and that entity also has an
-``Admin`` class), then the related ``Admin`` class will be accessible from
-within the first class.
-
-The admin class is a service implementing the ``AdminInterface`` interface,
-meaning that the following required dependencies are automatically injected:
+The ``Sonata\AdminBundle\Admin\Admin`` class is provided as an easy way to
+map your models, by extending it. However, any implementation of the 
+``Sonata\AdminBundle\Admin\AdminInterface`` can be used to define an ``Admin``
+service. For each ``Admin`` service, the following required dependencies are 
+automatically injected by the bundle:
 
+* ``ModelManager``: service which handles specific ORM code
+* ``FormContractor``: builds the edit/create views form using the Symfony ``FormBuilder``
+* ``ShowBuilder``: builds the 'show' view
 * ``ListBuilder``: builds the list fields
-* ``FormContractor``: builds the form using the Symfony ``FormBuilder``
 * ``DatagridBuilder``: builds the filter fields
-* ``Router``: generates the different urls
-* ``Request``
-* ``ModelManager``: Service which handles specific ORM code
-* ``Translator``
-
-Therefore, you can gain access to any service you want by injecting them into
-the admin class, like so:
-
-.. code-block:: xml
-
-    <service id="sonata.news.admin.post" class="%sonata.news.admin.post.class%">
-        <tag name="sonata.admin" manager_type="orm" group="sonata_blog" label="post"/>
-        <argument />
-        <argument>%sonata.news.admin.post.entity%</argument>
-        <argument>%sonata.news.admin.post.controller%</argument>
-
-        <call method="setUserManager">
-            <argument type="service" id="fos_user.user_manager" />
-        </call>
+* ``Translator``: generates translations
+* ``ConfigurationPool``: configuration pool where all Admin class instances are stored
+* ``RouterGenerator``: generates the different urls
+* ``Validator``: handles model validation
+* ``SecurityHandler``: handles permissions for model instances and actions
+* ``MenuFactory``: generates the side menu, depending on the current action
+* ``RouteBuilder``: allows you to easily add routes for new actions
+* ``Request`` : http request received
+* ``LabelTranslatorStrategy``: a strategy to use when generating labels 
 
-    </service>
+.. note::
 
-Here, the FOS' User Manager is injected into the Post service.
+    Each of these dependencies is used for a specific task, briefly described above.
+    If you wish to learn more about how they are used, check the respective documentation
+    chapter. In most cases, you won't need to worry about their underlying implementation.
 
-Field Definition
-----------------
 
-A field definition is a ``FieldDescription`` object. There is one definition per list
-field.
+All these dependencies have default values that you can override by a using 
+``call`` to the matching ``setter`` when declaring the ``Admin`` service, like so:
 
-The definition contains:
-
-* ``name``: The name of the field definition;
-* ``type``: The field type;
-* ``template``: The template used for displaying the field;
-* ``targetEntity``: The class name of the target entity for relations;
-* ``options``: Certain field types have additional options;
-
-Template Configuration
-----------------------
-
-The current implementation uses Twig as the template engine. All templates
-are located in the ``Resources/views/CRUD`` directory of the bundle. The base
-template extends two layouts:
-
-* ``AdminBundle::standard_layout.html.twig``
-* ``AdminBundle::ajax_layout.html.twig``
-
-The base templates can be configured in the Service Container. So you can easily tweak
-the layout to suit your requirements.
+.. code-block:: xml
 
-Each field is rendered in three different ways and each has its own Twig
-template. For example, for a field with a ``text`` type, the following three
-templates will be used:
+    <service id="sonata.admin.post" class="Acme\DemoBundle\Admin\PostAdmin">
+          <tag name="sonata.admin" manager_type="orm" group="Content" label="Post"/>
+          <argument />
+          <argument>Acme\DemoBundle\Entity\Post</argument>
+          <argument />
+          <call method="setLabelTranslatorStrategy">
+              <argument>sonata.admin.label.strategy.underscore</argument>
+          </call>
+      </service>
+      
+.. code-block:: yaml
 
-* ``filter_text.twig``: template used in the filter box
-* ``list_text.twig``: template used in the list table
+    services:
+        sonata.admin.post:
+            class: Acme\DemoBundle\Admin\PostAdmin
+            tags:
+                - { name: sonata.admin, manager_type: orm, group: "Content", label: "Post" }
+            arguments:
+                - ~
+                - Acme\DemoBundle\Entity\Post
+                - ~
+            calls:
+                - [ setLabelTranslatorStrategy, [sonata.admin.label.strategy.underscore]]
+
+Here, we declare the same ``Admin`` service as in the :doc:`getting_started` chapter, but using a
+different label translator strategy, replacing the default one. Notice that
+``sonata.admin.label.strategy.underscore`` is a service provided by ``SonataAdminBundle``, 
+but you could just as easily use a service of your own.
 
 CRUDController
 --------------
 
-The controller contains the basic CRUD actions. It is related to one
-``Admin`` class by mapping the controller name to the correct ``Admin``
-instance.
+The ``CRUDController`` contains the actions you have available to manipulate
+your model instances, like list, create or delete. It uses the ``Admin`` class
+to determine its behavior, like which fields to display in the edit form, or
+how to build the list view. Inside the ``CRUDController``, you can find the
+``Admin`` class instance in the ``$admin`` variable.
 
-Any or all actions can be overwritten to suit the project's requirements.
+The ``CRUDController`` is no different than any other Symfony2 controller,
+meaning you have all the usual options available to you, like getting services
+from the Dependency Injection Container (DIC).
 
-The controller uses the ``Admin`` class to construct the different actions.
-Inside the controller, the ``Admin`` object is accessible through the
-``configuration`` property.
-
-Obtaining an ``Admin`` Service
-------------------------------
-
-``Admin`` definitions are accessible through the ``sonata.admin.pool`` service or
-directly from the DIC (dependency injection container). The ``Admin`` definitions
-are lazy-loaded from the DIC to reduce overhead.
-
-Declaring a new Admin class
----------------------------
-
-Once you have created an admin class, you need to make the framework aware of
-it. To do that, you need to add a tag with the name ``sonata.admin`` to the
-service. Parameters for that tag are:
-
-* ``manager_type``: Label of the database manager to use;
-* ``group``: A label to allow grouping on the dashboard;
-* ``label``: Label to use for the name of the entity this manager handles;
-
-Examples:
+This is particulary useful if you decide to extend the ``CRUDController``, to
+add new actions or change the behavior of existing ones. You can specify which controller
+to use when declaring the ``Admin`` service, by passing it as the 3rd argument:
 
 .. code-block:: xml
 
-    <!-- app/config/config.xml -->
-    <service id="sonata.news.admin.post" class="Sonata\NewsBundle\Admin\PostAdmin">
-
-        <tag name="sonata.admin" manager_type="orm" group="sonata_blog" label="post"/>
-
-        <argument />
-        <argument>Sonata\NewsBundle\Entity\Post</argument>
-        <argument>SonataAdminBundle:CRUD</argument>
-    </service>
-
-If you want to define your own controller for handling CRUD operations, change the last argument
-in the service definition to::
-
-  <argument>SonataNewsBundle:PostAdmin</argument>
-
-Or if you're using a YML configuration file,
-
+    <services>
+       <service id="sonata.admin.post" class="Acme\DemoBundle\Admin\PostAdmin">
+          <tag name="sonata.admin" manager_type="orm" group="Content" label="Post"/>
+          <argument />
+          <argument>Acme\DemoBundle\Entity\Post</argument>
+          <argument>AcmeDemoBundle:PostAdmin</argument>
+          <call method="setTranslationDomain">
+              <argument>AcmeDemoBundle</argument>
+          </call>
+      </service>
+   </services>
+    
 .. code-block:: yaml
 
     services:
-       sonata.news.admin.post:
-          class: Sonata\NewsBundle\Admin\PostAdmin
-          tags:
-            - { name: sonata.admin, manager_type: orm, group: sonata_blog, label: post }
-          arguments: [null, Sonata\NewsBundle\Entity\Post, SonataNewsBundle:PostAdmin]
+        sonata.admin.post:
+            class: Acme\DemoBundle\Admin\PostAdmin
+            tags:
+                - { name: sonata.admin, manager_type: orm, group: "Content", label: "Post" }
+            arguments:
+                - ~
+                - Acme\DemoBundle\Entity\Post
+                - AcmeDemoBundle:PostAdmin
+            calls:
+                - [ setTranslationDomain, [AcmeDemoBundle]]
+
+When extending a ``CRUDController``, remember that the ``Admin`` class already has
+a set of automatically injected dependencies that are useful when implementing several
+scenarios. Refer to the existing ``CRUDController`` actions for examples on how to get
+the best out of them. 
+
+Fields Definition
+-----------------
+
+Your ``Admin`` class will map your model's fields to a field in every action defined in you 
+``CRUDController``. So, for each action, a list of field mappings is generated. These lists 
+are implemented using the ``FieldDescriptionCollection`` class which stores instances of
+``FieldDescriptionInterface``. Picking up on our previous ``Admin`` class example:
+
+.. code-block:: php
+
+   namespace Acme\DemoBundle\Admin;
+
+   use Sonata\AdminBundle\Admin\Admin;
+   use Sonata\AdminBundle\Datagrid\ListMapper;
+   use Sonata\AdminBundle\Datagrid\DatagridMapper;
+   use Sonata\AdminBundle\Form\FormMapper;
+
+   class PostAdmin extends Admin
+   {
+       //Fields to be shown on create/edit forms
+       protected function configureFormFields(FormMapper $formMapper)
+       {
+           $formMapper
+               ->add('title', 'text', array('label' => 'Post Title'))
+               ->add('author', 'entity', array('class' => 'Acme\DemoBundle\Entity\User'))
+               ->add('body') //if no type is specified, SonataAdminBundle tries to guess it
+           ;
+       }
+
+       //Fields to be shown on filter forms
+       protected function configureDatagridFilters(DatagridMapper $datagridMapper)
+       {
+           $datagridMapper
+               ->add('title')
+               ->add('author')
+           ;
+       }
+
+       //Fields to be shown on lists
+       protected function configureListFields(ListMapper $listMapper)
+       {
+           $listMapper
+               ->addIdentifier('title')
+               ->add('slug')
+               ->add('author')
+           ;
+       }
+   }
+
+Internally, the provided ``Admin`` class will use these three functions to create three 
+``FieldDescriptionCollection`` instances: 
+
+* ``$formFieldDescriptions``, containing three ``FieldDescriptionInterface`` instances
+* ``$filterFieldDescriptions``, containing two ``FieldDescriptionInterface`` instances
+* ``$listFieldDescriptions``, containing three ``FieldDescriptionInterface`` instances
+
+The actual ``FieldDescription`` implementation is provided by the storage
+abstraction bundle that you choose during the installation process, based on the
+``BaseFieldDescription`` abstract class provided by ``SonataAdminBundle``.
+
+Each ``FieldDescription`` contains various details about a field mapping. Some of
+them are independent of the action in which they are used, like ``name`` or ``type``,
+while other are used only in specific actions. More information can be found on the
+``BaseFieldDescription`` class file.
+
+In most scenarios, you won't actually need to handle ``FieldDescription`` yourself.
+However, it is important that you know it exists and how it's used, as it sits at the
+core of ``SonataAdminBundle``.
+
+Templates
+---------
+
+Like most actions, ``CRUDController`` actions use view files to render their output.
+``SonataAdminBundle`` provides ready to use views as well as ways to easily customize them.
+
+The current implementation uses ``Twig`` as the template engine. All templates
+are located in the ``Resources/views`` directory of the bundle.
+
+There are two base templates, one of which is ultimately used in every action:
+
+* ``SonataAdminBundle::standard_layout.html.twig``
+* ``SonataAdminBundle::ajax_layout.html.twig``
+
+Like the names say, one if for standard calls, the other one for AJAX.
+
+The subfolders include Twig files for specific sections of ``SonataAdminBundle``:
+
+* Block: ``SonataBlockBundle`` block views. Right now it only has one, that displays all the mapped classes on the dashboard
+* Button: Buttons such as ``Add new`` or ``Delete`` that you can see across several CRUD actions
+* CRUD: Base views for every CRUD action, plus several field views for each field type
+* Core: Dashboard view, together with deprecated and stub twig files.
+* Form: Views related to form rendering
+* Helper: a view providing a short object description, as part of a specific form field type provided by ``SonataAdminBundle``
+* Pager: Pagination related view files
+
+These will be discussed in greater detail in the specific :doc:`templates` section, where 
+you will also find instructions on how to configure ``SonataAdminBundle`` to use your templates
+instead of the default ones.
+
+Managing ``Admin`` Service
+------------------------------
+
+Your ``Admin`` service definitions are parsed when Symfony2 is loaded, and handled by
+the ``Pool`` class. This class, available as the ``sonata.admin.pool`` service from the
+DIC, handles the ``Admin`` classes, lazy-loading them on demand
+(to reduce overhead) and matching each of them to a group. It's also responsible for handling the
+top level template files, administration panel title and logo.
 
 
-You can extend ``Sonata\AdminBundle\Admin\Admin`` class to minimize the amount of
-code to write. This base admin class uses the routing services to build routes.
-Note that you can use both the Bundle:Controller format or a `service name`_ to
-specify what controller to load. If you provide null instead of SonataNewsBundle:PostAdmin,
-you will not need to create a controller class and the system will use the default.
 
 .. _`Django Project Website`: http://www.djangoproject.com/
-.. _`service name`: http://symfony.com/doc/2.1/cookbook/controller/service.html