2

I have two related entities in my symfony project - which are stored using Doctrine. One entity is "Advertiser" which has an id and a name. The other entity - which is causing the problem - is for a "Report" which has an id of it's own along with a field called "advertiser_id".

When adding a report for an advertiser, in the Symfony form, I'm using EntityType for the advertiser_id field so that I can display a select of advertisers. That part is working great, but when I try to submit the form, I get an error because it's passing an Advertiser object instead of the id of the advertiser to the advertiser_id field.

Here's what I have in my form's builder:

$builder
    ->add('advertiser_id', EntityType::class, [
        'class'        => Advertiser::class,
        'query_builder' => $this->advertiserRepository->findAllNotDeletedUnpaginated(),
        'choice_label' => 'name',

    ])
    ->add('submit', SubmitType::class, [
        'label' => 'Submit',
    ])
;

When I submit the form, I get this error: Expected argument of type "integer", "App\Entity\Advertiser" given.

Any idea on how I can force symfony to only try to save the id of the advertiser that was selected rather than passing the entire advertiser?

UPDATE: Now that I've refactored it so that the advertiser is a related entity to report, I'm trying to figure out how to make the advertiser a hidden field and getting nowhere.

I've tried the code the iiirxs mentioned previously with the callback transformer - changing 'advertiser_id' to 'advertiser' - but I've had no luck with that. I've been reading posts like this Symfony hiddenType using data_class for entity instead of transformer, but I'm having trouble getting the value for 'advertiser' the way they are getting $options['selected_course'] in that example.

When I try (for testing purposes) hard coding a value of 1 for advertiser, then putting this on the form, the form shows, but I get an error when submitting it:

    $advertiser=1;

    $builder
        ->add('advertiser', HiddenType::class,['data' => $advertiser, 'data_class' => null])

The error I get when submitting the form is: Expected argument of type "App\Entity\Advertiser or null", "string" given.

I'm sorry for beating a dead horse about this. This seems like it should be such a common/easy thing to do, but I'm having a hard time finding how to make it a hidden field. Any ideas would be greatly appreciated!

3
  • Can you please check advertiser_id this field definition in your Entity. If it defined as integer, you should change it. Commented Feb 12, 2019 at 5:29
  • can you share your Report.php entity file here? Did you indicate the field type as ManyToOne in the annotations? Commented Feb 12, 2019 at 6:23
  • Is there a reason why you want to pass an ID and not the Entity to your form ? (symfony is actually doing the job for you to serialize / deserialize EntityType) Commented Feb 12, 2019 at 8:17

2 Answers 2

2

The problem is that you have not defined correctly an association with Advertiser entity in your Report entity. You should have defined an association like this:

// inside Report.php class

/**
 * @ORM\ManyToOne(targetEntity="App\Entity\Advertiser")
 */
private $advertiser;

instead of defining a field that holds the foreign key advertiser_id. Doctrine is smart enough to map the advertiser field to an advertiser_id foreign key on your database on its own, so it would be better to use an association mapping. You can find more in the documentation.

However, if you indeed have to use only an integer to store the advertiser_id as an integer for your own reasons, you should use Symfony's Data Transformer to transform the advertiser entity to an integer like this:

$advertiserRepository = $this->advertiserRepository;
$builder->get('advertiser_id')
        ->addModelTransformer(new CallbackTransformer(
            function ($advertiserAsInteger) use ($advertiserRepository) {
                // transform the integer to an entity object
                return $advertiserRepository->find($advertiserAsInteger);
            },
            function ($advertiserAsEntity) {
                // transform the entity back to an integer
                return $advertiserAsEntity->getId();
            }
        ))
    ;

In my code above I used a CallbackTransformer to implement the transformation but you could also use a transformer class to do it. You can find also more about this in the data transformers documentation.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you, iiirxs! I'm going to refactor my entity per your first suggestion.
I've refactored the app so that the Advertiser is related to the Report. Now I'm trying to render the form for a report and I'm trying to figure out how to display the advertiser in the form. I've tried the basic "->add('advertiser')" within the builder, but that gives me this error: Catchable Fatal Error: Object of class App\Entity\Advertiser could not be converted to string. Any thoughts - or links - on how to render the form properly when there is a related entity would be appreciated!
Have you added advertiser field in your form as EntityType?
Thanks again, iiirxs! Changing the builder to include this worked! ->add('advertiser', EntityType::class, array( 'class' => Advertiser::class, 'choice_label' => 'name', ))
1

I wanted to add to part of the answer by @iiirxs with a custom type I just implemented based on the model transformer suggestion.

This type extends the built-in EntityType so for a lot of cases, you could replace EntityType with EntityReferenceType and map the id instead.

This is useful for if the form does not map directly to the entity, for example using the command pattern, so you'd prefer an identifier.

use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\PropertyAccess\PropertyAccess;

class EntityReferenceType extends AbstractType
{
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'id_property' => 'id'
        ]);
    }

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->addModelTransformer(
            new CallbackTransformer(
                function ($entityIdentifier) use ($options) {
                    if ($entityIdentifier === null) {
                        return null;
                    }
                    return $options['em']->getRepository($options['class'])->find($entityIdentifier);
                },
                function ($entity) use ($options) {
                    if ($entity === null) {
                        return null;
                    }
                    return PropertyAccess::createPropertyAccessor()->getValue($entity, $options['id_property']);
                }
            )
        );
    }


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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.