EDIT: Breakdown
Two or more time is recorded to one day.
There are Date Entity and Time Entity.
Date Entity oneToMany: Time Entity.
Time Entity manyToOne: Date Entity.
The field of form is added by javascript.
So far, so good. It does work all right.
The problem is that a category is registered with time.
$form = $this->createForm(new DateType(), new Date());
Structure now looks like this.
DateFormType collection with TimeFormType
TimeFormType collection with CategoryFormType
TimeFormType collection with SubCategoryFormType
Category Entity oneToMany: Time Entity.
SubCategory Entity oneToMany: Time Entity.
Time Entity manyToOne: Category Entity.
Time Entity manyToOne: SubCategory Entity.
The bind of form is occurred error at controller, if this condition.
$form->bind($request); // => Error: Category, array given
Catchable Fatal Error: Argument 1 passed to ...\Entity\Time::setCategory() must be an instance of ...\Entity\Category, array given
Can "Category" by which relation was carried out be collected by "manyToOne"?
Is how to use "collection of forms" wrong?
The details are as follows.
I want to achieve what is recording time day by day.
Two or more time is recorded to one day.
The field of form is added by javascript.
An outline is the following although it is difficult to explain that it does not become long.
Entities:
Date, Category, SubCategory, Time
Date.orm
Date:
oneToMany:
times:
targetEntity: Time
mappedBy: date
cascade: ["persist", "remove"]
Time.orm
Time:
// ...
manyToOne:
date:
targetEntity: Date
inversedBy: times
joinColumn:
name: date_id
referencedColumnName: id
category:
targetEntity: Category
inversedBy: times
joinColumn:
name: category_id
referencedColumnName: id
sub_category:
targetEntity: SubCategory
inversedBy: times
joinColumn:
name: sub_id
referencedColumnName: id
Category.orm
Category:
// ...
oneToMany:
times:
targetEntity: Time
mappedBy: categoy
cascade: ["persist", "remove"]
manyToMany:
subs:
targetEntity: SubCategory
inversedBy: main
joinTable:
name: main_and_sub
joinColumns:
joinColum:
name: main_id
referencedColumnName: id
onDelete: CASCADE
inverseJoinColumns:
joinColum:
name: sub_id
referencedColumnName: id
onDelete: CASCADE
SubCategory.orm
SubCategory:
// ...
oneToMany:
times:
targetEntity: Time
mappedBy: sub_category
cascade: ["persist", "remove"]
manyToMany:
main:
targetEntity: Category
mappedBy: subs
TimeType
// ...
$builder->add('category', 'collection', array(
'type' => new CategoryType(),
'options' => array(
'required' => true,
),
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
'prototype' => true,
'prototype_name' => '__cat_prot__'
));
$builder->add('subcategory', 'collection', array(
'type' => new SubCategoryType(),
'options' => array(
'required' => false,
),
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
'prototype' => true,
'prototype_name' => '__cat_prot__'
));
$builder->add('time', 'time', array(
'input' => 'datetime',
'widget' => 'choice',
));
DateType
// ...
$builder->add('date', 'date', array(
'input' => 'datetime',
'widget' => 'choice',
));
$builder->add('times', 'collection', array(
'type' => new TimeType(),
'options' => array(
'required' => true,
),
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
'prototype' => true,
'prototype_name' => '__times_prot__'
));
View:
{{ form_widget(form.date) }}
<ul id="TIMES" data-prototype="
<div>
{{ form_widget(form.times.vars.prototype.children['category'].vars.prototype.name) | e }}
<label>Category 1</label>
</div>
<div>
{{ form_widget(form.times.vars.prototype.children['subcategory'].vars.prototype.name) | e }}
<label>Category 2</label>
</div>
<div>
{{ form_widget(form.times.vars.prototype.time) | e }}
</div>
<input type='button' value='Remove' class='remove' />">
</ul>
<input type="button" value="add times" class="add-form-field" />
javascript
var nums = 0;
$(document).on('click', '.add-form-field', function(e){
var field = $("#TIMES"),
data = field.attr("data-prototype"),
widget = data.replace(/__cat_prot__/g, 0).replace(/__times_prot__/g, nums),
newLi = $('<li></li>').html(widget);
newLi.appendTo("#TIMES");
nums += 1;
return false;
});
$(document).on('click', '#TIMES .remove', function(){
$(this).parent().remove();
});
Controller:
$_date = new Date();
$form = $this->createForm(new DateType(), $_date);
if ($request->getMethod() == 'POST') {
$form->bind($request); // => Error: Category, array given
$form->bind($request); // => Error: Category, array given
Catchable Fatal Error: Argument 1 passed to ...\Entity\Time::setCategory() must be an instance of ...\Entity\Category, array given
If in this condition, "$form->bind($request)" has a error "Category, array given".
Isn't it well-designed?
Is it better not to use form builder?
The date has times.
A time has a category and a sub category.
A time has to have a category.
A time has not to have a sub category. (not must, nullable)
form data
date[date][day]
date[times][0][category][0][name]
date[times][0][subcategory][0][name]
date[times][0][time][hour]
date[times][0][time][minute]
date[times][1][category][0][name]
date[times][1][subcategory][0][name]
date[times][1][time][hour]
date[times][1][time][minute]
I think "date[times][0][category][0][name]" is have to be as "date[times][0][category][name]".
I think the way of consolidation of the form is wrong.