Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Binding Forms to Objects: data_class

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

Open GenusFormType and find the configureOptions() method. Add $resolver->setDefaults(). Pass that an array with a single key called data_class set to AppBundle\Entity\Genus:

... lines 1 - 8
class GenusFormType extends AbstractType
{
... lines 11 - 19
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Genus'
]);
}
}

Do nothing else: refresh to re-submit the form.

Tip

You can also use a newer syntax in PHP for this:

'data_class' => Genus::class

Most editors will auto-complete this for you!

Boom! Now we have a brand-new Genus object that's just waiting to be saved. Thanks to the data_class option, the form creates a new Genus object behind the scenes. And then it sets the data on it.

Earlier, when we got back an associative array, these field names - name, speciesCount and funFact – could have been anything:

... lines 1 - 8
class GenusFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('speciesCount')
->add('funFact')
;
}
... lines 19 - 25
}

But as soon as you bind your form to a class, name, speciesCount and funFact need to match property names inside of your class:

... lines 1 - 11
class Genus
{
... lines 14 - 23
private $name;
... lines 25 - 34
private $speciesCount;
... lines 36 - 39
private $funFact;
... lines 41 - 132
}

Actually, that's kind of a lie. These properties are private, so the form component can't set them directly. In reality, it guesses a setter function for each field and call that: setName(), setSpeciesCount() and setFunFact():

... lines 1 - 11
class Genus
{
... lines 14 - 67
public function setName($name)
{
$this->name = $name;
}
... lines 72 - 90
public function setSpeciesCount($speciesCount)
{
$this->speciesCount = $speciesCount;
}
... lines 95 - 100
public function setFunFact($funFact)
{
$this->funFact = $funFact;
}
... lines 105 - 132
}

Technically, you could add a form field call outOnAMagicalJourney as long as you had a public method in your class called setOutOnAMagicalJourney().

Form Field Guessing!

Head back to your browser, highlight the URL and hit enter. This just made a GET request, which skipped form processing and just rendered the template.

Let's add a few more field we need: like subFamily:

... lines 1 - 8
class GenusFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('subFamily')
->add('speciesCount')
->add('funFact')
... lines 18 - 19
;
}
... lines 22 - 28
}

Hey, we're even getting auto-complete now: PhpStorm knows Genus has a subFamily property!

Also add isPublished - that should eventually be a checkbox - and firstDiscoveredAt - that will need to be some sort of date field:

... lines 1 - 8
class GenusFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('subFamily')
->add('speciesCount')
->add('funFact')
->add('isPublished')
->add('firstDiscoveredAt')
;
}
... lines 22 - 28
}

Cool, try it out!

Huge error!

Catchable Fatal Error: Object of class SubFamily could not be converted to string

Okay: that's weird. What's going on?

Until now, it looked like Symfony renders every field as an input text field by default. But that's not true! There's a lot more coolness going on behind the scenes!

In reality, the form system looks at each field and tries to guess what type of field it should be. For example, for subFamily, it sees that this is a ManyToOne relationship to SubFamily:

... lines 1 - 11
class Genus
{
... lines 14 - 25
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\SubFamily")
* @ORM\JoinColumn(nullable=false)
*/
private $subFamily;
... lines 31 - 132
}

So, it tries to render this as a select drop-down of sub families. That's amazing, because it's exactly what we want.

But, it needs to be able to turn a SubFamily object into a string so it can render the text for each option in the select. That's the source of the error.

To help it, add a public function __toString() to the SubFamily class:

... lines 1 - 10
class SubFamily
{
... lines 13 - 39
public function __toString()
{
return $this->getName();
}
}

Refresh again!

Look at this! A free drop-down with almost no work. It also noticed that isPublished should be a checkbox because that's a boolean field in Doctrine:

... lines 1 - 11
class Genus
{
... lines 14 - 41
/**
* @ORM\Column(type="boolean")
*/
private $isPublished = true;
/**
* @ORM\Column(type="date")
*/
private $firstDiscoveredAt;
... lines 51 - 132
}

And since firstDiscoveredAt is a date, it rendered it with year-month-day drop-down boxes. Now, those three boxes are totally ugly and we'll fix it later, but isn't it cool that it's guessing the right field types?

