2

I'm looking for a way to extend Symfony 2 EntityType

Symfony\Bridge\Doctrine\Form\Type\EntityType

as in a new type extending this one, not creating a FormTypeExtension - and I can't figure it out. Does anyone know any proper way to do that?

I've tried simply extending it that way:

class NestedEntityType extends EntityType {

    public function getName() {
        return $this->getBlockPrefix();
    }

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

and then in sonata admin class I have:

protected function configureFormFields(FormMapper $formMapper)
{
    $formMapper->add('types', NestedEntityType::class, [
        'label' => false,
        'multiple' => true,
        'expanded' => true,
        'by_reference' => false
    ]);
}

but unfortunately it causes Fatal Error:

Catchable Fatal Error: Argument 1 passed to Symfony\Bridge\Doctrine\Form\Type\DoctrineType::__construct() must implement interface Doctrine\Common\Persistence\ManagerRegistry, none given, called in

I need to keep the whole functionality of EntityType, with one exception - the way it's presented. That's why I need to extend this type (I use it in other fields, so I can't just modify the template for it!).

I'm using Symfony 2.8 (just for the record).

Peyman Mohamadpour
  • 17,954
  • 24
  • 89
  • 100
pzaj
  • 1,062
  • 1
  • 17
  • 37

3 Answers3

9

You should not extend it directly but use parent option

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

So something like

class NestedEntityType extends AbstractType 
{

    public function getName() 
    {
        return $this->getBlockPrefix();
    }

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

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

That way if FormType you're extending from has something in injected (or setted) into constructor, you don't need to care as symfony will do it for you.

DonCallisto
  • 29,419
  • 9
  • 72
  • 100
  • `The required option "class" is missing. ` - I can only assume this has something to do with Sonata, not Symfony2 itself? – pzaj Sep 15 '16 at 13:56
  • 1
    @user1970395 it has something to do with the form where you are using `NestedEntityType`. Resolution: you need to pass, as an option, the class of entity you're using that form type on. – DonCallisto Sep 15 '16 at 13:57
  • Thank you for your help! I'd love to mark both answers as accepted, but Dmitry's covers the question more deeply, so I will mark his answer! :) Hope you don't mind. – pzaj Sep 15 '16 at 14:19
  • @user1970395 mark what you think is the best one to help community: we are here to help and be helped, not to gain reputation ;) – DonCallisto Sep 15 '16 at 14:20
1

If you would go to EntityType, you'll see it's extending DoctrineType and needs dependencies in constructor -

public function __construct(ManagerRegistry $registry, PropertyAccessorInterface $propertyAccessor = null)
{
    $this->registry = $registry;
    $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
}

So, your class NestedEntityType uses the same constructor and needs the same dependencies.

Actually, what you need is

class NestedEntityType extends AbstractType {

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

    public function getName() {
        return $this->getBlockPrefix();
    } 

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

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'class' => YourEntity::class
        ]);
    }
}

UPD: Of course you need configure options, according to EntityType doc. See method I've added to code.

Dmitry Malyshenko
  • 3,001
  • 1
  • 12
  • 20
  • You have a typo in `AbstractType`! Also thank you, it does push me forward, unfortunately I'm now getting: `The required option "class" is missing.`, thus it's not 1:1 extension of EntityType, because I still need some additional stuff there to make it work ;> I took an approach of passing arguments to constructor, but unfortunately I can't find any matching services to pass as arguments, any chance you'd know what should I put in service definition to make it work? – pzaj Sep 15 '16 at 13:49
  • Of course, you need option `class`. See updated answer. – Dmitry Malyshenko Sep 15 '16 at 13:56
  • Ah, right :> And I believe it's being passed automagically by Sonata to EntityType? – pzaj Sep 15 '16 at 13:57
  • Can I pass a trait there? I wanted to use Gedmo NestedTree here. – pzaj Sep 15 '16 at 14:02
  • by Symfony, to be precise. – Dmitry Malyshenko Sep 15 '16 at 14:03
  • Do you mean if the value of 'class' may be a trait? No, it may not. Only a valid Entity. It's used to recieve an EntityRepository. Explain, what do you want to archive. Maybe, you're going by wrong road. – Dmitry Malyshenko Sep 15 '16 at 14:05
  • Alright... so, to explain it briefly. I want to create a FormType working exactly like EntityType, but I need its extended and multiple variant to be presented in a different way - I need "nested" checkboxes - for Entity that can be nested using Gedmo's `NestedTree` - instead of just a list of checkboxes. Nested as in multilevel unordered lists of checkboxes :) Perhaps you know how does the Symfony automagically set the `class` option? – pzaj Sep 15 '16 at 14:07
1

So, if you need a create a reusable solution, for different Entities, you don't need a configureOptions in this case.

You need to create an elementType in you code like

$this->createForm(NestedEntityType::class, null, ['class' => YourEntity::class]);

And in this case, you will need to pass as an option a name of class Entity which is nested.

Dmitry Malyshenko
  • 3,001
  • 1
  • 12
  • 20
  • No way Sonata / Symfony could do this automatically like it does for EntityType? Well, I think I can live with that, if there's no other way :) – pzaj Sep 15 '16 at 14:17
  • I think it's possible. It's Symfony after all. But you need to dig deep into the code and understand completely first, how it's working with EntityType – Dmitry Malyshenko Sep 15 '16 at 14:19
  • That's what I assumed, well, for now I can live with `class` option. Thank you for your support! – pzaj Sep 15 '16 at 14:22