Skip to content

Commit

Permalink
tec: Improve performance when fetching from the database
Browse files Browse the repository at this point in the history
  • Loading branch information
marien-probesys committed Jul 3, 2024
2 parents 4a35802 + 84009a1 commit 9f5ce7b
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 73 deletions.
2 changes: 1 addition & 1 deletion src/Controller/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function index(UserRepository $userRepository, UserSorter $userSorter): R
{
$this->denyAccessUnlessGranted('admin:manage:users');

$users = $userRepository->findAll();
$users = $userRepository->findAllWithAuthorizations();
$userSorter->sort($users);

return $this->render('users/index.html.twig', [
Expand Down
102 changes: 46 additions & 56 deletions src/Repository/AuthorizationRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use App\Entity\User;
use App\Uid\UidGeneratorInterface;
use App\Uid\UidGeneratorTrait;
use App\Utils\Time;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

Expand Down Expand Up @@ -105,41 +104,27 @@ public function grantTeamAuthorization(Team $team, TeamAuthorization $teamAuthor
*/
public function getAuthorizations(string $authorizationType, User $user, mixed $scope): array
{
$cacheKey = self::getCacheKey($authorizationType, $user, $scope);
if (isset($this->cacheAuthorizations[$cacheKey])) {
return $this->cacheAuthorizations[$cacheKey];
}

if ($authorizationType === 'orga' && $scope !== null) {
$authorizations = $this->getOrgaAuthorizationsFor($user, $scope);
$this->cacheAuthorizations[$cacheKey] = $authorizations;
return $this->getOrgaAuthorizationsFor($user, $scope);
} elseif ($authorizationType === 'admin' && $scope === null) {
$authorizations = $this->getAdminAuthorizationsFor($user);
$this->cacheAuthorizations[$cacheKey] = $authorizations;
return $this->getAdminAuthorizationsFor($user);
} else {
throw new \DomainException('Given authorization type and scope are not supported together');
}

return $authorizations;
}

/**
* @return Authorization[]
*/
public function getAdminAuthorizationsFor(User $user): array
{
$entityManager = $this->getEntityManager();

$query = $entityManager->createQuery(<<<SQL
SELECT a, r
FROM App\Entity\Authorization a
JOIN a.role r
WHERE a.holder = :user
AND (r.type = 'admin' OR r.type = 'super')
SQL);
$query->setParameter('user', $user);

return $query->getResult();
$authorizations = $this->loadUserAuthorizations($user);

return array_filter($authorizations, function ($authorization): bool {
$role = $authorization->getRole();
$roleType = $role->getType();
return $roleType === 'admin' || $roleType === 'super';
});
}

/**
Expand All @@ -148,44 +133,49 @@ public function getAdminAuthorizationsFor(User $user): array
*/
public function getOrgaAuthorizationsFor(User $user, mixed $scope): array
{
$entityManager = $this->getEntityManager();

$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder->select(['a', 'r']);
$queryBuilder->from('App\Entity\Authorization', 'a');
$queryBuilder->join('a.role', 'r');
$queryBuilder->where('a.holder = :user');
$queryBuilder->andWhere("r.type = 'user' OR r.type = 'agent'");

$queryBuilder->setParameter('user', $user);

if ($scope instanceof Organization) {
$queryBuilder->andWhere('a.organization = :organization OR a.organization IS NULL');
$queryBuilder->setParameter('organization', $scope);
}

$query = $queryBuilder->getQuery();
return $query->getResult();
$authorizations = $this->loadUserAuthorizations($user);

return array_filter($authorizations, function ($authorization) use ($scope): bool {
$role = $authorization->getRole();
$roleType = $role->getType();
$correctType = $roleType === 'user' || $roleType === 'agent';

if ($scope instanceof Organization) {
$authOrganization = $authorization->getOrganization();
$correctScope = (
$authOrganization === null ||
$authOrganization->getId() === $scope->getId()
);
} else {
$correctScope = true;
}

return $correctType && $correctScope;
});
}

/**
* @param AuthorizationType $authorizationType
* @param ?Scope $scope
* @return Authorization[]
*/
private static function getCacheKey(string $authorizationType, User $user, mixed $scope): string
public function loadUserAuthorizations(User $user): array
{
$baseKey = "{$authorizationType}.{$user->getId()}";

if ($scope === 'any') {
$baseKey .= '.any';
} elseif ($scope instanceof Organization) {
$baseKey .= ".{$scope->getId()}";
} elseif ($scope === null) {
$baseKey .= '.null';
} else {
throw new \DomainException('The given scope is not supported');
$keyCache = $user->getUid();

if (!isset($this->cacheAuthorizations[$keyCache])) {
$entityManager = $this->getEntityManager();

$query = $entityManager->createQuery(<<<SQL
SELECT a, r, o
FROM App\Entity\Authorization a
JOIN a.role r
LEFT JOIN a.organization o
WHERE a.holder = :user
SQL);
$query->setParameter('user', $user);

$this->cacheAuthorizations[$keyCache] = $query->getResult();
}

return hash('sha256', $baseKey);
return $this->cacheAuthorizations[$keyCache];
}
}
15 changes: 10 additions & 5 deletions src/Repository/ContractRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ public function findByOrganizationQuery(Organization $organization): ORM\Query
$entityManager = $this->getEntityManager();

$query = $entityManager->createQuery(<<<SQL
SELECT c
SELECT c, t, ts
FROM App\Entity\Contract c
LEFT JOIN c.tickets t
LEFT JOIN c.timeSpents ts
WHERE c.organization = :organization
ORDER BY c.endAt DESC, c.name
SQL);
Expand All @@ -55,16 +57,19 @@ public function findOngoingByOrganizationsQuery(array $organizations): ORM\Query
$now = Utils\Time::now();

$query = $entityManager->createQuery(<<<SQL
SELECT c
SELECT c, t, ts
FROM App\Entity\Contract c
LEFT JOIN c.tickets t
LEFT JOIN c.timeSpents ts
WHERE c.startAt <= :now
AND :now < c.endAt
AND c.organization IN (:organizations)
GROUP BY c.id
HAVING c.maxHours > (COALESCE(SUM(ts.time), 0) / 60.0)
AND c.maxHours > (
SELECT (COALESCE(SUM(ts2.time), 0) / 60.0)
FROM App\Entity\TimeSpent ts2
WHERE ts2.contract = c
)
ORDER BY c.endAt DESC, c.name
SQL);
Expand Down
25 changes: 14 additions & 11 deletions src/Repository/OrganizationRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

namespace App\Repository;

use App\Entity\Authorization;
use App\Entity\Organization;
use App\Entity\User;
use App\Uid\UidGeneratorInterface;
Expand Down Expand Up @@ -55,17 +56,19 @@ public function findLike(string $value): array
public function findAuthorizedOrganizations(User $user): array
{
$entityManager = $this->getEntityManager();

$query = $entityManager->createQuery(<<<SQL
SELECT IDENTITY(a.organization)
FROM App\Entity\Authorization a
JOIN a.role r
WHERE a.holder = :user
AND (r.type = 'user' OR r.type = 'agent')
SQL);
$query->setParameter('user', $user);

$authorizedOrgaIds = $query->getSingleColumnResult();
/** @var AuthorizationRepository */
$authorizationRepository = $entityManager->getRepository(Authorization::class);
$authorizations = $authorizationRepository->getOrgaAuthorizationsFor($user, scope: 'any');

$authorizedOrgaIds = array_map(function ($authorization): ?int {
$organization = $authorization->getOrganization();

if ($organization) {
return $organization->getId();
} else {
return null;
}
}, $authorizations);

if (in_array(null, $authorizedOrgaIds)) {
// If "null" is returned, it means that an authorization is applied globally.
Expand Down
16 changes: 16 additions & 0 deletions src/Repository/UserRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ public function loadUserByEmail(string $email): ?User
return $query->getOneOrNullResult();
}

/**
* @return User[]
*/
public function findAllWithAuthorizations(): array
{
$entityManager = $this->getEntityManager();

$query = $entityManager->createQuery(<<<SQL
SELECT u, a
FROM App\Entity\User u
LEFT JOIN u.authorizations a
SQL);

return $query->getResult();
}

/**
* @return User[]
*/
Expand Down

0 comments on commit 9f5ce7b

Please sign in to comment.