Fill out the form again with super-realistic data and submit. Woh! One more error:

Neither the property isPublished nor one of the methods getIsPublished() exist and have public access in class Genus

Remember how every form field needs a setter function on your class? Like name and setName()? Every field also needs a getter function - like getIsPublished() or one of these other variations.

This was my bad: when I set this up, I added an isPublished property, a setIsPublished() method, but no getter! I'll use the "Code"->"Generate" menu - or command+N - to generate that getter:

... lines 1 - 11
class Genus
{
... lines 14 - 115
public function getIsPublished()
{
return $this->isPublished;
}
... lines 120 - 137
}

Refresh! It dumps the Genus object of course, but check out the subFamily field! It's not the SubFamily ID - the form field took the submitted ID, queried the database for the SubFamily object and set that on the property. That's HUGE.

We're ready to save this!

Leave a comment!

75
Login or Register to join the conversation
Jelle S. Avatar
Jelle S. Avatar Jelle S. | posted 5 years ago

Awesome tutorials, well worth the subscription!

What can I do against this error?

"Entities passed to the choice field must be managed. Maybe persist them in the entity manager?"

1 Reply

Hey Jelle S.!

Ah, super glad you're happy! Well, happy except for this error :). And it is a strange error! What does the code look like for your ChoiceType::class field? Are you possibly passing it a choices option explicitly? And if so, is it possible that some of the objects you're passing haven't been saved to the database yet?

Let me know - I'm sure we can debug it!

Cheers!

Reply
Jelle S. Avatar

