浏览代码

fix bugs, add country type, add add_empty option

Thomas 14 年之前
父节点
当前提交
540fb440a2

+ 24 - 4
Admin/Admin.php

@@ -30,7 +30,7 @@ abstract class Admin extends ContainerAware
 
     protected $maxPerPage = 25;
 
-    protected $baseRouteName = '';
+    protected $baseRouteName;
 
     protected $baseRoutePattern;
     
@@ -38,6 +38,12 @@ abstract class Admin extends ContainerAware
 
     protected $formGroups = false;
 
+    /**
+     *
+     * @var array options to set to the form (ie, validation_groups)
+     */
+    protected $formOptions = array();
+
     // note : don't like this, but havn't find a better way to do it
     protected $configurationPool;
 
@@ -83,6 +89,7 @@ abstract class Admin extends ContainerAware
         'date'       =>  'Symfony\\Component\\Form\\DateField',
         'choice'     =>  'Symfony\\Component\\Form\\ChoiceField',
         'array'      =>  'Symfony\\Component\\Form\\FieldGroup',
+        'country'    =>  'Symfony\\Component\\Form\\CountryField',
     );
 
     protected $choicesCache = array();
@@ -160,6 +167,7 @@ abstract class Admin extends ContainerAware
                     $this->urlize($matches[5])
                 );
             } else {
+
                 throw new \RuntimeException(sprintf('Please define a default `baseRouteName` value for the admin class `%s`', get_class($this)));
             }
         }
@@ -167,7 +175,9 @@ abstract class Admin extends ContainerAware
         return $this->baseRouteName;
     }
 
-    public function urlize($word, $sep = '_') {
+    public function urlize($word, $sep = '_')
+    {
+        
         return strtolower(preg_replace('~(?<=\\w)([A-Z])~', $sep.'$1', $word));
     }
 
@@ -359,6 +369,17 @@ abstract class Admin extends ContainerAware
         return new $class;
     }
 
+    /**
+     *
+     * @return Form the base form
+     */
+    public function getBaseForm($object)
+    {
+        $this->container->get('session')->start();
+
+        return new Form('data', $object, $this->container->get('validator'), $this->formOptions);
+    }
+
 
     /**
      * attach an admin instance to the given FieldDescription
@@ -370,7 +391,7 @@ abstract class Admin extends ContainerAware
 
         $admin = $pool->getAdminByClass($fieldDescription->getTargetEntity());
         if (!$admin) {
-            throw new \RuntimeException(sprintf('You must define an Admin class for the `%s` field', $fieldDescription->getFieldName()));
+            throw new \RuntimeException(sprintf('You must define an Admin class for the `%s` field (targetEntity=%s)', $fieldDescription->getFieldName(), $fieldDescription->getTargetEntity()));
         }
 
         $fieldDescription->setAssociationAdmin($admin);
@@ -475,7 +496,6 @@ abstract class Admin extends ContainerAware
         return $this->filterDatagrid;
     }
 
-
     /**
      *
      */

+ 73 - 15
Admin/EntityAdmin.php

@@ -240,7 +240,7 @@ abstract class EntityAdmin extends Admin
      * @param FieldDescription $description
      * @return array
      */
