Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

500 Error: The required option "class" is missing when using form with EntityType as parent #19517

Closed
pascalwacker opened this issue Aug 3, 2016 · 14 comments

Comments

@pascalwacker
Copy link

@wouterj I think, I found the same bug as mentioned in #17299 in Symfony 3.1.3

What I try to do, is to have a custom file uploader.
FormField:

<?php

namespace AppBundle\Form\Fields;

use Symfony\Component\Form\AbstractType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;


class UploadFileType extends AbstractType
{
    /**
      * {@inheritdoc}
      */
    public function getParent() {
        return EntityType::class;
    }

    /**
      * {@inheritdoc}
      */
    public function getName() {
        return 'app_upload_file';
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'app_upload_file';
    }
}

My Entity contains:

    [...]
    /**
     * @ORM\ManyToOne(targetEntity="File")
     * @ORM\JoinColumn(name="logo_id", referencedColumnName="id")
     */
    private $logo;
    [...]

And the view file looks like this

{%- block app_upload_file_widget -%}
    <input type="hidden" name="{{ full_name }}" value="1" />
    [...]
{%- endblock app_upload_file_widget -%}

The Form:

    [...]
    ->add('logo', \AppBundle\Form\Fields\UploadFileType::class)
    [...]

When rendered with a ChoiceType as parent, it renders the form, but the controller says, that the form isn't valid. While when I change the parent to EntityType, I get an "The required option "class" is missing." 500 error.

    [...]
    ->add('logo', \AppBundle\Form\Fields\UploadFileType::class, array(
        'class' => File::class
    ))
    [...]

When I add a class entry to the form, it works.

As far, as I can tell, the problem is, that a normal EntityType field guesses the class, based on the annotations in the Entity, while a field, that has the EntityType as parent can't do this.

@HeahDude
Copy link
Contributor

HeahDude commented Aug 3, 2016

@pascalwacker The problem comes from your type which has EntityTypeas parent so it inherits its options, and class is required in that case.

When you set ChoiceType as parent the value of your hidden field (in your example "1") should match a string value of one of the choices passed through the choices option in your field, and it seems you don't pass any.

@pascalwacker
Copy link
Author

@HeahDude I have the EntityType as parent, as it should act like an EntityType. I would like to be able to link File Entities (\AppBundle\Entity\File). The goal is, to have in the view an ajax uploader and a modal to select existing File entities.

The point about the class property, is that if I simply use

    [...]
    ->add('logo')
    [...]

it is able to auto detect the necessary class, but it doesn't in the case, when you have a custom type.

@HeahDude
Copy link
Contributor

HeahDude commented Aug 5, 2016

it is able to auto detect the necessary class

Where does it come from? I think your statement is wrong, isn't it?

@yceruto
Copy link
Member

yceruto commented Aug 5, 2016

@pascalwacker The class option is guessed when the form type is null and this property has association only, see
https://github.com/yceruto/symfony/blob/master/src/Symfony/Component/Form/FormBuilder.php#L105
https://github.com/yceruto/symfony/blob/master/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php#L51

Otherwise you need to specify all the required options of the form type.

@pascalwacker
Copy link
Author

I see, then it's my fault and not a bug!

A little off topic, but since I already explained, what I try to achieve and what the related code is, is there a way to not pre load all possible entities for the form? With files (images in the most cases), I fear, that there will soon be a few thousand entries in the DB. The Javascript part of the frontend will render a modal, which has some paging to select one/multiple File entities. So to not put to much unnecessary load on the DB, I'd preferred to skip the pre loading. I've tried to just use a ChoiceType field as parent, but the problem is, there I would also have to load all entries to allow them as choices. Is there a way to do that?

@jakzal
Copy link
Contributor

jakzal commented Aug 9, 2016

Closing as this is a user error after all.

@jakzal jakzal closed this as completed Aug 9, 2016
@xabbuh
Copy link
Member

xabbuh commented Aug 10, 2016

@pascalwacker For example, you can use form events to populate only the choices that match the user's choice.

@pascalwacker
Copy link
Author

@xabbuh I've solved it in the meantime using this bundle: https://github.com/Alsatian67/FormBundle and the solution here: http://stackoverflow.com/q/38792355/2989952

@Glideh
Copy link

Glideh commented Mar 8, 2017

Landing here from a search, back to the OP title:

The required option "class" is missing when using form with EntityType as parent

How can we extend EntityType and guess the class of an entity like DoctrineOrmTypeGuesser does ?

class EntityActiveType extends AbstractType
{
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'some_new_options' => 'some_value',
            'class' => guessSomehow(),
        ));
    }

    public function getParent()
    {
        return EntityType::class;
    }
}

If there is a simple way to do it or any example somewhere I'm interested.

@yceruto
Copy link
Member

yceruto commented Mar 8, 2017

How can we extend EntityType and guess the class of an entity like DoctrineOrmTypeGuesser does ?

@Glideh just don't define it (if it's variable), the guesser acts on runtime (if possible) and injects the entity class in your $options['class']

@Glideh
Copy link

Glideh commented Mar 8, 2017

I'm trying to extend EntityType (that's why I've added an example with a custom option) so I have to define it

@stof
Copy link
Member

stof commented Mar 8, 2017

you cannot guess in defaults. Defaults are not related to a single usage of the type. They are reused between places using the type.
But anyway, I dislike the option guessing system. This does not enter into action when you specify a type explicitly anyway.

@Glideh
Copy link

Glideh commented Mar 8, 2017

you cannot guess in defaults. Defaults are not related to a single usage of the type. They are reused between places using the type.

My example was wrong indeed, it was just to give the idea

This does not enter into action when you specify a type explicitly anyway

Yes I imagine the guesser is not used when you specify the type.
My case here is I need to use EntityType with this custom option in many entities of my application.
Will I have to specify the entity class in each (mapped) form where I need it ?

Actually I already encountered this issue when trying to make a "HiddenEntityType" in the past

@HeahDude
Copy link
Contributor

Guessing the class when the form is building is only possible if the data is predefined (not null), using:

$factory->create(EntityType::class, $entity);

But when using as nested form, the initial data is always null until it's mapped (when the parent is initialized), to hack this the option data can be used:

$factory->create(MyParentEntityType::class, $parentEntity); // $parent must not be null

// in MyParentType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $parentEntity = $builder->getData();
    $builder->('entity', MyEntityType::class, [
        'data' => $parentEntity->getEntity(),
    ]);
}

// in MyEntityType
public function getParent()
{
    return EntityType::class;
}

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefault('class', function (Options $options) {
        return isset($options['data']) ? get_class($options['data']) : DefaultEntity::class,
    });
}

but this looks very fragile.

In such cases, it would be more efficient to create a special type with a post set data listener that could add a child with a type depending on the data and the inherit_data option set to true.

A type like this would act as a proxy and the child inheriting its parent data, everything should work fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants