[Form] Improved form debugger
This commit is contained in:
parent
f56c5774a8
commit
89509d9847
@ -13,7 +13,8 @@
|
||||
<parameter key="data_collector.time.class">Symfony\Component\HttpKernel\DataCollector\TimeDataCollector</parameter>
|
||||
<parameter key="data_collector.memory.class">Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector</parameter>
|
||||
<parameter key="data_collector.router.class">Symfony\Bundle\FrameworkBundle\DataCollector\RouterDataCollector</parameter>
|
||||
<parameter key="data_collector.form.class">Symfony\Component\Form\Extension\DataCollector\Collector\FormCollector</parameter>
|
||||
<parameter key="data_collector.form.class">Symfony\Component\Form\Extension\DataCollector\FormDataCollector</parameter>
|
||||
<parameter key="data_collector.form.extractor.class">Symfony\Component\Form\Extension\DataCollector\FormDataExtractor</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
@ -55,8 +56,11 @@
|
||||
<tag name="data_collector" template="@WebProfiler/Collector/router.html.twig" id="router" priority="255" />
|
||||
</service>
|
||||
|
||||
<service id="data_collector.form.extractor" class="%data_collector.form.extractor.class%" />
|
||||
|
||||
<service id="data_collector.form" class="%data_collector.form.class%">
|
||||
<tag name="data_collector" template="@WebProfiler/Collector/form.html.twig" id="form" priority="255" />
|
||||
<argument type="service" id="data_collector.form.extractor" />
|
||||
</service>
|
||||
|
||||
</services>
|
||||
|
@ -5,11 +5,19 @@
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="form.resolved_type_factory.data_collector_proxy.class">Symfony\Component\Form\Extension\DataCollector\Proxy\ResolvedTypeFactoryDataCollectorProxy</parameter>
|
||||
<parameter key="form.type_extension.form.data_collector.class">Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<!-- DataCollectorExtension -->
|
||||
<service id="form.resolved_type_factory" class="%form.resolved_type_factory.data_collector_proxy.class%">
|
||||
<argument type="service">
|
||||
<service class="%form.resolved_type_factory.class%" />
|
||||
</argument>
|
||||
<argument type="service" id="data_collector.form" />
|
||||
</service>
|
||||
|
||||
<!-- DataCollectorTypeExtension -->
|
||||
<service id="form.type_extension.form.data_collector" class="%form.type_extension.form.data_collector.class%">
|
||||
<tag name="form.type_extension" alias="form" />
|
||||
<argument type="service" id="data_collector.form" />
|
||||
|
@ -1,10 +1,12 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% from _self import form_tree_entry, form_tree_details %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.data|length %}
|
||||
{% set icon %}
|
||||
<img width="20" height="28" alt="Forms" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAbCAYAAAB836/YAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDNkE1RUQ2RjBFMzcxMUUzOEIyQkVBRkQ5QzNENTIxMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDNkE1RUQ3MDBFMzcxMUUzOEIyQkVBRkQ5QzNENTIxMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkM2QTVFRDZEMEUzNzExRTM4QjJCRUFGRDlDM0Q1MjEzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkM2QTVFRDZFMEUzNzExRTM4QjJCRUFGRDlDM0Q1MjEzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+fkaAkgAAAQtJREFUeNpi/P//PwM1ARMDlQGLg4MDgsPCAqb//PnDwMzMDGJ+//v3L4oGqDgnSBxZPdwMLJa4ggioQRzoklBxVySh3SguBOICGOffv38TgNQuIny2C6oeRDEim8Fob28PjxVgBIEk/zMyMhIVXqAIZYQo/o81UlhZWcGYWIBNLQta+PACKS1iDYSGJy9OA4Fe+EBKUsKWhlmwpMtZZCS/NHzJ5iFFCRuN7wXEEtQ0sJDirIfGrwBiLiL0HSbWwLPoWQkH2I0tu+KKlOVk+BSngclALEnNMHSldqTMJtOcCFwGNlDbhZ1Y1LwhwpwTuAz0waI4gxIXPqC2lzuoYeAPalajjIO+ogcIMAAaCkonoOPIhQAAAABJRU5ErkJggg=="/>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{% if collector.errorCount %}red{% else %}green{% endif %}">{% if collector.errorCount %}{{ collector.errorCount }}{% else %}{{ collector.data|length }}{% endif %}</span>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{% if collector.data.nb_errors %}red{% else %}green{% endif %}">{% if collector.data.nb_errors %}{{ collector.data.nb_errors }}{% else %}{{ collector.data.forms|length }}{% endif %}</span>
|
||||
{% endset %}
|
||||
|
||||
{% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %}
|
||||
@ -13,48 +15,331 @@
|
||||
|
||||
{% block menu %}
|
||||
<span class="label">
|
||||
<span class="icon"><img src="data:image/png;base64,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" alt=""/></span>
|
||||
<span class="icon"><img src="data:image/png;base64,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" alt=""/></span>
|
||||
<strong>Forms</strong>
|
||||
{% if collector.data|length %}
|
||||
<span class="count"><span>{{ collector.data|length }}</span></span>
|
||||
{% if collector.data.forms|length %}
|
||||
<span class="count"><span>{{ collector.data.forms|length }}</span></span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>Form{% if collector.data|length > 1 %}s{% endif %}</h2>
|
||||
<style type="text/css">
|
||||
.window {
|
||||
/*background: #F6F6F6;*/
|
||||
margin: -30px -40px -40px;
|
||||
}
|
||||
.tree {
|
||||
width: 230px;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
float: left;
|
||||
}
|
||||
#content .tree h2 {
|
||||
font-size: 13px;
|
||||
padding: 5px 7px;
|
||||
margin: 0;
|
||||
}
|
||||
.tree li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.tree a {
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
padding: 5px 7px;
|
||||
border-radius: 6px;
|
||||
color: #313131;
|
||||
}
|
||||
.tree ul ul a {
|
||||
padding-left: 22px;
|
||||
}
|
||||
.tree ul ul ul a {
|
||||
padding-left: 37px;
|
||||
}
|
||||
.tree ul ul ul ul a {
|
||||
padding-left: 52px;
|
||||
}
|
||||
.tree ul ul ul ul ul a {
|
||||
padding-left: 67px;
|
||||
}
|
||||
.tree a:hover {
|
||||
background: #dfdfdf;
|
||||
}
|
||||
.tree a.active, a.active:hover {
|
||||
background: #dfdfdf;
|
||||
font-weight: bold;
|
||||
color: #313131;
|
||||
}
|
||||
.tree-details {
|
||||
border-left: 1px solid #dfdfdf;
|
||||
background: white;
|
||||
margin-left: 250px;
|
||||
padding: 30px 40px 40px;
|
||||
}
|
||||
.form-type {
|
||||
color: #999999;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% if collector.data.forms|length %}
|
||||
<div class="window">
|
||||
<div class="tree">
|
||||
<h2>Forms</h2>
|
||||
|
||||
{% for formName, fields in collector.data %}
|
||||
<h3>{{ formName }}</h3>
|
||||
{% if fields %}
|
||||
<table>
|
||||
<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Value</th>
|
||||
<th>Messages</th>
|
||||
</tr>
|
||||
{% for fieldName, field in fields %}
|
||||
<tr>
|
||||
<td><b>{{ fieldName }}</b></td>
|
||||
<td>{{ field.type }}</td>
|
||||
<td>{{ field.value }}</td>
|
||||
<td>
|
||||
<ul>
|
||||
{% for errorMessage in field.errors %}
|
||||
<li>
|
||||
{{ errorMessage.message }}
|
||||
</li>
|
||||
{% for formName, formData in collector.data.forms %}
|
||||
{{ form_tree_entry(formName, formData) }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% for formName, formData in collector.data.forms %}
|
||||
{{ form_tree_details(formName, formData) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p><em>No forms were submitted for this request.</em></p>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
function TabView() {
|
||||
var _activeLink = null,
|
||||
_activeView = null;
|
||||
|
||||
this.init = function () {
|
||||
var links = document.querySelectorAll('.tree a'),
|
||||
views = document.querySelectorAll('.tree-details'),
|
||||
i,
|
||||
l;
|
||||
|
||||
for (i = 0, l = links.length; i < l; ++i) {
|
||||
(function () {
|
||||
var link = links[i];
|
||||
|
||||
link.addEventListener('click', function (e) {
|
||||
var href = link.getAttribute('href'),
|
||||
targetId = href.substr(href.indexOf('#') + 1),
|
||||
view = document.getElementById(targetId);
|
||||
|
||||
if (view) {
|
||||
if (null !== _activeLink) {
|
||||
Sfjs.removeClass(_activeLink, 'active');
|
||||
}
|
||||
|
||||
if (null !== _activeView) {
|
||||
Sfjs.addClass(_activeView, 'hidden');
|
||||
}
|
||||
|
||||
Sfjs.addClass(link, 'active');
|
||||
Sfjs.removeClass(view, 'hidden');
|
||||
|
||||
_activeLink = link;
|
||||
_activeView = view;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
return false;
|
||||
})
|
||||
}());
|
||||
}
|
||||
|
||||
for (i = 0, l = views.length; i < l; ++i) {
|
||||
Sfjs.addClass(views[i], 'hidden');
|
||||
}
|
||||
|
||||
if (links.length > 0) {
|
||||
Sfjs.addClass(links[0], 'active');
|
||||
_activeLink = links[0];
|
||||
|
||||
if (views.length > 0) {
|
||||
Sfjs.removeClass(views[0], 'hidden');
|
||||
_activeView = views[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tabView = new TabView();
|
||||
|
||||
tabView.init();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% macro form_tree_entry(name, data) %}
|
||||
<li>
|
||||
<a href="#details_{{ data.view_vars.id|default("") }}">{{ name }}</a>
|
||||
|
||||
{% if data.children|length > 1 %}
|
||||
<ul>
|
||||
{% for childName, childData in data.children %}
|
||||
{{ _self.form_tree_entry(childName, childData) }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro form_tree_details(name, data) %}
|
||||
<div class="tree-details" id="details_{{ data.view_vars.id|default("") }}">
|
||||
<h2>
|
||||
{{ name }}
|
||||
{% if data.type_class is defined %}
|
||||
<span class="form-type">[<abbr title="{{ data.type_class }}">{{ data.type }}</abbr>]</span>
|
||||
{% endif %}
|
||||
</h2>
|
||||
|
||||
{% if data.errors is defined and data.errors|length > 0 %}
|
||||
<h3>Errors</h3>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th width="50%">Message</th>
|
||||
<th>Cause</th>
|
||||
</tr>
|
||||
{% for error in data.errors %}
|
||||
<tr>
|
||||
<td>{{ error.message }}</td>
|
||||
<td><em>Unknown.</em></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if data.default_data is defined %}
|
||||
<h3>Default Data</h3>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th width="180">Model Format</th>
|
||||
<td>
|
||||
{% if data.default_data.model is defined %}
|
||||
<pre>{{ data.default_data.model }}</pre>
|
||||
{% else %}
|
||||
<em>same as normalized format</em>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Normalized Format</th>
|
||||
<td><pre>{{ data.default_data.norm }}</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>View Format</th>
|
||||
<td>
|
||||
{% if data.default_data.view is defined %}
|
||||
<pre>{{ data.default_data.view }}</pre>
|
||||
{% else %}
|
||||
<em>same as normalized format</em>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if data.submitted_data is defined %}
|
||||
<h3>Submitted Data</h3>
|
||||
|
||||
{% if data.submitted_data.norm is defined %}
|
||||
<table>
|
||||
<tr>
|
||||
<th width="180">View Format</th>
|
||||
<td>
|
||||
{% if data.submitted_data.view is defined %}
|
||||
<pre>{{ data.submitted_data.view }}</pre>
|
||||
{% else %}
|
||||
<em>same as normalized format</em>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Normalized Format</th>
|
||||
<td><pre>{{ data.submitted_data.norm }}</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Model Format</th>
|
||||
<td>
|
||||
{% if data.submitted_data.model is defined %}
|
||||
<pre>{{ data.submitted_data.model }}</pre>
|
||||
{% else %}
|
||||
<em>same as normalized format</em>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% else %}
|
||||
<p><em>This form was not submitted.</em></p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if data.passed_options is defined %}
|
||||
<h3>Passed Options</h3>
|
||||
|
||||
{% if data.passed_options|length %}
|
||||
<table>
|
||||
<tr>
|
||||
<th width="180">Option</th>
|
||||
<th>Passed Value</th>
|
||||
<th>Resolved Value</th>
|
||||
</tr>
|
||||
{% for option, value in data.passed_options %}
|
||||
<tr>
|
||||
<th>{{ option }}</th>
|
||||
<td><pre>{{ value }}</pre></td>
|
||||
<td>
|
||||
{% if data.resolved_options[option] is sameas(value) %}
|
||||
<em>same as passed value</em>
|
||||
{% else %}
|
||||
<pre>{{ data.resolved_options[option] }}</pre>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<em>This form is valid.</em>
|
||||
<p><em>No options where passed when constructing this form.</em></p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<em>No forms were submitted for this request.</em>
|
||||
{% endif %}
|
||||
|
||||
{% if data.resolved_options is defined %}
|
||||
<h3>Resolved Options</h3>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th width="180">Option</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
{% for option, value in data.resolved_options %}
|
||||
<tr>
|
||||
<th>{{ option }}</th>
|
||||
<td><pre>{{ value }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
<h3>View Variables</h3>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th width="180">Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
{% for variable, value in data.view_vars %}
|
||||
<tr>
|
||||
<th>{{ variable }}</th>
|
||||
<td><pre>{{ value }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% for childName, childData in data.children %}
|
||||
{{ _self.form_tree_details(childName, childData) }}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
@ -63,6 +63,9 @@ table th, table td {
|
||||
font-size: 12px;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
table td em {
|
||||
color: #aaa;
|
||||
}
|
||||
fieldset {
|
||||
border: none;
|
||||
}
|
||||
@ -70,6 +73,9 @@ abbr {
|
||||
border-bottom: 1px dotted #000;
|
||||
cursor: help;
|
||||
}
|
||||
pre, code {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.clear {
|
||||
clear: both;
|
||||
height: 0;
|
||||
|
@ -1,130 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector\Collector;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector as BaseCollector;
|
||||
|
||||
/**
|
||||
* DataCollector for Form Validation.
|
||||
*
|
||||
* @author Robert Schönthal <robert.schoenthal@gmail.com>
|
||||
*/
|
||||
class FormCollector extends BaseCollector implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(FormEvents::POST_SUBMIT => array('collectForm', -255));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||
{
|
||||
//nothing to do, everything is added with addError()
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects Form-Validation-Data and adds them to the Collector.
|
||||
*
|
||||
* @param FormEvent $event The event object
|
||||
*/
|
||||
public function collectForm(FormEvent $event)
|
||||
{
|
||||
$form = $event->getForm();
|
||||
|
||||
if ($form->isRoot()) {
|
||||
$this->data[$form->getName()] = array();
|
||||
$this->addForm($form);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Form-Element to the Collector.
|
||||
*
|
||||
* @param FormInterface $form
|
||||
*/
|
||||
private function addForm(FormInterface $form)
|
||||
{
|
||||
if ($form->getErrors()) {
|
||||
$this->addError($form);
|
||||
}
|
||||
|
||||
// recursively add all child-errors
|
||||
foreach ($form->all() as $field) {
|
||||
$this->addForm($field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Form-Error to the Collector.
|
||||
*
|
||||
* @param FormInterface $form
|
||||
*/
|
||||
private function addError(FormInterface $form)
|
||||
{
|
||||
$storeData = array(
|
||||
'root' => $form->getRoot()->getName(),
|
||||
'name' => (string) $form->getPropertyPath(),
|
||||
'type' => $form->getConfig()->getType()->getName(),
|
||||
'errors' => $form->getErrors(),
|
||||
'value' => $this->varToString($form->getViewData())
|
||||
);
|
||||
|
||||
$this->data[$storeData['root']][$storeData['name']] = $storeData;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all collected Data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of Forms with Errors.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getErrorCount()
|
||||
{
|
||||
$errorCount = 0;
|
||||
|
||||
foreach ($this->data as $form) {
|
||||
if (count($form)) {
|
||||
$errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return $errorCount;
|
||||
}
|
||||
}
|
@ -15,20 +15,22 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\AbstractExtension;
|
||||
|
||||
/**
|
||||
* DataCollectorExtension for collecting Form Validation Failures.
|
||||
* Extension for collecting data of the forms on a page.
|
||||
*
|
||||
* @since 2.4
|
||||
* @author Robert Schönthal <robert.schoenthal@gmail.com>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class DataCollectorExtension extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* @var EventSubscriberInterface
|
||||
*/
|
||||
private $eventSubscriber;
|
||||
private $dataCollector;
|
||||
|
||||
public function __construct(EventSubscriberInterface $eventSubscriber)
|
||||
public function __construct(FormDataCollectorInterface $dataCollector)
|
||||
{
|
||||
$this->eventSubscriber = $eventSubscriber;
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,7 +39,7 @@ class DataCollectorExtension extends AbstractExtension
|
||||
protected function loadTypeExtensions()
|
||||
{
|
||||
return array(
|
||||
new Type\DataCollectorTypeExtension($this->eventSubscriber)
|
||||
new Type\DataCollectorTypeExtension($this->dataCollector)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
/**
|
||||
* Listener that invokes a data collector for the {@link FormEvents::POST_SET_DATA}
|
||||
* and {@link FormEvents::POST_SUBMIT} events.
|
||||
*
|
||||
* @since 2.4
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class DataCollectorListener implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var FormDataCollectorInterface
|
||||
*/
|
||||
private $dataCollector;
|
||||
|
||||
public function __construct(FormDataCollectorInterface $dataCollector)
|
||||
{
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
// High priority in order to be called as soon as possible
|
||||
FormEvents::POST_SET_DATA => array('postSetData', 255),
|
||||
// Low priority in order to be called as late as possible
|
||||
FormEvents::POST_SUBMIT => array('postSubmit', -255),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for the {@link FormEvents::POST_SET_DATA} event.
|
||||
*
|
||||
* @param FormEvent $event The event object
|
||||
*/
|
||||
public function postSetData(FormEvent $event)
|
||||
{
|
||||
if ($event->getForm()->isRoot()) {
|
||||
// Collect basic information about each form
|
||||
$this->dataCollector->collectConfiguration($event->getForm());
|
||||
|
||||
// Collect the default data
|
||||
$this->dataCollector->collectDefaultData($event->getForm());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for the {@link FormEvents::POST_SUBMIT} event.
|
||||
*
|
||||
* @param FormEvent $event The event object
|
||||
*/
|
||||
public function postSubmit(FormEvent $event)
|
||||
{
|
||||
if ($event->getForm()->isRoot()) {
|
||||
// Collect the submitted data of each form
|
||||
$this->dataCollector->collectSubmittedData($event->getForm());
|
||||
|
||||
// Assemble a form tree
|
||||
// This is done again in collectViewVariables(), but that method
|
||||
// is not guaranteed to be called (i.e. when no view is created)
|
||||
$this->dataCollector->buildPreliminaryFormTree($event->getForm());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,274 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
|
||||
/**
|
||||
* Data collector for {@link \Symfony\Component\Form\FormInterface} instances.
|
||||
*
|
||||
* @since 2.4
|
||||
* @author Robert Schönthal <robert.schoenthal@gmail.com>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FormDataCollector extends DataCollector implements FormDataCollectorInterface
|
||||
{
|
||||
/**
|
||||
* @var FormDataExtractor
|
||||
*/
|
||||
private $dataExtractor;
|
||||
|
||||
/**
|
||||
* Stores the collected data per {@link FormInterface} instance.
|
||||
*
|
||||
* Uses the hashes of the forms as keys. This is preferrable over using
|
||||
* {@link \SplObjectStorage}, because in this way no references are kept
|
||||
* to the {@link FormInterface} instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $dataByForm;
|
||||
|
||||
/**
|
||||
* Stores the collected data per {@link FormView} instance.
|
||||
*
|
||||
* Uses the hashes of the views as keys. This is preferrable over using
|
||||
* {@link \SplObjectStorage}, because in this way no references are kept
|
||||
* to the {@link FormView} instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $dataByView;
|
||||
|
||||
/**
|
||||
* Connects {@link FormView} with {@link FormInterface} instances.
|
||||
*
|
||||
* Uses the hashes of the views as keys and the hashes of the forms as
|
||||
* values. This is preferrable over storing the objects directly, because
|
||||
* this way they can safely be discarded by the GC.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $formsByView;
|
||||
|
||||
public function __construct(FormDataExtractorInterface $dataExtractor)
|
||||
{
|
||||
$this->dataExtractor = $dataExtractor;
|
||||
$this->data = array(
|
||||
'forms' => array(),
|
||||
'nb_errors' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. The data is collected during the form event listeners.
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function associateFormWithView(FormInterface $form, FormView $view)
|
||||
{
|
||||
$this->formsByView[spl_object_hash($view)] = spl_object_hash($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectConfiguration(FormInterface $form)
|
||||
{
|
||||
$hash = spl_object_hash($form);
|
||||
|
||||
if (!isset($this->dataByForm[$hash])) {
|
||||
$this->dataByForm[$hash] = array();
|
||||
}
|
||||
|
||||
$this->dataByForm[$hash] = array_replace(
|
||||
$this->dataByForm[$hash],
|
||||
$this->dataExtractor->extractConfiguration($form)
|
||||
);
|
||||
|
||||
foreach ($form as $child) {
|
||||
$this->collectConfiguration($child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectDefaultData(FormInterface $form)
|
||||
{
|
||||
$hash = spl_object_hash($form);
|
||||
|
||||
if (!isset($this->dataByForm[$hash])) {
|
||||
$this->dataByForm[$hash] = array();
|
||||
}
|
||||
|
||||
$this->dataByForm[$hash] = array_replace(
|
||||
$this->dataByForm[$hash],
|
||||
$this->dataExtractor->extractDefaultData($form)
|
||||
);
|
||||
|
||||
foreach ($form as $child) {
|
||||
$this->collectDefaultData($child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectSubmittedData(FormInterface $form)
|
||||
{
|
||||
$hash = spl_object_hash($form);
|
||||
|
||||
if (!isset($this->dataByForm[$hash])) {
|
||||
$this->dataByForm[$hash] = array();
|
||||
}
|
||||
|
||||
$this->dataByForm[$hash] = array_replace(
|
||||
$this->dataByForm[$hash],
|
||||
$this->dataExtractor->extractSubmittedData($form)
|
||||
);
|
||||
|
||||
// Count errors
|
||||
if (isset($this->dataByForm[$hash]['errors'])) {
|
||||
$this->data['nb_errors'] += count($this->dataByForm[$hash]['errors']);
|
||||
}
|
||||
|
||||
foreach ($form as $child) {
|
||||
$this->collectSubmittedData($child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collectViewVariables(FormView $view)
|
||||
{
|
||||
$hash = spl_object_hash($view);
|
||||
|
||||
if (!isset($this->dataByView[$hash])) {
|
||||
$this->dataByView[$hash] = array();
|
||||
}
|
||||
|
||||
$this->dataByView[$hash] = array_replace(
|
||||
$this->dataByView[$hash],
|
||||
$this->dataExtractor->extractViewVariables($view)
|
||||
);
|
||||
|
||||
foreach ($view->children as $child) {
|
||||
$this->collectViewVariables($child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildPreliminaryFormTree(FormInterface $form)
|
||||
{
|
||||
$this->data['forms'][$form->getName()] = array();
|
||||
|
||||
$this->recursiveBuildPreliminaryFormTree($form, $this->data['forms'][$form->getName()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFinalFormTree(FormInterface $form, FormView $view)
|
||||
{
|
||||
$this->data['forms'][$form->getName()] = array();
|
||||
|
||||
$this->recursiveBuildFinalFormTree($form, $view, $this->data['forms'][$form->getName()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output = null)
|
||||
{
|
||||
$hash = spl_object_hash($form);
|
||||
|
||||
$output = isset($this->dataByForm[$hash])
|
||||
? $this->dataByForm[$hash]
|
||||
: array();
|
||||
|
||||
$output['children'] = array();
|
||||
|
||||
foreach ($form as $name => $child) {
|
||||
$output['children'][$name] = array();
|
||||
|
||||
$this->recursiveBuildPreliminaryFormTree($child, $output['children'][$name]);
|
||||
}
|
||||
}
|
||||
|
||||
private function recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, &$output = null)
|
||||
{
|
||||
$viewHash = spl_object_hash($view);
|
||||
$formHash = null;
|
||||
|
||||
if (null !== $form) {
|
||||
$formHash = spl_object_hash($form);
|
||||
} elseif (isset($this->formsByView[$viewHash])) {
|
||||
// The FormInterface instance of the CSRF token is never contained in
|
||||
// the FormInterface tree of the form, so we need to get the
|
||||
// corresponding FormInterface instance for its view in a different way
|
||||
$formHash = $this->formsByView[$viewHash];
|
||||
}
|
||||
|
||||
$output = isset($this->dataByView[$viewHash])
|
||||
? $this->dataByView[$viewHash]
|
||||
: array();
|
||||
|
||||
if (null !== $formHash) {
|
||||
$output = array_replace(
|
||||
$output,
|
||||
isset($this->dataByForm[$formHash])
|
||||
? $this->dataByForm[$formHash]
|
||||
: array()
|
||||
);
|
||||
}
|
||||
|
||||
$output['children'] = array();
|
||||
|
||||
foreach ($view->children as $name => $childView) {
|
||||
// The CSRF token, for example, is never added to the form tree.
|
||||
// It is only present in the view.
|
||||
$childForm = null !== $form && $form->has($name)
|
||||
? $form->get($name)
|
||||
: null;
|
||||
|
||||
$output['children'][$name] = array();
|
||||
|
||||
$this->recursiveBuildFinalFormTree($childForm, $childView, $output['children'][$name]);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
|
||||
|
||||
/**
|
||||
* Collects and structures information about forms.
|
||||
*
|
||||
* @since 2.4
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface FormDataCollectorInterface extends DataCollectorInterface
|
||||
{
|
||||
/**
|
||||
* Stores configuration data of the given form and its children.
|
||||
*
|
||||
* @param FormInterface $form A root form
|
||||
*/
|
||||
public function collectConfiguration(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Stores the default data of the given form and its children.
|
||||
*
|
||||
* @param FormInterface $form A root form
|
||||
*/
|
||||
public function collectDefaultData(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Stores the submitted data of the given form and its children.
|
||||
*
|
||||
* @param FormInterface $form A root form
|
||||
*/
|
||||
public function collectSubmittedData(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Stores the view variables of the given form view and its children.
|
||||
*
|
||||
* @param FormView $view A root form view
|
||||
*/
|
||||
public function collectViewVariables(FormView $view);
|
||||
|
||||
/**
|
||||
* Specifies that the given objects represent the same conceptual form.
|
||||
*
|
||||
* @param FormInterface $form A form object
|
||||
* @param FormView $view A view object
|
||||
*/
|
||||
public function associateFormWithView(FormInterface $form, FormView $view);
|
||||
|
||||
/**
|
||||
* Assembles the data collected about the given form and its children as
|
||||
* a tree-like data structure.
|
||||
*
|
||||
* The result can be queried using {@link getData()}.
|
||||
*
|
||||
* @param FormInterface $form A root form
|
||||
*/
|
||||
public function buildPreliminaryFormTree(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Assembles the data collected about the given form and its children as
|
||||
* a tree-like data structure.
|
||||
*
|
||||
* The result can be queried using {@link getData()}.
|
||||
*
|
||||
* Contrary to {@link buildPreliminaryFormTree()}, a {@link FormView}
|
||||
* object has to be passed. The tree structure of this view object will be
|
||||
* used for structuring the resulting data. That means, if a child is
|
||||
* present in the view, but not in the form, it will be present in the final
|
||||
* data array anyway.
|
||||
*
|
||||
* When {@link FormView} instances are present in the view tree, for which
|
||||
* no corresponding {@link FormInterface} objects can be found in the form
|
||||
* tree, only the view data will be included in the result. If a
|
||||
* corresponding {@link FormInterface} exists otherwise, call
|
||||
* {@link associateFormWithView()} before calling this method.
|
||||
*
|
||||
* @param FormInterface $form A root form
|
||||
* @param FormView $view A root view
|
||||
*/
|
||||
public function buildFinalFormTree(FormInterface $form, FormView $view);
|
||||
|
||||
/**
|
||||
* Returns all collected data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getData();
|
||||
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link FormDataExtractorInterface}.
|
||||
*
|
||||
* @since 2.4
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FormDataExtractor implements FormDataExtractorInterface
|
||||
{
|
||||
/**
|
||||
* @var ValueExporter
|
||||
*/
|
||||
private $valueExporter;
|
||||
|
||||
/**
|
||||
* Constructs a new data extractor.
|
||||
*/
|
||||
public function __construct(ValueExporter $valueExporter = null)
|
||||
{
|
||||
$this->valueExporter = $valueExporter ?: new ValueExporter();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extractConfiguration(FormInterface $form)
|
||||
{
|
||||
$data = array(
|
||||
'type' => $form->getConfig()->getType()->getName(),
|
||||
'type_class' => get_class($form->getConfig()->getType()->getInnerType()),
|
||||
'synchronized' => $this->valueExporter->exportValue($form->isSynchronized()),
|
||||
'passed_options' => array(),
|
||||
'resolved_options' => array(),
|
||||
);
|
||||
|
||||
foreach ($form->getConfig()->getAttribute('data_collector/passed_options', array()) as $option => $value) {
|
||||
$data['passed_options'][$option] = $this->valueExporter->exportValue($value);
|
||||
}
|
||||
|
||||
foreach ($form->getConfig()->getOptions() as $option => $value) {
|
||||
$data['resolved_options'][$option] = $this->valueExporter->exportValue($value);
|
||||
}
|
||||
|
||||
ksort($data['passed_options']);
|
||||
ksort($data['resolved_options']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extractDefaultData(FormInterface $form)
|
||||
{
|
||||
$data = array(
|
||||
'default_data' => array(
|
||||
'norm' => $this->valueExporter->exportValue($form->getNormData()),
|
||||
),
|
||||
'submitted_data' => array(),
|
||||
);
|
||||
|
||||
if ($form->getData() !== $form->getNormData()) {
|
||||
$data['default_data']['model'] = $this->valueExporter->exportValue($form->getData());
|
||||
}
|
||||
|
||||
if ($form->getViewData() !== $form->getNormData()) {
|
||||
$data['default_data']['view'] = $this->valueExporter->exportValue($form->getViewData());
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extractSubmittedData(FormInterface $form)
|
||||
{
|
||||
$data = array(
|
||||
'submitted_data' => array(
|
||||
'norm' => $this->valueExporter->exportValue($form->getNormData()),
|
||||
),
|
||||
'errors' => array(),
|
||||
);
|
||||
|
||||
if ($form->getViewData() !== $form->getNormData()) {
|
||||
$data['submitted_data']['view'] = $this->valueExporter->exportValue($form->getViewData());
|
||||
}
|
||||
|
||||
if ($form->getData() !== $form->getNormData()) {
|
||||
$data['submitted_data']['model'] = $this->valueExporter->exportValue($form->getData());
|
||||
}
|
||||
|
||||
foreach ($form->getErrors() as $error) {
|
||||
$data['errors'][] = array(
|
||||
'message' => $error->getMessage(),
|
||||
);
|
||||
}
|
||||
|
||||
$data['synchronized'] = $this->valueExporter->exportValue($form->isSynchronized());
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extractViewVariables(FormView $view)
|
||||
{
|
||||
$data = array();
|
||||
|
||||
foreach ($view->vars as $varName => $value) {
|
||||
$data['view_vars'][$varName] = $this->valueExporter->exportValue($value);
|
||||
}
|
||||
|
||||
ksort($data['view_vars']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
|
||||
/**
|
||||
* Extracts arrays of information out of forms.
|
||||
*
|
||||
* @since 2.4
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface FormDataExtractorInterface
|
||||
{
|
||||
/**
|
||||
* Extracts the configuration data of a form.
|
||||
*
|
||||
* @param FormInterface $form The form
|
||||
*
|
||||
* @return array Information about the form's configuration
|
||||
*/
|
||||
public function extractConfiguration(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Extracts the default data of a form.
|
||||
*
|
||||
* @param FormInterface $form The form
|
||||
*
|
||||
* @return array Information about the form's default data
|
||||
*/
|
||||
public function extractDefaultData(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Extracts the submitted data of a form.
|
||||
*
|
||||
* @param FormInterface $form The form
|
||||
*
|
||||
* @return array Information about the form's submitted data
|
||||
*/
|
||||
public function extractSubmittedData(FormInterface $form);
|
||||
|
||||
/**
|
||||
* Extracts the view variables of a form.
|
||||
*
|
||||
* @param FormView $view The form view
|
||||
*
|
||||
* @return array Information about the view's variables
|
||||
*/
|
||||
public function extractViewVariables(FormView $view);
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector\Proxy;
|
||||
|
||||
use Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\ResolvedFormTypeInterface;
|
||||
|
||||
/**
|
||||
* Proxy that invokes a data collector when creating a form and its view.
|
||||
*
|
||||
* @since 2.4
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ResolvedTypeDataCollectorProxy implements ResolvedFormTypeInterface
|
||||
{
|
||||
/**
|
||||
* @var ResolvedFormTypeInterface
|
||||
*/
|
||||
private $proxiedType;
|
||||
|
||||
/**
|
||||
* @var FormDataCollectorInterface
|
||||
*/
|
||||
private $dataCollector;
|
||||
|
||||
public function __construct(ResolvedFormTypeInterface $proxiedType, FormDataCollectorInterface $dataCollector)
|
||||
{
|
||||
$this->proxiedType = $proxiedType;
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->proxiedType->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->proxiedType->getParent();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInnerType()
|
||||
{
|
||||
return $this->proxiedType->getInnerType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeExtensions()
|
||||
{
|
||||
return $this->proxiedType->getTypeExtensions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createBuilder(FormFactoryInterface $factory, $name, array $options = array())
|
||||
{
|
||||
$builder = $this->proxiedType->createBuilder($factory, $name, $options);
|
||||
|
||||
$builder->setAttribute('data_collector/passed_options', $options);
|
||||
$builder->setType($this);
|
||||
|
||||
return $builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createView(FormInterface $form, FormView $parent = null)
|
||||
{
|
||||
return $this->proxiedType->createView($form, $parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->proxiedType->buildForm($builder, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$this->proxiedType->buildView($view, $form, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function finishView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
$this->proxiedType->finishView($view, $form, $options);
|
||||
|
||||
// Remember which view belongs to which form instance, so that we can
|
||||
// get the collected data for a view when its form instance is not
|
||||
// available (e.g. CSRF token)
|
||||
$this->dataCollector->associateFormWithView($form, $view);
|
||||
|
||||
// Since the CSRF token is only present in the FormView tree, we also
|
||||
// need to check the FormView tree instead of calling isRoot() on the
|
||||
// FormInterface tree
|
||||
if (null === $view->parent) {
|
||||
$this->dataCollector->collectViewVariables($view);
|
||||
|
||||
// Re-assemble data, in case FormView instances were added, for
|
||||
// which no FormInterface instances were present (e.g. CSRF token).
|
||||
// Since finishView() is called after finishing the views of all
|
||||
// children, we can safely assume that information has been
|
||||
// collected about the complete form tree.
|
||||
$this->dataCollector->buildFinalFormTree($form, $view);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOptionsResolver()
|
||||
{
|
||||
return $this->proxiedType->getOptionsResolver();
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector\Proxy;
|
||||
|
||||
use Symfony\Component\Form\Exception;
|
||||
use Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface;
|
||||
use Symfony\Component\Form\FormTypeInterface;
|
||||
use Symfony\Component\Form\ResolvedFormTypeFactoryInterface;
|
||||
use Symfony\Component\Form\ResolvedFormTypeInterface;
|
||||
|
||||
/**
|
||||
* Proxy that wraps resolved types into {@link ResolvedTypeDataCollectorProxy}
|
||||
* instances.
|
||||
*
|
||||
* @since 2.4
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ResolvedTypeFactoryDataCollectorProxy implements ResolvedFormTypeFactoryInterface
|
||||
{
|
||||
/**
|
||||
* @var ResolvedFormTypeFactoryInterface
|
||||
*/
|
||||
private $proxiedFactory;
|
||||
|
||||
/**
|
||||
* @var FormDataCollectorInterface
|
||||
*/
|
||||
private $dataCollector;
|
||||
|
||||
public function __construct(ResolvedFormTypeFactoryInterface $proxiedFactory, FormDataCollectorInterface $dataCollector)
|
||||
{
|
||||
$this->proxiedFactory = $proxiedFactory;
|
||||
$this->dataCollector = $dataCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createResolvedType(FormTypeInterface $type, array $typeExtensions, ResolvedFormTypeInterface $parent = null)
|
||||
{
|
||||
return new ResolvedTypeDataCollectorProxy(
|
||||
$this->proxiedFactory->createResolvedType($type, $typeExtensions, $parent),
|
||||
$this->dataCollector
|
||||
);
|
||||
}
|
||||
}
|
@ -11,25 +11,28 @@
|
||||
|
||||
namespace Symfony\Component\Form\Extension\DataCollector\Type;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\AbstractTypeExtension;
|
||||
use Symfony\Component\Form\Extension\DataCollector\EventListener\DataCollectorListener;
|
||||
use Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* DataCollector Type Extension for collecting invalid Forms.
|
||||
* Type extension for collecting data of a form with this type.
|
||||
*
|
||||
* @since 2.4
|
||||
* @author Robert Schönthal <robert.schoenthal@gmail.com>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class DataCollectorTypeExtension extends AbstractTypeExtension
|
||||
{
|
||||
/**
|
||||
* @var EventSubscriberInterface
|
||||
* @var \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
||||
*/
|
||||
private $eventSubscriber;
|
||||
private $listener;
|
||||
|
||||
public function __construct(EventSubscriberInterface $eventSubscriber)
|
||||
public function __construct(FormDataCollectorInterface $dataCollector)
|
||||
{
|
||||
$this->eventSubscriber = $eventSubscriber;
|
||||
$this->listener = new DataCollectorListener($dataCollector);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,7 +40,7 @@ class DataCollectorTypeExtension extends AbstractTypeExtension
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder->addEventSubscriber($this->eventSubscriber);
|
||||
$builder->addEventSubscriber($this->listener);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,58 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Tests\Extension\DataCollector\Collector;
|
||||
|
||||
use Symfony\Component\Form\Extension\DataCollector\Collector\FormCollector;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
class FormCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testSubscribedEvents()
|
||||
{
|
||||
$events = FormCollector::getSubscribedEvents();
|
||||
|
||||
$this->assertInternalType('array', $events);
|
||||
$this->assertEquals(array(FormEvents::POST_SUBMIT => array('collectForm', -255)), $events);
|
||||
}
|
||||
|
||||
public function testCollect()
|
||||
{
|
||||
$form = $this->getMock('Symfony\Component\Form\Test\FormInterface');
|
||||
$subForm = $this->getMock('Symfony\Component\Form\Test\FormInterface');
|
||||
|
||||
$type = $this->getMock('Symfony\Component\Form\FormTypeInterface');
|
||||
$type->expects($this->atLeastOnce())->method('getName')->will($this->returnValue('fizz'));
|
||||
|
||||
$config = $this->getMock('Symfony\Component\Form\FormConfigInterface');
|
||||
$config->expects($this->atLeastOnce())->method('getType')->will($this->returnValue($type));
|
||||
|
||||
$form->expects($this->atLeastOnce())->method('all')->will($this->returnValue(array($subForm)));
|
||||
$form->expects($this->atLeastOnce())->method('isRoot')->will($this->returnValue(true));
|
||||
$form->expects($this->atLeastOnce())->method('getName')->will($this->returnValue('foo'));
|
||||
|
||||
$subForm->expects($this->atLeastOnce())->method('all')->will($this->returnValue(array()));
|
||||
$subForm->expects($this->atLeastOnce())->method('getErrors')->will($this->returnValue(array('foo')));
|
||||
$subForm->expects($this->atLeastOnce())->method('getRoot')->will($this->returnValue($form));
|
||||
$subForm->expects($this->atLeastOnce())->method('getConfig')->will($this->returnValue($config));
|
||||
$subForm->expects($this->atLeastOnce())->method('getPropertyPath')->will($this->returnValue('bar'));
|
||||
$subForm->expects($this->atLeastOnce())->method('getViewData')->will($this->returnValue('bazz'));
|
||||
|
||||
$event = new FormEvent($form, array());
|
||||
$c = new FormCollector();
|
||||
$c->collectForm($event);
|
||||
|
||||
$this->assertInternalType('array', $c->getData());
|
||||
$this->assertEquals(1, $c->getErrorCount());
|
||||
$this->assertEquals(array('foo' => array('bar' => array('value' => 'bazz', 'root' => 'foo', 'type' => 'fizz', 'name' => 'bar', 'errors' => array('foo')))), $c->getData());
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
namespace Symfony\Component\Form\Tests\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\Extension\DataCollector\DataCollectorExtension;
|
||||
|
||||
/**
|
||||
@ -25,14 +24,14 @@ class DataCollectorExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
private $extension;
|
||||
|
||||
/**
|
||||
* @var EventSubscriberInterface
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $eventSubscriber;
|
||||
private $dataCollector;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->eventSubscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface');
|
||||
$this->extension = new DataCollectorExtension($this->eventSubscriber);
|
||||
$this->dataCollector = $this->getMock('Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface');
|
||||
$this->extension = new DataCollectorExtension($this->dataCollector);
|
||||
}
|
||||
|
||||
public function testLoadTypeExtensions()
|
||||
@ -44,4 +43,3 @@ class DataCollectorExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertInstanceOf('Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension', array_shift($typeExtensions));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,465 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Tests\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\Form\Extension\DataCollector\FormDataCollector;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormView;
|
||||
|
||||
class FormDataCollectorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $dataExtractor;
|
||||
|
||||
/**
|
||||
* @var FormDataCollector
|
||||
*/
|
||||
private $dataCollector;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $factory;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $dataMapper;
|
||||
|
||||
/**
|
||||
* @var Form
|
||||
*/
|
||||
private $form;
|
||||
|
||||
/**
|
||||
* @var Form
|
||||
*/
|
||||
private $childForm;
|
||||
|
||||
/**
|
||||
* @var FormView
|
||||
*/
|
||||
private $view;
|
||||
|
||||
/**
|
||||
* @var FormView
|
||||
*/
|
||||
private $childView;
|
||||
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->dataExtractor = $this->getMock('Symfony\Component\Form\Extension\DataCollector\FormDataExtractorInterface');
|
||||
$this->dataCollector = new FormDataCollector($this->dataExtractor);
|
||||
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||
$this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface');
|
||||
$this->dataMapper = $this->getMock('Symfony\Component\Form\DataMapperInterface');
|
||||
$this->form = $this->createForm('name');
|
||||
$this->childForm = $this->createForm('child');
|
||||
$this->view = new FormView();
|
||||
$this->childView = new FormView();
|
||||
}
|
||||
|
||||
public function testBuildPreliminaryFormTree()
|
||||
{
|
||||
$this->form->add($this->childForm);
|
||||
|
||||
$this->dataExtractor->expects($this->at(0))
|
||||
->method('extractConfiguration')
|
||||
->with($this->form)
|
||||
->will($this->returnValue(array('config' => 'foo')));
|
||||
$this->dataExtractor->expects($this->at(1))
|
||||
->method('extractConfiguration')
|
||||
->with($this->childForm)
|
||||
->will($this->returnValue(array('config' => 'bar')));
|
||||
|
||||
$this->dataExtractor->expects($this->at(2))
|
||||
->method('extractDefaultData')
|
||||
->with($this->form)
|
||||
->will($this->returnValue(array('default_data' => 'foo')));
|
||||
$this->dataExtractor->expects($this->at(3))
|
||||
->method('extractDefaultData')
|
||||
->with($this->childForm)
|
||||
->will($this->returnValue(array('default_data' => 'bar')));
|
||||
|
||||
$this->dataExtractor->expects($this->at(4))
|
||||
->method('extractSubmittedData')
|
||||
->with($this->form)
|
||||
->will($this->returnValue(array('submitted_data' => 'foo')));
|
||||
$this->dataExtractor->expects($this->at(5))
|
||||
->method('extractSubmittedData')
|
||||
->with($this->childForm)
|
||||
->will($this->returnValue(array('submitted_data' => 'bar')));
|
||||
|
||||
$this->dataCollector->collectConfiguration($this->form);
|
||||
$this->dataCollector->collectDefaultData($this->form);
|
||||
$this->dataCollector->collectSubmittedData($this->form);
|
||||
$this->dataCollector->buildPreliminaryFormTree($this->form);
|
||||
|
||||
$this->assertSame(array(
|
||||
'forms' => array(
|
||||
'name' => array(
|
||||
'config' => 'foo',
|
||||
'default_data' => 'foo',
|
||||
'submitted_data' => 'foo',
|
||||
'children' => array(
|
||||
'child' => array(
|
||||
'config' => 'bar',
|
||||
'default_data' => 'bar',
|
||||
'submitted_data' => 'bar',
|
||||
'children' => array(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'nb_errors' => 0,
|
||||
), $this->dataCollector->getData());
|
||||
}
|
||||
|
||||
public function testBuildMultiplePreliminaryFormTrees()
|
||||
{
|
||||
$form1 = $this->createForm('form1');
|
||||
$form2 = $this->createForm('form2');
|
||||
|
||||
$this->dataExtractor->expects($this->at(0))
|
||||
->method('extractConfiguration')
|
||||
->with($form1)
|
||||
->will($this->returnValue(array('config' => 'foo')));
|
||||
$this->dataExtractor->expects($this->at(1))
|
||||
->method('extractConfiguration')
|
||||
->with($form2)
|
||||
->will($this->returnValue(array('config' => 'bar')));
|
||||
|
||||
$this->dataCollector->collectConfiguration($form1);
|
||||
$this->dataCollector->collectConfiguration($form2);
|
||||
$this->dataCollector->buildPreliminaryFormTree($form1);
|
||||
|
||||
$this->assertSame(array(
|
||||
'forms' => array(
|
||||
'form1' => array(
|
||||
'config' => 'foo',
|
||||
'children' => array(),
|
||||
),
|
||||
),
|
||||
'nb_errors' => 0,
|
||||
), $this->dataCollector->getData());
|
||||
|
||||
$this->dataCollector->buildPreliminaryFormTree($form2);
|
||||
|
||||
$this->assertSame(array(
|
||||
'forms' => array(
|
||||
'form1' => array(
|
||||
'config' => 'foo',
|
||||
'children' => array(),
|
||||
),
|
||||
'form2' => array(
|
||||
'config' => 'bar',
|
||||
'children' => array(),
|
||||
),
|
||||
),
|
||||
'nb_errors' => 0,
|
||||
), $this->dataCollector->getData());
|
||||
}
|
||||
|
||||
public function testBuildSamePreliminaryFormTreeMultipleTimes()
|
||||
{
|
||||
$this->dataExtractor->expects($this->at(0))
|
||||
->method('extractConfiguration')
|
||||
->with($this->form)
|
||||
->will($this->returnValue(array('config' => 'foo')));
|
||||
|
||||
$this->dataExtractor->expects($this->at(1))
|
||||
->method('extractDefaultData')
|
||||
->with($this->form)
|
||||
->will($this->returnValue(array('default_data' => 'foo')));
|
||||
|
||||
$this->dataCollector->collectConfiguration($this->form);
|
||||
$this->dataCollector->buildPreliminaryFormTree($this->form);
|
||||
|
||||
$this->assertSame(array(
|
||||
'forms' => array(
|
||||
'name' => array(
|
||||
'config' => 'foo',
|
||||
'children' => array(),
|
||||
),
|
||||
),
|
||||
'nb_errors' => 0,
|
||||
), $this->dataCollector->getData());
|
||||
|
||||
$this->dataCollector->collectDefaultData($this->form);
|
||||
$this->dataCollector->buildPreliminaryFormTree($this->form);
|
||||
|
||||
$this->assertSame(array(
|
||||
'forms' => array(
|
||||
'name' => array(
|
||||
'config' => 'foo',
|
||||
'default_data' => 'foo',
|
||||
'children' => array(),
|
||||
),
|
||||
),
|
||||
'nb_errors' => 0,
|
||||
), $this->dataCollector->getData());
|
||||
}
|
||||
|
||||
public function testBuildPreliminaryFormTreeWithoutCollectingAnyData()
|
||||
{
|
||||
$this->dataCollector->buildPreliminaryFormTree($this->form);
|
||||
|
||||
$this->assertSame(array(
|
||||
'forms' => array(
|
||||
'name' => array(
|
||||
'children' => array(),
|
||||
),
|
||||
),
|
||||
'nb_errors' => 0,
|
||||
), $this->dataCollector->getData());
|
||||
}
|
||||
|
||||
public function testBuildFinalFormTree()
|
||||
{
|
||||
$this->form->add($this->childForm);
|
||||
$this->view->children['child'] = $this->childView;
|
||||
|
||||
$this->dataExtractor->expects($this->at(0))
|
||||
->method('extractConfiguration')
|
||||
->with($this->form)
|
||||
->will($this->returnValue(array('config' => 'foo')));
|
||||
$this->dataExtractor->expects($this->at(1))
|
||||
->method('extractConfiguration')
|
||||
->with($this->childForm)
|
||||
->will($this->returnValue(array('config' => 'bar')));
|
||||
|
||||
$this->dataExtractor->expects($this->at(2))
|
||||
->method('extractDefaultData')
|
||||
->with($this->form)
|
||||
->will($this->returnValue(array('default_data' => 'foo')));
|
||||
$this->dataExtractor->expects($this->at(3))
|
||||
->method('extractDefaultData')
|
||||
->with($this->childForm)
|
||||
->will($this->returnValue(array('default_data' => 'bar')));
|
||||
|
||||
$this->dataExtractor->expects($this->at(4))
|
||||
->method('extractSubmittedData')
|
||||
->with($this->form)
|
||||
->will($this->returnValue(array('submitted_data' => 'foo')));
|
||||
$this->dataExtractor->expects($this->at(5))
|
||||
->method('extractSubmittedData')
|
||||
->with($this->childForm)
|
||||
->will($this->returnValue(array('submitted_data' => 'bar')));
|
||||
|
||||
$this->dataExtractor->expects($this->at(6))
|
||||
->method('extractViewVariables')
|
||||
->with($this->view)
|
||||
->will($this->returnValue(array('view_vars' => 'foo')));
|
||||
|
||||
$this->dataExtractor->expects($this->at(7))
|
||||
->method('extractViewVariables')
|
||||
->with($this->childView)
|
||||
->will($this->returnValue(array('view_vars' => 'bar')));
|
||||
|
||||
$this->dataCollector->collectConfiguration($this->form);
|
||||
$this->dataCollector->collectDefaultData($this->form);
|
||||
$this->dataCollector->collectSubmittedData($this->form);
|
||||
$this->dataCollector->collectViewVariables($this->view);
|
||||
$this->dataCollector->buildFinalFormTree($this->form, $this->view);
|
||||
|
||||
$this->assertSame(array(
|
||||
'forms' => array(
|
||||
'name' => array(
|
||||
'view_vars' => 'foo',
|
||||
'config' => 'foo',
|
||||
'default_data' => 'foo',
|
||||
'submitted_data' => 'foo',
|
||||
'children' => array(
|
||||
'child' => array(
|
||||
'view_vars' => 'bar',
|
||||
'config' => 'bar',
|
||||
'default_data' => 'bar',
|
||||
'submitted_data' => 'bar',
|
||||
'children' => array(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'nb_errors' => 0,
|
||||
), $this->dataCollector->getData());
|
||||
}
|
||||
|
||||
public function testFinalFormReliesOnFormViewStructure()
|
||||
{
|
||||
$this->form->add($this->createForm('first'));
|
||||
$this->form->add($this->createForm('second'));
|
||||
|
||||
$this->view->children['second'] = $this->childView;
|
||||
|
||||
$this->dataCollector->buildPreliminaryFormTree($this->form);
|
||||
|
||||
$this->assertSame(array(
|
||||
'forms' => array(
|
||||
'name' => array(
|
||||
'children' => array(
|
||||
'first' => array(
|
||||
'children' => array(),
|
||||
),
|
||||
'second' => array(
|
||||
'children' => array(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'nb_errors' => 0,
|
||||
), $this->dataCollector->getData());
|
||||
|
||||
$this->dataCollector->buildFinalFormTree($this->form, $this->view);
|
||||
|
||||
$this->assertSame(array(
|
||||
'forms' => array(
|
||||
'name' => array(
|
||||
'children' => array(
|
||||
// "first" not present in FormView
|
||||
'second' => array(
|
||||
'children' => array(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'nb_errors' => 0,
|
||||
), $this->dataCollector->getData());
|
||||
}
|
||||
|
||||
public function testChildViewsCanBeWithoutCorrespondingChildForms()
|
||||
{
|
||||
// don't add $this->childForm to $this->form!
|
||||
|
||||
$this->view->children['child'] = $this->childView;
|
||||
|
||||
$this->dataExtractor->expects($this->at(0))
|
||||
->method('extractConfiguration')
|
||||
->with($this->form)
|
||||
->will($this->returnValue(array('config' => 'foo')));
|
||||
$this->dataExtractor->expects($this->at(1))
|
||||
->method('extractConfiguration')
|
||||
->with($this->childForm)
|
||||
->will($this->returnValue(array('config' => 'bar')));
|
||||
|
||||
// explicitly call collectConfiguration(), since $this->childForm is not
|
||||
// contained in the form tree
|
||||
$this->dataCollector->collectConfiguration($this->form);
|
||||
$this->dataCollector->collectConfiguration($this->childForm);
|
||||
$this->dataCollector->buildFinalFormTree($this->form, $this->view);
|
||||
|
||||
$this->assertSame(array(
|
||||
'forms' => array(
|
||||
'name' => array(
|
||||
'config' => 'foo',
|
||||
'children' => array(
|
||||
'child' => array(
|
||||
// no "config" key
|
||||
'children' => array(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'nb_errors' => 0,
|
||||
), $this->dataCollector->getData());
|
||||
}
|
||||
|
||||
public function testChildViewsWithoutCorrespondingChildFormsMayBeExplicitlyAssociated()
|
||||
{
|
||||
// don't add $this->childForm to $this->form!
|
||||
|
||||
$this->view->children['child'] = $this->childView;
|
||||
|
||||
// but associate the two
|
||||
$this->dataCollector->associateFormWithView($this->childForm, $this->childView);
|
||||
|
||||
$this->dataExtractor->expects($this->at(0))
|
||||
->method('extractConfiguration')
|
||||
->with($this->form)
|
||||
->will($this->returnValue(array('config' => 'foo')));
|
||||
$this->dataExtractor->expects($this->at(1))
|
||||
->method('extractConfiguration')
|
||||
->with($this->childForm)
|
||||
->will($this->returnValue(array('config' => 'bar')));
|
||||
|
||||
// explicitly call collectConfiguration(), since $this->childForm is not
|
||||
// contained in the form tree
|
||||
$this->dataCollector->collectConfiguration($this->form);
|
||||
$this->dataCollector->collectConfiguration($this->childForm);
|
||||
$this->dataCollector->buildFinalFormTree($this->form, $this->view);
|
||||
|
||||
$this->assertSame(array(
|
||||
'forms' => array(
|
||||
'name' => array(
|
||||
'config' => 'foo',
|
||||
'children' => array(
|
||||
'child' => array(
|
||||
'config' => 'bar',
|
||||
'children' => array(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'nb_errors' => 0,
|
||||
), $this->dataCollector->getData());
|
||||
}
|
||||
|
||||
public function testCollectSubmittedDataCountsErrors()
|
||||
{
|
||||
$form1 = $this->createForm('form1');
|
||||
$childForm1 = $this->createForm('child1');
|
||||
$form2 = $this->createForm('form2');
|
||||
|
||||
$form1->add($childForm1);
|
||||
|
||||
$this->dataExtractor->expects($this->at(0))
|
||||
->method('extractSubmittedData')
|
||||
->with($form1)
|
||||
->will($this->returnValue(array('errors' => array('foo'))));
|
||||
$this->dataExtractor->expects($this->at(1))
|
||||
->method('extractSubmittedData')
|
||||
->with($childForm1)
|
||||
->will($this->returnValue(array('errors' => array('bar', 'bam'))));
|
||||
$this->dataExtractor->expects($this->at(2))
|
||||
->method('extractSubmittedData')
|
||||
->with($form2)
|
||||
->will($this->returnValue(array('errors' => array('baz'))));
|
||||
|
||||
$this->dataCollector->collectSubmittedData($form1);
|
||||
|
||||
$data = $this->dataCollector->getData();
|
||||
$this->assertSame(3, $data['nb_errors']);
|
||||
|
||||
$this->dataCollector->collectSubmittedData($form2);
|
||||
|
||||
$data = $this->dataCollector->getData();
|
||||
$this->assertSame(4, $data['nb_errors']);
|
||||
|
||||
}
|
||||
|
||||
private function createForm($name)
|
||||
{
|
||||
$builder = new FormBuilder($name, null, $this->dispatcher, $this->factory);
|
||||
$builder->setCompound(true);
|
||||
$builder->setDataMapper($this->dataMapper);
|
||||
|
||||
return $builder->getForm();
|
||||
}
|
||||
}
|
@ -0,0 +1,338 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Tests\Extension\DataCollector;
|
||||
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Symfony\Component\Form\Extension\DataCollector\FormDataExtractor;
|
||||
use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer;
|
||||
use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter;
|
||||
|
||||
class FormDataExtractorTest_SimpleValueExporter extends ValueExporter
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exportValue($value)
|
||||
{
|
||||
return var_export($value, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var FormDataExtractorTest_SimpleValueExporter
|
||||
*/
|
||||
private $valueExporter;
|
||||
|
||||
/**
|
||||
* @var FormDataExtractor
|
||||
*/
|
||||
private $dataExtractor;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $factory;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->valueExporter = new FormDataExtractorTest_SimpleValueExporter();
|
||||
$this->dataExtractor = new FormDataExtractor($this->valueExporter);
|
||||
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||
$this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface');
|
||||
}
|
||||
|
||||
public function testExtractConfiguration()
|
||||
{
|
||||
$type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface');
|
||||
$type->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue('type_name'));
|
||||
$type->expects($this->any())
|
||||
->method('getInnerType')
|
||||
->will($this->returnValue(new \stdClass()));
|
||||
|
||||
$form = $this->createBuilder('name')
|
||||
->setType($type)
|
||||
->getForm();
|
||||
|
||||
$this->assertSame(array(
|
||||
'type' => 'type_name',
|
||||
'type_class' => 'stdClass',
|
||||
'synchronized' => 'true',
|
||||
'passed_options' => array(),
|
||||
'resolved_options' => array(),
|
||||
), $this->dataExtractor->extractConfiguration($form));
|
||||
}
|
||||
|
||||
public function testExtractConfigurationSortsPassedOptions()
|
||||
{
|
||||
$type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface');
|
||||
$type->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue('type_name'));
|
||||
$type->expects($this->any())
|
||||
->method('getInnerType')
|
||||
->will($this->returnValue(new \stdClass()));
|
||||
|
||||
$options = array(
|
||||
'b' => 'foo',
|
||||
'a' => 'bar',
|
||||
'c' => 'baz',
|
||||
);
|
||||
|
||||
$form = $this->createBuilder('name')
|
||||
->setType($type)
|
||||
// passed options are stored in an attribute by
|
||||
// ResolvedTypeDataCollectorProxy
|
||||
->setAttribute('data_collector/passed_options', $options)
|
||||
->getForm();
|
||||
|
||||
$this->assertSame(array(
|
||||
'type' => 'type_name',
|
||||
'type_class' => 'stdClass',
|
||||
'synchronized' => 'true',
|
||||
'passed_options' => array(
|
||||
'a' => "'bar'",
|
||||
'b' => "'foo'",
|
||||
'c' => "'baz'",
|
||||
),
|
||||
'resolved_options' => array(),
|
||||
), $this->dataExtractor->extractConfiguration($form));
|
||||
}
|
||||
|
||||
public function testExtractConfigurationSortsResolvedOptions()
|
||||
{
|
||||
$type = $this->getMock('Symfony\Component\Form\ResolvedFormTypeInterface');
|
||||
$type->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue('type_name'));
|
||||
$type->expects($this->any())
|
||||
->method('getInnerType')
|
||||
->will($this->returnValue(new \stdClass()));
|
||||
|
||||
$options = array(
|
||||
'b' => 'foo',
|
||||
'a' => 'bar',
|
||||
'c' => 'baz',
|
||||
);
|
||||
|
||||
$form = $this->createBuilder('name', $options)
|
||||
->setType($type)
|
||||
->getForm();
|
||||
|
||||
$this->assertSame(array(
|
||||
'type' => 'type_name',
|
||||
'type_class' => 'stdClass',
|
||||
'synchronized' => 'true',
|
||||
'passed_options' => array(),
|
||||
'resolved_options' => array(
|
||||
'a' => "'bar'",
|
||||
'b' => "'foo'",
|
||||
'c' => "'baz'",
|
||||
),
|
||||
), $this->dataExtractor->extractConfiguration($form));
|
||||
}
|
||||
|
||||
public function testExtractDefaultData()
|
||||
{
|
||||
$form = $this->createBuilder('name')->getForm();
|
||||
|
||||
$form->setData('Foobar');
|
||||
|
||||
$this->assertSame(array(
|
||||
'default_data' => array(
|
||||
'norm' => "'Foobar'",
|
||||
),
|
||||
'submitted_data' => array(),
|
||||
), $this->dataExtractor->extractDefaultData($form));
|
||||
}
|
||||
|
||||
public function testExtractDefaultDataStoresModelDataIfDifferent()
|
||||
{
|
||||
$form = $this->createBuilder('name')
|
||||
->addModelTransformer(new FixedDataTransformer(array(
|
||||
'Foo' => 'Bar'
|
||||
)))
|
||||
->getForm();
|
||||
|
||||
$form->setData('Foo');
|
||||
|
||||
$this->assertSame(array(
|
||||
'default_data' => array(
|
||||
'norm' => "'Bar'",
|
||||
'model' => "'Foo'",
|
||||
),
|
||||
'submitted_data' => array(),
|
||||
), $this->dataExtractor->extractDefaultData($form));
|
||||
}
|
||||
|
||||
public function testExtractDefaultDataStoresViewDataIfDifferent()
|
||||
{
|
||||
$form = $this->createBuilder('name')
|
||||
->addViewTransformer(new FixedDataTransformer(array(
|
||||
'Foo' => 'Bar'
|
||||
)))
|
||||
->getForm();
|
||||
|
||||
$form->setData('Foo');
|
||||
|
||||
$this->assertSame(array(
|
||||
'default_data' => array(
|
||||
'norm' => "'Foo'",
|
||||
'view' => "'Bar'",
|
||||
),
|
||||
'submitted_data' => array(),
|
||||
), $this->dataExtractor->extractDefaultData($form));
|
||||
}
|
||||
|
||||
public function testExtractSubmittedData()
|
||||
{
|
||||
$form = $this->createBuilder('name')->getForm();
|
||||
|
||||
$form->submit('Foobar');
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Foobar'",
|
||||
),
|
||||
'errors' => array(),
|
||||
'synchronized' => 'true',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
}
|
||||
|
||||
public function testExtractSubmittedDataStoresModelDataIfDifferent()
|
||||
{
|
||||
$form = $this->createBuilder('name')
|
||||
->addModelTransformer(new FixedDataTransformer(array(
|
||||
'Foo' => 'Bar',
|
||||
'' => '',
|
||||
)))
|
||||
->getForm();
|
||||
|
||||
$form->submit('Bar');
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Bar'",
|
||||
'model' => "'Foo'",
|
||||
),
|
||||
'errors' => array(),
|
||||
'synchronized' => 'true',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
}
|
||||
|
||||
public function testExtractSubmittedDataStoresViewDataIfDifferent()
|
||||
{
|
||||
$form = $this->createBuilder('name')
|
||||
->addViewTransformer(new FixedDataTransformer(array(
|
||||
'Foo' => 'Bar',
|
||||
'' => '',
|
||||
)))
|
||||
->getForm();
|
||||
|
||||
$form->submit('Bar');
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Foo'",
|
||||
'view' => "'Bar'",
|
||||
),
|
||||
'errors' => array(),
|
||||
'synchronized' => 'true',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
}
|
||||
|
||||
public function testExtractSubmittedDataStoresErrors()
|
||||
{
|
||||
$form = $this->createBuilder('name')->getForm();
|
||||
|
||||
$form->submit('Foobar');
|
||||
$form->addError(new FormError('Invalid!'));
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Foobar'",
|
||||
),
|
||||
'errors' => array(
|
||||
array('message' => 'Invalid!'),
|
||||
),
|
||||
'synchronized' => 'true',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
}
|
||||
|
||||
public function testExtractSubmittedDataRemembersIfNonSynchronized()
|
||||
{
|
||||
$form = $this->createBuilder('name')
|
||||
->addModelTransformer(new CallbackTransformer(
|
||||
function () {},
|
||||
function () {
|
||||
throw new TransformationFailedException('Fail!');
|
||||
}
|
||||
))
|
||||
->getForm();
|
||||
|
||||
$form->submit('Foobar');
|
||||
|
||||
$this->assertSame(array(
|
||||
'submitted_data' => array(
|
||||
'norm' => "'Foobar'",
|
||||
'model' => 'NULL',
|
||||
),
|
||||
'errors' => array(),
|
||||
'synchronized' => 'false',
|
||||
), $this->dataExtractor->extractSubmittedData($form));
|
||||
}
|
||||
|
||||
public function testExtractViewVariables()
|
||||
{
|
||||
$view = new FormView();
|
||||
|
||||
$view->vars = array(
|
||||
'b' => 'foo',
|
||||
'a' => 'bar',
|
||||
'c' => 'baz',
|
||||
);
|
||||
|
||||
$this->assertSame(array(
|
||||
'view_vars' => array(
|
||||
'a' => "'bar'",
|
||||
'b' => "'foo'",
|
||||
'c' => "'baz'",
|
||||
),
|
||||
), $this->dataExtractor->extractViewVariables($view));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $options
|
||||
*
|
||||
* @return FormBuilder
|
||||
*/
|
||||
private function createBuilder($name, array $options = array())
|
||||
{
|
||||
return new FormBuilder($name, null, $this->dispatcher, $this->factory, $options);
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@
|
||||
|
||||
namespace Symfony\Component\Form\Tests\Extension\DataCollector\Type;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\Extension\DataCollector\Type\DataCollectorTypeExtension;
|
||||
|
||||
class DataCollectorTypeExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
@ -21,14 +20,14 @@ class DataCollectorTypeExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
private $extension;
|
||||
|
||||
/**
|
||||
* @var EventSubscriberInterface
|
||||
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $eventSubscriber;
|
||||
private $dataCollector;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->eventSubscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface');
|
||||
$this->extension = new DataCollectorTypeExtension($this->eventSubscriber);
|
||||
$this->dataCollector = $this->getMock('Symfony\Component\Form\Extension\DataCollector\FormDataCollectorInterface');
|
||||
$this->extension = new DataCollectorTypeExtension($this->dataCollector);
|
||||
}
|
||||
|
||||
public function testGetExtendedType()
|
||||
@ -39,7 +38,9 @@ class DataCollectorTypeExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
public function testBuildForm()
|
||||
{
|
||||
$builder = $this->getMock('Symfony\Component\Form\Test\FormBuilderInterface');
|
||||
$builder->expects($this->atLeastOnce())->method('addEventSubscriber')->with($this->eventSubscriber);
|
||||
$builder->expects($this->atLeastOnce())
|
||||
->method('addEventSubscriber')
|
||||
->with($this->isInstanceOf('Symfony\Component\Form\Extension\DataCollector\EventListener\DataCollectorListener'));
|
||||
|
||||
$this->extension->buildForm($builder, array());
|
||||
}
|
||||
|
Reference in New Issue
Block a user