winter icon indicating copy to clipboard operation
winter copied to clipboard

When deleting and restoring an admin, UserGroups relations are not restored

Open xyz1123581321 opened this issue 3 years ago • 3 comments

Winter CMS Build

1.1

PHP Version

8.1

Database engine

MySQL/MariaDB

Plugins installed

No response

Issue description

I want to prevent User Group deletion when an admin is deleted. The records exists but not the relations. After restoring the admin, groups are not restored.

Tried with changing the core User.php file with:

public $belongsToMany = [
        'groups' => [UserGroup::class, 'table' => 'backend_users_groups', 'softDelete' => true]
];

Doesnt help. I even tried to modify SoftDelete trait and commented out the methods for deleting the relations, doesnt help either.

Steps to replicate

.

Workaround

No response

xyz1123581321 avatar Dec 21 '21 16:12 xyz1123581321

This issue will be closed and archived in 3 days, as there has been no activity in the last 60 days. If this issue is still relevant or you would like to see it actioned, please respond and we will re-open this issue. If this issue is critical to your business, consider joining the Premium Support Program where a Service Level Agreement is offered.

github-actions[bot] avatar Feb 20 '22 00:02 github-actions[bot]

This issue will be closed and archived in 3 days, as there has been no activity in the last 60 days. If this issue is still relevant or you would like to see it actioned, please respond and we will re-open this issue. If this issue is critical to your business, consider joining the Premium Support Program where a Service Level Agreement is offered.

github-actions[bot] avatar Apr 22 '22 00:04 github-actions[bot]

I can confirm that this issue exists and maybe we also need to check if this problem isn't much bigger - because it looks like when soft deleting main model which has SoftDelete trait (in this case User model and backend_users table) then all related entries in Many To Many table (in this case UserGroup and backend_users_groups) are deleted automatically although if we look at https://wintercms.com/docs/database/relations#detailed-relationships then by default it should be 'delete' => false.

I was also experimenting to manually set delete and/or detach on $belongsToMany in User and UserGroup models (in various variations) but nothing changed this previously described behaviour.

arvislacis avatar Jul 15 '22 16:07 arvislacis

This issue will be closed and archived in 3 days, as there has been no activity in this issue for the last 6 months. If this issue is still relevant or you would like to see it actioned, please respond within 3 days. If this issue is critical for your business, please reach out to us at [email protected].

github-actions[bot] avatar Jan 14 '23 00:01 github-actions[bot]

@arvislacis are you able to submit a PR to the test plugin reproducing the issue?

LukeTowers avatar Jan 16 '23 05:01 LukeTowers

@LukeTowers I may try to implement some tests in test plugin although, IMHO, this issue would need back-end related/automated tests more than UI ones...

Overall the problem is that existing pivot table backend_users_groups doesn't have deleted_at column or similar mechanism to implement soft delete state in many-to-many relation. But I see that Laravel has been having problems with this, for example, https://laracasts.com/discuss/channels/general-discussion/soft-deletes-with-pivot-table

arvislacis avatar Jan 16 '23 11:01 arvislacis

A step-to-step guide how to reproduce this:

  1. Go to the Settings -> Administrators;
  2. Create new user (testuser etc.) and add it to group, for example, "Owners" group;

2023-01-16_19-03

  1. Open newly created user (testuser) record and delete it from UI (soft delete on User model will happen);
  2. Return to user list, select to "Show deleted" and then restore the deleted user (testuser) account;
  3. If you then open testuser record and investigate "Groups" tab then all checkboxes are empty (group relationshios has been deleted with step 3).

2023-01-16_19-03_1

arvislacis avatar Jan 16 '23 17:01 arvislacis

This issue will be closed and archived in 3 days, as there has been no activity in this issue for the last 6 months. If this issue is still relevant or you would like to see it actioned, please respond within 3 days. If this issue is critical for your business, please reach out to us at [email protected].

github-actions[bot] avatar Jul 18 '23 00:07 github-actions[bot]

@arvislacis @xyzqtc The UserGroup model would need to use the SoftDelete trait, and the User model would need to specify 'softDelete' => true in its relatoin definition.

Or am I missing something ?

mjauvin avatar Sep 07 '23 19:09 mjauvin