-    protected function getChoices(FieldDescription $description)
+    protected function getChoices(FieldDescription $description, $prependChoices = array())
     {
 
         if (!isset($this->choicesCache[$description->getTargetEntity()])) {
@@ -265,7 +265,7 @@ abstract class EntityAdmin extends Admin
             $this->choicesCache[$description->getTargetEntity()] = $choices;
         }
 
-        return $this->choicesCache[$description->getTargetEntity()];
+        return $prependChoices + $this->choicesCache[$description->getTargetEntity()];
     }
 
     /**
@@ -313,7 +313,6 @@ abstract class EntityAdmin extends Admin
             ));
         }
         
-
         foreach ($targetForm->getFields() as $name => $formField) {
             if ($name == '_token') {
                 continue;
@@ -363,7 +362,7 @@ abstract class EntityAdmin extends Admin
         $instance = $fieldDescription->getAssociationAdmin()->getNewInstance();
         $mapping  = $fieldDescription->getAssociationMapping();
 
-        $method = sprintf('add%s', $mapping['fieldName']);
+        $method = sprintf('add%s', FieldDescription::camelize($mapping['fieldName']));
 
         $object->$method($instance);
     }
@@ -403,9 +402,17 @@ abstract class EntityAdmin extends Admin
         // set valid default value
         if ($class == 'Symfony\\Component\\Form\\ChoiceField') {
 
+            $choices = array();
+            if($fieldDescription->getOption('add_empty', false)) {
+                $choices = array(
+                    $fieldDescription->getOption('add_empty_value', '') => $fieldDescription->getOption('add_empty_value', '')
+                );
+            }
+
             $options = array_merge(array(
-                'expanded' => false,
-                'choices' => $this->getChoices($fieldDescription),
+                'expanded'      => false,
+                'choices'       => $this->getChoices($fieldDescription, $choices),
+                'empty_value'   => $fieldDescription->getOption('add_empty_value', '')
              ), $options);
         }
 
@@ -460,16 +467,68 @@ abstract class EntityAdmin extends Admin
         // set valid default value
         if ($class == 'Symfony\\Component\\Form\\ChoiceField') {
 
+            $choices = array();
+            if($fieldDescription->getOption('add_empty', false)) {
+                $choices = array(
+                    $fieldDescription->getOption('add_empty_value', '') => $fieldDescription->getOption('add_empty_value', '')
+                );
+            }
+
             $options = array_merge(array(
-                'expanded' => true,
-                'multiple' => true,
-                'choices' => $this->getChoices($fieldDescription),
+                'expanded'      => true,
+                'multiple'      => true,
+                'choices'       => $this->getChoices($fieldDescription, $choices),
+                'empty_value'   => $fieldDescription->getOption('add_empty_value', '')
              ), $options);
         }
 
         return new $class($fieldDescription->getFieldName(), $options);
     }
-    
+
+    protected function getManyToOneField($object, FieldDescription $fieldDescription)
+    {
+                // tweak the widget depend on the edit mode
+        if ($fieldDescription->getOption('edit') == 'inline') {
+
+            return $this->getRelatedAssociatedField($object, $fieldDescription);
+        }
+
+        $options = array(
+            'value_transformer' => new \Symfony\Bundle\DoctrineBundle\Form\ValueTransformer\EntityToIDTransformer(array(
+                'em'        =>  $this->getEntityManager(),
+                'className' => $fieldDescription->getTargetEntity()
+            ))
+        );
+        $options = array_merge($options, $fieldDescription->getOption('form_field_options', array()));
+
+
+        if ($fieldDescription->getOption('edit') == 'list') {
+
+            return new \Symfony\Component\Form\TextField($fieldDescription->getFieldName(), $options);
+        }
+
+        $class = $fieldDescription->getOption('form_field_widget', 'Symfony\\Component\\Form\\ChoiceField');
+
+        // set valid default value
+        if ($class == 'Symfony\\Component\\Form\\ChoiceField') {
+
+            $choices = array();
+            if($fieldDescription->getOption('add_empty', false)) {
+                $choices = array(
+                    $fieldDescription->getOption('add_empty_value', '') => $fieldDescription->getOption('add_empty_value', '')
+                );
+            }
+
+            $options = array_merge(array(
+                'expanded'      => false,
+                'choices'       => $this->getChoices($fieldDescription, $choices),
+                'empty_value'   => $fieldDescription->getOption('add_empty_value', '')
+             ), $options);
+        }
+
+        return new $class($fieldDescription->getFieldName(), $options);
+    }
+
     protected function getFormFieldInstance($object, FieldDescription $fieldDescription)
     {
 
@@ -484,15 +543,16 @@ abstract class EntityAdmin extends Admin
                 return $this->getManyToManyField($object, $fieldDescription);
 
             case ClassMetadataInfo::MANY_TO_ONE:
+                return $this->getManyToOneField($object, $fieldDescription);
+
             case ClassMetadataInfo::ONE_TO_ONE:
 
                 return $this->getOneToOneField($object, $fieldDescription);
 
             default:
+                $class   = $this->getFormFieldClass($fieldDescription);
                 $options = $fieldDescription->getOption('form_field_options', array());
 
-                $class = $this->getFormFieldClass($fieldDescription);
-                
                 return new $class($fieldDescription->getFieldName(), $options);
         }
     }
@@ -508,9 +568,7 @@ abstract class EntityAdmin extends Admin
     public function getForm($object, $fields)
     {
 
-        $this->container->get('session')->start();
-
-        $form = new Form('data', $object, $this->container->get('validator'));
+        $form = $this->getBaseForm($object);
 
         foreach ($fields as $fieldDescription) {
 

+ 9 - 0
Admin/FieldDescription.php

@@ -280,4 +280,13 @@ class FieldDescription
        return preg_replace(array('/(^|_)+(.)/e', '/\.(.)/e'), array("strtoupper('\\2')", "'_'.strtoupper('\\1')"), $property);
     }
 
+    public function mergeOption($name, array $options = array())
+    {
+        if(!isset($this->options[$name])) {
+            $this->options[$name] = array();
+        }
+
+        $this->options[$name] = array_merge($this->options[$name], $options);
+    }
+
 }

+ 9 - 1
CHANGES

@@ -1,4 +1,12 @@
-18/01/2001
+24/01/2011
+----------
+
+* add list mode
+* add 'add_empty' option to association widget (ie: select)
+* add country field type
+* refactor the form creation
+
+18/01/2011
 ----------
 
 * respect symfony conventions

+ 1 - 1
Controller/CRUDController.php

@@ -177,7 +177,7 @@ class CRUDController extends Controller
             }
 
             if ($this->get('request')->isXmlHttpRequest()) {
-                return $this->createResponse('ok');
+                return $this->createResponse(json_encode(array('result' => 'ok', 'object_id' => $object->getId())));
             }
 
             // redirect to edit mode

+ 4 - 0
Controller/CoreController.php

@@ -109,6 +109,10 @@ class CoreController extends Controller
         // retrieve the posted data
         $data = $this->get('request')->get('data');
 
+        if(!isset($data[$field_element->getKey()])) {
+            $data[$field_element->getKey()] = array();
+        }
+
         $object_count   = count($value);
         $post_count     = count($data[$field_element->getKey()]);
 

+ 0 - 1
Form/ValueTransformer/ArrayToObjectTransformer.php

@@ -34,7 +34,6 @@ class ArrayToObjectTransformer extends BaseValueTransformer
         $class      = $this->getOption('className');
         $metadata   = $this->getOption('em')->getClassMetadata($class);
 
-
         // when the object is created the form return an array
         // one the object is persited, the edit $array is the user instance
         if ($array instanceof $class)

+ 13 - 0
Resources/views/CRUD/edit_country.twig.html

@@ -0,0 +1,13 @@
+{#
+
+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.
+
+#}
+
+{% extends base_template %}
+

+ 45 - 17
Resources/views/CRUD/edit_many_association_script.twig.html

@@ -17,7 +17,7 @@ This code manage the many-to-[one|many] association field popup
 #}
 
 <script>
-
+    <!-- edit many association -->
 
     {#
       handle link click in a list :
@@ -25,6 +25,8 @@ This code manage the many-to-[one|many] association field popup
         - if the parent has NO object then an ajax request is made to refresh the popup
     #}
     var field_dialog_form_list_link_{{ admin.code }}_{{ field_element.id }} = function(event) {
+        initialize_popup_{{ admin.code }}_{{ field_element.id }}();
+        
         event.preventDefault();
 
         var element = jQuery(this).parents('td.sonata-ba-list-field');
@@ -52,6 +54,9 @@ This code manage the many-to-[one|many] association field popup
 
     // handle the add link
     var field_dialog_form_list_{{ admin.code }}_{{ field_element.id }} = function(event) {
+
+        initialize_popup_{{ admin.code }}_{{ field_element.id }}();
+
         event.preventDefault();
 
         var a = jQuery(this);
@@ -62,6 +67,7 @@ This code manage the many-to-[one|many] association field popup
         jQuery.ajax({
             url: a.attr('href'),
             success: function(html) {
+
                 // populate the popup container
                 field_dialog_{{ admin.code }}_{{ field_element.id }}.html(html);
 
@@ -104,6 +110,8 @@ This code manage the many-to-[one|many] association field popup
 
     // handle the add link
     var field_dialog_form_add_{{ admin.code }}_{{ field_element.id }} = function(event) {
+        initialize_popup_{{ admin.code }}_{{ field_element.id }}();
+
         event.preventDefault();
 
         var a = jQuery(this);
@@ -114,6 +122,7 @@ This code manage the many-to-[one|many] association field popup
         jQuery.ajax({
             url: a.attr('href'),
             success: function(html) {
+                
                 // populate the popup container
                 field_dialog_{{ admin.code }}_{{ field_element.id }}.html(html);
 
@@ -135,34 +144,50 @@ This code manage the many-to-[one|many] association field popup
     
     // handle the post data
     var field_dialog_form_action_{{ admin.code }}_{{ field_element.id }} = function(event) {
+
+        initialize_popup_{{ admin.code }}_{{ field_element.id }}();
+
         event.preventDefault();
 
         var form = jQuery(this);
-        
+
         // the ajax post
         jQuery.ajax({
             url: form.attr('action'),
             type: "POST",
             data: form.serializeArray(),
-            success: function(html) {
+            dataType: 'json',
+            success: function(json) {
                 // if the crud action return ok, then the element has been added
                 // so the widget container must be refresh with the last option available
-                if (html == 'ok') {
+                if (json.result == 'ok') {
                     field_dialog_{{ admin.code }}_{{ field_element.id }}.dialog('close');
 
-                    // reload the form element
-                    jQuery.ajax({
-                        url: '{{ url('sonata_base_application_retrieve_form_element', {
-                            'code':         admin.root.code,
-                            'element_id':   field_element.id,
-                            'object_id':    admin.root.subject.id,
-                        }) }}',
-                        data: jQuery('#field_widget_{{ admin.code }}_{{ field_element.id}}').closest('form').serializeArray(),
-                        type: 'POST',
-                        success: function(html) {
-                            jQuery('#field_widget_{{ admin.code }}_{{ field_element.id}}').html(html);
-                        }
-                    });
+                    {% if field_description.options.edit == 'list' %}
+                        {#
+                           in this case we update the hidden input, and call the change event to
+                           retrieve the post information
+                        #}
+                        jQuery('#{{ field_element.id}}').val(json.object_id);
+                        jQuery('#{{ field_element.id}}').change();
+
+                    {% else %}
+
+                        // reload the form element
+                        jQuery.ajax({
+                            url: '{{ url('sonata_base_application_retrieve_form_element', {
+                                'code':         admin.root.code,
+                                'element_id':   field_element.id,
+                                'object_id':    admin.root.subject.id,
+                            }) }}',
+                            data: jQuery('#field_widget_{{ admin.code }}_{{ field_element.id}}').closest('form').serializeArray(),
+                            type: 'POST',
+                            success: function(html) {
+                                jQuery('#field_widget_{{ admin.code }}_{{ field_element.id}}').html(html);
+                            }
+                        });
+
+                    {% endif %}
 
                     return;
                 }
@@ -204,6 +229,8 @@ This code manage the many-to-[one|many] association field popup
         var a = jQuery(event.target).closest('a');
         a.attr('onclick', '');
 
+        console.log('click');
+
         initialize_popup_{{ admin.code }}_{{ field_element.id }}();
 
        // add the jQuery event to the a element
@@ -262,4 +289,5 @@ This code manage the many-to-[one|many] association field popup
 
     {% endif %}
 
+    <!-- / edit many association -->
 </script>

+ 40 - 13
Resources/views/CRUD/edit_many_to_one.twig.html

@@ -12,19 +12,46 @@ file that was distributed with this source code.
 {% extends base_template %}
 
 {% block field %}
-    <div id="field_container_{{ admin.code }}_{{ field_element.id}}">
-        <span id="field_widget_{{ admin.code }}_{{ field_element.id}}" >
-            {{ form_field(field_element) }}
-        </span>
-        
-        <span id="field_actions_{{ admin.code }}_{{ field_element.id}}" >
-            <a href="{{ field_description.associationadmin.generateUrl('create') }}" class="action" onclick="start_field_dialog_form_add_{{ admin.code }}_{{ field_element.id }}(event)"><img src="{{ asset('bundles/sonatabaseapplication/famfamfam/add.png') }}" alt="{% trans 'btn_add' from 'BaseApplicationBundle' %}" /></a>
-        </span>
-        
-        <div style="display: hidden" id="field_dialog_{{ admin.code }}_{{ field_element.id }}">
-
+    {% if field_description.options.edit == 'inline' %}
+        {% for nested_field_description in field_description.associationadmin.formfields %}
+            {{ nested_field_description|render_form_element(field_element, value) }}
+        {% endfor %}
+
+    {% else %}
+
+        <div id="field_container_{{ admin.code }}_{{ field_element.id}}">
+
+            {% if field_description.options.edit == 'list' %}
+                <span id="field_widget_{{ admin.code }}_{{ field_element.id}}" >
+                    {% if value.id %}
+                        {% render 'SonataBaseApplicationBundle:Core:getShortObjectDescription' with {
+                            'code': field_description.associationadmin.code,
+                            'object_id': value.id
+                        }%}
+                    {% endif %}
+                </span>
+                <span style="display: none" >
+                    {{ form_field(field_element) }}
+                </span>
+            {% else %}
+                <span id="field_widget_{{ admin.code }}_{{ field_element.id}}" >
+                    {{ form_field(field_element) }}
+                </span>
+            {% endif %}
+            <span id="field_actions_{{ admin.code }}_{{ field_element.id}}" >
+
+                {% if field_description.options.edit == 'list' %}
+                    <a href="{{ field_description.associationadmin.generateUrl('list') }}" onclick="start_field_dialog_form_list_{{ admin.code }}_{{ field_element.id }}(event)" class="action"><img src="{{ asset('bundles/sonatabaseapplication/famfamfam/application_view_list.png') }}" alt="{% trans 'btn_add' from 'BaseApplicationBundle' %}" /></a>
+                {% endif %}
+
+                <a href="{{ field_description.associationadmin.generateUrl('create') }}" onclick="start_field_dialog_form_add_{{ admin.code }}_{{ field_element.id }}(event)" class="action"><img src="{{ asset('bundles/sonatabaseapplication/famfamfam/add.png') }}" alt="{% trans 'btn_add' from 'BaseApplicationBundle' %}" /></a>
+            </span>
+
+            <div class="container sonata-ba-modal sonata-ba-modal-edit-one-to-one" style="display: none" id="field_dialog_{{ admin.code }}_{{ field_element.id }}">
+
+            </div>
         </div>
-    </div>
 
-    {% include 'SonataBaseApplicationBundle:CRUD:edit_many_association_script.twig.html' %}
+        {% include 'SonataBaseApplicationBundle:CRUD:edit_many_association_script.twig.html' %}
+    {% endif %}
 {% endblock %}

+ 5 - 0
Resources/views/CRUD/edit_one_association_script.twig.html

@@ -18,6 +18,8 @@ This code manage the one-to-many association field popup
 
 <script>
 
+    <!-- edit one association -->
+
     // handle the add link
     var field_add_{{ admin.code }}_{{ field_element.id }} = function(event) {
 
@@ -44,6 +46,7 @@ This code manage the one-to-many association field popup
 
     var field_widget_{{ admin.code }}_{{ field_element.id }} = false;
 
+    
     // this function initialize the popup
     // this can be only done this way has popup can be cascaded
     function start_field_retrieve_{{ admin.code }}_{{ field_element.id }}(event) {
@@ -64,5 +67,7 @@ This code manage the one-to-many association field popup
 
         return false;
     }
+
+    <!-- edit one association -->
     
 </script>