TYPO3 generate two Models with one Form - typo3

I want to generate and persit multiple objects with only one form.
In My Form I have fields für Model Object House and fields for Model People, First 3 are for House and last 3 are for People.
Wenn I submit the form I want to create a Object for House and A Object for People. And the People should get the Relation to the house.
I tried this:
public function createAction(\Blubb\Blubb\Domain\Model\House $newHouse, \Blubb\Blubb\Domain\Model\People $newPeople) {
$this->houseRepository->add($newHouse);
$this->peopleRepository->add($newPeople);
$this->redirect('list');
}
Form looks like this:
how can I select which elements are for the House and which for the People?
Alternatively i tried set another form where i specified the name of Input like this:
name="house[housenr]" --> Then I got this error: Required argument "newHouse" is not set.

Remove params from your createAction() so you can use methods
$this->request->hasArgument('foo')
$this->request->getArgument('foo')
for custom collecting fields.

Related

In Typo3, with a model structure set up through the Extension Builder, how can I edit the values of child-models connected via 1:n to parent-model?

I am working on a project that uses Typo3 9.5.14 with Extension Builder 9.10.2. I have a "provider"-model with a 1:n-relationship to a "label"-model. It looks like this:
Extension Builder provider-label relation
My goal: Through a HTML Form I want to edit the values of the labels that are connected to a specific provider. At the moment my implemention looks as follows.
I have a controller in place that has the method "companyProfileSaveAction". The method works and allows me to set properties of the provider-Model. It looks like this:
public function companyProfileSaveAction(Provider $provider)
{
$persistenceManager = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class);
if ($provider->getUid()) {
$this->providerRepository->update($provider);
} else {
$this->providerRepository->add($provider);
}
$persistenceManager->persistAll();
$this->redirect('companyProfilePage', null, null, ['provider' => $provider, 'saved' => true]);
}
The here mentioned providerRepository extends \TYPO3\CMS\Extbase\Persistence\Repository. The functions "update" and "add" come from this inheritance.
Now I try to use this function to add or edit labels of the provider, which are connected via 1:n-relationship as mentioned above. I send data via html form. The data basically looks like this:
tx_my_extension[provider][labels][0][name]: label 1
tx_my_extension[[provider][labels][0][text]: description 1
tx_my_extension[[provider][labels][1][name]: label 2
tx_my_extension[[provider][labels][1][text]: description 2
tx_my_extension[[provider][labels][2][name]: label 3
tx__my_extension[[provider][labels][2][text]: description 3
This also works and the labels are created and connected to the provider. But now, as an example let's say I want to edit my labels like so:
tx_my_extension[provider][labels][0][name]: label 1 edited
tx_my_extension[[provider][labels][0][text]: description 1
tx_my_extension[[provider][labels][1][name]: label 2 edited
tx_my_extension[[provider][labels][1][text]: description 2
tx_my_extension[[provider][labels][2][name]: label 3 edited
tx__my_extension[[provider][labels][2][text]: description 3
When I send it like this, the old three labels are completely disconnected from my provider and three new labels are created. Over time, this fills my database with many disconnected labels, that don't serve any purpose. Instead of creating new labels every time I save my form, how can I edit the already existing labels?
It's been a while since I messed with these parts of TYPO3, but:
Have you tried adding the __identity virtual post field, e.g. tx_my_extension[provider][labels][0][__identity]: 123 where the value is the UID of the related object?
This should make the property mapper fetch this object before mapping values onto the properties, which in turn should mean that your root entity is still related to the children and children are marked as "dirty" which then gets persisted.

Thymeleaf form with multiple objects of the same class

Simple problem but can't find a solution: I have a Thymeleaf form used to add a new object, say of a Book class. It works perfectly well and I only need that particular form for adding new objects, not editing the existing ones. The question is: how can I put several objects of the Book class in the same single form? So, purely for convenience, instead of filling form for a single book and clicking Send you can fill form for several books at once and only then click Send, have them all inserted into the database (in whatever order) and also have the option to fill the form partially (e.g. the form has room for 5 books but it will also accept 1, 2, 3 or 4 and you can leave the rest blank).
Edit: I've tried passing a list of object to the Thymeleaf template with the form bound to the whole list and iteration inside, but Thymeleaf throws BingingResultError upon rendering it.
You need to use a wrapper object to realize what you want.
Something like:
public class BooksCreationDto {
private List<Book> books;
// default and parameterized constructor
public void addBook(Book book) {
this.books.add(book);
}
// getter and setter
}
Then you need to pass this object as a model attribute in your controller:
BooksCreationDto booksForm = new BooksCreationDto();
model.addAttribute("form", booksForm);
bind fields using index property
th:field="*{books[__${itemStat.index}__].title}"
and get back the result with
#ModelAttribute BooksCreationDto form
in your controller.
For a complete and detailled explaination visit: https://www.baeldung.com/thymeleaf-list

How can I get the Zend_Form object via the Zend_Form_Element child

I've built a Zend_Form_Decorator_Input class which extends Zend_Form_Decorator_Abstract, so that I could customize my form inputs -- works great. I ran into a problem in the decorate class, in trying to get the form name of the element, so as to built a unique id for each field (in case there are multiple forms with identical field names).
There is no method like this: Zend_Form_Element::getForm(); It seems Zend_Form_Decorator_Abstract doesn't have this ability either. Any ideas?
I don't think changing the id from the decorator is the right approach. At the time the decorator is called the element already has been rendered. Thus changing the id would have no effect to the source code. Additionally, as you already have pointed out, the relation between a form and its elements is unidirectional, i.e. (to my best knowledge) there is no direct way to access the form from the element.
So far the bad news.
The good news is, that there actually is a pretty easy solution to your problem: The Zend_Form option elementsBelongTo. It prevents that the same ID is assigned to two form elements that have the same name but belong to different forms:
$form1 = new Zend_Form(array('elementsBelongTo' => 'form1'));
$form1->addElement('Text', 'text1');
$form2 = new Zend_Form(array('elementsBelongTo' => 'form2'));
$form2->addElement('Text', 'text1');
Although both forms have a text field named 'text1', they have different ids: 'form1-text1' and 'form2-text1'. However, there is a major drawback to this: This also changes the name elements in such a way that they are in the format formname[elementname]. Therefore $this->getRequest()->getParam('formname') will return an associative array containing the form elements.

Parsing comma separated string into multiple database entries (eg. Tags)

I want to create a Symfony 2 Form for a Blog Post. One of the fields that I am wondering how might I implement is the tags field. Post has a many-to-many relationship with Tag. I want my Form to have 1 text box where users enter in a comma separated list of tags. Which will then be converted into multiple Tag's.
How should I implement it? Do I:
Have a tagsInput field (named differently from in the Entity where $tags should be an ArrayCollection)
On POST, I split the tags and create/get multiple tags. Then validate the tags (eg. MaxLength of 32)
I think you are already on the right way since I saw your other question about the form type. I will just comfort you with your choice.
A form type is probably the best way to go. With the form type, you will be able to display a single text field in your form. You will also be able to transform the data into a string for display to the user and to an ArrayCollection to set it in your model. For this, you use a DataTransformer exactly as you are doing in your other question.
With this technique, you don't need an extra field tagsInput in your model, you can have only a single field named tags that will an ArrayCollection. Having one field is possible because the you form type will transform that data from a string to an ArrayCollection.
For the validation, I think you could use the Choice validator. This validator directive seems to be able to validate that an array does not have less than a number of item and not more than another number. You can check the documentation for it here. You would use it like this:
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Post
{
/**
* #Assert\Choice(min = 1, max = 32)
*/
protected $tags;
}
If it does not work or not as intended, what you could do is to create a custom validator. This validator will then be put in your model for the tags field. This validator would validate the an array have a maximum number of element no greater than a fixed number (32 in your case).
Hope this helps.
Regards,
Matt

Pass a variable to a Symfony Form

I am building a web application using Symfony 1.4 and Doctrine for a school and I want to make a very simple form to add a course to a student.
The main problem I have is that in the drop down list I only want to show the courses in which the student is currently not enrolled.
I already have a function in the model (in Student.class.php) which returns all the courses in which the student is not enrolled but the problem is I don't know how to pass the student to the configure() of the form. I have tried several options like passing it with the constructor of the form to a global variable or a special set method but none of them have worked.
Is there any form to pass the student to the configure() method?
Thanks!
This should work for you...
In your action:
$this->form = new StudentCourseForm(array(), array('student_id' => $student_id));
In the form class:
$this->getOption('student_id');