Multiple entities in one form [Symfony 3] - php

Here are my tables:
Movie
id
name
year
description
Person
id
name
date_of_birth
Role
id
name
movie_id
person_id
So, Movie should contain, obviously, movies. Person consists of people (actors, directors or writers). Table Role connects with other 2 tables by having $movie and $person variable inside its class, with Many-to-One relationship for both of them, which in the end results in movie_id & person_id.
What I want to achieve is having a form, where I will be able to insert new movie, but also its relevant actors, director(s) & writer(s).
Final form result should contain these fields:
movie_name, movie_year, movie_description
dropdown with list of persons from DB (person.name)
dropdown with 3 possibilities - actor, director, writer (which would eventually be inserted to DB)
So, after filling out the first three inputs, user chooses a person from a dropdown list, and then, chooses appropriate role for chosen person. If I'm thinking right, I should be able to SELECT from Person (name) table , and to INSERT into Movie (name, year, descr.) & Role (name, movie_id, person_id) tables.
I'm aware that I should be using Form collections, but I don't understand how to use them, at least in this situation. I guess I should start by creating something like RoleModel class, but not sure whether I made a mistake before going into this, which makes this problem more complex than it should be.
My questions really is - how to make this work with Form collections?
I can provide any necessary code, if needed.

Symfony make it really easy, so first, you need to add bidirectional relation (One-to-many) between your person entity and your movie entity so you need to:
Add property called person to your movie entity:
Code:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Person", inversedBy="movies")
*/
protected $person;
Add property called movies to your person entity:
Code:
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Movie", mappedBy="person")
*/
protected $movies;
Now in your MovieType file (form configuration file) you can do it by:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('year')
->add('description', TextareaType::class)
->add('person', EntityType::class, array(
'class' => Person::class, //This existed usually in (AppBundle\Entity\Person)
'choice_label' => 'name'
));
}
This is a simple demonstration on how to use EntityType in Symfony.
Good luck.

You can solve this by embedding FormTypes, you can create a new one which is a composition of a PersonFormType and MovieFormType, and it would need an extra field for the role type (director, actor, etc)
Then, after submit, you can retrieve your objects something like this:
// SomeController's action
...
$data = $form->getData();
$person = $data['person'] // or whatever is the name of the field
You can find more information about embedding FormTypes in the docs: https://symfony.com/doc/cur...

Related

PHP Symfony FormType EntityCollection into Choices

