Bläddra i källkod

add many-to-many in crud, add static choice management, refactor the way crud information is built

Thomas 14 år sedan
förälder
incheckning
98583a5a5b

+ 147 - 18
Controller/CRUDController.php

@@ -31,6 +31,24 @@ class CRUDController extends Controller
 
     protected $base_controller_name;
 
+    /**
+     * Sets the Container associated with this Controller.
+     *
+     * @param ContainerInterface $container A ContainerInterface instance
+     */
+    public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container = null)
+    {
+        $this->container = $container;
+
+        $this->configure();
+    }
+
+    public function configure()
+    {
+        $this->buildFormFields();
+        $this->buildListFields();
+    }
+
     public function getClass()
     {
         return $this->class;
@@ -157,13 +175,12 @@ class CRUDController extends Controller
     {
         // if nothing is defined we display all fields
         if(!$selected_fields) {
-            $selected_fields = array_keys($this->getClassMetaData()->reflFields);
+            $selected_fields = array_keys($this->getClassMetaData()->reflFields) + array_keys($this->getClassMetaData()->associationMappings);
         }
 
         $metadata = $this->getClassMetaData();
-        
+
         // make sure we works with array
-        $fields = array();
         foreach($selected_fields as $name => $options) {
             if(is_array($options)) {
                 $fields[$name] = $options;
@@ -179,6 +196,14 @@ class CRUDController extends Controller
                 );
             }
 
+
+            if(isset($metadata->associationMappings[$name])) {
+                $fields[$name] = array_merge(
+                    $metadata->associationMappings[$name],
+                    $fields[$name]
+                );
+            }
+
             if(isset($metadata->reflFields[$name])) {
                 $fields[$name]['reflection']  =& $metadata->reflFields[$name];
             }
@@ -188,17 +213,35 @@ class CRUDController extends Controller
     }
 
     /**
-     * Construct and build the form field definitions
-     *
-     * @return list form field definition
+     * @return void
      */
-    public function getFormFields()
+    public function configureFormFields()
+    {
+
+    }
+
+    public function buildFormFields()
     {
         $this->form_fields = $this->getBaseFields($this->form_fields);
 
         foreach($this->form_fields as $name => $options) {
-            if(!isset($this->form_fields[$name]['template'])) {
+
+            if(!isset($this->form_fields[$name]['options'])) {
+                $this->form_fields[$name]['options'] = array(); 
+            }
+
+            if(!isset($this->form_fields[$name]['template']) && isset($this->form_fields[$name]['type'])) {
                 $this->form_fields[$name]['template'] = sprintf('BaseApplicationBundle:CRUD:edit_%s.twig', $this->form_fields[$name]['type']);
+
+                if($this->form_fields[$name]['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::ONE_TO_ONE)
+                {
+                    $this->form_fields[$name]['template'] = 'BaseApplicationBundle:CRUD:edit_one_to_one.twig';
+                }
+
+                if($this->form_fields[$name]['type'] == \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY)
+                {
+                    $this->form_fields[$name]['template'] = 'BaseApplicationBundle:CRUD:edit_many_to_many.twig';
+                }
             }
 
             if(isset($this->form_fields[$name]['id'])) {
@@ -206,12 +249,13 @@ class CRUDController extends Controller
             }
         }
 
+        $this->configureFormFields();
+
         return $this->form_fields;
     }
 
-    public function getListFields()
+    public function buildListFields()
     {
-        
         $this->list_fields = $this->getBaseFields($this->list_fields);
 
         foreach($this->list_fields as $name => $options) {
@@ -234,6 +278,20 @@ class CRUDController extends Controller
             ) ) + $this->list_fields;
         }
         
+    }
+
+    /**
+     * Construct and build the form field definitions
+     *
+     * @return list form field definition
+     */
+    public function getFormFields()
+    {
+        return $this->form_fields;
+    }
+
+    public function getListFields()
+    {
         return $this->list_fields;
     }
 
@@ -293,6 +351,29 @@ class CRUDController extends Controller
         ));
     }
 
