src/Cms/FrontendBundle/Service/Resolvers/SchoolResolver.php line 131

Open in your IDE?
  1. <?php
  2. namespace Cms\FrontendBundle\Service\Resolvers;
  3. use App\Doctrine\Repository\System\SchoolRepository;
  4. use App\Entity\System\School;
  5. use Cms\ContainerBundle\Entity\Container;
  6. use Cms\ContainerBundle\Entity\Containers\GenericContainer;
  7. use Cms\ContainerBundle\Entity\Containers\IntranetContainer;
  8. use Cms\ContainerBundle\Entity\Containers\PersonalContainer;
  9. use Cms\TenantBundle\Entity\Tenant;
  10. use Cms\TenantBundle\Model\SimpleTenantableInterface;
  11. use Cms\TenantBundle\Model\TenantableInterface;
  12. use Products\NotificationsBundle\Entity\AbstractList;
  13. use Products\NotificationsBundle\Entity\Lists\ConditionList;
  14. use Products\NotificationsBundle\Entity\Lists\DistrictList;
  15. use Products\NotificationsBundle\Entity\Lists\SchoolList;
  16. use Products\NotificationsBundle\Entity\Student;
  17. /**
  18.  *
  19.  */
  20. final class SchoolResolver extends AbstractResolver
  21. {
  22.     /**
  23.      * @param TenantableInterface $tenantable
  24.      * @return School|null
  25.      */
  26.     public function resolveDistrictByTenant(SimpleTenantableInterface $tenantable): ?School
  27.     {
  28.         // generate the lookup
  29.         $lookup $this->attempt(__FUNCTION__$tenantable->getTenant()->getId());
  30.         if ( ! is_string($lookup)) {
  31.             return $lookup;
  32.         }
  33.         // set in cache and return the value
  34.         return $this->cache(
  35.             $lookup,
  36.             $this->em->getRepository(School::class)->findOneBy([
  37.                 'tenant' => $tenantable->getTenant(),
  38.                 'type' => School::TYPES__DISTRICT,
  39.             ])
  40.         );
  41.     }
  42.     /**
  43.      * @param SimpleTenantableInterface $tenantable
  44.      * @return array|School[]
  45.      */
  46.     public function resolveSchoolsByTenant(SimpleTenantableInterface $tenantable): array
  47.     {
  48.         // generate the lookup
  49.         $lookup $this->attempt(__FUNCTION__$tenantable->getTenant()->getId());
  50.         if ( ! is_string($lookup)) {
  51.             return $lookup;
  52.         }
  53.         // find all the schools
  54.         $schools $this->em->getRepository(School::class)->findAll();
  55.         // set in cache and return the value
  56.         return $this->cache($lookup$schools);
  57.     }
  58.     /**
  59.      * @param int $type
  60.      * @return array|School[]
  61.      */
  62.     public function resolveSchoolsByType(int $type): array
  63.     {
  64.         // generate the lookup
  65.         $lookup $this->attempt(__FUNCTION__$type);
  66.         if ( ! is_string($lookup)) {
  67.             return $lookup;
  68.         }
  69.         // find schools by type
  70.         $schools $this->em->getRepository(School::class)->findBy(
  71.             [
  72.                 'type' => $type,
  73.             ],
  74.             [
  75.                 'name' => 'ASC',
  76.             ]
  77.         );
  78.         return $this->cache($lookup$schools);
  79.     }
  80.     /**
  81.      * @param SimpleTenantableInterface $tenantable
  82.      * @param int $types
  83.      * @return array|School[]
  84.      */
  85.     public function resolveSchoolsByTypes(SimpleTenantableInterface $tenantableint $types): array
  86.     {
  87.         // generate the lookup
  88.         $lookup $this->attempt(__FUNCTION__$types);
  89.         if ( ! is_string($lookup)) {
  90.             return $lookup;
  91.         }
  92.         /** @var SchoolRepository $schoolRepository */
  93.         $schoolRepository $this->em->getRepository(School::class);
  94.         $schools $schoolRepository->findAllForTenantAndSchoolTypes(
  95.             $tenantable->getTenant(),
  96.             $types,
  97.         );
  98.         return $this->cache($lookup$schools);
  99.     }
  100.     /**
  101.      * @param Container $department
  102.      * @return School|null
  103.      */
  104.     public function resolveSchoolByDepartment(Container $department): ?School
  105.     {
  106.         // generate the lookup
  107.         $lookup $this->attempt(__FUNCTION__$department->getId());
  108.         if ( ! is_string($lookup)) {
  109.             return $lookup;
  110.         }
  111.         // branch on the type
  112.         switch (true) {
  113.             // passthrough to specific handlers
  114.             case $department instanceof GenericContainer:
  115.                 $school $this->resolveSchoolByPublicDepartment($department);
  116.                 break;
  117.             case $department instanceof IntranetContainer:
  118.                 $school $this->resolveSchoolByIntranetDepartment($department);
  119.                 break;
  120.             case $department instanceof PersonalContainer:
  121.                 $school $this->resolveSchoolByPrivateDepartment($department);
  122.                 break;
  123.             // unsupported
  124.             default:
  125.                 throw new \LogicException();
  126.         }
  127.         // set in cache and return the value
  128.         return $this->cache($lookup$school);
  129.     }
  130.     /**
  131.      * @param GenericContainer $department
  132.      * @return School|null
  133.      */
  134.     protected function resolveSchoolByPublicDepartment(GenericContainer $department): ?School
  135.     {
  136.         // generate the lookup
  137.         $lookup $this->attempt(__FUNCTION__$department->getId());
  138.         if ( ! is_string($lookup)) {
  139.             return $lookup;
  140.         }
  141.         // get all the schools
  142.         // TODO: prevent this by attaching school to department objects as well as departments to schools
  143.         $schools $this->resolveSchoolsByTenant($department);
  144.         // if not found we need to pull our ancestors, the closest first
  145.         $ancestors $this->rm->getDepartmentResolver()->resolveNearestAncestorsWithDepartment($department);
  146.         // start looping over them to try and find a school
  147.         $found null;
  148.         foreach ($ancestors as $ancestor) {
  149.             foreach ($schools as $school) {
  150.                 if ($school->getDepartment() && $school->getDepartment()->getId() === $ancestor->getId()) {
  151.                     $found $school;
  152.                     break 2;
  153.                 }
  154.             }
  155.         }
  156.         // set in cache and return the value
  157.         return $this->cache($lookup$found);
  158.     }
  159.     /**
  160.      * @param IntranetContainer $department
  161.      * @return School|null
  162.      */
  163.     protected function resolveSchoolByIntranetDepartment(IntranetContainer $department): ?School
  164.     {
  165.         // generate the lookup
  166.         $lookup $this->attempt(__FUNCTION__$department->getId());
  167.         if ( ! is_string($lookup)) {
  168.             return $lookup;
  169.         }
  170.         // TODO: currently no school support for intranets?
  171.         // set in cache and return the value
  172.         return $this->cache($lookupnull);
  173.     }
  174.     /**
  175.      * @param PersonalContainer $department
  176.      * @return School|null
  177.      */
  178.     protected function resolveSchoolByPrivateDepartment(PersonalContainer $department): ?School
  179.     {
  180.         // generate the lookup
  181.         $lookup $this->attempt(__FUNCTION__$department->getId());
  182.         if ( ! is_string($lookup)) {
  183.             return $lookup;
  184.         }
  185.         // TODO
  186.         // set in cache and return the value
  187.         return $this->cache($lookupnull);
  188.     }
  189.     /**
  190.      * When given a set of sourcedIds, will return an array of information for those IDs.
  191.      * The function will attempt to pull all IDs it can from the resolver cache first.
  192.      * Then, any remaining IDs that were not in cache in any way (NULL or a School entity) will be looked for in the database.
  193.      * This will return an associative array with keys of the "sourcedIds" and values of what was found (can be NULL or a School).
  194.      * If a cleaner array result is needed, the calling code will need to utilize array functions to clean up the result.
  195.      * (If this becomes a fairly common practice, a helper function in this class could be introduced to perform that logic.)
  196.      *
  197.      * TODO: 2024-06-25
  198.      * Do we want to keep NULLs if the sourcedId lookup doesn't return anything?
  199.      *
  200.      * @param array|string[] $sourcedIds
  201.      * @return array|School[]
  202.      */
  203.     public function resolveSchoolsBySourcedIds(array $sourcedIds): array
  204.     {
  205.         // holder for ids we have to grab from the database still
  206.         // this method optimizes the resolver cache, so if we don't need to find an entity in the db, we don't query for it
  207.         $oneRosterOrgs = [];
  208.         // generate the lookups
  209.         // a string value in the array is a cache key meaning the entity has not been loaded yet
  210.         // a non-string value means the sourcedid has been looked up prior in the db and the result was cached
  211.         $lookups = [];
  212.         foreach ($sourcedIds as $sourcedId) {
  213.             // attempts to find whether the sourcedId has already been looked for in the db
  214.             // uses the function name of the single-sourcedId method so that we can piggyback off anything already cached
  215.             $lookups[$sourcedId] = $lookup $this->attempt('resolveSchoolBySourcedId'$sourcedId);
  216.             // if a string, that means we need to try to find this in the db still
  217.             if (is_string($lookup)) {
  218.                 $oneRosterOrgs[] = $sourcedId;
  219.             }
  220.         }
  221.         // find the rest of the schools
  222.         // do only if we have ids to try and find
  223.         if ($oneRosterOrgs) {
  224.             // run the database query
  225.             // note that some sourcedIds may not be found
  226.             $schools $this->em->getRepository(School::class)->findBy([
  227.                 'oneRosterOrg' => $oneRosterOrgs,
  228.             ]);
  229.             // loop over the results
  230.             foreach ($schools as $school) {
  231.                 // first, we need to add what we found to the cache for future use
  232.                 $this->cache($lookups[$school->getOneRosterOrg()], $school);
  233.                 // then we need to fix our local copy of results
  234.                 $lookups[$school->getOneRosterOrg()] = $school;
  235.             }
  236.         }
  237.         // need to run through the array one more time
  238.         // any values that are still strings need to be nulled as they were not found
  239.         // this means caching nulls so a future attempt to find the same sourcedId does not result in another db query
  240.         foreach ($lookups as $key => $value) {
  241.             if (is_string($value)) {
  242.                 $this->cache($valuenull);
  243.                 $lookups[$key] = null;
  244.             }
  245.         }
  246.         // note that this will return nulls in the final array
  247.         // use array functions to clean up the result if needed
  248.         return $lookups;
  249.     }
  250.     /**
  251.      * Attempts to find a single School based on it's One Roster "org" association based on the "sourcedId".
  252.      *
  253.      * @param string|null $sourcedId
  254.      * @return School|null
  255.      */
  256.     public function resolveSchoolBySourcedId(?string $sourcedId): ?School
  257.     {
  258.         // allow for nulls for dx
  259.         if ( ! ($sourcedId ?: null)) {
  260.             return null;
  261.         }
  262.         // generate the lookup
  263.         $lookup $this->attempt(__FUNCTION__$sourcedId);
  264.         if ( ! is_string($lookup)) {
  265.             return $lookup;
  266.         }
  267.         // find school by the id
  268.         $school $this->em->getRepository(School::class)->findOneBy([
  269.             'oneRosterOrg' => $sourcedId,
  270.         ]);
  271.         // set in cache and return the value
  272.         return $this->cache($lookup$school);
  273.     }
  274.     /**
  275.      * @param Student[] $students
  276.      * @return School[]
  277.      */
  278.     public function resolveSchoolsByStudents(array $students): array
  279.     {
  280.         $primarySchoolSourceIds array_unique(
  281.             array_filter(
  282.                 array_map(static function (Student $student) {
  283.                     return $student->getMetadataPrimarySchool();
  284.                 }, $students)
  285.             )
  286.         );
  287.         return $this->resolveSchoolsBySourcedIds($primarySchoolSourceIds);
  288.     }
  289.     /**
  290.      * @param AbstractList[] $lists
  291.      * @return School[]
  292.      */
  293.     public function resolveSchoolsByLists(array $lists): array
  294.     {
  295.         $schoolSourceIds array_unique(
  296.             array_filter(
  297.                 array_map(static function (AbstractList $list) {
  298.                     switch (true) {
  299.                         case $list instanceof DistrictList || $list instanceof SchoolList:
  300.                             return $list->getOneRosterId();
  301.                         case $list instanceof ConditionList:
  302.                             return $list->getSchool() ? $list->getSchool()->getOneRosterOrg() : null;
  303.                         default:
  304.                             return null;
  305.                     }
  306.                 }, $lists)
  307.             )
  308.         );
  309.         return $this->resolveSchoolsBySourcedIds($schoolSourceIds);
  310.     }
  311. }