I am beginner in PHP and Symfony and I started recently on a project.
We have 2 entities which are related by doctrine Annotation ManyToMany with JoinTable
Example: entity and datatable Room, entity and datatable Person and datatable RoomPerson (entity does not exist and just exist in doctrine annotation JoinTable).
The entity Room has a property Collection of Persons.
With a formType it is rendered in page as a select html element multiple (so we can choose several persons)
FormType with preEvent to fill list of possible persons:
$allowedPersons = $this->em->getRepository(Person::class)->findBy(***);
$form
->add('personList', EntityType::class, [
'class' => Person::class,
'choices' => $allowedPersons ,
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Room::class,
New request is to order the list of Persons.
So I made changes described in some tutorials to change declare the Entity RoomPerson et remove annotations ManyToMany for ManyToOne and OneToMany. I added the property "Order" on RoomPerson.
I Added the methods AddRoomPerson and RemoveRoomPerson to make updates of the list by the mapping of my FormType.
So my entity "Room" now only contains a property Collection of RoomPerson.
But I don't know how to use it in my FormType because I always want to show the list of Persons.
I tried a lot of things without success.
example:
$allowedPersons = $this->em->getRepository(Person::class)->findBy(***);
$form
->add('roomPersonList', EntityType::class, [
'class' => RoomPerson::class,
'choices' => $allowedPersons ,
'choice_value' => function(?RoomPerson $roomPerson) {
return $roomPerson? $roomPerson->getPerson()->getIdPerson() : '';
},
'choice_label' => function(?RoomPerson $roomPerson) {
return $roomPerson? $roomPerson->getPerson()->getName() : '';
},
I get following error:
Argument 1 passed to App\Form\RoomPersonType::App\Form{closure}() must be an instance of App\Entity\RoomPerson or null, instance of App\Entity\Person given, called in ..\vendor\symfony\form\ChoiceList\ArrayChoiceList.php on line 200"
So please I need help to transform my list of RoomPerson in my formtype and to make an update of this list in page.
When you are creating a form, the ->adds will make a reference to different columns you have defined in your App\Entity.
So, if you want to add something to your form this way, it has to be represented, again, in App\Entity.
Now, what you want to do, AFAIU, is fill a Room with people (class Person), and keep a list of those in the room (RoomPerson), and also a list of where every person has been (that's why it is a ManyToMany).
If instead you are looking at only where a Person has been the last time or at the moment, then it is a OneToMany relation, cause a room would be able to hold a bunch of people but any person would only be able to be at one place at a time (obviously).
Now I will simply take for granted you want to achieve the ManyToMany case. You should have all the columns needed in both Room and Person.
Now, RoomPerson should simply have a connection of those two and it's own id. You can add more things but for the purpose of this example let's just suppose we just need it to be a simple relating table so... your tables would look like (as an example):
<-M:N->
Room: <-M-> RoomPerson <-N-> Person:
- id (pk) - id (pk) - id (pk)
- space - room_id (fk) - gender
- style - person_id (fk) - room_usage
- purpose - age
- etc etc... - etc etc...
M being M number of posible objects on one side, N being N number of posible objects on the other side.
Once you have a DB like this, you can simply create a Form where you pass a Room, with every row you need in it.
If what you want is to select a room and have all the people displayed, you can, on the controller-side, get via queries a list of all the people that have been in each room, send it to the form, and then, on your template-side, play with html and js to dinamically show the correct list in each room.
I would need a more precise description to know exactly what you are trying to accomplish.

How to use a many to many relationship in Symfony2

I am working on a Symfony 2 based web app and struggling to create a many to many relationship between to entities:
Each Task entity should be assigned to any number of Categories. Of course each Category can be used by any number of Task entities. While a Task needs to know its Categories the Category class has no relationship back to the Tasks.
Following this tutorial I created:
class Task {
/**
* #ORM\Column(name="categories")
* #ORM\ManyToMany(targetEntity="MyBundle\Entity\Category")
*/
protected $categories;
public function __construct() {
$this->productVariations = new \Doctrine\Common\Collections\ArrayCollection();
...
}
}
Which results in the following SQL table:
CREATE TABLE task ... categories VARCHAR(255) NOT NULL, ...
A new Task is created use a form with the following type:
class TaskType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('categories', EntityType::class, array(
'class' => 'MyBundle:Category',
'choice_label' => 'name',
'multiple' => true,
'expanded' => true,
))
...
}
}
Submitting the form and persiting the new Taskinstance result in a SQL row with the following content in the categoriescolumn:
Doctrine\Common\Collections\ArrayCollection#000000007448a17a0000000048941f2f
What does this mean? Storing the referenced Category entities as array is not normal form but of course valid (in fact I would prefer this solution over adding a relationship table since in practice the number of categories per task will be quite small, thus a third table would be more overhead).
However I would assume that the array stores the IDs of the referenced objects/rows in some form. How would it be possible to store any number of references in a VARCHAR(255) column?
While submitting and persisting creates a DB entry, the Category references are not stored correctly. When I try to re-read this entry, the categories property is empty.
You have redundant column definition. Try to simply remove following line:
* #ORM\Column(name="categories")
Then you need to update you database schema.
This line creates a varchar field in task table which shouldn't be there. Many to many relations are implemented via separate table.
If you want to store ids in a single field instead of separate table, then you will handle it on your own. That's because it won't be able to be a relation anymore. You loose possibility to create foreign key or doing any join queries. Also filtering records will be harder to achieve.
I think you're missing something here.
Like you said, a Task can be associated to many Category(ies) AND the Category class DOES NOT need a relational mapping to the Task class.
All you need in your Task class is a property to reference your Categories.
I think what you're looking for is this.

Symfony Entity Type with values from another Entity

I'm still new in Symfony and I'm trying something (I think) common.
I've some entities: product, theme, event, with one to many and many to one connection; so I've other entities called products_in_theme, products_in_event.
A theme is a type of party, that use some kind of products (like colors, paper, etc).
When I made an event, I need all or some of that products, and I need to save that products in products_in_event.
With CraueFormFlowBundle I've created the form (step1: select theme and date, step2: select products).
The problem is that: I need to show in an EntityType "Product in Event" (in order to persist that), values from "Product In Theme".
The query doesn't work because is looking from "product in event" instead only "product in theme"
public function buildForm(FormBuilderInterface $builder, array $options)
{
$theme= $options['theme'];
$builder
->add('products', EntityType::class, array(
'class' => 'AppBundle\Entity\ProductInEvent',
'expanded'=>'true',
'multiple' => 'true',
'query_builder' => function (EntityRepository $er) use ($theme){
return $er->createQueryBuilder('product_in_event')
->from('AppBundle:ProductInTheme', 'product_in_theme')
->where('product_in_theme.theme = :theme')
->setParameter('theme', $theme);
}
));
}
The query is:
SELECT p0_.id AS id_0, p0_.price AS price_1, p0_.event_id AS event_id_2, p0_.product_id AS product_id_3
FROM product_in_event p0_, product_in_theme p1_
WHERE p1_.theme_id = 27;`
which returns 0 entries (because product_in_event is empty)
Update 2015/02/01
I've some entities:
Product:
-Id, Name, quantity, price
Event:
Id, Name, Products (mapped by product_id in product_in_event)
Product_In_Event:
Id, Event_id, Product_id (inversed by products in Event)
Theme
Id, Name, Products (mapped by theme_id)
Product_in_theme:
Id, Product_id, Theme_id (inversed by products in theme)
It's a common connection: A product in event have some FK related to Event and Product.
You can view the situation like: Categories and Products. When you buy something, the "product_in_category" became "product_in_order"
If I understood correctly, you want to query a list of ProductInThemes and attach some of those elements to your products attribute in Events (which is supposed to be a list of ProductInEvents). This is definitely impossible, since those two kind of entities are not the same.
I think your model is not compatible with what you are trying to achieve. If ProductInTheme and ProductInEvent are both representation of the same kind of items, but attached with a different entity type each, why don't you create a Product entity, create many-to-many relationships between Products and Themes and between Products and Events, and then manipulate Products in your query_builder? You could still filter those Products in the query_builder to match what you want based on Events and Themes.
TL;DR: Except if I misunderstood your problem, that's a conception problem, not an implementation problem. Try reworking your model to fit your needs a bit better.

Fetch a Model's associations in Laravel to simplify creation/update

I have a Movie model with the following associations (belongsToMany):
Actor
Country
Genre
...
When a form is posted, I have this data (skipping a lot of details here):
'actors' => array(
'Cary Grant',
'Grace Kelly',
...
),
'genres' => array(
'Drama',
...
),
...
I'd like my update()/store() controller function to easily associate these Models.
An actor with the name 'Cary Grant' may or may not exist and may or may not be already associated with the movie I'm editing. Also I could remove him from this movie, so I'd need to remove the association. Same with Genre and everything else.
So I thought I'd do a BaseModel and do all of this only once in there, like this:
1. get the Movie Model's defined associations.
2. check if POST data contains those associations.
3. for each of them, check if they exist (if not create them) and return an array of ids. the column I'm checking is 'name', but it could be configurable.
4. sync() those ids.
For now, I don't need to add more stuff to the related model from the movie form (ex. an actor's birthdate).
I'm stuck at n.1 ($movie->getRelations() only works for existing movies) and in general I'm not sure if this is the right approach. Hints?
use direct relations: $model->actors
http://laravel.com/docs/4.2/eloquent#relationships
All sync() stuff will be done by ORM, so the most complex thing you should do is n.3. You must create diff between $model->actors and $_POST['actors'] and understand what actors you need to insert (create new actors).
I've met the same problem and this is what I do in my project:
Instead of retrieving all defined relations of the models, I white-listing relations that can be updated by adding a static member
Movie::$editableRelations = ['actors', 'genres', 'countries'];
Loop through the post data, and match with the $editableRelations arrays. If the data of a relation exists, following below steps, otherwise we simply do not touch this relation.
Step 3 and step 4 are the same as yours.

One-to-one association in form?

In symfony 2.0, how to create a drop down list using one-to-one association in form? Can you guys put good example please?
I will try to answer your question the way I understand it. Let's say I have a Faculty object bound to a single University object. So in the form used to create or edit a faculty, I display a combo box of all the university in the database and the user choose one among them. There is one special Symfony field type that does exactly this: the entity type. Below is the code of the buildForm method that I use in my FacultyType object used to create the faculty form:
// Application\AcmeBundle\Form\Type\FacultyType
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name');
$builder->add('university', 'entity', array(
// The class of the entity used as a combo box item
'class' => 'AcmeBundle:University',
// The property of the entity displaying the entity as text
'property' => 'name',
// The query builder used to populate the combo box, accepts
// a QueryBuilder object or a \Closure like below
'query_builder' => function(EntityRepository $repository) {
// This will return a query builder selecting all universities
return $repository->createQueryBuilder('u');
}
));
}
Note: There are other properties that can be set for the entity field type, I invite you to take a look at this page for more information on it.
Rendered, this will show a combo box with all the universities I have set in the database. When the user save the form, the university chose is assigned to the faculty object bound to the form via a setter. You could probably render a drop-down list instead of a combo box. If you need to select multiple entities, the 'multiple' option of the field type entity could be useful.
This being said, the example I showed is not a One-to-One relation but rather a Many-to-One for the Faculty object and a One-to-Many for the University object. A One-to-One relation would be something more like a relation where a University has a unique Address. In this case, a combo box wouldn't be useful since the university can only have one adress so a sub-form would be more appropriate. If it has many adresses, then it becomes a One-to-Many relation like the relation between the university and its faculties.
Not sure if this will answer your question correctly but I hope it will lead you to a final solution.
Regards,
Matt
You need to use the entity field type in Symfony2. A good example is found at http://symfony.com/doc/current/reference/forms/types/entity.html

Categories