From e0dd99cffa05918ebde1c8744cfe582eaef64520 Mon Sep 17 00:00:00 2001 From: Ambroise Maupate Date: Wed, 28 Jun 2023 19:35:31 +0200 Subject: [PATCH] feat(Log): Made Log entity autonomous without any relationship, only loose references --- .../migrations/Version20230628170203.php | 40 +++++++++++++++++++ .../Api/ListManager/SolrSearchListManager.php | 1 + lib/RoadizCoreBundle/src/Entity/Log.php | 25 ++++++++---- .../ListManager/AbstractEntityListManager.php | 4 +- .../src/ListManager/EntityListManager.php | 18 +-------- .../ListManager/QueryBuilderListManager.php | 20 +++++++++- .../src/Repository/LogRepository.php | 30 ++++++++++++++ .../src/TwigExtension/LogExtension.php | 1 - .../src/Controllers/HistoryController.php | 8 ++-- .../Controllers/Nodes/HistoryController.php | 22 ++++++---- 10 files changed, 130 insertions(+), 39 deletions(-) create mode 100644 lib/RoadizCoreBundle/migrations/Version20230628170203.php diff --git a/lib/RoadizCoreBundle/migrations/Version20230628170203.php b/lib/RoadizCoreBundle/migrations/Version20230628170203.php new file mode 100644 index 00000000..84dc01b3 --- /dev/null +++ b/lib/RoadizCoreBundle/migrations/Version20230628170203.php @@ -0,0 +1,40 @@ +addSql('ALTER TABLE log DROP FOREIGN KEY FK_8F3F68C5A76ED395'); + $this->addSql('DROP INDEX IDX_8F3F68C5A76ED395 ON log'); + $this->addSql('ALTER TABLE log CHANGE user_id user_id VARCHAR(36) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE log CHANGE user_id user_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE log ADD CONSTRAINT FK_8F3F68C5A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_8F3F68C5A76ED395 ON log (user_id)'); + } + + public function isTransactional(): bool + { + return false; + } +} diff --git a/lib/RoadizCoreBundle/src/Api/ListManager/SolrSearchListManager.php b/lib/RoadizCoreBundle/src/Api/ListManager/SolrSearchListManager.php index e19b9f66..a1b4a7df 100644 --- a/lib/RoadizCoreBundle/src/Api/ListManager/SolrSearchListManager.php +++ b/lib/RoadizCoreBundle/src/Api/ListManager/SolrSearchListManager.php @@ -67,6 +67,7 @@ public function handle(bool $disabled = false) protected function handleSearchParam(string $search): void { + parent::handleSearchParam($search); $this->query = trim($search); } diff --git a/lib/RoadizCoreBundle/src/Entity/Log.php b/lib/RoadizCoreBundle/src/Entity/Log.php index cc08549b..e6412e5b 100644 --- a/lib/RoadizCoreBundle/src/Entity/Log.php +++ b/lib/RoadizCoreBundle/src/Entity/Log.php @@ -41,11 +41,11 @@ class Log extends AbstractEntity public const DEBUG = Logger::DEBUG; public const LOG = Logger::INFO; - #[ORM\ManyToOne(targetEntity: User::class)] - #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', unique: false, onDelete: 'SET NULL')] + #[ORM\Column(name: 'user_id', type: 'string', length: 36, unique: false, nullable: true)] #[SymfonySerializer\Groups(['log_user'])] #[Serializer\Groups(['log_user'])] - protected ?User $user = null; + // @phpstan-ignore-next-line + protected int|string|null $userId = null; #[ORM\Column(name: 'username', type: 'string', length: 255, nullable: true)] #[SymfonySerializer\Groups(['log_user'])] @@ -119,12 +119,21 @@ public function __construct(int $level, string $message) } /** - * @return User|null - * @deprecated Use additionalData or username instead + * @return int|string|null */ - public function getUser(): ?User + public function getUserId(): int|string|null { - return $this->user; + return $this->userId; + } + + /** + * @param int|string|null $userId + * @return Log + */ + public function setUserId(int|string|null $userId): Log + { + $this->userId = $userId; + return $this; } /** @@ -134,7 +143,7 @@ public function getUser(): ?User */ public function setUser(User $user): Log { - $this->user = $user; + $this->userId = $user->getId(); $this->username = $user->getUsername(); return $this; } diff --git a/lib/RoadizCoreBundle/src/ListManager/AbstractEntityListManager.php b/lib/RoadizCoreBundle/src/ListManager/AbstractEntityListManager.php index fa5dad82..ed74b273 100644 --- a/lib/RoadizCoreBundle/src/ListManager/AbstractEntityListManager.php +++ b/lib/RoadizCoreBundle/src/ListManager/AbstractEntityListManager.php @@ -13,6 +13,7 @@ abstract class AbstractEntityListManager implements EntityListManagerInterface protected ?array $queryArray = null; protected ?int $currentPage = null; protected ?int $itemPerPage = null; + protected ?string $searchPattern = null; protected bool $displayNotPublishedNodes; protected bool $displayAllNodesStatuses; protected bool $allowRequestSorting = true; @@ -136,6 +137,7 @@ public function getAssignation(): array 'pageCount' => $this->getPageCount(), 'itemPerPage' => $this->getItemPerPage(), 'itemCount' => $this->getItemCount(), + 'search' => $this->searchPattern, 'nextPageQuery' => null, 'previousPageQuery' => null, ]; @@ -272,7 +274,7 @@ protected function handleRequestQuery(bool $disabled): void protected function handleSearchParam(string $search): void { - // Do nothing on abstract + $this->searchPattern = $search; } protected function handleOrderingParam(string $field, string $ordering): void diff --git a/lib/RoadizCoreBundle/src/ListManager/EntityListManager.php b/lib/RoadizCoreBundle/src/ListManager/EntityListManager.php index 3d3324a0..b2a22b0b 100644 --- a/lib/RoadizCoreBundle/src/ListManager/EntityListManager.php +++ b/lib/RoadizCoreBundle/src/ListManager/EntityListManager.php @@ -4,6 +4,7 @@ namespace RZ\Roadiz\CoreBundle\ListManager; +use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator; use Doctrine\Persistence\ObjectManager; use RZ\Roadiz\Core\AbstractEntities\PersistableInterface; use RZ\Roadiz\Core\AbstractEntities\TranslationInterface; @@ -13,7 +14,6 @@ use RZ\Roadiz\CoreBundle\Repository\NodeRepository; use RZ\Roadiz\CoreBundle\Repository\StatusAwareRepository; use Symfony\Component\HttpFoundation\Request; -use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator; /** * Perform basic filtering and search over entity listings. @@ -28,7 +28,6 @@ class EntityListManager extends AbstractEntityListManager protected ?Paginator $paginator = null; protected ?array $orderingArray = null; protected ?array $filteringArray = null; - protected ?string $searchPattern = null; protected ?array $assignation = null; protected ?TranslationInterface $translation = null; @@ -134,11 +133,6 @@ public function handle(bool $disabled = false) } } - protected function handleSearchParam(string $search): void - { - $this->searchPattern = $search; - } - protected function handleOrderingParam(string $field, string $ordering): void { $this->orderingArray = [ @@ -188,16 +182,6 @@ protected function createPaginator(): void $this->paginator->setDisplayingAllNodesStatuses($this->isDisplayingAllNodesStatuses()); } - /** - * @return array - */ - public function getAssignation(): array - { - return array_merge(parent::getAssignation(), [ - 'search' => $this->searchPattern, - ]); - } - /** * @return int */ diff --git a/lib/RoadizCoreBundle/src/ListManager/QueryBuilderListManager.php b/lib/RoadizCoreBundle/src/ListManager/QueryBuilderListManager.php index 7ad72068..735866f6 100644 --- a/lib/RoadizCoreBundle/src/ListManager/QueryBuilderListManager.php +++ b/lib/RoadizCoreBundle/src/ListManager/QueryBuilderListManager.php @@ -14,6 +14,10 @@ class QueryBuilderListManager extends AbstractEntityListManager protected ?Paginator $paginator = null; protected string $identifier; protected bool $debug = false; + /** + * @var null|callable + */ + protected $searchingCallable = null; /** * @param Request|null $request @@ -33,12 +37,26 @@ public function __construct( $this->debug = $debug; } + /** + * @param callable|null $searchingCallable + * @return QueryBuilderListManager + */ + public function setSearchingCallable(?callable $searchingCallable): QueryBuilderListManager + { + $this->searchingCallable = $searchingCallable; + return $this; + } + /** * @param string $search */ protected function handleSearchParam(string $search): void { - // Implement your custom logic + parent::handleSearchParam($search); + + if (\is_callable($this->searchingCallable)) { + \call_user_func($this->searchingCallable, $this->queryBuilder, $search); + } } public function handle(bool $disabled = false) diff --git a/lib/RoadizCoreBundle/src/Repository/LogRepository.php b/lib/RoadizCoreBundle/src/Repository/LogRepository.php index c076eaac..b7b648d4 100644 --- a/lib/RoadizCoreBundle/src/Repository/LogRepository.php +++ b/lib/RoadizCoreBundle/src/Repository/LogRepository.php @@ -5,6 +5,7 @@ namespace RZ\Roadiz\CoreBundle\Repository; use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\Tools\Pagination\Paginator; use Doctrine\Persistence\ManagerRegistry; use RZ\Roadiz\CoreBundle\Entity\Log; @@ -57,4 +58,33 @@ public function findLatestByNodesSources(int $maxResult = 5): Paginator return new Paginator($qb2->getQuery(), true); } + + public function findByNode(Node $node): array + { + $qb = $this->getAllRelatedToNodeQueryBuilder($node); + return $qb->getQuery()->getResult(); + } + + public function getAllRelatedToNodeQueryBuilder(Node $node): QueryBuilder + { + $qb = $this->createQueryBuilder('obj'); + $qb->andWhere($qb->expr()->orX( + $qb->expr()->andX( + $qb->expr()->eq('obj.entityClass', ':nodeClass'), + $qb->expr()->in('obj.entityId', ':nodeId') + ), + $qb->expr()->andX( + $qb->expr()->eq('obj.entityClass', ':nodeSourceClass'), + $qb->expr()->in('obj.entityId', ':nodeSourceId') + ), + )); + $qb->addOrderBy('obj.datetime', 'DESC'); + $qb->setParameter('nodeClass', Node::class); + $qb->setParameter('nodeSourceClass', NodesSources::class); + $qb->setParameter('nodeId', [$node->getId()]); + $qb->setParameter('nodeSourceId', $node->getNodeSources()->map(function (NodesSources $ns) { + return $ns->getId(); + })->toArray()); + return $qb; + } } diff --git a/lib/RoadizCoreBundle/src/TwigExtension/LogExtension.php b/lib/RoadizCoreBundle/src/TwigExtension/LogExtension.php index abafb765..634e57c4 100644 --- a/lib/RoadizCoreBundle/src/TwigExtension/LogExtension.php +++ b/lib/RoadizCoreBundle/src/TwigExtension/LogExtension.php @@ -111,7 +111,6 @@ public function getEditPath(?object $log): ?string ]); } break; - } return null; diff --git a/lib/Rozier/src/Controllers/HistoryController.php b/lib/Rozier/src/Controllers/HistoryController.php index 56e7a727..64d4a37f 100644 --- a/lib/Rozier/src/Controllers/HistoryController.php +++ b/lib/Rozier/src/Controllers/HistoryController.php @@ -64,20 +64,20 @@ public function indexAction(Request $request): Response * List user logs action. * * @param Request $request - * @param int $userId + * @param int|string $userId * * @return Response * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException * @throws \Doctrine\ORM\TransactionRequiredException */ - public function userAction(Request $request, int $userId): Response + public function userAction(Request $request, int|string $userId): Response { $this->denyAccessUnlessGranted(['ROLE_BACKEND_USER', 'ROLE_ACCESS_LOGS']); if ( !($this->isGranted(['ROLE_ACCESS_USERS', 'ROLE_ACCESS_LOGS']) - || ($this->getUser() instanceof User && $this->getUser()->getId() == $userId)) + || ($this->getUser() instanceof User && $this->getUser()->getId() === $userId)) ) { throw $this->createAccessDeniedException("You don't have access to this page: ROLE_ACCESS_USERS"); } @@ -94,7 +94,7 @@ public function userAction(Request $request, int $userId): Response */ $listManager = $this->createEntityListManager( Log::class, - ['user' => $user], + ['userId' => $user->getId()], ['datetime' => 'DESC'] ); $listManager->setDisplayingNotPublishedNodes(true); diff --git a/lib/Rozier/src/Controllers/Nodes/HistoryController.php b/lib/Rozier/src/Controllers/Nodes/HistoryController.php index 54ef618d..b6bf2e70 100644 --- a/lib/Rozier/src/Controllers/Nodes/HistoryController.php +++ b/lib/Rozier/src/Controllers/Nodes/HistoryController.php @@ -4,9 +4,12 @@ namespace Themes\Rozier\Controllers\Nodes; +use Doctrine\ORM\QueryBuilder; use RZ\Roadiz\CoreBundle\Entity\Log; use RZ\Roadiz\CoreBundle\Entity\Node; +use RZ\Roadiz\CoreBundle\Entity\NodesSources; use RZ\Roadiz\CoreBundle\Entity\Translation; +use RZ\Roadiz\CoreBundle\ListManager\QueryBuilderListManager; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -35,13 +38,18 @@ public function historyAction(Request $request, int $nodeId): Response throw new ResourceNotFoundException(); } - $listManager = $this->createEntityListManager( - Log::class, - [ - 'nodeSource' => $node->getNodeSources()->toArray(), - ], - ['datetime' => 'DESC'] - ); + $qb = $this->em() + ->getRepository(Log::class) + ->getAllRelatedToNodeQueryBuilder($node); + + $listManager = new QueryBuilderListManager($request, $qb, 'obj'); + $listManager->setSearchingCallable(function (QueryBuilder $queryBuilder, string $search) { + $queryBuilder->andWhere($queryBuilder->expr()->orX( + $queryBuilder->expr()->like('obj.message', ':search'), + $queryBuilder->expr()->like('obj.channel', ':search') + )); + $queryBuilder->setParameter('search', '%' . $search . '%'); + }); $listManager->setDisplayingNotPublishedNodes(true); $listManager->setDisplayingAllNodesStatuses(true); /*