diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20241230175100.php b/src/CoreBundle/Migrations/Schema/V200/Version20241230175100.php new file mode 100644 index 00000000000..edbc30656e4 --- /dev/null +++ b/src/CoreBundle/Migrations/Schema/V200/Version20241230175100.php @@ -0,0 +1,69 @@ +getForeignKeyName('c_lp_category_rel_user', 'user_id'); + + if ($foreignKeyName) { + $this->addSql("ALTER TABLE c_lp_category_rel_user DROP FOREIGN KEY `$foreignKeyName`"); + } + + // Add the updated foreign key + $this->addSql(' + ALTER TABLE c_lp_category_rel_user + ADD CONSTRAINT FK_83D35829A76ED395 + FOREIGN KEY (user_id) + REFERENCES user(id) + ON DELETE SET NULL + ON UPDATE CASCADE + '); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE c_lp_category_rel_user DROP FOREIGN KEY FK_83D35829A76ED395'); + + $this->addSql(' + ALTER TABLE c_lp_category_rel_user + ADD CONSTRAINT c_lp_category_rel_user_ibfk_1 + FOREIGN KEY (user_id) + REFERENCES user(id) + ON DELETE SET NULL + '); + } + + private function getForeignKeyName(string $tableName, string $columnName): ?string + { + $query = " + SELECT CONSTRAINT_NAME + FROM information_schema.KEY_COLUMN_USAGE + WHERE TABLE_NAME = :tableName + AND COLUMN_NAME = :columnName + AND TABLE_SCHEMA = DATABASE() + "; + + $result = $this->connection->fetchOne($query, [ + 'tableName' => $tableName, + 'columnName' => $columnName, + ]); + + return $result ?: null; + } +} diff --git a/src/CoreBundle/Repository/Node/UserRepository.php b/src/CoreBundle/Repository/Node/UserRepository.php index 805f9687318..81251aa4d31 100644 --- a/src/CoreBundle/Repository/Node/UserRepository.php +++ b/src/CoreBundle/Repository/Node/UserRepository.php @@ -144,178 +144,195 @@ public function getRootUser(): User public function deleteUser(User $user, bool $destroy = false): void { - $em = $this->getEntityManager(); - $em->getConnection()->beginTransaction(); + $connection = $this->getEntityManager()->getConnection(); + $connection->beginTransaction(); try { if ($destroy) { $fallbackUser = $this->getFallbackUser(); if ($fallbackUser) { - $this->reassignUserResourcesToFallback($user, $fallbackUser); - $em->flush(); - } - - foreach ($user->getGroups() as $group) { - $user->removeGroup($group); + $this->reassignUserResourcesToFallbackSQL($user, $fallbackUser, $connection); } - if ($user->getResourceNode()) { - $em->remove($user->getResourceNode()); - } - - $em->remove($user); + // Remove group relationships + $connection->executeStatement( + 'DELETE FROM usergroup_rel_user WHERE user_id = :userId', + ['userId' => $user->getId()] + ); + + // Remove resource node if exists + $connection->executeStatement( + 'DELETE FROM resource_node WHERE id = :nodeId', + ['nodeId' => $user->getResourceNode()->getId()] + ); + + // Remove the user itself + $connection->executeStatement( + 'DELETE FROM user WHERE id = :userId', + ['userId' => $user->getId()] + ); } else { - $user->setActive(User::SOFT_DELETED); - $em->persist($user); + // Soft delete the user + $connection->executeStatement( + 'UPDATE user SET active = :softDeleted WHERE id = :userId', + ['softDeleted' => User::SOFT_DELETED, 'userId' => $user->getId()] + ); } - $em->flush(); - $em->getConnection()->commit(); + $connection->commit(); } catch (Exception $e) { - $em->getConnection()->rollBack(); - + $connection->rollBack(); throw $e; } } - protected function reassignUserResourcesToFallback(User $userToDelete, User $fallbackUser): void + /** + * Reassigns resources and related data from a deleted user to a fallback user in the database. + */ + protected function reassignUserResourcesToFallbackSQL(User $userToDelete, User $fallbackUser, $connection): void { - $em = $this->getEntityManager(); - - $userResourceNodes = $em->getRepository(ResourceNode::class)->findBy(['creator' => $userToDelete]); - foreach ($userResourceNodes as $resourceNode) { - $resourceNode->setCreator($fallbackUser); - $em->persist($resourceNode); - } - - $childResourceNodes = $em->getRepository(ResourceNode::class)->findBy(['parent' => $userToDelete->getResourceNode()]); - foreach ($childResourceNodes as $childNode) { - $fallbackUserResourceNode = $fallbackUser->getResourceNode(); - if ($fallbackUserResourceNode) { - $childNode->setParent($fallbackUserResourceNode); - } else { - $childNode->setParent(null); - } - $em->persist($childNode); - } - - $relations = [ - ['bundle' => 'CoreBundle', 'entity' => 'AccessUrlRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'Admin', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'AttemptFeedback', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'Chat', 'field' => 'toUser', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'ChatVideo', 'field' => 'toUser', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'CourseRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'CourseRelUserCatalogue', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'CourseRequest', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CAttendanceResult', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CourseBundle', 'entity' => 'CAttendanceResultComment', 'field' => 'userId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CAttendanceSheet', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CourseBundle', 'entity' => 'CAttendanceSheetLog', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CourseBundle', 'entity' => 'CChatConnected', 'field' => 'userId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CourseBundle', 'entity' => 'CDropboxCategory', 'field' => 'userId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CDropboxFeedback', 'field' => 'authorUserId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CDropboxPerson', 'field' => 'userId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CDropboxPost', 'field' => 'destUserId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CForumMailcue', 'field' => 'userId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CourseBundle', 'entity' => 'CForumNotification', 'field' => 'userId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CourseBundle', 'entity' => 'CForumPost', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CForumThread', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CForumThreadQualify', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CForumThreadQualifyLog', 'field' => 'userId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CGroupRelTutor', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CGroupRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CLpCategoryRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CourseBundle', 'entity' => 'CLpRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CourseBundle', 'entity' => 'CLpView', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CourseBundle', 'entity' => 'CStudentPublicationComment', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CourseBundle', 'entity' => 'CStudentPublicationRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CourseBundle', 'entity' => 'CSurveyInvitation', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CWiki', 'field' => 'userId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CourseBundle', 'entity' => 'CWikiMailcue', 'field' => 'userId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'ExtraFieldSavedSearch', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'GradebookCategory', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'GradebookCertificate', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'GradebookComment', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'GradebookLinkevalLog', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'GradebookResult', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'GradebookResultLog', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'GradebookScoreLog', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'Message', 'field' => 'sender', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'MessageRelUser', 'field' => 'receiver', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'MessageTag', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'Notification', 'field' => 'destUserId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'PageCategory', 'field' => 'creator', 'type' => 'object', 'action' => 'convert'], - // ['bundle' => 'CoreBundle', 'entity' => 'PersonalAgenda', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'Portfolio', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'PortfolioCategory', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'PortfolioComment', 'field' => 'author', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'ResourceComment', 'field' => 'author', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'SequenceValue', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'SessionRelCourseRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'SessionRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'SkillRelItemRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'SkillRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'SkillRelUserComment', 'field' => 'feedbackGiver', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'SocialPost', 'field' => 'sender', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'SocialPost', 'field' => 'userReceiver', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'SocialPostAttachment', 'field' => 'insertUserId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'SocialPostFeedback', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'Templates', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'TicketAssignedLog', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'TicketCategory', 'field' => 'insertUserId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'TicketCategoryRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TicketMessage', 'field' => 'insertUserId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'TicketMessageAttachment', 'field' => 'lastEditUserId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'TicketPriority', 'field' => 'insertUserId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'TicketProject', 'field' => 'insertUserId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'TicketProject', 'field' => 'lastEditUserId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackEAccess', 'field' => 'accessUserId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackEAccessComplete', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackEAttempt', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackECourseAccess', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackEDefault', 'field' => 'defaultUserId', 'type' => 'int', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackEDownloads', 'field' => 'downUserId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackEExercise', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackEExerciseConfirmation', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackEHotpotatoes', 'field' => 'exeUserId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackEHotspot', 'field' => 'hotspotUserId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackELastaccess', 'field' => 'accessUserId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackELinks', 'field' => 'linksUserId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackELogin', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackEOnline', 'field' => 'loginUserId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'TrackEUploads', 'field' => 'uploadUserId', 'type' => 'int', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'UsergroupRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'convert'], - ['bundle' => 'CoreBundle', 'entity' => 'UserRelTag', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ['bundle' => 'CoreBundle', 'entity' => 'UserRelUser', 'field' => 'user', 'type' => 'object', 'action' => 'delete'], - ]; + // Update resource nodes created by the user + $connection->executeStatement( + 'UPDATE resource_node SET creator_id = :fallbackUserId WHERE creator_id = :userId', + ['fallbackUserId' => $fallbackUser->getId(), 'userId' => $userToDelete->getId()] + ); + + // Update child resource nodes + $connection->executeStatement( + 'UPDATE resource_node SET parent_id = :fallbackParentId WHERE parent_id = :userParentId', + [ + 'fallbackParentId' => $fallbackUser->getResourceNode()?->getId(), + 'userParentId' => $userToDelete->getResourceNode()->getId(), + ] + ); + + // Relations to update or delete + $relations = $this->getRelations(); foreach ($relations as $relation) { - $entityClass = 'Chamilo\\'.$relation['bundle'].'\Entity\\'.$relation['entity']; - $repository = $em->getRepository($entityClass); - $records = $repository->findBy([$relation['field'] => $userToDelete]); - - foreach ($records as $record) { - $setter = 'set'.ucfirst($relation['field']); - if ('delete' === $relation['action']) { - $em->remove($record); - } elseif (method_exists($record, $setter)) { - $valueToSet = 'object' === $relation['type'] ? $fallbackUser : $fallbackUser->getId(); - $record->{$setter}($valueToSet); - if (method_exists($record, 'getResourceFiles')) { - foreach ($record->getResourceFiles() as $resourceFile) { - if (!$em->contains($resourceFile)) { - $em->persist($resourceFile); - } - } - } - $em->persist($record); - } + $table = $relation['table']; + $field = $relation['field']; + $action = $relation['action']; + + if ($action === 'delete') { + $connection->executeStatement( + "DELETE FROM $table WHERE $field = :userId", + ['userId' => $userToDelete->getId()] + ); + } elseif ($action === 'update') { + $connection->executeStatement( + "UPDATE $table SET $field = :fallbackUserId WHERE $field = :userId", + [ + 'fallbackUserId' => $fallbackUser->getId(), + 'userId' => $userToDelete->getId(), + ] + ); } } + } - $em->flush(); + /** + * Retrieves a list of database table relations and their corresponding actions + * to handle user resource reassignment or deletion. + */ + protected function getRelations(): array + { + return [ + ['table' => 'access_url_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'admin', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'attempt_feedback', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'chat', 'field' => 'to_user', 'action' => 'update'], + ['table' => 'chat_video', 'field' => 'to_user', 'action' => 'update'], + ['table' => 'course_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'course_rel_user_catalogue', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'course_request', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'c_attendance_result', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'c_attendance_result_comment', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'c_attendance_sheet', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'c_attendance_sheet_log', 'field' => 'lastedit_user_id', 'action' => 'delete'], + ['table' => 'c_chat_connected', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'c_dropbox_category', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'c_dropbox_feedback', 'field' => 'author_user_id', 'action' => 'update'], + ['table' => 'c_dropbox_person', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'c_dropbox_post', 'field' => 'dest_user_id', 'action' => 'update'], + ['table' => 'c_forum_mailcue', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'c_forum_notification', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'c_forum_post', 'field' => 'poster_id', 'action' => 'update'], + ['table' => 'c_forum_thread', 'field' => 'thread_poster_id', 'action' => 'update'], + ['table' => 'c_forum_thread_qualify', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'c_forum_thread_qualify_log', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'c_group_rel_tutor', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'c_group_rel_user', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'c_lp_category_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'c_lp_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'c_lp_view', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'c_student_publication_comment', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'c_student_publication_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'c_survey_invitation', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'c_wiki', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'c_wiki_mailcue', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'extra_field_saved_search', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'gradebook_category', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'gradebook_certificate', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'gradebook_comment', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'gradebook_linkeval_log', 'field' => 'user_id_log', 'action' => 'delete'], + ['table' => 'gradebook_result', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'gradebook_result_log', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'gradebook_score_log', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'message', 'field' => 'user_sender_id', 'action' => 'update'], + ['table' => 'message_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'message_tag', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'notification', 'field' => 'dest_user_id', 'action' => 'delete'], + ['table' => 'page_category', 'field' => 'creator_id', 'action' => 'update'], + ['table' => 'portfolio', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'portfolio_category', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'portfolio_comment', 'field' => 'author_id', 'action' => 'update'], + ['table' => 'resource_comment', 'field' => 'author_id', 'action' => 'update'], + ['table' => 'sequence_value', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'session_rel_course_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'session_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'skill_rel_item_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'skill_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'skill_rel_user_comment', 'field' => 'feedback_giver_id', 'action' => 'delete'], + ['table' => 'social_post', 'field' => 'sender_id', 'action' => 'update'], + ['table' => 'social_post', 'field' => 'user_receiver_id', 'action' => 'update'], + ['table' => 'social_post_attachments', 'field' => 'sys_insert_user_id', 'action' => 'update'], + ['table' => 'social_post_attachments', 'field' => 'sys_lastedit_user_id', 'action' => 'update'], + ['table' => 'social_post_feedback', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'templates', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'ticket_assigned_log', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'ticket_assigned_log', 'field' => 'sys_insert_user_id', 'action' => 'update'], + ['table' => 'ticket_category', 'field' => 'sys_insert_user_id', 'action' => 'update'], + ['table' => 'ticket_category', 'field' => 'sys_lastedit_user_id', 'action' => 'update'], + ['table' => 'ticket_category_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'ticket_message', 'field' => 'sys_insert_user_id', 'action' => 'update'], + ['table' => 'ticket_message', 'field' => 'sys_lastedit_user_id', 'action' => 'update'], + ['table' => 'ticket_message_attachments', 'field' => 'sys_insert_user_id', 'action' => 'update'], + ['table' => 'ticket_message_attachments', 'field' => 'sys_lastedit_user_id', 'action' => 'update'], + ['table' => 'ticket_priority', 'field' => 'sys_insert_user_id', 'action' => 'update'], + ['table' => 'ticket_priority', 'field' => 'sys_lastedit_user_id', 'action' => 'update'], + ['table' => 'ticket_project', 'field' => 'sys_insert_user_id', 'action' => 'update'], + ['table' => 'ticket_project', 'field' => 'sys_lastedit_user_id', 'action' => 'update'], + ['table' => 'track_e_access', 'field' => 'access_user_id', 'action' => 'delete'], + ['table' => 'track_e_access_complete', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'track_e_attempt', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'track_e_course_access', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'track_e_default', 'field' => 'default_user_id', 'action' => 'update'], + ['table' => 'track_e_downloads', 'field' => 'down_user_id', 'action' => 'delete'], + ['table' => 'track_e_exercises', 'field' => 'exe_user_id', 'action' => 'delete'], + ['table' => 'track_e_exercise_confirmation', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'track_e_hotpotatoes', 'field' => 'exe_user_id', 'action' => 'delete'], + ['table' => 'track_e_hotspot', 'field' => 'hotspot_user_id', 'action' => 'delete'], + ['table' => 'track_e_lastaccess', 'field' => 'access_user_id', 'action' => 'delete'], + ['table' => 'track_e_links', 'field' => 'links_user_id', 'action' => 'delete'], + ['table' => 'track_e_login', 'field' => 'login_user_id', 'action' => 'delete'], + ['table' => 'track_e_online', 'field' => 'login_user_id', 'action' => 'delete'], + ['table' => 'track_e_uploads', 'field' => 'upload_user_id', 'action' => 'delete'], + ['table' => 'usergroup_rel_user', 'field' => 'user_id', 'action' => 'update'], + ['table' => 'user_rel_tag', 'field' => 'user_id', 'action' => 'delete'], + ['table' => 'user_rel_user', 'field' => 'user_id', 'action' => 'delete'], + ]; } public function getFallbackUser(): ?User diff --git a/src/CourseBundle/Entity/CLpCategoryRelUser.php b/src/CourseBundle/Entity/CLpCategoryRelUser.php index de65336ac83..3de11033bdd 100644 --- a/src/CourseBundle/Entity/CLpCategoryRelUser.php +++ b/src/CourseBundle/Entity/CLpCategoryRelUser.php @@ -30,8 +30,8 @@ class CLpCategoryRelUser implements Stringable protected CLpCategory $category; #[ORM\ManyToOne(targetEntity: User::class)] - #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', onDelete: 'SET NULL')] - protected User $user; + #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')] + protected ?User $user = null; public function __toString(): string {