Thanks, but I`ve found the solution in the controller I tried pass extra data about the object which was not needed and caused the error

Reply

Sweet - good debugging!

Reply
Gianluca M. Avatar
Gianluca M. Avatar Gianluca M. | posted 3 years ago

How can I create form fields relation entity?
I have relation invoice entity and detail entity, I would create a form type with fields of invoice and detail, please can you help me?
Thanks

Reply

Hey Gianluca M.

You can embed forms. If you create a DetailEntity form, then you would be able to embed it into the InvoiceEntity form. You can see an example here: https://symfony.com/doc/cur...

Another approach is to use a DTO to map all the fields you want to have in your form. Here is an example of how to do it: https://symfonycasts.com/sc...

Cheers!

Reply
Default user avatar
Default user avatar Maksym Minenko | posted 5 years ago

Why don't you use doctrine:generate:entities instead of Phpstorm autocompletion?

Reply

Hey Maksym,

That's a good tool and definitely in some cases you can prefer to use it instead, but it *always* generates both setters and getters. However, for example, some entities could not have setters, or probably in some cases you don't need both setters /getters. So, in some cases, better to add them manually instead of remove unnecessary methods after this tool.

IMO, in PhpStorm I can add setters/getters quicker, i.e. I don't need to switch to the console and back to the IDE :p

Cheers!

Reply
Default user avatar

HI! This is an amazing course!

I have a question, what about form binding against an object with oneToMany relationship?

I have an Organization Entity that haves a telephone column. The column is related to a table of phones with area code, country code and FK of the organization.

When i print the entire form just for a test, i have an error saying that the columns of the Phones table doesnt exist. Should i create a new Object Phone before the createForm function, and then pass it as an argument? I dont know if in that case the phone will be related to the Organization.

Thank you!

Reply

Hey Fran!

When your entity has a relationship to other entity, in your FormType, you have to add that field as an EntityType, so you will see a menu with a list of phones. You can read more about EntityType's here: http://symfony.com/doc/curr...

Have a nice day!

Reply
Default user avatar

Hey, thank you for the reply. But if I have to just print 3 number field types that belongs to other tables I have to use the EntityType too? What I need to do is that the fields phone - area code and country code get saved in the Phone number's column, with the FK of the organization (I'm not the owner of the database so unfortunately I can´t change that)

Reply

Oh, so you are not using Doctrine's ORM ?
In that case you would have to do it manually, add 3 "not mapped" fields to you Form, and create a new Phone instance, setting up those values manually.

You may find these links helpful:
Form mapped option: http://symfony.com/doc/curr...
Form without data class: https://symfony.com/doc/cur...

Cheers!

Reply
Default user avatar

Sorry, im not explaining it well.
I'm using Doctrine´s ORM but i don´t need to fill a select box with number's from another Entity, i just need to display 3 input´s (Area Code - Country code - Phone Number) that will save data in another table (Phone's table) The Organization has a relation oneToMany with the Phone's Entity (with Doctrine Annotation)
These 3 fields are the only that not belong to my class related to the form, so i don't know how to show them properly.

Thank you so much for your replies and your time!

Reply

Yo Fran!

Ah, now I think I understand :). There are sort of 2 answers:

1) I don't love the way your relationship is setup, but you may have a good reason :). With this relation, it means that one Organization can have many Phone numbers. Is that correct? It's even a bit more confusing because it seems like you want to create a form where the user is entering just one Phone number, not many. But, sometimes this is exactly what you want :).

2) If this is exactly what you want, then here is what you should do:

A) Add these 2 new methods to your Organization entity:


public function getMainPhone()
{
    return $this->phones->first();
}

public function setMainPhone(Phone $phone)
{
    if ($this->phones->contains($phone)) {
        return;
    }

    $this->phones[] = $phone;
    // make sure to set the owning side of the relation
    $phone->setOrganization($this);
}

B) Create a PhoneFormType class with the fields you need on it: areaCode, countryCode, phoneNumber (make sure to set the data_class to the Phone entity)

C) In OrganizationFormType (or however you call it), add a new mainPhone field:


$builder
    // ...
    ->add('mainPhone', PhoneFormType::class)

That should be it! You've now tricked the form system into thinking that your Organization has one Phone (mainPhone). When you fill out the 3 fields, it will create a new Phone and call setMainPhone(). It will even work if you edit an organization.

Let us know if this helps!

Cheers!

Reply
Default user avatar

Yes, thank you! I know is a little bit weird, im trying to convince my boss that we can change that about the Phone's list.
I have one last question! What about validation inside PhoneFormType? When i try to apply the Validate() annotation for embedded forms, y have an error saying:
"Expected argument of type "AppBundle\Entity\Phone", "array" given
I think that's because the 3 fields (area code, country code and phone) are passing as an array? Im mapping the PhoneFormType just like the other forms, with the data_class

Thank you so much for your help!

Reply

Yo Fran!

Hmmm. Do you mean the @Valid annotation? Can you post a screenshot of the full stacktrace... I'm not sure exactly where that error is coming from. And also post your 2 form classes if you can. And you do absolutely have data_class => Phone::class inside PhoneFormType, right? I'm sure it's some small details we've missed!

Cheers!

Reply
Default user avatar

Fortunately I was able to change the structure of the phone table, but I have the same problem with another embedded form, it's exactly the same error. Sorry if the explanation is very long, but I have not been able to solve it for many hours! Thank you so much for your help anyway!

I have a user form related to an entity:
Https://puu.sh/xbFKZ/e24d7b...
The entity: https://puu.sh/xbGhL/5049e4...
The user is related to the Organization:
Https://puu.sh/xbGlN/1f57a6...
Reverse relation: https://puu.sh/xbGmO/e0cb8c...

In this way an organization has a contact user.

In the new organization view/controller I have a form:
Https://puu.sh/xbGJh/854eb7... (and is linked with data_class
The form is nested to the UserOrganizationContact form, so that an organization can be registered along with the contact user:
Https://puu.sh/xbGR0/efd928...

I get this form and send it to twig, which shows it correctly. The only problem is when I want to validate it, otherwise I correctly create the contact entity!
Https://puu.sh/xbH9m/da3173...

Finally, when I apply the Valid () annotation in Entity / Organization, on the contact relation column, the following error occurs, only in the submit form:
Https://puu.sh/xbHpR/fd0399...

If I delete the Valid annotation, it works correctly and creates the Organizationcontact entity:
Https://puu.sh/xbHsd/dc399d...

Thank you for your time, this courses are helping me a lot!

Reply

Hmmm, it's very weird. The error is actually coming from the UniqueEntity validator that the PUGMultiUserBundle provides. And it's quite confusing. Above your entity, you have @UniqueEntity(fields={"email"}...). But, looking at the validator, for some reason, it looks like it expects fields to be a string only. Look, it's clearly concatenating your "fields" like a string: https://github.com/PUGX/PUGXMultiUserBundle/blob/226a5adf3f23f9820cbe038f351e59fe2e268d7b/Validator/Constraints/UniqueEntityValidator.php#L34. I don't know this bundle, but this looks like a possible bug to me!

Try this: just do @UniqueEntity(fields="email"...). So, just make fields be a string. I think this is allowed, and it may fix the issue.

Cheers!

Reply
Default user avatar

Yes, thank you for your help!

Reply
Default user avatar
Default user avatar Mohammad Althayabeh | posted 5 years ago

Amazing tutorial!

For some reason subFamily field does not render as select type, instead it is normal text field. Am I missing something?

Reply

Hey Mohammad,

Thanks! Hm, that's weird. Did you specify any type as the second argument for subFamily field ? Or, do you have only:

$builder
->add('subFamily')
// other fields...

And how does your Genus::$subFamily ORM definition looks like?

Cheers!

Reply
Default user avatar
Default user avatar Mohammad Althayabeh | Victor | posted 5 years ago

Hi Victor,
No, I have only $builder->add('name')
->add('subFamily')
->add('speciesCount')
...

and the ORM looks like this:

/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\SubFamily")
* @ORM\Column(nullable=false)
*/
private $subFamily;

Thanks

Reply

Hey Mohammad Althayabeh

Did you bind your Form to an entity class? Something like this:


// YourFormType class
...

public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\Genus'
        ]);
    }

If you did, could you show me the code of you Form, please? So, I can help you debugging

Cheers!

Reply
Default user avatar
Default user avatar Mohammad Althayabeh | MolloKhan | posted 5 years ago | edited

Yes I did. Below the class code of the formType


class GenusFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name')
                ->add('subFamily')
                ->add('speciesCount')
                ->add('funFact')
                ->add('isPublished')
                ->add('firstDiscoveredAt');
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
           'data_class'=>'AppBundle\Entity\Genus'
        ]);

    }

    public function getBlockPrefix()
    {
        return 'app_bundle_genus_form_type';
    }
}
Reply

This is weird indeed, makes me wonder a few fundamental things

Did you update the DataBase schema ? $ php bin/console doctrine:schema:update --force
Did you change anything about how Doctrine reads Entity metadata?
What happen if you define the second argument as EntityType?


$builder->add('name')
                ->add('subFamily', EntityType::class)
Reply
Default user avatar
Default user avatar Mohammad Althayabeh | MolloKhan | posted 5 years ago | edited

I did remove the the whole database and recreated it. I have not changed anything about Doctrine settings. I wonder if this is related to the version of Symfony, since I am using the 3.3

if I add`
$builder->add('name')

            ->add('subFamily', EntityType::class)```

then I get error The required option "class" is missing. on the mentioned line of the following method

 
public function newAction(Request $resquest)
{        
$form = $this->createForm(GenusFormType::class); <--
...
}
Reply

Oh, that error is my fault, you need to specify which class is related to.


$builder->add('subFamily', EntityType::class, array(
    'class' => SubFamily::class,
     ...
));

There are other useful options you can define, if you want to know more, you can read this section of the documentation http://symfony.com/doc/current/reference/forms/types/entity.html

Cheers!

Reply
Default user avatar
Default user avatar Mohammad Althayabeh | MolloKhan | posted 5 years ago | edited

Thanks MolloKhan
The following actually solved the issue, But I wonder why would I need to do that and it worked without specifying the class in the video.


$builder->add('subFamily', EntityType::class, array(
    'class' => SubFamily::class,
     ...
));
Reply

Yeah, it's strange, maybe the cache is messing up something? or, I'm not sure about this but you may need to have a setter for that field "setSubFamily()"

Reply
Default user avatar
Default user avatar Daan Hage | posted 5 years ago

He Ryan,

BEcause of this clip I got a bit further with the fixtures load. However I now get the following error:

[Doctrine\DBAL\Exception\InvalidFieldNameException]
An exception occurred while executing 'INSERT INTO genus (name, species_count, fun_fact, is_published, first_dis
covered_at, sub_family_id) VALUES (?, ?, ?, ?, ?, ?)' with params ["Eumetopias", 807, "Recusandae asperiores acc
usamus nihil repellat vero omnis voluptates.", 1, "1854-01-29", 37]:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'sub_family_id' in 'field list'

Do you know why this happens?

Reply

Yo Daan!

My answer on your other comment will address this too, fortunately :). When you copy the relationship code for the subFamily into your Genus entity, your migrations will suddenly want to generate a sub_family_id column. Sorry for sneaking that in!

Cheers!

Reply
Default user avatar

He Ryan,

Thanks for your assistance. It helped alot. I think it had something to do with adding Assert\NotBlank().
the -vvv tip helped alot too!

Reply
Default user avatar
Default user avatar Boban Acimovic | posted 5 years ago

'data_class' could be set to AppBundle\Entity\Genus::class also.

Reply

Hey Boban,

Yes, nice tip! This is a new feature for PHP 5.5 and it's a very nice to have an autocomplete for FQCN.

Cheers!

Reply
Default user avatar
Default user avatar Raphael Schubert | posted 5 years ago

Hello there, i just cant configure symfony to use moneytype with BRL and grouping... It knows its R$ (BRL) but needs to format inputed number as 000,000,000.00 but the correct is 000.000.000,00

how can i solve this? Thanks!!!

Reply

Hey Raphael!

Hmm, that's very interesting. The MoneyType is supposed to look up the number format by looking at the currency that you pass as an option. It's possible that the issue is because you don't have the "intl" PHP extension installed. Can you see if you see "intl" in the list, when you run this command?


php -m

This command displays the modules installed in your PHP :).

Cheers!

Reply
Default user avatar
Default user avatar Raphael Schubert | weaverryan | posted 5 years ago

Hello Weaverryan!!! Thanks... i do not have it installed... i`ll provide and test... will post here when finishe...

