Issues (13)

src/Model/Table/SoftDeleteTrait.php (12 issues)

1
<?php
2
namespace SoftDelete\Model\Table;
3
4
use Cake\ORM\RulesChecker;
5
use Cake\Datasource\EntityInterface;
6
use Cake\Utility\Hash;
7
use SoftDelete\Error\MissingColumnException;
8
use SoftDelete\ORM\Query;
9
10
trait SoftDeleteTrait
11
{
12
13
    /**
14
     * @var boolean
15
     */
16
    protected $_findWithDeleted;
17
18
    /**
19
     * Get the configured deletion field
20
     *
21
     * @return string
22
     * @throws \SoftDelete\Error\MissingFieldException
23
     */
24
    public function ensureSoftDeleteFieldExists()
25
    {
26
        $callable = [$this, 'getSoftDeleteField'];
27
        if (!is_callable($callable)) {
28
            throw new \BadMethodCallException();
29
        }
30
31
        $field = call_user_func($callable);
32
33
        if ($this->getSchema()->getColumn($field) === null) {
0 ignored issues
show
It seems like getSchema() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

33
        if ($this->/** @scrutinizer ignore-call */ getSchema()->getColumn($field) === null) {
Loading history...
34
            throw new MissingColumnException(
35
                __('Configured field `{0}` is missing from the table `{1}`.',
36
                    $field,
37
                    $this->getAlias()
0 ignored issues
show
It seems like getAlias() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

37
                    $this->/** @scrutinizer ignore-call */ 
38
                           getAlias()
Loading history...
38
                )
39
            );
40
        }
41
42
        return $field;
43
    }
44
45
    public function query()
46
    {
47
        return new Query($this->getConnection(), $this);
0 ignored issues
show
It seems like getConnection() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

47
        return new Query($this->/** @scrutinizer ignore-call */ getConnection(), $this);
Loading history...
$this of type SoftDelete\Model\Table\SoftDeleteTrait is incompatible with the type Cake\ORM\Table expected by parameter $table of SoftDelete\ORM\Query::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

47
        return new Query($this->getConnection(), /** @scrutinizer ignore-type */ $this);
Loading history...
48
    }
49
50
    /**
51
     * Perform the delete operation.
52
     *
53
     * Will soft delete the entity provided. Will remove rows from any
54
     * dependent associations, and clear out join tables for BelongsToMany associations.
55
     *
56
     * @param \Cake\DataSource\EntityInterface $entity The entity to soft delete.
57
     * @param \ArrayObject $options The options for the delete.
58
     * @throws \InvalidArgumentException if there are no primary key values of the
59
     * passed entity
60
     * @return bool success
61
     */
62
    protected function _processDelete($entity, $options)
63
    {
64
        if ($entity->isNew()) {
65
            return false;
66
        }
67
68
        $primaryKey = (array)$this->getPrimaryKey();
0 ignored issues
show
It seems like getPrimaryKey() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

68
        $primaryKey = (array)$this->/** @scrutinizer ignore-call */ getPrimaryKey();
Loading history...
69
        if (!$entity->has($primaryKey)) {
70
            $msg = 'Deleting requires all primary key values.';
71
            throw new \InvalidArgumentException($msg);
72
        }
73
74
        if ($options['checkRules'] && !$this->checkRules($entity, RulesChecker::DELETE, $options)) {
0 ignored issues
show
It seems like checkRules() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

74
        if ($options['checkRules'] && !$this->/** @scrutinizer ignore-call */ checkRules($entity, RulesChecker::DELETE, $options)) {
Loading history...
75
            return false;
76
        }
77
78
        $event = $this->dispatchEvent('Model.beforeDelete', [
0 ignored issues
show
It seems like dispatchEvent() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

78
        /** @scrutinizer ignore-call */ 
79
        $event = $this->dispatchEvent('Model.beforeDelete', [
Loading history...
79
            'entity' => $entity,
80
            'options' => $options
81
        ]);
82
83
        if ($event->isStopped()) {
84
            return $event->result;
85
        }
86
87
        $this->_associations->cascadeDelete(
88
            $entity,
89
            ['_primary' => false] + $options->getArrayCopy()
90
        );
91
92
        $query = $this->query();
93
        $conditions = (array)$entity->extract($primaryKey);
94
        $statement = $query->update()
95
            ->set([$this->ensureSoftDeleteFieldExists() => $this->getSoftDeleteValue()])
0 ignored issues
show
It seems like getSoftDeleteValue() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

95
            ->set([$this->ensureSoftDeleteFieldExists() => $this->/** @scrutinizer ignore-call */ getSoftDeleteValue()])
Loading history...
96
            ->where($conditions)
97
            ->execute();
98
99
        $success = $statement->rowCount() > 0;
100
        if (!$success) {
101
            return $success;
102
        }
103
104
        $this->dispatchEvent('Model.afterDelete', [
105
            'entity' => $entity,
106
            'options' => $options
107
        ]);
108
109
        return $success;
110
    }
111
112
    /**
113
     * Soft deletes all records matching `$conditions`.
114
     * @return int number of affected rows.
115
     */
116
    public function deleteAll($conditions)
117
    {
118
        $query = $this->query()
119
            ->update()
120
            ->set([$this->ensureSoftDeleteFieldExists() => $this->getSoftDeleteValue()])
121
            ->where($conditions);
122
        $statement = $query->execute();
123
        $statement->closeCursor();
124
        return $statement->rowCount();
125
    }
126
127
    /**
128
     * Hard deletes the given $entity.
129
     * @return bool true in case of success, false otherwise.
130
     */
131
    public function hardDelete(EntityInterface $entity)
132
    {
133
        if (!$this->delete($entity)) {
0 ignored issues
show
The method delete() does not exist on SoftDelete\Model\Table\SoftDeleteTrait. Did you maybe mean deleteAll()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

133
        if (!$this->/** @scrutinizer ignore-call */ delete($entity)) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
134
            return false;
135
        }
136
        $primaryKey = (array)$this->getPrimaryKey();
137
        $query = $this->query();
138
        $conditions = (array)$entity->extract($primaryKey);
139
        $statement = $query->delete()
140
            ->where($conditions)
141
            ->execute();
142
143
        $success = $statement->rowCount() > 0;
144
        if (!$success) {
145
            return $success;
146
        }
147
148
        return $success;
149
    }
150
151
    /**
152
     * Hard deletes all records that were soft deleted before a given date.
153
     * @param $conditions
154
     * @return int number of affected rows.
155
     */
156
    public function hardDeleteAll($conditions)
157
    {
158
        $conditions = Hash::merge($conditions, ['NOT' => [$this->getActiveExpression()]]);
159
160
        $query = $this->query()
161
            ->delete()
162
            ->where($conditions);
163
        $statement = $query->execute();
164
        $statement->closeCursor();
165
        return $statement->rowCount();
166
    }
167
168
    /**
169
     * Restore a soft deleted entity into an active state.
170
     * @param EntityInterface $entity Entity to be restored.
171
     * @return bool true in case of success, false otherwise.
172
     */
173
    public function restore(EntityInterface $entity)
174
    {
175
        $softDeleteField = $this->ensureSoftDeleteFieldExists();
176
        $entity->$softDeleteField = $this->getRestoreValue();
0 ignored issues
show
It seems like getRestoreValue() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

176
        /** @scrutinizer ignore-call */ 
177
        $entity->$softDeleteField = $this->getRestoreValue();
Loading history...
177
        return $this->save($entity);
0 ignored issues
show
It seems like save() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

177
        return $this->/** @scrutinizer ignore-call */ save($entity);
Loading history...
178
    }
179
180
    /**
181
     * @param bool $enable
182
     */
183
    public function enableFindWithDeleted($enable = true)
184
    {
185
        $this->_findWithDeleted = (bool)$enable;
186
    }
187
188
    /**
189
     * @return bool
190
     */
191
    public function findWithDeleted()
192
    {
193
        return $this->_findWithDeleted;
194
    }
195
196
    /**
197
     * @return array|string
198
     */
199
    public function getActiveExpression()
200
    {
201
        $aliasedField = $this->aliasField($this->ensureSoftDeleteFieldExists());
0 ignored issues
show
It seems like aliasField() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

201
        /** @scrutinizer ignore-call */ 
202
        $aliasedField = $this->aliasField($this->ensureSoftDeleteFieldExists());
Loading history...
202
        $activeValue = $this->getRestoreValue();
203
204
        if ($activeValue === null) {
205
            return $aliasedField . ' IS NULL';
206
        }
207
208
        return [$aliasedField => $activeValue];
209
    }
210
}
211