I have two Entities - Users & Challenges. A User can participate in many challenges and a challenge can have many participants (users). I began approaching this problem by creating a Many-To-Many relationship on my Users class:
/**
* @ORM\ManytoMany(targetEntity="Challenge")
* @ORM\JoinTable(name="users_challenges",joinColumns={@ORM\JoinColumn(name="user_id",referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="challenge_id",referencedColumnName="id")})
*
*/
protected $challenges;
However, I then realised that I need to store a distance attribute against a user/challenge combination (how far the user has travelled in their challenge). The Doctrine2 docs state:
"Why are many-to-many associations less common? Because frequently you want to associate additional attributes with an association, in which case you introduce an association class. Consequently, the direct many-to-many association disappears and is replaced by one-to-many/many-to-one associations between the 3 participating classes."
So my question is what should these associations be between User, Challenge and UsersChallenges?
UPDATE
See comment to first answer for links to the Entity code. I have a controller method below which always creates a new UsersChallenges record rather than updating an existing one (which is what I want)
public function updateUserDistanceAction()
{
$request = $this->getRequest();
$distance = $request->get('distance');
$challenge_id = $request->get('challenge_id');
if($request->isXmlHttpRequest()) {
$em = $this->getDoctrine()->getEntityManager();
$user = $this->get('security.context')->getToken()->getUser();
$existingChallenges = $user->getChallenges();
$challengeToUpdate = $em->getRepository('GymloopCoreBundle:Challenge')
->find( (int) $challenge_id);
if(!$challengeToUpdate) {
throw $this->createNotFoundException('No challenge found');
}
//does the challengeToUpdate exist in existingChallenges? If yes, update UsersChallenges with the distance
//if not, create a new USersChallenges object, set distance and flush
if ( !$existingChallenges->isEmpty() && $existingChallenges->contains($challengeToUpdate)) {
$userChallenge = $em->getRepository('GymloopCoreBundle:UsersChallenges')
->findOneByChallengeId($challengeToUpdate->getId());
$userChallenge->setDistance( $userChallenge->getDistance() + (int) $distance );
$em->flush();
} else {
$newUserChallenge = new UsersChallenges();
$newUserChallenge->setDistance($distance);
$newUserChallenge->setChallenge($challengeToUpdate);
$newUserChallenge->setUser($user);
$user->addUsersChallenges($newUserChallenge);
$em->persist($user);
$em->persist($newUserChallenge);
$em->flush();
}
//if success
return new Response('success');
//else
}
}
User (one-to-many) -> UsersChallenges
UsersChallenges (many-to-one) -> User
UsersChallenges (many-to-one) -> Challenge
Challenge (one-to-many) -> UsersChallenges
i belive you want a $em->persist($userChallenge);
before $em->flush();
$userChallenge->setDistance( $userChallenge->getDistance() + (int) $distance );
$em->persist($userChallenge);
$em->flush();
However i am not sure if it solves your problem.
Can you post isEmpty
and contains
function in existingChallenges
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With