Reply
Default user avatar
Default user avatar Moises Cano | posted 5 years ago

I am binding my form to an entity that will return more than one row. When I look at the debug tool my entity query has a 'LIMIT 1' clause. How do I remove this?

Reply

Hey Moises Cano!

Can you post some of your code? I don't fully understand what you mean by "binding my form to an entity that will return more than one row"? If you want to edit a product, for example, you are editing just one product at a time. Are you trying to somehow edit more than one object on the same form?

Cheers!

Reply
Default user avatar
Default user avatar Moises Cano | weaverryan | posted 5 years ago | edited

I'm going to ask it a different way. Here is my FormType code.


namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ExceptionFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('usrid')
            ->add('trcode')
            ->add('cola', NumberType::class,
                ['scale' => 2])
            ->add('colb', NumberType::class,
                ['scale' => 2]
            )
            ->add('colc', NumberType::class,
                ['scale' => 2])
            ->add('cold')
            ->add('fringe', ChoiceType::class,
                array(
                    'choices' => array(
                        'No' => 0,
                        'Yes' => 1
                    )
                ));

    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\Exception'
        ]);
    }

    public function getBlockPrefix()
    {
        return 'app_bundle_exception_form_type';
    }

I want to fetch more than one row. When I look at the debug tool i see this sql.


