Pārlūkot izejas kodu

add one to one inline association

Thomas 14 gadi atpakaļ
vecāks
revīzija
97b5f95820

+ 52 - 9
Admin/Admin.php

@@ -294,6 +294,8 @@ abstract class Admin extends ContainerAware
 
                 if($this->form_fields[$name]['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::ONE_TO_ONE)
                 {
+                    $this->form_fields[$name]['edit'] = isset($this->form_fields[$name]['edit']) ? $this->form_fields[$name]['edit'] : 'popup';
+                    
                     $this->form_fields[$name]['template'] = 'Sonata\BaseApplicationBundle:CRUD:edit_one_to_one.twig';
                     $this->form_fields[$name]['configuration']  = $this->getConfigurationPool()
                         ->getConfigurationByClass($this->form_fields[$name]['targetEntity']);
@@ -302,6 +304,8 @@ abstract class Admin extends ContainerAware
 
                 if($this->form_fields[$name]['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_ONE)
                 {
+                    $this->form_fields[$name]['edit'] = isset($this->form_fields[$name]['edit']) ? $this->form_fields[$name]['edit'] : 'popup';
+
                     $this->form_fields[$name]['template'] = 'Sonata\BaseApplicationBundle:CRUD:edit_many_to_one.twig';
                     $this->form_fields[$name]['configuration']  = $this->getConfigurationPool()
                         ->getConfigurationByClass($this->form_fields[$name]['targetEntity']);
@@ -309,6 +313,8 @@ abstract class Admin extends ContainerAware
 
                 if($this->form_fields[$name]['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY)
                 {
+                    $this->form_fields[$name]['edit'] = isset($this->form_fields[$name]['edit']) ? $this->form_fields[$name]['edit'] : 'popup';
+
                     $this->form_fields[$name]['template'] = 'Sonata\BaseApplicationBundle:CRUD:edit_many_to_many.twig';
                     $this->form_fields[$name]['configuration']  = $this->getConfigurationPool()
                         ->getConfigurationByClass($this->form_fields[$name]['targetEntity']);
@@ -316,6 +322,8 @@ abstract class Admin extends ContainerAware
 
                 if($this->form_fields[$name]['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::ONE_TO_MANY)
                 {
+                    $this->form_fields[$name]['edit'] = isset($this->form_fields[$name]['edit']) ? $this->form_fields[$name]['edit'] : 'popup';
+                    
                     $this->form_fields[$name]['template'] = 'Sonata\BaseApplicationBundle:CRUD:edit_one_to_many.twig';
                     $this->form_fields[$name]['configuration']  = $this->getConfigurationPool()
                         ->getConfigurationByClass($this->form_fields[$name]['targetEntity']);
@@ -528,16 +536,51 @@ abstract class Admin extends ContainerAware
                 case \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_ONE:
                 case \Doctrine\ORM\Mapping\ClassMetadataInfo::ONE_TO_ONE:
 
-                    $transformer = new \Symfony\Bundle\DoctrineBundle\Form\ValueTransformer\EntityToIDTransformer(array(
-                        'em'        =>  $this->getEntityManager(),
-                        'className' => $description['targetEntity']
-                    ));
+                    if($description['edit'] == 'inline') {
 
-                    $field = new \Symfony\Component\Form\ChoiceField($name, array_merge(array(
-                        'expanded' => false,
-                        'choices' => $this->getChoices($description),
-                        'value_transformer' => $transformer,
-                    ), $description['options']));
+                        // retrieve the related object
+                        $target_object = $description['reflection']->getValue($object);
+
+                        if(!$target_object) {
+                            $target_object = $description['configuration']->getNewInstance();
+                        }
+
+                        // retrieve the related form
+                        $target_fields = $description['configuration']->getFormFields();
+                        $target_form    = $description['configuration']->getForm($target_object, $target_fields);
+
+                        // create the transformer
+                        $transformer = new \Bundle\Sonata\BaseApplicationBundle\Form\ValueTransformer\ArrayToObjectTransformer(array(
+                            'em'        =>  $this->getEntityManager(),
+                            'className' => $description['targetEntity']
+                        ));
+
+                        // create the "embedded" field
+                        $field = new \Symfony\Component\Form\FieldGroup($name, array(
+                            'value_transformer' => $transformer,
+                        ));
+
+                        foreach($target_form->getFields() as $name => $form_field) {
+                            if($name == '_token') {
+                                continue;
+                            }
+
+                            $field->add($form_field);
+                        }
+                    }
+                    else
+                    {
+                        $transformer = new \Symfony\Bundle\DoctrineBundle\Form\ValueTransformer\EntityToIDTransformer(array(
+                            'em'        =>  $this->getEntityManager(),
+                            'className' => $description['targetEntity']
+                        ));
+
+                        $field = new \Symfony\Component\Form\ChoiceField($name, array_merge(array(
+                            'expanded' => false,
+                            'choices' => $this->getChoices($description),
+                            'value_transformer' => $transformer,
+                        ), $description['options']));
+                    }
 
                     break;
 

+ 1 - 2
Controller/CRUDController.php

@@ -167,7 +167,7 @@ class CRUDController extends Controller
             } else {
                 $this->configuration->preUpdate($object);
             }
-
+            
             $this->configuration->getEntityManager()->persist($object);
             $this->configuration->getEntityManager()->flush($object);
 
@@ -224,7 +224,6 @@ class CRUDController extends Controller
             $object = $id->getData();
             $form   = $id;
         } else {
-            $class = $this->configuration->getClass();
             $object = $this->configuration->getNewInstance();
 
             $form   = $this->configuration->getForm($object, $fields);

+ 112 - 0
Form/ValueTransformer/ArrayToObjectTransformer.php

@@ -0,0 +1,112 @@
+<?php
+
+/*
+ * This file is part of the Sonata project.
+ *
+ * (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.
+ */
+
+namespace Bundle\Sonata\BaseApplicationBundle\Form\ValueTransformer;
+
+use Symfony\Component\Form\ValueTransformer\BaseValueTransformer;
+use Symfony\Component\Form\Exception\InvalidPropertyException;
+use Symfony\Component\Form\Exception\PropertyAccessDeniedException;
+
+class ArrayToObjectTransformer extends BaseValueTransformer
+{
+    protected function configure()
+    {
+        $this->addRequiredOption('em');
+        $this->addRequiredOption('className');
+
+        parent::configure();
+    }
+
+    /**
+     * @param array $ids
+     * @param Collection $collection
+     */
+    public function reverseTransform($array, $originalValue)
+    {
+        $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)
+        {
+            return $array;
+        }
+
+        $instance = new $class;
+
+        if(!is_array($array)) {
+            
+            return $instance;
+        }
+
+        $reflClass = $metadata->reflClass;
+        foreach($array as $name => $value) {
+
+            $reflection_property = false;
+            // property or association ?
+            if(array_key_exists($name, $metadata->fieldMappings)) {
+
+                $property = $metadata->fieldMappings[$name]['fieldName'];
+                $reflection_property = $metadata->reflFields[$name];
+
+            } else if (array_key_exists($name, $metadata->associationMappings)) {
+                $property = $metadata->associationMappings[$name]['fieldName'];
+            } else {
+                $property = $name;
+            }
+
+            $setter = 'set'.$this->camelize($name);
+
+            if ($reflClass->hasMethod($setter)) {
+                if (!$reflClass->getMethod($setter)->isPublic()) {
+                    throw new PropertyAccessDeniedException(sprintf('Method "%s()" is not public in class "%s"', $setter, $reflClass->getName()));
+                }
+
+                $instance->$setter($value);
+            } else if ($reflClass->hasMethod('__set')) {
+                // needed to support magic method __set
+                $instance->$property = $value;
+            } else if ($reflClass->hasProperty($property)) {
+                if (!$reflClass->getProperty($property)->isPublic()) {
+                    throw new PropertyAccessDeniedException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "set%s()"?', $property, $reflClass->getName(), ucfirst($property)));
+                }
+
+                $instance->$property = $value;
+            } else if($reflection_property) {
+                $reflection_property->setValue($instance, $value);
+            }
+        }
+
+        return $instance;
+    }
+
+    /**
+     * @param Collection $value
+     */
+    public function transform($value)
+    {
+
+        return $value;
+    }
+
+    /**
+     * method taken from PropertyPath
+     *
+     * @param  $property
+     * @return mixed
+     */
+    protected function camelize($property)
+    {
+       return preg_replace(array('/(^|_)+(.)/e', '/\.(.)/e'), array("strtoupper('\\2')", "'_'.strtoupper('\\1')"), $property);
+    }
+}

+ 19 - 13
Resources/views/CRUD/edit_one_to_one.twig

@@ -12,19 +12,25 @@ file that was distributed with this source code.
 {% extends 'Sonata/BaseApplicationBundle:CRUD:base_edit_field.twig' %}
 
 {% block field %}
-    <div id="field_container_{{ configuration.code }}_{{ field_element.id}}">
-        <span id="field_widget_{{ configuration.code }}_{{ field_element.id}}" >
-            {{ form_field(field_element) }}
-        </span>
-
-        <span id="field_actions_{{ configuration.code }}_{{ field_element.id}}" >
-            <a href="{{ field_description.configuration.generateUrl('create') }}" onclick="start_field_dialog_form_add_{{ configuration.code }}_{{ field_element.id }}(event)" class="action"><img src="{{ asset('bundles/baseapplication/famfamfam/add.png') }}" alt="{% trans 'btn_add' from 'BaseApplicationBundle' %}" /></a>
-        </span>
-
-        <div style="display: hidden" id="field_dialog_{{ configuration.code }}_{{ field_element.id }}">
-
+    {% if field_description.edit == 'inline' %}
+        {% for nested_field_description in field_description.configuration.formfields %}
+            {{ nested_field_description|render_form_element(field_element, value, {'configuration' : field_description.configuration}) }}
+        {% endfor %}
+    {% else %}
+        <div id="field_container_{{ configuration.code }}_{{ field_element.id}}">
+            <span id="field_widget_{{ configuration.code }}_{{ field_element.id}}" >
+                {{ form_field(field_element) }}
+            </span>
+
+            <span id="field_actions_{{ configuration.code }}_{{ field_element.id}}" >
+                <a href="{{ field_description.configuration.generateUrl('create') }}" onclick="start_field_dialog_form_add_{{ configuration.code }}_{{ field_element.id }}(event)" class="action"><img src="{{ asset('bundles/baseapplication/famfamfam/add.png') }}" alt="{% trans 'btn_add' from 'BaseApplicationBundle' %}" /></a>
+            </span>
+
+            <div style="display: hidden" id="field_dialog_{{ configuration.code }}_{{ field_element.id }}">
+
+            </div>
         </div>
-    </div>
 
-    {% include 'Sonata/BaseApplicationBundle:CRUD:edit_many_association_script.twig' %}
+        {% include 'Sonata/BaseApplicationBundle:CRUD:edit_many_association_script.twig' %}
+    {% endif %}
 {% endblock %}

+ 16 - 8
Twig/Extension/BaseApplicationExtension.php

@@ -58,6 +58,19 @@ class BaseApplicationExtension extends \Twig_Extension
     }
 
     public function renderListElement($object, $field_description, $params = array())
+    {
+
+
+        $template = $this->environment->loadTemplate($field_description['template']);
+
+        return $template->render(array_merge($params, array(
+            'object' => $object,
+            'value'  => $this->getValueFromFieldDescription($object, $field_description),
+            'field_description' => $field_description
+        )));
+    }
+
+    public function getValueFromFieldDescription($object, $field_description)
     {
         $value = null;
 
@@ -65,18 +78,12 @@ class BaseApplicationExtension extends \Twig_Extension
 
             $value = $field_description['reflection']->getValue($object);
 
-        } else if(method_exists($object, $field_description['code'])) {
+        } else if(array_key_exists('code', $field_description) && method_exists($object, $field_description)) {
 
             $value = call_user_func(array($object, $field_description['code']));
         }
 
-        $template = $this->environment->loadTemplate($field_description['template']);
-
-        return $template->render(array_merge($params, array(
-            'object' => $object,
-            'value'  => $value,
-            'field_description' => $field_description
-        )));
+        return $value;
     }
 
     public function renderFilterElement($filter, $params = array())
@@ -108,6 +115,7 @@ class BaseApplicationExtension extends \Twig_Extension
         return $template->render(array_merge($params, array(
             'object'            => $object,
             'field_description' => $field_description,
+            'value'             => $this->getValueFromFieldDescription($object, $field_description),
             'field_element'     => $form->get($field_description['fieldName']),
         )));
     }