User: Fix and optimize user deletion logic - refs BT#22215

pull/6008/head
Christian Beeznest 8 months ago
parent 00dc554d17
commit f9127cfcda
  1. 69
      src/CoreBundle/Migrations/Schema/V200/Version20241230175100.php
  2. 317
      src/CoreBundle/Repository/Node/UserRepository.php
  3. 4
      src/CourseBundle/Entity/CLpCategoryRelUser.php

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Migrations\Schema\V200;
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema;
final class Version20241230175100 extends AbstractMigrationChamilo
{
public function getDescription(): string
{
return 'Fix cascading delete for c_lp_category_rel_user table foreign key dynamically';
}
public function up(Schema $schema): void
{
// Find the foreign key name dynamically
$foreignKeyName = $this->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;
}
}

@ -144,178 +144,195 @@ class UserRepository extends ResourceRepository implements PasswordUpgraderInter
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

@ -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
{

Loading…
Cancel
Save