Completed
Branch CASC/base (79f9d1)
by
unknown
16:50 queued 08:50
created

ExecuteBatchDeletion   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 0
loc 155
rs 10
c 0
b 0
f 0
wmc 15
lcom 1
cbo 8

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A create_job() 0 30 5
B continue_job() 0 65 7
A cleanup_job() 0 26 2
1
<?php
2
3
namespace EventEspressoBatchRequest\JobHandlers;
4
5
use EE_Change_Log;
6
use EE_Registry;
7
use EEM_Event;
8
use EEM_Price;
9
use EEM_Ticket;
10
use EventEspresso\core\exceptions\InvalidClassException;
11
use EventEspresso\core\exceptions\InvalidDataTypeException;
12
use EventEspresso\core\exceptions\InvalidInterfaceException;
13
use EventEspresso\core\exceptions\UnexpectedEntityException;
14
use EventEspresso\core\services\loaders\LoaderFactory;
15
use EventEspresso\core\services\orm\tree_traversal\NodeGroupDao;
16
use EventEspresso\core\services\orm\tree_traversal\ModelObjNode;
17
use EventEspressoBatchRequest\Helpers\BatchRequestException;
18
use EventEspressoBatchRequest\Helpers\JobParameters;
19
use EventEspressoBatchRequest\Helpers\JobStepResponse;
20
use EventEspressoBatchRequest\JobHandlerBaseClasses\JobHandler;
21
use InvalidArgumentException;
22
23
/**
24
 * Class EventDeletion
25
 *
26
 * Given a job code (eg generated by PreviewEventDeletion), performs the deletion of the indicated items.
27
 *
28
 * @package     Event Espresso
29
 * @author         Mike Nelson
30
 * @since         $VID:$
31
 *
32
 */
