浏览代码

Merge pull request #1570 from caponica/docs-20130814

Docs 20130814
Thomas 12 年之前
父节点
当前提交
3d3536f364

+ 3 - 3
Form/ChoiceList/ModelChoiceList.php

@@ -104,9 +104,9 @@ class ModelChoiceList extends SimpleChoiceList
      *
      * If the entities were passed in the "choices" option, this method
      * does not have any significant overhead. Otherwise, if a query builder
-     * was passed in the "query_builder" option, this builder is now used
-     * to construct a query which is executed. In the last case, all entities
-     * for the underlying class are fetched from the repository.
+     * was passed in the "query" option, this builder is now used to construct 
+     * a query which is executed. In the last case, all entities for the 
+     * underlying class are fetched from the repository.
      *
      * If the option "property" was passed, the property path in that option
      * is used as option values. Otherwise this method tries to convert

+ 1 - 0
Resources/doc/index.rst

@@ -45,6 +45,7 @@ Reference Guide
    reference/troubleshooting
    reference/recipe_file_uploads
    reference/recipe_image_previews
+   reference/recipe_row_templates
 
 Overview
 --------

+ 167 - 14
Resources/doc/reference/form_types.rst

@@ -4,25 +4,150 @@ Form Types
 Admin related form types
 ------------------------
 
-The bundle come with different form types to handle model values:
+When defining fields in your Admin classes you can use any of the standard
+`Symfony field types`_ and configure them as you would normally. In addition
+there are some special Sonata field types which allow you to work with 
+relationships between one entity class and another.
 
 sonata_type_model
 ^^^^^^^^^^^^^^^^^
 
-The ``Model Type`` allows you to choose an existing model or create new ones. 
-This type doesn't allow to directly edit the selected model.
+Setting a field type of ``sonata_type_model`` will use an instance of 
+``ModelType`` to render that field. This Type allows you to choose an existing 
+entity from the linked model class. In effect it shows a list of options from 
+which you can choose a value (or values).
+
+For example, we have an entity class called ``Page`` which has a field called 
+``image1`` which maps a relationship to another entity class called ``Image``.
+All we need to do now is add a reference for this field in our ``PageAdmin`` class:
+
+.. code-block:: php
+
+    class PageAdmin extends Admin
+    {
+        protected function configureFormFields(FormMapper $formMapper)
+        {
+            $imageFieldOptions = array(); // see available options below
+            $formMapper
+                ->add('image1', 'sonata_type_model', $imageFieldOptions)
+            ;
+        }
+    }
+
+Since the ``image1`` field refers to a related entity we do not need to specify 
+any options. Sonata will calculate that the linked class is of type ``Image`` and, 
+by default, retrieve a list of all existing Images to display as choices in the 
+selector.
+
+Note that the third parameter to ``FormMapper::add()`` is optional so
+there is no need to pass in an empty array, it is shown here just to demonstrate
+where the options go when you want to use them.
+
+The available options are:
+
+property
+  defaults to null. You can set this to a `Symfony PropertyPath`_ compatible
+  string to designate which field to use for the choice values.
+
+query
+  defaults to null. You can set this to a QueryBuilder instance in order to
+  define a custom query for retrieving the available options.
+
+template
+  defaults to 'choice' (not currently used?)
+
+multiple
+  defaults to false - see the `Symfony choice Field Type docs`_ for more info
+  
+expanded
+  defaults to false - see the `Symfony choice Field Type docs`_ for more info
+
+choices
+  defaults to null - see the `Symfony choice Field Type docs`_ for more info
+
+preferred_choices
+  defaults to array() - see the `Symfony choice Field Type docs`_ for more info
+
+choice_list
+  defaults to a ``ModelChoiceList`` built from the other options
+
+model_manager
+  defaults to null, but is actually calculated from the linked Admin class.
+  You usually should not need to set this manually.
+
+class
+  The entity class managed by this field. Defaults to null, but is actually 
+  calculated from the linked Admin class. You usually should not need to set 
+  this manually.
+
 
 sonata_type_admin
 ^^^^^^^^^^^^^^^^^
 
