Skip to content

Commit

Permalink
adds helper to find unique values from repositories
Browse files Browse the repository at this point in the history
  • Loading branch information
Elorfin committed Sep 6, 2024
1 parent e6ff46d commit e6463ab
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 104 deletions.
76 changes: 76 additions & 0 deletions src/main/app/Repository/UniqueValueFinder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace Claroline\AppBundle\Repository;

/**
* Helper to search if a string value is used and will increment it till the value is not used anymore.
* This is useful to generate unique names or codes.
*/
trait UniqueValueFinder
{
abstract protected function getEntityManager();

abstract protected function getEntityName();

/**
* Generates a unique value from given one by iterating it.
*/
public function findNextUnique(string $propName, string $searchValue, string $incrementSeparator = '_'): string
{
// search in DB
$existingValues = $this->findByPropWithPrefix($propName, $searchValue);

$getter = 'get'.ucfirst($propName);

// search in pending insertions
$toInsert = $this->getEntityManager()->getUnitOfWork()->getScheduledEntityInsertions();
foreach ($toInsert as $entityInsertion) {
if (is_a($entityInsertion, $this->getEntityName())) {
$entityValue = strtolower($entityInsertion->$getter());
if (str_starts_with($entityValue, strtolower($searchValue))) {
$existingValues[] = $entityValue;
}
}
}

// search in pending updates
$toUpdate = $this->getEntityManager()->getUnitOfWork()->getScheduledEntityUpdates();
foreach ($toUpdate as $entityUpdate) {
if (is_a($entityUpdate, $this->getEntityName())) {
$entityValue = strtolower($entityUpdate->$getter());
if (str_starts_with($entityValue, strtolower($searchValue))) {
$existingValues[] = $entityValue;
}
}
}

if (empty($existingValues)) {
return $searchValue;
}

$index = count($existingValues);
do {
++$index;
$currentValue = $searchValue.$incrementSeparator.$index;
$lowerCurrentValue = strtolower($currentValue);
} while (in_array($lowerCurrentValue, $existingValues));

return $currentValue;
}

private function findByPropWithPrefix(string $propName, string $prefix): array
{
$found = $this->getEntityManager()->createQueryBuilder()
->select("LOWER(obj.$propName) AS prop")
->from($this->getEntityName(), 'obj')
->where("LOWER(obj.$propName) LIKE :search")
->setParameter('search', strtolower(addcslashes($prefix, '%_')).'%')
->getQuery()
->getResult()
;

return array_map(function (array $ws) {
return $ws['prop'];
}, $found);
}
}
37 changes: 1 addition & 36 deletions src/main/core/Manager/ResourceManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public function create(
$resource->getMimeType();
$node->setMimeType($mimeType);
$node->setName($resource->getName());
$node->setCode($this->getUniqueCode($resource->getName()));
$node->setCode($this->resourceNodeRepo->findNextUnique('code', $resource->getName()));

if (!empty($creator)) {
$node->setCreator($creator);
Expand Down Expand Up @@ -377,41 +377,6 @@ public function isManager(ResourceNode $resourceNode): bool
return $this->rightsManager->isManager($resourceNode);
}

/**
* Generates a unique resource code from given one by iterating it.
*/
public function getUniqueCode(string $code): string
{
$existingCodes = $this->resourceNodeRepo->findCodesWithPrefix($code);

$toInsert = $this->om->getUnitOfWork()->getScheduledEntityInsertions();
foreach ($toInsert as $entityInsertion) {
if ($entityInsertion instanceof ResourceNode && str_starts_with(strtolower($entityInsertion->getCode()), strtolower($code))) {
$existingCodes[] = strtolower($entityInsertion->getCode());
}
}

$toUpdate = $this->om->getUnitOfWork()->getScheduledEntityUpdates();
foreach ($toUpdate as $entityUpdate) {
if ($entityUpdate instanceof ResourceNode && str_starts_with(strtolower($entityUpdate->getCode()), strtolower($code))) {
$existingCodes[] = strtolower($entityUpdate->getCode());
}
}

if (empty($existingCodes)) {
return $code;
}

$index = 0;
$currentCode = $code;
while (in_array(strtolower($currentCode), $existingCodes)) {
++$index;
$currentCode = $code.'_'.$index;
}

return $currentCode;
}

/**
* @deprecated
*/
Expand Down
24 changes: 2 additions & 22 deletions src/main/core/Manager/Workspace/WorkspaceManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,8 @@ public function getTokenRoles(TokenInterface $token, Workspace $workspace): arra

public function archive(Workspace $workspace): Workspace
{
//$workspace->setName('[archive]'.$workspace->getName());
//$workspace->setCode('[archive]'.$workspace->getCode().uniqid());
// $workspace->setName('[archive]'.$workspace->getName());
// $workspace->setCode('[archive]'.$workspace->getCode().uniqid());
$workspace->setArchived(true);

$this->om->persist($workspace);
Expand Down Expand Up @@ -272,24 +272,4 @@ public function unregister(AbstractRoleSubject $subject, Workspace $workspace, a
{
$this->crud->patch($subject, 'role', Crud::COLLECTION_REMOVE, $workspace->getRoles()->toArray(), $options);
}

/**
* Generates a unique workspace code from given one by iterating it.
*/
public function getUniqueCode(string $code): string
{
$existingCodes = $this->workspaceRepo->findCodesWithPrefix($code);
if (empty($existingCodes)) {
return $code;
}

$index = count($existingCodes);
do {
++$index;
$currentCode = $code.'_'.$index;
$lowerCurrentCode = strtolower($currentCode);
} while (in_array($lowerCurrentCode, $existingCodes));

return $currentCode;
}
}
23 changes: 3 additions & 20 deletions src/main/core/Repository/Resource/ResourceNodeRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@

namespace Claroline\CoreBundle\Repository\Resource;

use Claroline\AppBundle\Repository\UniqueValueFinder;
use Claroline\CoreBundle\Entity\Resource\ResourceNode;
use Claroline\CoreBundle\Entity\Workspace\Workspace;
use Doctrine\ORM\AbstractQuery;
use Gedmo\Tree\Entity\Repository\MaterializedPathRepository;

class ResourceNodeRepository extends MaterializedPathRepository
{
use UniqueValueFinder;

public function search(string $search, int $nbResults)
{
return $this->createQueryBuilder('n')
Expand Down Expand Up @@ -163,25 +165,6 @@ public function countActiveResources(array $workspaces = [], array $organization
return (int) $qb->getQuery()->getSingleScalarResult();
}

/**
* Returns the list of resource codes starting with $prefix.
* Useful to auto generate unique resource codes.
*/
public function findCodesWithPrefix(string $prefix): array
{
$results = $this->getEntityManager()->createQuery('
SELECT LOWER(n.code) AS code
FROM Claroline\CoreBundle\Entity\Resource\ResourceNode n
WHERE LOWER(n.code) LIKE :search
')
->setParameter('search', strtolower(addcslashes($prefix, '%_')).'%')
->getResult(AbstractQuery::HYDRATE_ARRAY);

return array_map(function (array $resource) {
return $resource['code'];
}, $results);
}

/**
* DO NOT USE IT !
* It's only here to avoid updating the updatedAt prop each time a user open a resource.
Expand Down
23 changes: 3 additions & 20 deletions src/main/core/Repository/WorkspaceRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@

namespace Claroline\CoreBundle\Repository;

use Claroline\AppBundle\Repository\UniqueValueFinder;
use Claroline\CoreBundle\Component\Context\WorkspaceContext;
use Claroline\CoreBundle\Entity\Workspace\Workspace;
use Doctrine\ORM\EntityRepository;

class WorkspaceRepository extends EntityRepository
{
use UniqueValueFinder;

public function search(string $search, int $nbResults)
{
return $this->createQueryBuilder('w')
Expand Down Expand Up @@ -167,24 +170,4 @@ public function findByCodes(array $codes)

return $query->getResult();
}

/**
* Returns the list of workspace codes starting with $prefix.
* Useful to auto generate unique workspace codes.
*/
public function findCodesWithPrefix(string $prefix): array
{
return array_map(
function (array $ws) {
return $ws['code'];
},
$this->getEntityManager()->createQuery('
SELECT LOWER(w.code) AS code
FROM Claroline\CoreBundle\Entity\Workspace\Workspace w
WHERE LOWER(w.code) LIKE :search
')
->setParameter('search', strtolower(addcslashes($prefix, '%_')).'%')
->getResult()
);
}
}
17 changes: 13 additions & 4 deletions src/main/core/Subscriber/Crud/ResourceNodeSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
use Claroline\AppBundle\Event\Crud\CreateEvent;
use Claroline\AppBundle\Event\Crud\DeleteEvent;
use Claroline\AppBundle\Event\Crud\UpdateEvent;
use Claroline\AppBundle\Event\CrudEvents;
use Claroline\AppBundle\Persistence\ObjectManager;
use Claroline\CoreBundle\API\Serializer\Resource\ResourceNodeSerializer;
use Claroline\CoreBundle\Entity\Resource\AbstractResource;
use Claroline\CoreBundle\Entity\Resource\ResourceNode;
use Claroline\CoreBundle\Entity\User;
use Claroline\AppBundle\Event\CrudEvents;
use Claroline\CoreBundle\Library\Normalizer\CodeNormalizer;
use Claroline\CoreBundle\Manager\FileManager;
use Claroline\CoreBundle\Manager\Resource\ResourceLifecycleManager;
use Claroline\CoreBundle\Manager\Resource\RightsManager;
Expand Down Expand Up @@ -53,8 +54,11 @@ public function preCreate(CreateEvent $event): void
$resourceNode = $event->getObject();

// make sure the resource code is unique
// use name as code if not defined
$resourceNode->setCode($this->resourceManager->getUniqueCode(!empty($resourceNode->getCode()) ? $resourceNode->getCode() : $resourceNode->getName()));
$resourceNodeCode = $this->om->getRepository(ResourceNode::class)->findNextUnique(
'code',
$resourceNode->getCode() ?? CodeNormalizer::normalize($resourceNode->getName())
);
$resourceNode->setCode($resourceNodeCode);

// set the creator of the resource
$user = $this->tokenStorage->getToken()->getUser();
Expand Down Expand Up @@ -155,7 +159,12 @@ public function preCopy(CopyEvent $event): void
return;
}

$newNode->setCode($this->resourceManager->getUniqueCode($newNode->getCode() ?? $newNode->getName()));
// make sure the resource code is unique
$resourceNodeCode = $this->om->getRepository(ResourceNode::class)->findNextUnique(
'code',
$newNode->getCode() ?? CodeNormalizer::normalize($newNode->getName())
);
$newNode->setCode($resourceNodeCode);

// set the creator of the copy
$user = $this->tokenStorage->getToken()->getUser();
Expand Down
6 changes: 4 additions & 2 deletions src/main/core/Subscriber/Crud/WorkspaceSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ public function preCreate(CreateEvent $event): void
$options = $event->getOptions();

// make sure the workspace code is unique and generate one if missing
$workspaceCode = $this->manager->getUniqueCode(
$workspaceCode = $this->om->getRepository(Workspace::class)->findNextUnique(
'code',
$workspace->getCode() ?? CodeNormalizer::normalize($workspace->getName())
);
$workspace->setCode($workspaceCode);
Expand Down Expand Up @@ -108,7 +109,8 @@ public function preCopy(CopyEvent $event): void

// make sure the workspace code is unique
if (!empty($copy->getCode())) {
$copy->setCode($this->manager->getUniqueCode($copy->getCode()));
$workspaceCode = $this->om->getRepository(Workspace::class)->findNextUnique('code', $copy->getCode());
$copy->setCode($workspaceCode);
}

$this->copy($original, $copy, in_array(Options::AS_MODEL, $options));
Expand Down

0 comments on commit e6463ab

Please sign in to comment.