SELECT 
  t0.usrid AS usrid_1, 
  t0.trcode AS trcode_2, 
  t0.cola AS cola_3, 
  t0.colb AS colb_4, 
  t0.colc AS colc_5, 
  t0.cold AS cold_6, 
  t0.dept AS dept_7, 
  t0.fringe AS fringe_8, 
  t0.seq AS seq_9, 
  t0.id AS id_10 
FROM 
  exception t0 
WHERE 
  t0.usrid = ? 
LIMIT 
  1

How do i remove 'LIMIT 1' ?

Reply

Hey Moises,

Hm, I wonder how your exceptions() method looks like. Does it really return an array of entities instead of a single entity? Could you show its code? Btw, entity type has "multiple" option, probably you want to set it to "true":


        $builder
            ->add('trcode', EntityType::class, [
                'class' => ExceptionCode::class,
                'multiple' => true,
                'query_builder' => function (ExceptionRepository $repo) {
                    return $repo->exceptions();
                }
            ])

Cheers!

Reply
Default user avatar
Default user avatar Moises Cano | Victor | posted 5 years ago

Sorry about that. I should have taken that part out. I have it commented out because i'm not using it right now.

Reply
Default user avatar
Default user avatar Vasili Repin | posted 5 years ago

Super tutorials! Great thank you!
And I have a question...
I have a 2-3 Entities (tables in Database) and those entities are related one-to-many. I need 1 form (for ../admin/new) for populating data to database. I need 2-3 entities in one form. How can I do this? What is the best way?
I used this code it's works:
$product->setPrice($form->get('price')->getData());
$product->setModel($form->get('model')->getData());
$product->setDateCreated($form->get('dateCreated')->getData());
$description->setSmallDesc($form->get('smallDesc')->getData());
$description->setBigDesc($form->get('bigDesc')->getData());