-The ``Admin Type`` will delegate the form construction for this model to its 
-related admin class. This type is useful to cascade edition or creation of 
-linked models.
+Setting a field type of ``sonata_type_admin`` will embed another Admin class
+and use the embedded Admin's configuration when editing this field. 
+``sonata_type_admin`` fields should only be used when editing a field which 
+represents a relationship between two model classes.
+
+This Type allows you to embed a complete form for the related element, which
+you can configure to allow the creation, editing and (optionally) deletion of 
+related objects.
+
+For example, lets use a similar example to the one for ``sonata_type_model`` above.
+This time, when editing a ``Page`` using ``PageAdmin`` we want to enable the inline
+creation (and editing) of new Images instead of just selecting an existing Image 
+from a list.
+
+First we need to create an ``ImageAdmin`` class and register it as an Admin class 
+for managing ``Image`` objects. In our admin.yml we have an entry for ``ImageAdmin`` 
+that looks like this:
+
+.. code-block:: yaml
+
+    # Acme/DemoBundle/Resources/config/admin.yml
+
+    sonata.admin.image:
+        class: Acme\DemoBundle\Admin\ImageAdmin
+        tags:
+            - { name: sonata.admin, manager_type: orm, label: "Image" }
+        arguments:
+            - ~
+            - Acme\DemoBundle\Entity\Image
+            - 'SonataAdminBundle:CRUD'
+        calls:
+            - [ setTranslationDomain, [Acme\DemoBundle]]
+
+
+To embed ``ImageAdmin`` within ``PageAdmin`` we just need to change the reference 
+for the ``image1`` field to ``sonata_type_admin`` in our ``PageAdmin`` class:
+
+.. code-block:: php
+
+    class PageAdmin extends Admin
+    {
+        protected function configureFormFields(FormMapper $formMapper)
+        {
+            $formMapper
+                ->add('image1', 'sonata_type_admin')
+            ;
+        }
+    }
+
+We do not need to define any options since Sonata calculates that the linked class 
+is of type ``Image`` and the service definition (in admin.yml) defines that ``Image`` 
+objects are managed by the ``ImageAdmin`` class.
+
+The available options (which can be passed as a third parameter to ``FormMapper::add()``) are:
+
+delete
+  defaults to true and indicates that a 'delete' checkbox should be shown allowing 
+  the user to delete the linked object.
+
 
 sonata_type_collection
 ^^^^^^^^^^^^^^^^^^^^^^
 
-The ``Collection Type`` is meant to handle creation and edition of model 
+The ``Collection Type`` is meant to handle creation and editing of model 
 collections. Rows can be added and deleted, and your model abstraction layer may
 allow you to edit fields inline. You can use ``type_options`` to pass values
 to the underlying forms.
@@ -50,14 +175,38 @@ to the underlying forms.
 or deleted (``sonata-collection-item-deleted``). You can bind to these events to trigger custom 
 javascript imported into your templates (eg: add a calendar widget to a newly added date field)
 
-Field configuration
-^^^^^^^^^^^^^^^^^^^
+FieldDescription options
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+The fourth parameter to FormMapper::add() allows you to pass in ``FieldDescription`` 
+options as an array. The most useful of these is ``admin_code``, which allows you to 
+specify which Admin to use for managing this relationship. It is most useful for inline 
+editing in conjunction with the ``sonata_type_admin`` form type.
+
+The value used should be the admin *service* name, not the class name. If you do
+not specify an ``admin_code`` in this way, the default admin class for the field's 
+model type will  be used.
 
