src/Products/NotificationsBundle/Subscriber/OneRoster/OneRosterUserLinkSubscriber.php line 30

Open in your IDE?
  1. <?php
  2. namespace Products\NotificationsBundle\Subscriber\OneRoster;
  3. use Cms\CoreBundle\Entity\OneRoster\OneRosterUser;
  4. use Cms\CoreBundle\Entity\OneRosterSync;
  5. use Cms\CoreBundle\Events\OneRosterLinkEvent;
  6. use Doctrine\Common\Util\ClassUtils;
  7. use Products\NotificationsBundle\Entity\Profile;
  8. use Products\NotificationsBundle\Entity\ProfileRelationship;
  9. use Products\NotificationsBundle\Entity\Student;
  10. final class OneRosterUserLinkSubscriber extends AbstractNotificationsOneRosterSubscriber
  11. {
  12.     /**
  13.      * {@inheritdoc}
  14.      */
  15.     public static function getSubscribedEvents(): array
  16.     {
  17.         return [
  18.             OneRosterLinkEvent::EVENT__USER => [
  19.                 ['associateStudents'0],
  20.             ],
  21.         ];
  22.     }
  23.     /**
  24.      * @param OneRosterLinkEvent $event
  25.      */
  26.     public function associateStudents(OneRosterLinkEvent $event): void
  27.     {
  28.         // ensure we are meant to process this
  29.         if ( ! $this->checkTypes($event->getJob(), [
  30.             OneRosterSync::STRATEGIES__NOTIFICATIONS__FAMILY,
  31.             OneRosterSync::STRATEGIES__NOTIFICATIONS__STUDENTS,
  32.         ])) {
  33.             return;
  34.         }
  35.         // get the user
  36.         $user $event->getEntity();
  37.         if ( ! $user instanceof OneRosterUser) {
  38.             throw new \RuntimeException(sprintf(
  39.                 'User is not of proper type, got "%s".',
  40.                 ClassUtils::getClass($user)
  41.             ));
  42.         }
  43.         // branch on the role type
  44.         switch (true) {
  45.             case $this->checkTypes($event->getJob(), [OneRosterSync::STRATEGIES__NOTIFICATIONS__FAMILY]) && $user->isRoleFamily():
  46.                 // noop, code will continue after the switch statement
  47.                 break;
  48.             default:
  49.                 // DEBUGGING
  50.                 $event->getOutput()->writeln(sprintf(
  51.                     'User role for #%s is "%s", skipping...',
  52.                     $user->getSourcedId(),
  53.                     $user->getRole()
  54.                 ));
  55.                 // return to prevent further processing
  56.                 return;
  57.         }
  58.         // load up the profile
  59.         $profile $this->em->getRepository(Profile::class)->findOneBy([
  60.             'onerosterId' => $user->getSourcedId(),
  61.         ]);
  62.         // if we don't have a profile we have a problem
  63.         if ( ! $profile) {
  64.             throw new \RuntimeException(sprintf(
  65.                 'Could not load profile "%s".',
  66.                 $user->getSourcedId()
  67.             ));
  68.         }
  69.         // load up all the students based on oneroster ids
  70.         $students $this->em->getRepository(Student::class)->findBy([
  71.             'onerosterId' => $user->getAgentsSourcedIds(),
  72.         ]);
  73.         // we should have found the same amount
  74.         if (count($students) !== count($user->getAgents())) {
  75.             // TODO: just log the issue and allow it to continue...
  76. //            throw new \RuntimeException(sprintf(
  77. //                'Could not load all students, missing: "%s".',
  78. //                implode('", "', array_diff(
  79. //                    array_map(
  80. //                        static function (Student $student) {
  81. //                            return $student->getOneRosterId();
  82. //                        },
  83. //                        $students
  84. //                    ),
  85. //                    array_column($user->getAgents(), 'sourcedId')
  86. //                ))
  87. //            ));
  88.         }
  89.         // if there is relevant metadata, grab it
  90.         // TODO: where is this used?
  91.         $metadata = [];
  92.         if ($user->hasMetadataEntry('_children')) {
  93.             $metadata $user->getMetadataEntry('_children') ?: $metadata;
  94.         }
  95.         // load existing relationships
  96.         $relationships $this->em->getRepository(ProfileRelationship::class)->findBy([
  97.             'profile' => $profile,
  98.         ]);
  99.         // set on the profile
  100.         $managed = [];
  101.         foreach ($students as $student) {
  102.             // if either the profile or the student is "discarded", do not manage this relationship
  103.             if ($student->isDiscarded() || $profile->isDiscarded()) {
  104.                 continue;
  105.             }
  106.             // try to find an existing relationship
  107.             $relationship null;
  108.             foreach ($relationships as $thing) {
  109.                 if ($thing->getStudent() === $student) {
  110.                     $relationship $thing;
  111.                     break;
  112.                 }
  113.             }
  114.             // make if not found
  115.             if ( ! $relationship) {
  116.                 $relationship = new ProfileRelationship();
  117.             }
  118.             // set things
  119.             $managed[] = $relationship
  120.                 ->setProfile($profile)
  121.                 ->setStudent($student)
  122.                 ->setOneRosterId($profile->getOneRosterId())
  123.                 ->setOneRosterArchived($profile->isOneRosterArchived())
  124.             ;
  125.             // see if the student we are attaching has relationship metadata
  126.             if (array_key_exists($student->getOneRosterId(), $metadata)) {
  127.                 $relationship->setMetadata($metadata[$student->getOneRosterId()] ?: []);
  128.             }
  129.         }
  130.         // DEBUGGING
  131.         array_walk($managed, static function (ProfileRelationship $relationship) use ($event) {
  132.             $event->getOutput()->writeln(sprintf(
  133.                 '    %s    %s (%s >> %s | %s | %s)',
  134.                 (empty($relationship->getId())) ? 'Generating' 'Updating',
  135.                 ClassUtils::getClass($relationship),
  136.                 $relationship->getProfile()->getCensoredName(),
  137.                 $relationship->getStudent()->getCensoredName(),
  138.                 $relationship->getOneRosterId(),
  139.                 $relationship->getId() ?: '-'
  140.             ));
  141.         });
  142.         // go ahead and save the things we know want to keep
  143.         // this allows us to get ids that are needed in the following checks
  144.         $this->em->saveAll($managed);
  145.         // determine which ones we need to remove
  146.         $removals array_udiff($relationships$managed, static function (ProfileRelationship $aProfileRelationship $b) {
  147.             return ($a === $b) ? $a->getId() <=> $b->getId();
  148.         });
  149.         // DEBUGGING
  150.         array_walk($removals, static function (ProfileRelationship $relationship) use ($event$profile) {
  151.             try {
  152.                 $event->getOutput()->writeln(sprintf(
  153.                     '    Deleting    %s (%s >> %s | %s | %s)',
  154.                     ClassUtils::getClass($relationship),
  155.                     $relationship->getProfile()->getCensoredName(),
  156.                     ($relationship->getStudent()) ? $relationship->getStudent()->getCensoredName() : '?',
  157.                     $relationship->getOneRosterId(),
  158.                     $relationship->getId() ?: '-'
  159.                 ));
  160.             } catch (\Throwable $e) {
  161.                 // TODO: remove this at some point if it appears to no longer be needed...
  162.                 throw new \RuntimeException(sprintf(
  163.                     'REL ID: %s | REL OR: %s | REL PRO ID: %s | REL STU ID: %s | PRO ID: %s',
  164.                     $relationship->getId() ?: '-',
  165.                     $relationship->getOneRosterId() ?: '-',
  166.                     $relationship->getProfile() ? $relationship->getProfile()->getId() : '-',
  167.                     $relationship->getStudent() ? $relationship->getStudent()->getId() : '-',
  168.                     $profile->getId() ?: '-'
  169.                 ), 0$e);
  170.             }
  171.         });
  172.         // remove things we no longer need
  173.         $this->em->deleteAll($removals);
  174.     }
  175. }