33
class ExecuteBatchDeletion extends JobHandler
34
{
35
    /**
36
     * @var NodeGroupDao
37
     */
38
    protected $model_obj_node_group_persister;
39
    public function __construct(NodeGroupDao $model_obj_node_group_persister)
40
    {
41
        $this->model_obj_node_group_persister = $model_obj_node_group_persister;
42
    }
43
44
45
    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
46
    /**
47
     *
48
     * @param JobParameters $job_parameters
49
     * @throws BatchRequestException
50
     * @return JobStepResponse
51
     */
52
    public function create_job(JobParameters $job_parameters)
53
    {
54
        $deletion_job_code = $job_parameters->request_datum('deletion_job_code', null);
55
        $roots = $this->model_obj_node_group_persister->getModelObjNodesInGroup($deletion_job_code);
56
        if ($roots === null) {
57
            throw new UnexpectedEntityException($roots, 'array', esc_html__('The job seems to be stale. Please press the back button in your browser twice.', 'event_espresso'));
58
        }
59
        $models_and_ids_to_delete = [];
60
        foreach ($roots as $root) {
61
            if (! $root instanceof ModelObjNode) {
62
                throw new UnexpectedEntityException($root, 'ModelObjNode');
63
            }
64
            $models_and_ids_to_delete = array_replace_recursive($models_and_ids_to_delete, $root->getIds());
65
        }
66
        $job_parameters->set_extra_data(
67
            [
68
                'models_and_ids_to_delete' => $models_and_ids_to_delete
69
            ]
70
        );
71
        // Find the job's actual size.
72
        $job_size = 0;
73
        foreach ($models_and_ids_to_delete as $model_name => $ids) {
74
            $job_size += count($ids);
75
        }
76
        $job_parameters->set_job_size($job_size);
77
        return new JobStepResponse(
78
            $job_parameters,
79
            esc_html__('Beginning to delete items...', 'event_espresso')
80
        );
81
    }
82
83
    /**
84
     * Performs another step of the job
85
     * @param JobParameters $job_parameters
86
     * @param int $batch_size
87
     * @return JobStepResponse
88
     * @throws BatchRequestException
89
     */
90
    public function continue_job(JobParameters $job_parameters, $batch_size = 50)
91
    {
92
        // We already have the items IDs. So deleting is really fast. Let's speed it up.
93
        $batch_size *= 10;
94
        $units_processed = 0;
95
        $models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
96
        // Build a new list of everything leftover after this request's of deletions.
97
        $models_and_ids_remaining = [];
98
        foreach ($models_and_ids_to_delete as $model_name => $ids_to_delete) {
0 ignored issues
show
Bug introduced by
The expression $models_and_ids_to_delete of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
99
            if ($units_processed < $batch_size) {
100
                $model = EE_Registry::instance()->load_model($model_name);
101
                $ids_to_delete_this_query = array_slice($ids_to_delete, 0, $batch_size - $units_processed, true);
102
                if ($model->has_primary_key_field()) {
103
                    $where_conditions = [
104
                        $model->primary_key_name() => [
105
                            'IN',
106
                            $ids_to_delete_this_query
107
                        ]
108
                    ];
109
                } else {
110
                    $where_conditions = [
111
                        'OR' => []
112
                    ];
113
                    foreach ($ids_to_delete_this_query as $index_primary_key_string) {
114
                        $keys_n_values = $model->parse_index_primary_key_string($index_primary_key_string);
115
                        $where_conditions['OR'][ 'AND*' . $index_primary_key_string ] = $keys_n_values;
116
                    }
117
                }
118
                // Deleting time!
119
                // The model's deletion method reports every ROW deleted, and in the case of CPT models that will be
120
                // two rows deleted for event CPT item. So don't rely on it for the count of items deleted.
121
                $model->delete_permanently(
122
                    [
123
                        $where_conditions
124
                    ],
125
                    false
126
                );
127
                $units_processed += count($ids_to_delete_this_query);
128
                $remaining_ids = array_diff_key($ids_to_delete, $ids_to_delete_this_query);
129
                // If there's any more from this model, we'll do them next time.
130
                if (count($remaining_ids) > 0) {
131
                    $models_and_ids_remaining[ $model_name ] = $remaining_ids;
132
                }
133
            } else {
134
                $models_and_ids_remaining[ $model_name ] = $models_and_ids_to_delete[ $model_name ];
135
            }
136
        }
137
        $job_parameters->mark_processed($units_processed);
138
        // All done deleting for this request. Is there anything to do next time?
139
        if (empty($models_and_ids_remaining)) {
140
            $job_parameters->set_status(JobParameters::status_complete);
141
            return new JobStepResponse(
142
                $job_parameters,
143
                esc_html__('Deletion complete.', 'event_espresso')
144
            );
145
        }
146
        $job_parameters->add_extra_data('models_and_ids_to_delete', $models_and_ids_remaining);
147
        return new JobStepResponse(
148
            $job_parameters,
149
            sprintf(
150
                esc_html__('Deleted %d items.', 'event_espresso'),
151
                $units_processed
152
            )
153
        );
154
    }
155
156
    /**
157
     * Performs any clean-up logic when we know the job is completed
158
     * @param JobParameters $job_parameters
159
     * @return JobStepResponse
160
     */
161
    public function cleanup_job(JobParameters $job_parameters)
162
    {
163
        $this->model_obj_node_group_persister->deleteModelObjNodesInGroup(
164
            $job_parameters->request_datum('deletion_job_code')
165
        );
166
        // For backwards compatibility with how we used to delete events, make sure we still trigger the old action.
167
        $models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
168
        foreach ($models_and_ids_to_delete['Event'] as $event_id) {
169
            // Create a log entry so we know who and when this event was permanently deleted.
170
            (EE_Change_Log::new_instance(
171
                [
172
                    'OBJ_ID' => $event_id,
173
                    'OBJ_type' => 'Event',
174
                    'LOG_message' => sprintf(
175
                        esc_html__('Event %1$d permanently deleted using ExecuteBatchDeletion.', 'event_espresso'),
176
                        $event_id
177
                    )
178
                ]
179
            ))->save();
180
            do_action('AHEE__Events_Admin_Page___permanently_delete_event__after_event_deleted', $event_id);
181
        }
182
        return new JobStepResponse(
183
            $job_parameters,
184
            esc_html__('All done', 'event_espresso')
185
        );
186
    }
187
}
188
// End of file EventDeletion.php
189
// Location: EventEspressoBatchRequest\JobHandlers/EventDeletion.php
190