src/Products/NotificationsBundle/Subscriber/OneRoster/OneRosterOrgProcessSubscriber.php line 37

Open in your IDE?
  1. <?php
  2. namespace Products\NotificationsBundle\Subscriber\OneRoster;
  3. use Cms\CoreBundle\Entity\AbstractOneRosterEntity;
  4. use Cms\CoreBundle\Entity\OneRoster\OneRosterOrg;
  5. use Cms\CoreBundle\Entity\OneRoster\OneRosterUser;
  6. use Cms\CoreBundle\Entity\OneRosterSync;
  7. use Cms\CoreBundle\Events\OneRosterProcessEvent;
  8. use Doctrine\Common\Util\ClassUtils;
  9. use Products\NotificationsBundle\Entity\AbstractList;
  10. use Products\NotificationsBundle\Entity\Lists\DistrictList;
  11. use Products\NotificationsBundle\Entity\Lists\SchoolList;
  12. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  13. /**
  14.  * Class OneRosterOrgProcessSubscriber
  15.  * @package Products\NotificationsBundle\Subscriber\OneRoster
  16.  */
  17. final class OneRosterOrgProcessSubscriber extends AbstractNotificationsOneRosterSubscriber implements EventSubscriberInterface
  18. {
  19.     /**
  20.      * {@inheritdoc}
  21.      */
  22.     public static function getSubscribedEvents(): array
  23.     {
  24.         return [
  25.             OneRosterProcessEvent::EVENT__ORG => [
  26.                 ['syncList'0],
  27.             ],
  28.         ];
  29.     }
  30.     /**
  31.      * @param OneRosterProcessEvent $event
  32.      */
  33.     public function syncList(OneRosterProcessEvent $event): void
  34.     {
  35.         // ensure we are meant to process this
  36.         if ( ! $this->checkTypes($event->getJob(), [
  37.             OneRosterSync::STRATEGIES__NOTIFICATIONS__STAFF,
  38.             OneRosterSync::STRATEGIES__NOTIFICATIONS__FAMILY,
  39.             OneRosterSync::STRATEGIES__NOTIFICATIONS__STUDENTS,
  40.             OneRosterSync::STRATEGIES__NOTIFICATIONS__COMMUNITY,
  41.         ])) {
  42.             return;
  43.         }
  44.         // get the org
  45.         $org $event->getEntity();
  46.         if ( ! $org instanceof OneRosterOrg) {
  47.             throw new \Exception(sprintf(
  48.                 'Org is not of proper type, got "%s".',
  49.                 ClassUtils::getClass($org)
  50.             ));
  51.         }
  52.         // branch on the org type
  53.         switch (true) {
  54.             case $org->isType(AbstractOneRosterEntity::ENUMS__ORG_TYPE__DISTRICT):
  55.             case $org->isType(AbstractOneRosterEntity::ENUMS__ORG_TYPE__SCHOOL):
  56.                 break;
  57.             default:
  58.                 // DEBUGGING
  59.                 $event->getOutput()->writeln(sprintf(
  60.                     'Org type for #%s is "%s", skipping...',
  61.                     $org->getSourcedId(),
  62.                     $org->getType()
  63.                 ));
  64.                 return;
  65.         }
  66.         // if we have a district, ensure it is the same id that should be on the sync config
  67.         // also helps ensure we only have one district just in case incoming data gets funky
  68.         if ($org->getType() === AbstractOneRosterEntity::ENUMS__ORG_TYPE__DISTRICT && strcasecmp($org->getSourcedId(), $event->getSync()->getDistrictId()) !== 0) {
  69.             throw new \Exception(sprintf(
  70.                 'District org found that does not match sync settings; got "%s", expected "%s".',
  71.                 $org->getSourcedId(),
  72.                 $event->getSync()->getDistrictId()
  73.             ));
  74.         }
  75.         // if we are dealing with the district, we want to manage a district list
  76.         if ($org->getType() === AbstractOneRosterEntity::ENUMS__ORG_TYPE__DISTRICT && strcasecmp($org->getSourcedId(), $event->getSync()->getDistrictId()) === 0) {
  77.             // find existing district list
  78.             $lists $this->em->getRepository(DistrictList::class)->findBy([
  79.                 'onerosterId' => $org->getSourcedId(),
  80.             ]);
  81.             // manage the types we are interested in
  82.             $types array_values(array_filter([
  83.                 ($this->checkTypes($event->getJob(), [OneRosterSync::STRATEGIES__NOTIFICATIONS__STAFF])) ? OneRosterUser::TYPES__STAFF null,
  84.                 ($this->checkTypes($event->getJob(), [OneRosterSync::STRATEGIES__NOTIFICATIONS__FAMILY])) ? OneRosterUser::TYPES__FAMILY null,
  85.                 ($this->checkTypes($event->getJob(), [OneRosterSync::STRATEGIES__NOTIFICATIONS__STUDENTS])) ? OneRosterUser::TYPES__STUDENT null,
  86.                 ($this->checkTypes($event->getJob(), [OneRosterSync::STRATEGIES__NOTIFICATIONS__COMMUNITY])) ? OneRosterUser::TYPES__COMMUNITY null,
  87.             ]));
  88.             // loop over all the grades
  89.             $managed = [];
  90.             foreach ($types as $type) {
  91.                 // try to find if we have a list already
  92.                 $list null;
  93.                 foreach ($lists as $thing) {
  94.                     if ($thing->getTypes() === $type) {
  95.                         $managed[] = $list $thing;
  96.                         break;
  97.                     }
  98.                 }
  99.                 // if we don't have one, need to make one
  100.                 if ( ! $list) {
  101.                     $managed[] = $list = new DistrictList();
  102.                 }
  103.                 // set things
  104.                 $list
  105.                     ->setName(sprintf(
  106.                         '[District] %s (%s)',
  107.                         $org->getName(),
  108.                         $this->translator->trans(sprintf(
  109.                             'app.notifications.profiles.types.%s',
  110.                             OneRosterUser::TYPES_LOOKUP[$type]
  111.                         ))
  112.                     ))
  113.                     ->setTypes($type)
  114.                     ->markFlag(AbstractList::FLAGS__FIXED)
  115.                     ->setOneRosterId($org->getSourcedId())
  116.                 ;
  117.                 // handle deletion tracking
  118.                 $list->setOneRosterArchived($org->isStatusToBeDeleted());
  119.             }
  120.             // make a diff and reset contacts to those that are not matched
  121.             // these will need trashed as they are extra and do not match what is in the record from oneroster
  122.             $removals array_udiff($lists$managed, function (DistrictList $aDistrictList $b) {
  123.                 return ($a === $b) ? $a->getId() <=> $b->getId();
  124.             });
  125.             // DEBUGGING
  126.             array_walk($removals, function (DistrictList $list) use ($event) {
  127.                 $event->getOutput()->writeln(sprintf(
  128.                     '    Deleting    %s (%s | %s | %s)',
  129.                     ClassUtils::getClass($list),
  130.                     $list->getName(),
  131.                     $list->getOneRosterId(),
  132.                     $list->getId() ?: '-'
  133.                 ));
  134.             });
  135.             array_walk($managed, function (DistrictList $list) use ($event) {
  136.                 $event->getOutput()->writeln(sprintf(
  137.                     '    %s    %s (%s | %s | %s)',
  138.                     (empty($list->getId())) ? 'Generating' 'Updating',
  139.                     ClassUtils::getClass($list),
  140.                     $list->getName(),
  141.                     $list->getOneRosterId(),
  142.                     $list->getId() ?: '-'
  143.                 ));
  144.             });
  145.             // everything is patched up, ready to do database work
  146.             $this->em->persistAll($managed);
  147.             $this->em->removeAll($removals);
  148.             $this->em->flush();
  149.             // need to nuke extra district lists that don't match us
  150.             $this->em->createQueryBuilder()
  151.                 ->delete(DistrictList::class, 'lists')
  152.                 ->andWhere('lists.onerosterId != :onerosterId')
  153.                 ->setParameter('onerosterId'$org->getSourcedId())
  154.                 ->getQuery()
  155.                 ->execute();
  156.             // need to nuke extra school lists in the even that district object used to be school
  157.             $this->em->createQueryBuilder()
  158.                 ->delete(SchoolList::class, 'lists')
  159.                 ->andWhere('lists.onerosterId = :onerosterId')
  160.                 ->setParameter('onerosterId'$org->getSourcedId())
  161.                 ->getQuery()
  162.                 ->execute();
  163.             // quit early
  164.             return;
  165.         }
  166.         // already handled the district
  167.         // if we are a single school, we just skip from here
  168.         // no need to manage existing data as the topic would have already been removed and fk constraints would kick in to delete the lists
  169.         if ($event->getSync()->hasFlag(OneRosterSync::FLAGS__SINGLE_SCHOOL)) {
  170.             // DEBUGGING
  171.             $event->getOutput()->writeln(sprintf(
  172.                 'Skipping org "%s"; single school setup and this org is not the district',
  173.                 $org->getSourcedId()
  174.             ));
  175.             // quit early
  176.             return;
  177.         }
  178.         // load all of our existing lists, if any
  179.         $lists $this->em->getRepository(SchoolList::class)->findBy([
  180.             'onerosterId' => $org->getSourcedId(),
  181.         ]);
  182.         // manage the types we are interested in
  183.         $types array_values(array_filter([
  184.             ($this->checkTypes($event->getJob(), [OneRosterSync::STRATEGIES__NOTIFICATIONS__STAFF])) ? OneRosterUser::TYPES__STAFF null,
  185.             ($this->checkTypes($event->getJob(), [OneRosterSync::STRATEGIES__NOTIFICATIONS__FAMILY])) ? OneRosterUser::TYPES__FAMILY null,
  186.             ($this->checkTypes($event->getJob(), [OneRosterSync::STRATEGIES__NOTIFICATIONS__STUDENTS])) ? OneRosterUser::TYPES__STUDENT null,
  187.             ($this->checkTypes($event->getJob(), [OneRosterSync::STRATEGIES__NOTIFICATIONS__COMMUNITY])) ? OneRosterUser::TYPES__COMMUNITY null,
  188.         ]));
  189.         // loop over all the grades
  190.         $managed = [];
  191.         foreach ($types as $type) {
  192.             // try to find if we have a list already
  193.             $list null;
  194.             foreach ($lists as $thing) {
  195.                 if ($thing->getTypes() === $type) {
  196.                     $managed[] = $list $thing;
  197.                     break;
  198.                 }
  199.             }
  200.             // if we don't have one, need to make one
  201.             if ( ! $list) {
  202.                 $managed[] = $list = new SchoolList();
  203.             }
  204.             // set things
  205.             $list
  206.                 ->setName(sprintf(
  207.                     '%s (%s)',
  208.                     $org->getName(),
  209.                     $this->translator->trans(sprintf(
  210.                         'app.notifications.profiles.types.%s',
  211.                         OneRosterUser::TYPES_LOOKUP[$type]
  212.                     ))
  213.                 ))
  214.                 ->setTypes($type)
  215.                 ->markFlag(AbstractList::FLAGS__FIXED)
  216.                 ->setOneRosterId($org->getSourcedId())
  217.             ;
  218.             // handle deletion tracking
  219.             $list->setOneRosterArchived($org->isStatusToBeDeleted());
  220.         }
  221.         // make a diff and reset contacts to those that are not matched
  222.         // these will need trashed as they are extra and do not match what is in the record from oneroster
  223.         $removals array_udiff($lists$managed, function (SchoolList $aSchoolList $b) {
  224.             return ($a === $b) ? $a->getId() <=> $b->getId();
  225.         });
  226.         // DEBUGGING
  227.         array_walk($removals, function (SchoolList $list) use ($event) {
  228.             $event->getOutput()->writeln(sprintf(
  229.                 '    Deleting    %s (%s | %s | %s)',
  230.                 ClassUtils::getClass($list),
  231.                 $list->getName(),
  232.                 $list->getOneRosterId(),
  233.                 $list->getId() ?: '-'
  234.             ));
  235.         });
  236.         array_walk($managed, function (SchoolList $list) use ($event) {
  237.             $event->getOutput()->writeln(sprintf(
  238.                 '    %s    %s (%s | %s | %s)',
  239.                 (empty($list->getId())) ? 'Generating' 'Updating',
  240.                 ClassUtils::getClass($list),
  241.                 $list->getName(),
  242.                 $list->getOneRosterId(),
  243.                 $list->getId() ?: '-'
  244.             ));
  245.         });
  246.         // everything is patched up, ready to do database work
  247.         $this->em->persistAll($managed);
  248.         $this->em->removeAll($removals);
  249.         $this->em->flush();
  250.     }
  251. }