Преглед на файлове

[WIP] Add Inheritance admin to support CRUD on inherited classes

Tweak

Refactor sub class access logic
- factorize create button (with some css fixes)
- remove unnecessary exception

Add doc, and fix some css
Michel Salib преди 13 години
родител
ревизия
49b3ee8998

+ 87 - 2
Admin/Admin.php

@@ -55,6 +55,13 @@ abstract class Admin implements AdminInterface, DomainObjectInterface
      */
     private $class;
 
+    /**
+     * The subclasses supported by the admin class
+     *
+     * @var array
+     */
+    private $subClasses = array();
+
     /**
      * The list collection
      *
@@ -859,6 +866,84 @@ abstract class Admin implements AdminInterface, DomainObjectInterface
         return $this->class;
     }
 
+    /**
+     * Returns the list of supported sub classes
+     *
+     * @return array the list of sub classes
+     */
+    public function getSubClasses()
+    {
+        return $this->subClasses;
+    }
+
+    /**
+     * Sets the list of supported sub classes
+     *
+     * @param array $subClasses the list of sub classes
+     */
+    public function setSubClasses(array $subClasses)
+    {
+        $this->subClasses = $subClasses;
+    }
+
+    /**
+     * Gets the subclass corresponding to the given name
+     *
+     * @param  string $name The name of the sub class
+     *
+     * @return string the subclass
+     */
+    protected function getSubClass($name)
+    {
+        if ($this->hasSubClass($name)) {
+            return $this->subClasses[$name];
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns true if the admin has the sub classes
+     *
+     * @param  string $name The name of the sub class
+     *
+     * @return bool
+     */
+    public function hasSubClass($name)
+    {
+        return isset($this->subClasses[$name]);
+    }
+
+    /**
+     * Returns true if a subclass is currently active
+     *
+     * @return bool
+     */
+    public function hasActiveSubClass()
+    {
+        if ($this->request) {
+            return null !== $this->getRequest()->get('subclass');
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the currently active sub class
+     *
+     * @return string the active sub class
+     */
+    public function getActiveSubClass()
+    {
+        if (!$this->hasActiveSubClass()) {
+            return null;
+        }
+
+        $subClass = $this->getRequest()->get('subclass');
+
+        return $this->getSubClass($subClass);
+    }
+
     /**
      * Returns the list of batchs actions
      *
@@ -1066,7 +1151,7 @@ abstract class Admin implements AdminInterface, DomainObjectInterface
      */
     public function getNewInstance()
     {
-        return $this->getModelManager()->getModelInstance($this->getClass());
+        return $this->getModelManager()->getModelInstance($this->getActiveSubClass() ?: $this->getClass());
     }
 
     /**
@@ -1097,7 +1182,7 @@ abstract class Admin implements AdminInterface, DomainObjectInterface
             }
         )));
 
-        $this->formOptions['data_class'] = $this->getClass();
+        $this->formOptions['data_class'] = $this->getActiveSubClass() ?: $this->getClass();
 
         $formBuilder = $this->getFormContractor()->getFormBuilder(
             $this->getUniqid(),

+ 44 - 0
Resources/doc/reference/advance.rst

@@ -117,3 +117,47 @@ the ``datagridValues`` array property. All three keys ``_page``, ``_sort_order``
 
         // ...
     }
+
+Inherited classes
+-----------------
+
+You can manage inherited classes by injected subclasses using the service configuration.
+
+Lets consider a base class named `Person` and its subclasses `Student` and `Teacher`:
+
+.. code-block:: xml
+
+    <services>
+        <service id="sonata.admin.person" class="YourNS\AdminBundle\Admin\PersonAdmin">
+            <tag name="sonata.admin" manager_type="orm" group="admin" label="Person"/>
+            <argument/>
+            <argument>YourNS\AdminBundle\Entity\Person</argument>
+            <argument></argument>
+            <call method="setSubClasses">
+                <argument type="collection">
+                    <argument key="student">YourNS\AdminBundle\Entity\Student</argument>
+                    <argument key="teacher">YourNS\AdminBundle\Entity\Teacher</argument>
+                </argument>
+            </call>
+        </service>
+    </services>
+
+You will just need to change the way forms are configured in order to take into account this new subclasses:
+
+.. code-block:: php
+
+    <?php
+
+    protected function configureFormFields(FormMapper $form)
+    {
+        $subject = $this->getSubject();
+
+        $form->add('name');
+
+        if ($subject instanceof Teacher) {
+            $form->add('course', 'text');
+        }
+        elseif ($subject instanceof Student) {
+            $form->add('year', 'integer');
+        }
+    }

Файловите разлики са ограничени, защото са твърде много
+ 3820 - 3820
Resources/public/bootstrap/css/bootstrap.css


+ 7 - 2
Resources/public/css/layout.css

@@ -246,11 +246,16 @@ body.sonata-bc {
   background-color:#f5f5f5;
 }
 
-.sonata-bc .container-fluid > .sidebar
-{
+.sonata-bc .container-fluid > .sidebar {
     top: auto;
 }
 
+.sonata-action-element.btn-group {
+    display: inline-block;
+    padding: 4px 10px 4px;
+    vertical-align: middle;
+}
+
 .sonata-collection-add, .sonata-collection-delete {
     display: inline-block;
     width: 16px;

+ 22 - 5
Resources/views/Block/block_admin_list.html.twig

@@ -27,10 +27,27 @@ file that was distributed with this source code.
                             <td class="sonata-ba-list-label">{{ admin.label|trans({}, admin.translationdomain) }}</td>
                             <td>
                                 {% if admin.hasroute('create') and admin.isGranted('CREATE') %}
-                                    <a href="{{ admin.generateUrl('create')}}">
-                                        <img src="{{ asset('bundles/sonataadmin/famfamfam/add.png') }}"  alt="{%- trans from 'SonataAdminBundle' %}link_add{% endtrans -%}" />
-                                        {%- trans from 'SonataAdminBundle' %}link_add{% endtrans -%}
-                                    </a>
+                                    {% if admin.subClasses is empty %}
+                                        <a href="{{ admin.generateUrl('create')}}">
+                                            <img src="{{ asset('bundles/sonataadmin/famfamfam/add.png') }}"  alt="{%- trans from 'SonataAdminBundle' %}link_add{% endtrans -%}" />
+                                            {%- trans from 'SonataAdminBundle' %}link_add{% endtrans -%}
+                                        </a>
+                                    {% else %}
+                                        <div class="btn-group">
+                                            <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
+                                                <img src="{{ asset('bundles/sonataadmin/famfamfam/add.png') }}"  alt="{%- trans from 'SonataAdminBundle' %}link_add{% endtrans -%}" />
+                                                {% trans from 'SonataAdminBundle' %}link_add{% endtrans %}
+                                                <span class="caret"></span>
+                                            </a>
+                                            <ul class="dropdown-menu">
+                                                {% for subclass in admin.subclasses|keys %}
+                                                <li>
+                                                    <a href="{{ admin.generateUrl('create', {'subclass': subclass}) }}">{{ subclass }}</a>
+                                                </li>
+                                                {% endfor %}
+                                            </ul>
+                                        </div>
+                                    {% endif %}
                                 {% endif %}
                             </td>
                             <td>
@@ -47,4 +64,4 @@ file that was distributed with this source code.
             </tbody>
         </table>
     {% endfor %}
-{% endblock %}
+{% endblock %}

+ 2 - 4
Resources/views/CRUD/base_edit.html.twig

@@ -28,9 +28,7 @@ file that was distributed with this source code.
             {% if admin.hasroute('history') and admin.id(object) and admin.isGranted('EDIT', object) %}
                 <li class="btn sonata-action-element"><a href="{{ admin.generateObjectUrl('history', object) }}">{% trans from 'SonataAdminBundle' %}link_action_history{% endtrans %}</a></li>
             {% endif %}
-            {% if admin.hasroute('create') and admin.isGranted('CREATE') and admin.id(object)%}
-                <li class="btn sonata-action-element"><a href="{{ admin.generateUrl('create') }}">{% trans from 'SonataAdminBundle' %}link_action_create{% endtrans %}</a></li>
-            {% endif %}
+            {% include 'SonataAdminBundle:Core:create_button.html.twig' %}
             {% if admin.hasroute('list') and admin.isGranted('LIST')%}
                 <li class="btn sonata-action-element"><a href="{{ admin.generateUrl('list') }}">{% trans from 'SonataAdminBundle' %}link_action_list{% endtrans %}</a></li>
             {% endif %}
@@ -49,7 +47,7 @@ file that was distributed with this source code.
             {{ "form_not_available"|trans({}, "SonataAdminBundle") }}
         </div>
     {% else %}
-        <form class="form-horizontal" action="{{ admin.generateUrl(url, {'id': admin.id(object), 'uniqid': admin.uniqid}) }}" {{ form_enctype(form) }} method="POST">
+        <form class="form-horizontal" action="{{ admin.generateUrl(url, {'id': admin.id(object), 'uniqid': admin.uniqid, 'subclass': app.request.get('subclass')}) }}" {{ form_enctype(form) }} method="POST">
 
             {% if form.vars.errors|length > 0 %}
                 <div class="sonata-ba-form-error">

+ 3 - 3
Resources/views/CRUD/base_list.html.twig

@@ -13,9 +13,9 @@ file that was distributed with this source code.
 
 {% block actions %}
     <div class="sonata-actions">
-        {% if admin.hasRoute('create') and admin.isGranted('CREATE')%}
-            <a class="btn" href="{{ admin.generateUrl('create') }}">{% trans from 'SonataAdminBundle' %}link_action_create{% endtrans %}</a>
-        {% endif %}
+        <ul>
+            {% include 'SonataAdminBundle:Core:create_button.html.twig' %}
+        </ul>
     </div>
 {% endblock %}
 

+ 6 - 6
Resources/views/CRUD/base_show.html.twig

@@ -13,18 +13,18 @@ file that was distributed with this source code.
 
 {% block actions %}
     <div class="sonata-actions">
+        <ul>
             {% if admin.hasRoute('edit') and admin.isGranted('EDIT', object)%}
-                <a class="btn sonata-action-element" href="{{ admin.generateObjectUrl('edit', object) }}">{% trans from 'SonataAdminBundle' %}link_action_edit{% endtrans %}</a>
+                <li class="btn sonata-action-element"><a href="{{ admin.generateObjectUrl('edit', object) }}">{% trans from 'SonataAdminBundle' %}link_action_edit{% endtrans %}</a></li>
             {% endif %}
             {% if admin.hasroute('history') and admin.id(object) and admin.isGranted('EDIT', object) %}
-                <a class="btn sonata-action-element" href="{{ admin.generateObjectUrl('history', object) }}">{% trans from 'SonataAdminBundle' %}link_action_history{% endtrans %}</a>
-            {% endif %}
-            {% if admin.hasRoute('create') and admin.isGranted('CREATE')%}
-                <a class="btn sonata-action-element" href="{{ admin.generateUrl('create') }}">{% trans from 'SonataAdminBundle' %}link_action_create{% endtrans %}</a>
+                <li class="btn sonata-action-element"><a href="{{ admin.generateObjectUrl('history', object) }}">{% trans from 'SonataAdminBundle' %}link_action_history{% endtrans %}</a></li>
             {% endif %}
+            {% include 'SonataAdminBundle:Core:create_button.html.twig' %}
             {% if admin.hasroute('list') and admin.isGranted('LIST')%}
-                <a class="btn sonata-action-element" href="{{ admin.generateUrl('list') }}">{% trans from 'SonataAdminBundle' %}link_action_list{% endtrans %}</a>
+                <li class="btn sonata-action-element"><a href="{{ admin.generateUrl('list') }}">{% trans from 'SonataAdminBundle' %}link_action_list{% endtrans %}</a></li>
             {% endif %}
+        </ul>
     </div>
 {% endblock %}
 

+ 30 - 0
Resources/views/Core/create_button.html.twig

@@ -0,0 +1,30 @@
+{#
+
+This file is part of the Sonata package.
+
+(c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+
+For the full copyright and license information, please view the LICENSE
+file that was distributed with this source code.
+
+#}
+
+{% if admin.hasRoute('create') and admin.isGranted('CREATE')%}
+    {% if admin.subClasses is empty %}
+        <li class="btn sonata-action-element"><a href="{{ admin.generateUrl('create') }}">{% trans from 'SonataAdminBundle' %}link_action_create{% endtrans %}</a>
+    {% else %}
+        <span class="btn-group sonata-action-element">
+            <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
+                {% trans from 'SonataAdminBundle' %}link_action_create{% endtrans %}
+                <span class="caret"></span>
+            </a>
+            <ul class="dropdown-menu">
+                {% for subclass in admin.subclasses|keys %}
+                    <li>
+                        <a href="{{ admin.generateUrl('create', {'subclass': subclass}) }}">{{ subclass }}</a>
+                    </li>
+                {% endfor %}
+            </ul>
+        </span>
+    {% endif %}
+{% endif %}