DevDonkey
DevDonkey

Reputation: 4880

persisting a new user to the db

I am aware of the FOSuserbundle availability but in order to help learn symfony2 I decided to try to create everything from scratch by using the book/cookbook.

After following the cookbooks tutorials for security I am having trouble working out how to persist a new user to the database. I have everything else working fine, logins, firewall security domains, password encryption etc

When I try to add a user I get the following error:

Neither the property "roles" nor one of the methods "addRol()"/"removeRol()", "addRole()"/"removeRole()", "setRoles()", "roles()", "__set()" or "__call()" exist and have public access in class "Ampisoft\Bundle\etrackBundle\Entity\users"

I've noticed that the users() class doesn't have a setRoles method, but I'm unsure how to add one as it needs to use an array object(?). the other thing confusing me is that the error highlights no addRole() (and not addRoles method, even though the member is roles.

my code is as follows:

users entity class (its got quite bit so these is just the sections I think are important

class users implements AdvancedUserInterface, \Serializable
{
    // ......

    /**
     * @ORM\ManyToMany(targetEntity="roles", inversedBy="users")
     *
     */
    private $roles;


    /**
     * @inheritDoc
     */
    public function getRoles()
    {
        return $this->roles->toArray();
    }

   /**
     * Add roles
     *
     * @param \Ampisoft\Bundle\etrackBundle\Entity\roles $roles
     * @return users
     */
    public function addRoles(roles $roles)
    {
        $this->roles[] = $roles;

        return $this;
    }
    // ......
}

roles entity class (again, related sections)

class roles implements RoleInterface
{
    // .........

    /**
     * @ORM\ManyToMany(targetEntity="users", mappedBy="roles")
     */
    private $users;

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

   /**
     * Add users
     *
     * @param \Ampisoft\Bundle\etrackBundle\Entity\users $user
     * @return roles
     */
    public function addUser(users $user)
    {
        $this->users[] = $user;

        return $this;
    }
    // .........
}

controller

public function admin_new_userAction()
{
    $user = new users();
    $form = $this->createForm(new usersType(), $user);

    $request = $this->getRequest();

    // if form is posted
    if ($request->getMethod() === 'POST') {
        $form->bind($request);

        $user->setSalt(md5(time()));

        $encoder = $this->container->get('security.encoder_factory')->getEncoder($user); //get encoder for hashing pwd later
        $tempPassword = $encoder->encodePassword($user->getPassword(), $user->getSalt());
        $user->setPassword($tempPassword);

        // flush to db
        $em = $this->getDoctrine()->getEntityManager();
        $em->persist($user);
        $em->flush();

        return $this->redirect($this->generateUrl('admin_users'));

    }

    // BUILD FORM
    return $this->render('etBundle:Admin:new_user.html.twig', array(
        'form' => $form->createView(),
    ));
}

the usersType class (I plan to use this for editing users as well hense the listener)

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $user = $event->getData();
        $form = $event->getForm();

        // check if the User object is "new"
        if (!$user || null === $user->getId()) {
            $form->add('username', 'text');
            $form->add('firstname', 'text');
            $form->add('lastname', 'text');
            $form->add('password', 'repeated', array(
                'type' => 'password',
                'invalid_message' => 'The password fields must match.',
                'options' => array('attr' => array('class' => 'password-field')),
                'required' => false,
                'first_options' => array('label' => 'Password'),
                'second_options' => array('label' => 'Repeat Password'),
            ));
            $form->add('email', 'repeated', array(
                'type' => 'email',
                'invalid_message' => 'The email address fields must match.',
                'options' => array('attr' => array('class' => 'email-field')),
                'required' => true,
                'first_options' => array('label' => 'Email'),
                'second_options' => array('label' => 'Repeat Email'),
            ));
            $form->add('email', 'repeated', array(
                'type' => 'email',
                'invalid_message' => 'The email address fields must match.',
                'options' => array('attr' => array('class' => 'email-field')),
                'required' => true,
                'first_options' => array('label' => 'Email'),
                'second_options' => array('label' => 'Repeat Email'),
            ));
            $form->add('lastLogged', 'hidden');
            $form->add('roles', 'entity', array(
                'class' => 'etBundle:roles',
                'property' => 'name',
            ));

        } else {
            $form->add('id', 'hidden');
            $form->add('username', 'text');
            $form->add('firstname', 'text');
            $form->add('lastname', 'text');
            $form->add('password', 'repeated', array(
                'type' => 'password',
                'invalid_message' => 'The password fields must match.',
                'options' => array('attr' => array('class' => 'password-field')),
                'required' => false,
                'first_options' => array('label' => 'Password'),
                'second_options' => array('label' => 'Repeat Password'),
            ));
            $form->add('email', 'repeated', array(
                'type' => 'email',
                'invalid_message' => 'The email address fields must match.',
                'options' => array('attr' => array('class' => 'email-field')),
                'required' => true,
                'first_options' => array('label' => 'Email'),
                'second_options' => array('label' => 'Repeat Email'),
            ));

        }

    });

I see there is an addUser method in the roles() class but Im unsure on how to use it. I would be very grateful if someone could point me in the right direction.

** update 1**

I tried inserting the suggested setRoles() method above and now Im recieving the following error: Catchable Fatal Error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given, called in C:\Dropbox\xampp\htdocs\etrack3\vendor\doctrine\orm\lib\Doctrine\ORM\UnitOfWork.‌​php on line 555 and defined

** update 2** fixed this problem: realised that I had a manytomany/manytomany relationship between my Users/Roles entities. Changed to manyToOne/OneToMany and all is good.

Upvotes: 0

Views: 421

Answers (1)

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76405

Your class users is missing its constructor, which initializes the $roles property to an ArrayCollection instance. I'd also advise against your using the $this->users[] notation, in favour of treating these properties as what they really are:

$this->users->add($user);

As for the error you're getting, that's down to how Doctrine loads its entities. The error shows what doctrine looks for in an entity class, in order to set the properties: either the roles property should be public, or you need a method with a name that looks like set<ucfirst-name-of-table-field>, or a slow magic getter/setter interface.

As far as the setRoles and setUsers methods are concerned, that's a simple matter:

public function setRoles($roles)
{
    $this->roles = $roles;
    return $this;
}

With the option of adding a type-hint:

public function setRoles( ArrayCollection $roles)
{
    $this->roles = $roles;
    return $this;
}

Some other issues I'd like to mention:

Coding standards are important, check the most commonly adopted standards here. Doctrine, Symfony2, ZendFW2, ... all the major players subscribe to these standards, and so should you. A class name, for example starts with an upper-case character, so class users should become class Users etc...
I'd also advise against implementing the Serializable interface on doctrine entities, especially if you make users Serializable, which might contain Roles instances, which does not implement Serializable.

Upvotes: 2

Related Questions