But in $form = $this->createForm(DescriptionFormType::class, $product); i need more objects...for next work of code.
but it's not allowing to do like this:
$description = new Description;
$image = new Image;
$form = $this->createForm(DescriptionFormType::class, $product, $description, $image);

And I need to do this without any bundles.
Plese tell me what I need to do? Thank you. Sorry for bad English.

Reply

Hey Vasili,

I think I got what you mean, since your entities are related to each other like one-to-many, it's easy to do with sub-forms. You need to create a form for each entity you want to see in your final form. For example, you have 3 entities: Product, Description, and Image. So as I understand, Description and Image relate to Product like many-to-one, i.e. Product can have many Description and Image objects. Then you need to create forms for those entities, i.e. ProductForm, DescriptionForm, and ImageForm. So if your ProductForm is your final form, you need to include those 2 sub-forms to it, but using Collection type:


$productFormBuilder
    ->add('descriptions', CollectionType::class, [
        'entry_type' => DescriptionType::class,
    ])
    ->add('images', CollectionType::class, [
        'entry_type' => ImageType::class,
    ])
    // ... other fields you need to see in ProductForm
;

Then you'll be able to do just:
$form = $this->createForm(ProductForm::class, $product);

And all the sub-forms also will be populated with data. Anyway, I hope you got the idea, CollectionType is something you need, see https://symfony.com/doc/current/reference/forms/types/collection.html

Also, see our screencast about it for an example: https://knpuniversity.com/screencast/collections/embedded-form-collection . And in general, this course has a lot of interesting information about working with collections: https://knpuniversity.com/screencast/collections

Cheers!

Reply
Default user avatar
Default user avatar Vasili Repin | Victor | posted 5 years ago | edited

Thank for response! I saw links as you give...
Yes, in Entity: Description and Image are related to Product with many-to-one. I need 1 form with clear fields for filling this with my data from ...admin/new. This is 3 entities with many fields (producr->name, product->price, description->smallDescription, image->linkBigImage...and others) in one form. And I need populating this form manually.


$this 
    ->add('descriptions', CollectionType::class, ['entry_type' =>DescriptionFormType::class]

where DescriptionFormType have...


$builder
    ->add('smallDesc', TextType::class, ['data_class' => Description::class])
    ->add('bigDesc', TextareaType::class ,['data_class' => Description::class])

doesn't work with next error:

<blockquote>
-Neither the property "descriptions" nor one of the methods "getDescriptions()", "descriptions()", "isDescriptions()", "hasDescriptions()", "__get()" exist and have public access in class "RevprodBundle\Entity\Description".
</blockquote>

But all getters and setters is in Entity...

Reply

Hey Vasili,

From the error I see Symfony can't find proper methods. Please, make sure you do "$this->add('descriptions', ...)" in ProductForm, and this form's data_class is set to "Product::class", not "Description::class". Because from error I see that Symfony trying to find those methods in "RevprodBundle\Entity\Description" entity which is incorrect if I understand you right. And the error makes sense because Description entity does not have getDescriptions() method, right? :)

Cheers!

1 Reply
Default user avatar
Default user avatar Vasili Repin | Victor | posted 5 years ago

Я вижу, вы русский знаете. Уже кучу времени потратил, но никак не могу решить эту задачу. У меня есть 3 эти сущности: Продукт, Картинки, Описание. И мне нужна одна форма, чтобы заполнить все это, просто форма с несколькими полями и кнопкой для загрузки картинок, это для страницы /new. Встраивание форм попробовал, что-то выдает только название (Products и image) без самих форм. Уже не знаю, что делать. Вроде простая задача, часто используется, а реализации нормальной нет( Может есть какой нормальный вариант?
->add('products', CollectionType::class, ['entry_type' => ProductFormType::class])
->add('image', CollectionType::class, ['entry_type' => ImageFormType::class])
Да и что делать, если в $this->createForm(ProductForm::class, $product) нужно передать несколько аргументов? Спасибо за помощь!

Reply

Добрый день,

Из нашей команды русский понимаю только я, поэтому для вопросов на английском у вас есть шанс получить ответ быстрее :)

