5

I know there are several topics about this in SA but i can't figure out why my code doesn't work... Let me explain :

I have a Company entity, which may have many related Users. When i create a company, i want to create an "admin" User (the first user) using the same form.

My entites :

class Company
{
    **
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
    */
    private $id;
    ...

    /**
     * A company has many users.
     * @ORM\OneToMany(targetEntity="User", mappedBy="company", cascade={"persist"})
     */
    private $users;
    ...

    public function __construct() {
        $this->users = new ArrayCollection();
    }

    public function addUser(User $user)
    {
        $user->setCompany($this);
        $this->users->add($user);
        return $this; // doesn't appear in the documentation but found in SA... doesn't change anything
    }

    public function removeUser(User $user)
    {
        // ...
    }

}


class User
{
    **
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
    */
    private $id;
    ...

    /**
     * Many users belong to a company.
     * @ORM\ManyToOne(targetEntity="Company", inversedBy="users")
     * @ORM\JoinColumn(name="company_id", referencedColumnName="id")
     */
    private $company;
    ...

    /**
     * @param mixed $company
     */
    public function setCompany ($company)
    {
        $this->company = $company;
    }
}

When i submit the form (which contains the fields for creating a company and the first user) the company is saved in DB, as well as the first user, but the company_id of the User is set to NULL. I have to do this to make it work (the code below is in a service dedicated to manage companies) :

public function createCompany($company)
    {
    ...
    $company->getUsers()->get(0)->setCompany($company); // <- HERE (btw is there a way to access the first user without using get(0) ?)
    ...

    $this->entityManager->persist($company);
    $this->entityManager->flush();
    }

I shouldn't do it like that, right ? I thought that

$user->setCompany($this); 

in addUser would do it automatically...

Where am i wrong ?

EDIT :

The Company Form : (once again, i do not put all the code to keep it clear, i just post the useful lines) :

class RegisterCompanyForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
        ->add('name',null,[
            'translation_domain' => 'forms',
            'label' => 'register_company.name.label'
        ])
        ...
        ->add('users', CollectionType::class, [
            'entry_type' => RegisterUserForm::class,
            'entry_options' => array('label' => false),
            'allow_add' => true,
            'by_reference' => false,    
        ])
        ; 
    }
}

The User Form :

class RegisterUserForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
        ->add('givenName',TextType::class,[
            'translation_domain' => 'forms',
            'label' => 'register_user.givenName.label'
        ])
        ->add('familyName',null,[
            'translation_domain' => 'forms',
            'label' => 'register_user.familyName.label'
        ])
        ...
        ->add('password',null,[
            'translation_domain' => 'forms',
            'label' => 'register_user.password.label'
        ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'App\Entity\User',
        ));
    }
}

My twig template for the form :

{% extends 'layout/layout.html.twig' %}
{% trans_default_domain 'ui' %}

{% block title %}My Form {% endblock %}

{% block content %}
    <div class="container">
        <div class="starter-template">
            <h1>{% trans %}title.register{% endtrans %}</h1>
            {{ form_start(form) }}
            {{ form_errors(form) }}
            {{ form_row(form.name) }}
            ...
            {% for user in form.users %}
            {{ form_row(user.givenName) }}
            {{ form_row(user.familyName) }}
            ...
            {{ form_row(user.password) }}
            {% endfor %}
            {{ form_end(form) }}
        </div>
    </div>
{% endblock %}

The controller :

class CompanyController extends Controller
{
    public function inscription(CompanyManager $companyManager, Request 
$request)
    {
        $company = new Company();      
        $user = new User();
        $company->getUsers()->add($user);
        $form = $this->createForm(RegisterCompanyForm::class, $company);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            // saving to DB is managed by a service
            $companyManager->createCompany($company);
        }

        return $this->render('views/company/register.html.twig', [
                'form'  => $form->createView()
        ]);
    }
}

in CompanyManager service :

public function createCompany($company)
    {

        // i'd like to avoid the next line !!! because i think it shouldn't be there...
        $company->getUsers()->get(0)->setCompany($company);

        $this->entityManager->persist($company);
        $this->entityManager->flush();
        return true;
    }

BTW, i followed this guide to create my forms : https://symfony.com/doc/master/form/form_collections.html

sylvain
  • 565
  • 1
  • 6
  • 15
  • Maybe you have some mistake in entity. Please review this answer for manyToOne relation and if you have problem again type: https://stackoverflow.com/questions/49026436/relationships-in-doctrine/49028183#49028183 – l13 Mar 19 '18 at 12:59
  • add your user entity too please – Preciel Mar 19 '18 at 14:00
  • user entity is just below company entity :) FYI I didn't post all the code for the entities, just the useful parts – sylvain Mar 19 '18 at 14:31
  • Can you show the complete code for the Form, and for the Controller holding it ? Also, in the Company constructor, did you declare `$users` as an ArrayCollection: `$this->users = new ArrayCollection();`? – scandel Mar 19 '18 at 14:52
  • I just added the forms, controller, service and twig. And yes i declare $users as an ArrayCollection. Just to be clear : my code works, it's just i think that i shouldn't have to call $company->getUsers()->get(0)->setCompany($company); before persisting... i like to do things the right way :) – sylvain Mar 19 '18 at 16:55

2 Answers2

1

We're missing some code of the form, but here is a wild guess...

As I see it, you're trying to persist your User before your company.
Thus, it can't set a company_id as you haven't persisted Company yet.
Try this instead.

public function createCompany($company) {
    // Code folding [...]
    $this->entityManager->persist($company);
    $this->entityManager->flush();
    $this->entityManager->refresh($company); // <== Refreshing persisted eneity to get the new created ID.

    $company->getUsers()->get(0)->setCompany($company);
    // Code folding [...]
}
Preciel
  • 2,666
  • 3
  • 20
  • 45
  • The foreign key is well populated when i do : $company->getUsers()->get(0)->setCompany($company); even if i do it before persisting $company. What i want is avoiding doing this setCompany as (i guess...) it is supposed to be done inside the addUser() of Company entity – sylvain Mar 19 '18 at 16:44
1

Well, the answer is in fact pretty obvious...

The mistake was in CompanyController :

$company = new Company();      
$user = new User();
$company->getUsers()->add($user);

should be :

$company = new Company();      
$user = new User();
$company->addUser($user);

In the original code :

$user->setCompany($this);

was never used because i added the user to the collection 'manually'... so the link between the created user and the company was never set. I was mistaken because the tutorial i followed (https://symfony.com/doc/master/form/form_collections.html) was slightly different...

sylvain
  • 565
  • 1
  • 6
  • 15