-- ``admin_code``: Force for any field involving a model the admin class used to 
-  handle it (useful for inline editing with ``sonata_type_admin``). The 
-  expected value here is the admin service name, not the class name. If not 
-  defined, the default admin class for the model type will be used (even if 
-  you didn't define any admin for the model type).
+For example, to specify the use of the Admin class which is registered as 
+``sonata.admin.imageSpecial`` for managing the ``image1`` field from our ``PageAdmin``
+example above: 
+
+.. code-block:: php
+
+    class PageAdmin extends Admin
+    {
+        protected function configureFormFields(FormMapper $formMapper)
+        {
+            $formMapper
+                ->add(
+                  'image1', 
+                  'sonata_type_admin', 
+                  array(), 
+                  array('admin_code' => 'sonata.admin.imageSpecial')
+                )
+            ;
+        }
+    }
 
 Other specific field configuration options are detailed in the related 
 abstraction layer documentation.
@@ -176,3 +325,7 @@ General
 
         <?php
         $form->add('status', null, array('label' => false);
+
+.. _`Symfony field types`: http://symfony.com/doc/current/book/forms.html#built-in-field-types
+.. _`Symfony choice Field Type docs`: http://symfony.com/doc/current/reference/forms/types/choice.html
+.. _`Symfony PropertyPath`: http://api.symfony.com/2.0/Symfony/Component/Form/Util/PropertyPath.html

+ 7 - 0
Resources/doc/reference/getting_started.rst

@@ -35,6 +35,13 @@ to your application's routing file:
     the above routing configuration must be placed in routing.xml or
     routing.php according to your format (i.e. XML or PHP).
 
+.. note::
+
+    For those curious about the ``resource: .`` setting: it is unusual syntax but used 
+    because Symfony requires a resource to be defined (which points to a real file). 
+    Once this validation passes Sonata's ``AdminPoolLoader`` is in charge of processing 
+    this route and it simply ignores the resource setting.
+
 At this point you can already access the (empty) admin dashboard by visiting the url:
 ``http://yoursite.local/admin/dashboard``.
 

+ 1 - 0
Resources/doc/reference/recipe_file_uploads.rst

@@ -240,6 +240,7 @@ class to determine which fields to show.
 In PageAdmin we then have the following code to manage the relationships' lifecycles:
 
 .. code-block:: php
+
     class PageAdmin extends Admin
     {
         // ...

+ 83 - 0
Resources/doc/reference/recipe_row_templates.rst

@@ -0,0 +1,83 @@
+Row templates
+=============
+
+From Sonata-2.2 it is possible to define a template per row for the list action. 
+The default template is a standard table but there are circumstances where this
+type of layout might not be suitable. By defining a custom template for the row, 
+you can tweak the layout into something like this:
+
+.. figure:: ./../images/sonata_inline_row.png
+   :align: center
+   :alt: Inline Row from the SonataNewsBundle
+   :width: 700px
+
+
+The recipe
+----------
+
+Configure your Admin service
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The configuration takes place in the DIC by calling the ``setTemplates`` method. 
+Two template keys need to be set:
+
+- ``inner_list_row``: The template for the row, which you will customize. Often
+  you will want this to extend ``SonataAdminBundle:CRUD:base_list_flat_inner_row.html.twig``
+- ``base_list_field``: The base template for the cell, the default of
+  ``SonataAdminBundle:CRUD:base_list_flat_field.html.twig`` is suitable for most 
+  cases but it can be customized if required.
+
+.. code-block:: xml
+
+    <service id="sonata.admin.comment" class="%sonata.admin.comment.class%">
+        <tag name="sonata.admin" manager_type="orm" group="sonata_blog" label="comments" 
+            label_catalogue="%sonata.admin.comment.translation_domain%" 
+            label_translator_strategy="sonata.admin.label.strategy.underscore" />
+        <argument />
+        <argument>%sonata.admin.comment.entity%</argument>
+        <argument>%sonata.admin.comment.controller%</argument>
+
+        <call method="setTemplates">
+            <argument type="collection">
+                <argument key="inner_list_row">
+                    YourNSYourBundle:Admin:inner_row_comment.html.twig
+                </argument>
+                <argument key="base_list_field">
+                    SonataAdminBundle:CRUD:base_list_flat_field.html.twig
+                </argument>
+            </argument>
+        </call>
+    </service>
+
+
+Create your customized template
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Once the templates are defined, create the template to render the row:
+
+.. code-block:: jinja
+
+    {# This file is YourNSYourBundle:Admin:inner_row_comment.html.twig #}
+
+    {# Extend the default template, which provides batch and action cells #}
+    {#     as well as the valid colspan computation #}
+    {% extends 'SonataAdminBundle:CRUD:base_list_flat_inner_row.html.twig' %}
+
+    {% block row %}
+
+        {# you can use fields define in the the Admin class #}
+        {{ object|render_list_element(admin.list['name']) }} -
+        {{ object|render_list_element(admin.list['url']) }} -
+        {{ object|render_list_element(admin.list['email']) }} <br />
+
+        <small>
+            {# or you can use the object variable to render a property #}
+            {{ object.message }}
+        </small>
+
+    {% endblock %}
+
+While this feature is nice to generate a rich list, it is also very easy to 
+break the layout and admin features such as batch and object actions. It is
+best to familiarise yourself with the default templates and extend them where
+possible, only changing what you need to customize.

+ 54 - 5
Resources/doc/reference/routing.rst

@@ -62,7 +62,8 @@ You will then have route URLs like ``http://yourdomain.com/admin/foo/list`` and
 Routing usage
 -------------
 
-Inside a CRUD template, a route can be generated by using the ``Admin`` class.
+Inside a CRUD template, a route for the current ``Admin`` class can be generated via
+the admin variable's ``generateUrl()`` command:
 
 .. code-block:: html+jinja
 
@@ -71,15 +72,21 @@ Inside a CRUD template, a route can be generated by using the ``Admin`` class.
     <a href="{{ admin.generateUrl('list', params|merge('page': 1) }}">List</a>
 
 Note that you do not need to provide the Admin's route prefix (baseRouteName) to 
-generate the URL, just the action name.
+generate a URL for the current Admin, just the action name.
+
+To generate a URL for a different Admin you just use the Route Name with the usual 
+Twig helpers:
+
+.. code-block:: html+jinja
+
+    <a href="{{ path('admin_acme_demo_foo_list') }}">List</a>
 
 
 Create a route
 --------------
 
 You can register new routes by defining them in your ``Admin`` class. Only Admin 
-routes should be registered this way. Of course this requires the related action 
-to be defined in the controller.
+routes should be registered this way.
 
 The routes you define in this way are generated within your Admin's context, and
 the only required parameter to ``add()`` is the action name. The second parameter
@@ -95,11 +102,53 @@ explicitly this defaults to the action name.
     {
         protected function configureRoutes(RouteCollection $collection)
         {
-            $collection->add('duplicate');
+            $collection->add('myCustomAction');
             $collection->add('view', $this->getRouterIdParameter().'/view');
         }
     }
 
+Other steps needed to create your new action
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In addition to defining the route for your new action you also need to create a
+handler for it in your Controller. By default Admin classes use ``SonataAdminBundle:CRUD``
+as their controller, but this can be changed by altering the third argument when defining
+your Admin service (in your admin.yml file).
+
+For example, lets change the Controller for our MediaAdmin class to AcmeDemoBundle:MediaCRUD:
+
+.. code-block:: jinja
+
+    # src/Acme/DemoBundle/Resources/config/admin.yml
+    sonata.admin.media:
+        class: Acme\DemoBundle\Admin\MediaAdmin
+        tags:
+            - { name: sonata.admin, manager_type: orm, label: "Media" }
+        arguments:
+            - ~
+            - Acme\DemoBundle\Entity\Page
+            - 'AcmeDemoBundle:MediaCRUD' # define the new controller via the third argument
+        calls:
+            - [ setTranslationDomain, [Acme\DemoBundle]]
+
+We now need to create our Controller, the easiest way is to extend the basic Sonata CRUD controller:
+
+.. code-block:: php
+
+    <?php
+    // /src/Acme/DemoBundle/Controller/MediaCRUDController.php
+    namespace Acme\DemoBundle\Controller;
+
+    use Sonata\AdminBundle\Controller\CRUDController;
+
+    class MediaCRUDController extends CRUDController
+    {
+        public function myCustomAction()
+        {
+            // your code here ...
+        }
+    }
+
 Removing a route
 --------------
 

+ 23 - 6
Resources/doc/reference/saving_hooks.rst

@@ -1,12 +1,29 @@
 Saving hooks
 ============
 
-When the model is persisted upon on the stated object two Admin methods are
-always called. You can extend this method to add custom business logic.
+When a SonataAdmin is submitted for processing, two events are always called. One 
+is before any persistence layer interaction and the other is afterwards, the 
+events are named as follows:
 
-    - new object : ``prePersist($object)`` / ``postPersist($object)``
-    - edited object : ``preUpdate($object)`` / ``postUpdate($object)``
-    - deleted object : ``preRemove($object)`` / ``postRemove($object)``
+- new object : ``prePersist($object)`` / ``postPersist($object)``
+- edited object : ``preUpdate($object)`` / ``postUpdate($object)``
+- deleted object : ``preRemove($object)`` / ``postRemove($object)``
+
+It is worth noting that the update events are called whenever the Admin is successfully
+submitted, regardless of whether there are any actual persistence layer events. This 
+differs from the use of preUpdate and postUpdate events in DoctrineORM and perhaps some
+other persistence layers.
+
+For example: if you submit an edit form without changing any of the values on the form
+then there is nothing to change in the database and DoctrineORM would not fire the **Entity**
+class's own ``preUpdate`` and ``postUpdate`` events. However, your **Admin** class's 
+``preUpdate``  and  ``postUpdate`` methods *are* called and this can be used to your 
+advantage.
+
+.. note::
+
+    When embedding one Admin within another, for example using the ``sonata_type_admin``
+    field type, the child Admin's hooks are **not** fired.
 
 
 Example used with the FOS/UserBundle
@@ -18,7 +35,7 @@ https://github.com/FriendsOfSymfony/FOSUserBundle/ for more information.
 
 The user management system requires to perform specific call when the user
 password or username are updated. This is how the Admin bundle can be used to
-solve the issue by using the ``prePersist`` saving hook.
+solve the issue by using the ``preUpdate`` saving hook.
 
 .. code-block:: php
 

+ 3 - 57
Resources/doc/reference/templates.rst

@@ -98,60 +98,6 @@ You can easily extend the provided templates in your own template and customize
 Row Template
 ------------
 
-From 2.2, it is possible to define a template per row for the list action, the default one is a standard table. However,
-depends on the data the table layout might not be suitable. So by defining a custom template for the row, it will be
-possible to tweak the layout as:
-
-.. figure:: ./../images/sonata_inline_row.png
-   :align: center
-   :alt: Inline Row from the SonataNewsBundle
-   :width: 700px
-
-
-How to use it
-~~~~~~~~~~~~~
-
-The configuration takes place in the DIC by calling the ``setTemplates`` method. Two template keys need to be set:
-
-- ``inner_list_row`` : The template for the row, this one need to be customized
-- ``base_list_field`` : The base template for the cell, the ``SonataAdminBundle:CRUD:base_list_flat_field.html.twig``
-is suitable for most cases, however advance use might want to change it.
-
-.. code-block:: xml
-
-    <service id="sonata.news.admin.comment" class="%sonata.news.admin.comment.class%">
-        <tag name="sonata.admin" manager_type="orm" group="sonata_blog" label="comments" label_catalogue="%sonata.news.admin.comment.translation_domain%" label_translator_strategy="sonata.admin.label.strategy.underscore" />
-        <argument />
-        <argument>%sonata.news.admin.comment.entity%</argument>
-        <argument>%sonata.news.admin.comment.controller%</argument>
-
-        <call method="setTemplates">
-            <argument type="collection">
-                <argument key="inner_list_row">SonataNewsBundle:Admin:inner_row_comment.html.twig</argument>
-                <argument key="base_list_field">SonataAdminBundle:CRUD:base_list_flat_field.html.twig</argument>
-            </argument>
-        </call>
-    </service>
-
-Once the template is set, edit the template ``SonataNewsBundle:Admin:inner_row_comment.html.twig``
-
-.. code-block:: jinja
-
-    {# The default template which provides batch and action cells, with the valid colspan computation #}
-    {% extends 'SonataAdminBundle:CRUD:base_list_flat_inner_row.html.twig' %}
-
-    {% block row %}
-
-        {# use field define in the the Admin class #}
-        {{ object|render_list_element(admin.list['name']) }} -
-        {{ object|render_list_element(admin.list['url']) }} -
-        {{ object|render_list_element(admin.list['email']) }} <br />
-
-        <small>
-            {# or you can use the object variable to render a property #}
-            {{ object.message }}
-        </small>
-
-    {% endblock %}
-
-While this feature is nice to generate rich list, it is also very easy to break the layout and admin features: batch and actions.
+It is possible to completely change how each row of results is rendered in the 
+list view. For more information about this, see the :doc:`recipe_row_templates` 
+cookbook entry.