+    public function getChoices($description)
+    {
+        $targets = $this->getEntityManager()
+            ->createQueryBuilder()
+            ->select('t')
+            ->from($description['targetEntity'], 't')
+            ->getQuery()
+            ->execute();
+
+        $choices = array();
+        foreach($targets as $target) {
+            // todo : puts this into a configuration option and use reflection
+            foreach(array('getTitle', 'getName', '__toString') as $getter) {
+                if(method_exists($target, $getter)) {
+                    $choices[$target->getId()] = $target->$getter();
+                    break;
+                }
+            }
+        }
+
+        return $choices;
+    }
+
     public function getForm($object, $fields)
     {
 
@@ -300,43 +381,88 @@ class CRUDController extends Controller
 
         foreach($fields as $name => $description) {
 
+            if(!isset($description['type'])) {
+
+                continue;
+            }
+
             switch($description['type']) {
+
+                case \Doctrine\ORM\Mapping\ClassMetadataInfo::MANY_TO_MANY:
+
+                    $transformer = new \Symfony\Bundle\DoctrineBundle\Form\ValueTransformer\CollectionToChoiceTransformer(array(
+                        'em'        =>  $this->getEntityManager(),
+                        'className' => $description['targetEntity']
+                    ));
+
+                    $field = new \Symfony\Component\Form\ChoiceField($name, array_merge(array(
+                        'expanded' => true,
+                        'multiple' => true,
+                        'choices' => $this->getChoices($description),
+                        'value_transformer' => $transformer,
+                    ), $description['options']));
+
+                    break;
+
+                case \Doctrine\ORM\Mapping\ClassMetadataInfo::ONE_TO_ONE:
+
+                    $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;
+                
                 case 'string':
-                    $field = new \Symfony\Component\Form\TextField($name);
+                    $field = new \Symfony\Component\Form\TextField($name, $description['options']);
                     break;
 
                 case 'text':
-                    $field = new \Symfony\Component\Form\TextareaField($name);
+                    $field = new \Symfony\Component\Form\TextareaField($name, $description['options']);
                     break;
 
                 case 'boolean':
-                    $field = new \Symfony\Component\Form\CheckboxField($name);
+                    $field = new \Symfony\Component\Form\CheckboxField($name, $description['options']);
                     break;
 
                 case 'integer':
-                    $field = new \Symfony\Component\Form\IntegerField($name);
+                    $field = new \Symfony\Component\Form\IntegerField($name, $description['options']);
                     break;
 
                 case 'decimal':
-                    $field = new \Symfony\Component\Form\NumberField($name);
+                    $field = new \Symfony\Component\Form\NumberField($name, $description['options']);
                     break;
 
                 case 'datetime':
-                    $field = new \Symfony\Component\Form\DateTimeField($name);
+                    $field = new \Symfony\Component\Form\DateTimeField($name, $description['options']);
                     break;
 
                 case 'date':
-                    $field = new \Symfony\Component\Form\DateField($name);
+                    $field = new \Symfony\Component\Form\DateField($name, $description['options']);
+                    break;
+
+                case 'choice':
+                    $field = new \Symfony\Component\Form\ChoiceField($name, $description['options']);
                     break;
 
                 case 'array':
-                    $field = new \Symfony\Component\Form\FieldGroup($name);
+                    $field = new \Symfony\Component\Form\FieldGroup($name, $description['options']);
 
                     $values = $description['reflection']->getValue($object);
 
                     foreach((array)$values as $k => $v) {
                         $field->add(new \Symfony\Component\Form\TextField($k));
                     }
+                    break;
+
+                default:
+                    throw new \RuntimeException(sprintf('unknow type `%s`', $description['type']));
             }
 
             $form->add($field);
@@ -404,6 +530,9 @@ class CRUDController extends Controller
 
         if(count($idx) == 0) { // no item selected
             // todo : add flash information
+
+            $url = $this->getUrl('list');
+            return $this->redirect($this->generateUrl($url['url']));
         }
 
         // execute the action, batchActionXxxxx

+ 21 - 20
Resources/views/CRUD/base_edit.twig

@@ -9,37 +9,38 @@ file that was distributed with this source code.
 
 #}
 
+{% extends 'BaseApplicationBundle::layout.twig' %}
 
 {% block title %}
     {% if object.id %}
-        <h1>Edit</h1>
+        {% trans 'title_edit' from 'BaseApplicationBundle'%}
     {% else %}
-        <h1>Create</h1>
+        {% trans 'title_create' from 'BaseApplicationBundle'%}
     {% endif %}
 {% endblock %}
 
 {% block actions %}
-    <div>
-        <ul>
-            <li><a href="{{ url(urls.create.url) }}">{% trans "link_action_create" from "BaseApplicationBundle" %}</a></li>
-            <li><a href="{{ url(urls.list.url) }}">{% trans "link_action_list" from "BaseApplicationBundle" %}</a></li>
-        </ul>
-    </div>
+    <ul>
+        <li><a href="{{ url(urls.create.url) }}">{% trans "link_action_create" from "BaseApplicationBundle" %}</a></li>
+        <li><a href="{{ url(urls.list.url) }}">{% trans "link_action_list" from "BaseApplicationBundle" %}</a></li>
+    </ul>
 {% endblock %}
 
-{% block form %}
-    <form action="{{ url(urls.update.url, {'id': object.id}) }}" method="POST">
+{% block content %}
+    {% block form %}
+        <form action="{{ url(urls.update.url, {'id': object.id}) }}" method="POST">
 
-        {{ form|render_hidden }}
+            {{ form|render_hidden }}
 
-        {% for field in fields %}
-            {{ field|render_form_element(form, object, {'urls' : urls}) }}
-        {% endfor %}
+            {% for field in fields %}
+                {{ field|render_form_element(form, object, {'urls' : urls}) }}
+            {% endfor %}
 
-        {% if object.id %}
-            <input type="submit" value="{% trans "btn_update" from "BaseApplicationBundle" %}"/>
-        {% else %}
-            <input type="submit" value="{% trans "btn_create" from "BaseApplicationBundle" %}"/>
-        {% endif %}
-    </form>
+            {% if object.id %}
+                <input type="submit" value="{% trans "btn_update" from "BaseApplicationBundle" %}"/>
+            {% else %}
+                <input type="submit" value="{% trans "btn_create" from "BaseApplicationBundle" %}"/>
+            {% endif %}
+        </form>
+    {% endblock %}
 {% endblock %}

+ 16 - 0
Resources/views/CRUD/base_edit_field.twig

@@ -0,0 +1,16 @@
+{#
+
+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.
+
+#}
+
+<p>
+    {% block label %}{{ field_element|render_label }} <br />{% endblock %}
+    {% block field %}{{ field_element|render }}{% endblock %}
+    {% block errors %}{{ field_element|render_errors }}{% endblock %}
+</p>

+ 51 - 49
Resources/views/CRUD/base_list.twig

@@ -9,63 +9,65 @@ file that was distributed with this source code.
 
 #}
 
-<h1>{% block title %}{% trans 'title_list' from 'BaseApplicationBundle'%}{% endblock %}</h1>
+{% extends 'BaseApplicationBundle::layout.twig' %}
 
+{% block title %}{% trans 'title_list' from 'BaseApplicationBundle'%}{% endblock %}
 
 {% block actions %}
-    <div>
-        <ul>
-            <li><a href="{{ url(urls.create.url) }}">{% trans "link_action_create" from "BaseApplicationBundle" %}</a></li>
-        </ul>
-    </div>
+    <ul>
+        <li><a href="{{ url(urls.create.url) }}">{% trans "link_action_create" from "BaseApplicationBundle" %}</a></li>
+    </ul>
 {% endblock %}
 
-<form action="{{ url(urls.batch.url) }}" method="POST" >
-    <table>
-        {% block table_header %}
-            <tr>
-                {% for field_name, field_description in fields %}
-                    <td>{{ field_name }}</td>
-                {% endfor %}
-            </tr>
-        {% endblock %}
-
-        {% block table_body %}{% endblock %}
+{% block content %}
+    {% block list %}
+        <form action="{{ url(urls.batch.url) }}" method="POST" >
+            <table>
+                {% block table_header %}
+                    <tr>
+                        {% for field_name, field_description in fields %}
+                            <td>{{ field_name }}</td>
+                        {% endfor %}
+                    </tr>
+                {% endblock %}
 
-        {% block table_footer %}
-            {% if pager.haveToPaginate() %}
-                <tr>
-                    <td colspan="{{ fields|length }}">
+                {% block table_body %}{% endblock %}
 
-                        {% if pager.page != pager.previouspage %}
-                            <li><a href="{{ pager.renderLink(pager.previouspage) }}">{% trans 'link_previous_pager' from 'BaseApplicationBundle' %}</a></li>
-                        {% endif %}
+                {% block table_footer %}
+                    {% if pager.haveToPaginate() %}
+                        <tr>
+                            <td colspan="{{ fields|length }}">
+                                {% if pager.page != pager.previouspage %}
+                                    <li><a href="{{ pager.renderLink(pager.previouspage) }}">{% trans 'link_previous_pager' from 'BaseApplicationBundle' %}</a></li>
+                                {% endif %}
 
-                        {# Set the number of pages to display in the pager #}
-                        {% for page in pager.getLinks(5) %}
-                            {% if page == pager.page %}
-                                <li>{{ page }}</li>
-                            {% else %}
-                                <li><a href="{{ pager.renderLink(page) }}">{{ page }}</a></li>
-                            {% endif %}
-                        {% endfor %}
+                                {# Set the number of pages to display in the pager #}
+                                {% for page in pager.getLinks(5) %}
+                                    {% if page == pager.page %}
+                                        <li>{{ page }}</li>
+                                    {% else %}
+                                        <li><a href="{{ pager.renderLink(page) }}">{{ page }}</a></li>
+                                    {% endif %}
+                                {% endfor %}
 
-                        {% if pager.page != pager.nextpage %}
-                            <li><a href="{{ pager.renderLink(pager.nextpage) }}">{% trans 'link_previous_pager' from 'BaseApplicationBundle' %}</a></li>
-                        {% endif %}
-                    </td>
-                </tr>
-            {% endif %}
-        {% endblock %}
-    </table>
+                                {% if pager.page != pager.nextpage %}
+                                    <li><a href="{{ pager.renderLink(pager.nextpage) }}">{% trans 'link_previous_pager' from 'BaseApplicationBundle' %}</a></li>
+                                {% endif %}
+                            </td>
+                        </tr>
+                    {% endif %}
+                {% endblock %}
+            </table>
 
-    <div>
-        <select name="action" >
-            {% for action, label in batch_actions %}
-                <option value="{{ action }}">{{ label }}</option>
-            {% endfor %}
-        </select>
-        <input type="submit" value="{% trans 'btn_batch' from 'BaseApplicationBundle' %}" />
+            <div>
+                <select name="action" >
+                    {% for action, label in batch_actions %}
+                        <option value="{{ action }}">{{ label }}</option>
+                    {% endfor %}
+                </select>
+                <input type="submit" value="{% trans 'btn_batch' from 'BaseApplicationBundle' %}" />
 
-    </div>
-</form>
+            </div>
+        </form>
+    {% endblock %}
+{% endblock %}

+ 3 - 5
Resources/views/CRUD/edit_array.twig

@@ -9,8 +9,6 @@ file that was distributed with this source code.
 
 #}
 
-<p>
-    {{ field_element|render_label }} <br />
-    {{ field_element|render({'class' : 'title'}) }}
-    {{ field_element|render_errors }}
-</p>
+{% extends 'BaseApplicationBundle:CRUD:base_edit_field.twig' %}
+
+{% block field %}{{ field_element|render({'class' : 'title'}) }}{% endblock %}

+ 1 - 5
Resources/views/CRUD/edit_boolean.twig

@@ -9,8 +9,4 @@ file that was distributed with this source code.
 
 #}
 
-<p>
-    {{ field_element|render_label }} <br />
-    {{ field_element|render({'class' : 'title'}) }}
-    {{ field_element|render_errors }}
-</p>
+{% extends 'BaseApplicationBundle:CRUD:base_edit_field.twig' %}

+ 12 - 0
Resources/views/CRUD/edit_choice.twig

@@ -0,0 +1,12 @@
+{#
+
+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 'BaseApplicationBundle:CRUD:base_edit_field.twig' %}

+ 1 - 5
Resources/views/CRUD/edit_datetime.twig

@@ -9,8 +9,4 @@ file that was distributed with this source code.
 
 #}
 
-<p>
-    {{ field_element|render_label }} <br />
-    {{ field_element|render }}
-    {{ field_element|render_errors }}
-</p>
+{% extends 'BaseApplicationBundle:CRUD:base_edit_field.twig' %}

+ 3 - 5
Resources/views/CRUD/edit_decimal.twig

@@ -9,8 +9,6 @@ file that was distributed with this source code.
 
 #}
 
-<p>
-    {{ field_element|render_label }} <br />
-    {{ field_element|render({'class' : 'title'}) }}
-    {{ field_element|render_errors }}
-</p>
+{% extends 'BaseApplicationBundle:CRUD:base_edit_field.twig' %}
+
+{% block field %}{{ field_element|render({'class' : 'title'}) }}{% endblock %}

+ 3 - 5
Resources/views/CRUD/edit_integer.twig

@@ -9,8 +9,6 @@ file that was distributed with this source code.
 
 #}
 
-<p>
-    {{ field_element|render_label }} <br />
-    {{ field_element|render({'class' : 'title'}) }}
-    {{ field_element|render_errors }}
-</p>
+{% extends 'BaseApplicationBundle:CRUD:base_edit_field.twig' %}
+
+{% block field %}{{ field_element|render({'class' : 'title'}) }}{% endblock %}

+ 13 - 0
Resources/views/CRUD/edit_many_to_many.twig

@@ -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 'BaseApplicationBundle:CRUD:base_edit_field.twig' %}
+

+ 12 - 0
Resources/views/CRUD/edit_one_to_one.twig

@@ -0,0 +1,12 @@
+{#
+
+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 'BaseApplicationBundle:CRUD:base_edit_field.twig' %}

+ 3 - 5
Resources/views/CRUD/edit_string.twig

@@ -9,8 +9,6 @@ file that was distributed with this source code.
 
 #}
 
-<p>
-    {{ field_element|render_label }} <br />
-    {{ field_element|render({'class' : 'title'}) }}
-    {{ field_element|render_errors }}
-</p>
+{% extends 'BaseApplicationBundle:CRUD:base_edit_field.twig' %}
+
+{% block field %}{{ field_element|render({'class' : 'title'}) }}{% endblock %}

+ 3 - 5
Resources/views/CRUD/edit_text.twig

@@ -9,8 +9,6 @@ file that was distributed with this source code.
 
 #}
 
-<p>
-    {{ field_element|render_label }} <br />
-    {{ field_element|render({'class' : 'title'}) }}
-    {{ field_element|render_errors }}
-</p>
+{% extends 'BaseApplicationBundle:CRUD:base_edit_field.twig' %}
+
+{% block field %}{{ field_element|render({'class' : 'title'}) }}{% endblock %}

+ 11 - 0
Resources/views/CRUD/list_text.twig

@@ -0,0 +1,11 @@
+{#
+
+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.
+
+#}
+{{ value }}

+ 24 - 0
Resources/views/layout.twig

@@ -0,0 +1,24 @@
+{#
+
+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.
+
+#}
+
+{% block actions %}{% endblock %}
+
+<h1>{% block title %}{% endblock %}</h1>
+
+{% block content %}
+
+    {% block preview %}{% endblock %}
+
+    {% block form %}{% endblock %}
+
+    {% block list %}{% endblock %}
+
+{% endblock %}

+ 2 - 1
Tool/DoctrinePager.php

@@ -69,7 +69,8 @@ class DoctrinePager extends Pager implements \Serializable
         return $query_builder->getQuery();
     }
 
-    public function getCountColumn() {
+    public function getCountColumn()
+    {
 
         return $this->count_column;
     }

+ 4 - 0
Twig/Extension/BaseApplicationExtension.php

@@ -66,6 +66,10 @@ class BaseApplicationExtension extends \Twig_Extension
     public function renderFormElement($field_description, $form, $object, $params = array())
     {
 
+        if(!isset($field_description['fieldName'])) {
+            return '';
+        }
+        
         $field = $form->get($field_description['fieldName']);
 
         if($field->isHidden())