Теперь вы меня запутали окончательно с этим примером, так как речь вроде шла о том что у вас Product относится к Image и Description как один-ко-многим, но из вашего примера я вижу что вы пытаетесь создать возможность редактировать сразу несколько Product а Image к ним вообще не относится. Или вы поменяли требования? Давайте пойдем по другому пути, можете показать вашу сущность Product с теми свойствами которые вы хотите видеть в форме но у вас не получается отобразить? И опишите как вы себе видите конечную форму, а я попробую помочь.

Что вы имеете ввиду что нужно передать несколько аргументов в $this->createForm(ProductForm::class, $product)? Симфони формы могут привязываются только на одну сущность, которая является главной. Все дочерние сущности должны привязываться автоматически если у вас правильно написанный form type, т.е. если у вас есть Product который связан как один-ко-многим к Image, то в createForm() вы просто передаете объект Product. Но прежде чем создать форму и передать в нее Product, вы можете модифицировать его, например, добавить в него несколько картинок, но все это зависит от ваших целей. Вот очень упрощенный пример:

$product->addImage(new Image());
$product->addImage(new Image());
$product->addImage(new Image());
$this->createForm(ProductForm::class, $product);

Т/е/ вы передаете объект Product сразу с 3-мя картинками, и если у вас есть коллекция картинок в форме - то вы должны будете увидеть отображение этих трех картинок в форме. Как видите, мне не нужно передавать в форму еще отдельно картинки так как они являются дочерними по отношению к Product.

Также, эта статья может быть полезна для вас: http://symfony.com/doc/curr... - там как раз идет речь о том как встраивать коллекции в формы. Кстати, для правильной реализации добавления / удаления нужно будет поработать с JavaScript: http://symfony.com/doc/curr... . Поэтому я рекомендую вам сначала сделать возможность редактирования уже существующей информации в БД, так будет проще начать и будет понятнее.

Удачи!

1 Reply
Default user avatar
Default user avatar Vasili Repin | Victor | posted 5 years ago

Спасибо за ответ!
На данном этапе я хочу создать форму для добавления новой записи, а не для редактирования. В форме много полей, поля группами принадлежат разным объектам. Допустим, заполняем продукт, его название, стоимость, дата производства, большое и малое описание и картинка к нему или несколько картинок. Т.е. форма относится к разным классам-сущностям. Отношения уже потом. Сейчас нужно всего лишь создать форму, чтобы потом в БД она разошлась по разным таблицам. Не нашел подробной инструкции как это сделать. Слышал про композитный класс, но не знаю как это сделать, да и кажется, что это лишнее... Спасибо Вам за помощь!

Reply

Добрый день,

Начинать с формы добавления будет немного сложнее чем с формы редактирования. Композитный класс скорее всего не нужен если есть главная сущность, а все остальные сущности являются дочерними, т/е/ связаны с главной сущностью по схеме один к одному, многие к одному или многие ко многим. Если же это ваш случай - супер, осталось определить главную сущность и создать дня нее форм тайп. В форм тайпе сначала пропустите поля которые являются отдельными сущностями, т/е/ создайте форму которая будет содержать только простые поля типа "input type=text" или "textarea", например где вы заполняете описание, стоимость, дату, и т/д/. После того как эта простая форма будет готова - добейтесь того чтобы ее отрендерить на странице, а после этого чтобы когда вы отправляете форму - обработайте ее так чтобы информация писалась в БД. Если все работает, дайте мне знать и после этого можно будет переходить на второй этап.

Кстати о формах, думаю будет полезным этот курс: https://knpuniversity.com/s... . Также есть https://knpuniversity.com/s... - где мы строим более сложные формы, по сути именно такие как вам надо. Думаю он должен автоматически ответить на большинство ваших вопросов, но рекомендую его только после "Symfony Forms: Build, Render & Conquer!".

Удачи!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}
userVoice