@mjauvin Yes, in general UserGroup model needs SoftDelete trait and DB migration with deleted_at column.

Side note: The problem may also be with the fact that backend_users_groups table is a pivot table, as I have linked before then previously Laravel had issues with them and needed work-arounds but maybe it's fixed now.

arvislacis avatar Sep 08 '23 06:09 arvislacis

You're right, the pivot table is probably not supported, unless we need to add a deleted_at column to the pivot table itself ?

mjauvin avatar Sep 08 '23 10:09 mjauvin

@mjauvin Yeah, probably... https://laracasts.com/discuss/channels/general-discussion/soft-deletes-with-pivot-table article also gives code examples how to handle these pivot table cases - but still, a bit tricky solution which needs work-around.

arvislacis avatar Sep 08 '23 11:09 arvislacis

@arvislacis Winter's SoftDelete trait should handle this case here:

    protected function performSoftDeleteOnRelations()
    {                                                                                                                                            $definitions = $this->getRelationDefinitions();
        foreach ($definitions as $type => $relations) {
            foreach ($relations as $name => $options) {
                if (!array_get($options, 'softDelete', false)) {
                    continue;
                }

                if (!$relation = $this->{$name}) {
                    continue;
                }

                if ($relation instanceof EloquentModel) {
                    $relation->delete();
                }
                elseif ($relation instanceof CollectionBase) {
                    $relation->each(function ($model) {
                        $model->delete();
                    });
                }
            }
        }
    }

And here:

    protected function performRestoreOnRelations()
    {
        $definitions = $this->getRelationDefinitions();
        foreach ($definitions as $type => $relations) {
            foreach ($relations as $name => $options) {
                if (!array_get($options, 'softDelete', false)) {
                    continue;
                }   

                $relation = $this->{$name}()->onlyTrashed()->getResults();
                if (!$relation) {
                    continue;
                }   

                if ($relation instanceof EloquentModel) {
                    $relation->restore();
                }   
                elseif ($relation instanceof CollectionBase) {
                    $relation->each(function ($model) {
                        $model->restore();
                    }); 
                }   
            }
        }
    }

In a belongsToMany relation, the related model should not be deleted, of course, but a special case should be added to this trait's performSoftDeleteOnRelations() / performRestoreOnRelations() methods to handle this (i.e. update the pivot record's deleted_at column instead).

Then, it is the responsibility of the developper marking a belongsToMany relation as softDelete to add a deleted_at column to the pivot table migration.

Any feedback on this @LukeTowers @bennothommo @arvislacis ?

mjauvin avatar Sep 09 '23 15:09 mjauvin

We should move the discussion in the associated PR.

mjauvin avatar Sep 09 '23 15:09 mjauvin

@arvislacis @mjauvin I appreciate the efforts put into this over the weekend, but let's put a pause on this just for a second.

I do have a minor concern here that we're now unilaterally adding soft deletion to the pivot. Has it been tested and confirmed all working even if people don't enable soft deletion of the Backend users or groups themselves, or will we end up with a whole lot of orphaned pivot records?

I was tempted to suggest that perhaps we should merge @mjauvin's Storm PR (because that does fix a legitimate issue), and maybe in the case of @arvislacis' PR, instead convert that to a plugin so it becomes optional, but I realised that wouldn't work as we can't declare traits programatically, so I just want to make sure we've covered all bases.

bennothommo avatar Sep 11 '23 00:09 bennothommo

@bennothommo this is not going to happen because of this:

https://github.com/wintercms/storm/blob/9fac408d370718c91c40db9e797b736b4d3a42b7/src/Database/Traits/SoftDelete.php

mjauvin avatar Sep 11 '23 00:09 mjauvin

@mjauvin is there a section you're referencing in that file? I'm not sure what functionality you're specifically referring to that will prevent the orphans from occurring.

bennothommo avatar Sep 11 '23 01:09 bennothommo

@mjauvin is there a section you're referencing in that file? I'm not sure what functionality you're specifically referring to that will prevent the orphans from occurring.

Sorry, I'm on mobile and I meant to reference this line:

https://github.com/wintercms/storm/blob/9fac408d370718c91c40db9e797b736b4d3a42b7/src/Database/Traits/SoftDelete.php#L118

mjauvin avatar Sep 11 '23 01:09 mjauvin