php - Doctrine Module: entity relationships being hydrated but not sticking to entity

292

I have a many-to-many relationship between users (the owning side) and user groups, and am having issues using doctrine module's hydrator to create a new user group.

When I create a new user group and hydrate, persist, and flush it, the records change in the database, but the entity variable itself representing the user group doesn't end up with any users in it post-hydration.

Context: We have a REST controller route that we use to create a new user group via POST. It accepts parameters to initialize it with some users via hydration. This operation successfully updates the database, but its response is incorrect. It is supposed to extract the data from the now-persistent entity and echo it back to the client. However, it fails to extract any users, so the response incorrectly returns as an empty group. Not using the hydrator's extract method and instead using more basic doctrine commands fails too--it seems like the entity variable itself is just not kept up to date after being persisted.

So my question really is: why is the hydrator not extracting users? If we've messed up the owner/inverse assocation, why is it working at all (i.e. persisting the users to the database but not to the entity).

Here is the relevant code, probably only the first two blocks are needed.

public function create($data) {
    ...

    $hydrator = $this->getHydrator();
    $em = $this->getEntityManager();
    $entity = $this->getEntity();
    $entity = $hydrator->hydrate($data, $entity);

    // Persist the newly created entity
    $em->persist($entity);

    // Flush the changes to the database
    $em->flush();

    return $this->createResponse(
        201,
        true,
        'Created item',
        $this->getHydrator()->extract($entity)
    );

Here is are the setters and getters the hydrator is using:

... more fields... 

/**
 * @ORM\ManyToMany(targetEntity="User", mappedBy="groups")
 */
protected $users;

...

/**
 * Getter for users
 *
 * @return mixed
 */
public function getUsers() {
    return $this->users;
}

public function addUsers(Collection $users) {
    foreach ($users as $user) {
        $user->addGroups(new ArrayCollection(array($this)));
    }
}

I think the above is the only relevant code, but I'll include some more in case I'm wrong. Here is the getHydrator method:

  public function getHydrator() {
      if(null === $this->hydrator) {
          $hydrator = new DoctrineObject($this->getEntityManager(), $this->getEntityName());
          // Use Entity metadata to add extraction stategies for associated fields
          $metadata = $this->em->getClassMetadata($this->getEntityName());

          foreach ($metadata->associationMappings as $field => $mapping) {
              // Use our custom extraction strategies for single and collection valued associations
              if($metadata->isSingleValuedAssociation($field)) {
                  $hydrator->addStrategy($field, new RestfulExtractionStrategy());
              }
              else if($metadata->isCollectionValuedAssociation($field)) {
                  $hydrator->addStrategy($field, new RestfulExtractionCollectionStrategy());
              }
          }
          $this->hydrator = $hydrator;
      }

      return $this->hydrator;
  }

Here is the RestfulExtractionCollectionStrategy (the other strategy isn't being used here, I have verified this).

namespace Puma\Controller;
use DoctrineModule\Stdlib\Hydrator\Strategy\AllowRemoveByValue;
use Doctrine\Common\Collections\Collection;

/**
 * You can use this strategy with fields that are collections,
 * e.g. one to many.  You need to use the RestfulExtractionStrategy
 * if you want to get extract entities from a singleton field, e.g. manyToOne.
 **/
class RestfulExtractionCollectionStrategy extends AllowRemoveByValue
{
    public function extract($value)
    {
        if ($value instanceof Collection) {
            $return = array();
            foreach ($value as $entity) {
                if(method_exists($entity, 'getId')){
                    $return[] = $entity->getId();
                }
                else {
                    $return[] = $entity;
                }
            }
            return $return;
        }
        return $value;
    }
}
720

Answer

Solution:

I am not quite familiar with hydration, etc., so your code looks kind of strange to me and I cannot guarantee this will work, but have you tried to refresh the entity after flushing (i.e.$em->refresh($entity)) and maybe return the entity instead of$this->getHydrator()->extract($entity)?

26

Answer

Solution:

I think I've finally solved it--I added a line to the "setter" method,addUsers which manually updates the users property of the group after updating the related users. I would be a bit surprised if this was best practice, though. I had thought that updating the owning side (the users) would automatically update the inverse side (the user group). Perhaps I was wrong. If anyone else has a better idea I'll gladly give the answer credit to them.

public function addUsers(Collection $users) {
    foreach ($users as $user) {
        $user->addGroups(new ArrayCollection(array($this)));

        // This is the new line (updating the usergroup property manually)
        $this->users->add($user);
    }
}

People are also looking for solutions to the problem: PHP serving a large file download times out

Source

Didn't find the answer?

Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.

Ask a Question

Write quick answer

Do you know the answer to this question? Write a quick response to it. With your help, we will make our community stronger.

Similar questions

Find the answer in similar questions on our website.