Completed
Push — master ( 78a8ff...f55562 )
by Christopher
02:55
created

ActiveRecord::updateAll()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 2
eloc 4
nc 2
nop 2
1
<?php
2
/**
3
 * @link      https://github.com/chrmorandi/yii2-ldap for the canonical source repository
4
 * @package   yii2-ldap
5
 * @author    Christopher Mota <[email protected]>
6
 * @license   MIT License - view the LICENSE file that was distributed with this source code.
7
 */
8
9
namespace chrmorandi\ldap;
10
11
use chrmorandi\ldap\ActiveQuery;
12
use chrmorandi\ldap\Connection;
13
use Exception;
14
use Yii;
15
use yii\db\BaseActiveRecord;
16
17
/**
18
 * ActiveRecord is the base class for classes representing relational data in terms of objects.
19
 *
20
 * This class implements the ActiveRecord pattern for the [ldap] protocol.
21
 *
22
 * For defining a record a subclass should at least implement the [[attributes()]] method to define
23
 * attributes or use some prepared traits for specific objects. A primary key can be defined via [[primaryKey()]] which defaults to `cn` if not specified.
24
 *
25
 * The following is an example model called `User`:
26
 *
27
 * ```php
28
 * class User extends \chrmorandi\ldap\ActiveRecord
29
 * {
30
 *     public function attributes()
31
 *     {
32
 *         return ['objectClass', 'cn', 'name'];
33
 *     }
34
 * }
35
 * ```
36
 *
37
 * Or simply
38
 *
39
 * ```php
40
 * use \chrmorandi\ldap\schemas\ADUserTrait;
41
 * ```
42
 *
43
 * @since 1.0.0
44
 */
45
class ActiveRecord extends BaseActiveRecord
46
{    
0 ignored issues
show
Coding Style introduced by
The opening class brace should be on a newline by itself.
Loading history...
47
    /**
48
     * Returns the LDAP connection used by this AR class.
49
     *
50
     * @return Connection the LDAP connection used by this AR class.
51
     */
52
    public static function getDb()
53
    {
54
        return Yii::$app->get('ldap');
55
    }
56
    
57
    /**
58
     * @inheritdoc
59
     * @return ActiveQuery the newly created [[ActiveQuery]] instance.
60
     */
61
    public static function find()
62
    {
63
        return Yii::createObject(ActiveQuery::class, [get_called_class()]);
64
    }
65
66
    /**
67
     * Returns the primary key name(s) for this AR class.
68
     * This method should be overridden by child classes to define the primary key.
69
     *
70
     * @return string[] the primary keys of this record.
71
     */
72
    public static function primaryKey()
73
    {
74
        return ['dn'];
75
    }
76
    
77
    /**
78
     * Returns the list of attribute names.
79
     * You must override this method to define avaliable attributes.
80
     * @return array list of attribute names.
81
     */
82
    public function attributes()
83
    {
84
        throw new InvalidConfigException('The attributes() method of ldap ActiveRecord has to be implemented by child classes.');
85
    }
86
    
87
    /**
88
     * Inserts a row into the associated database table using the attribute values of this record.
89
     *
90
     * This method performs the following steps in order:
91
     *
92
     * 1. call [[beforeValidate()]] when `$runValidation` is true. If [[beforeValidate()]]
93
     *    returns `false`, the rest of the steps will be skipped;
94
     * 2. call [[afterValidate()]] when `$runValidation` is true. If validation
95
     *    failed, the rest of the steps will be skipped;
96
     * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`,
97
     *    the rest of the steps will be skipped;
98
     * 4. insert the record into database. If this fails, it will skip the rest of the steps;
99
     * 5. call [[afterSave()]];
100
     *
101
     * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],
102
     * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_INSERT]], and [[EVENT_AFTER_INSERT]]
103
     * will be raised by the corresponding methods.
104
     *
105
     * Only the [[dirtyAttributes|changed attribute values]] will be inserted into database.
106
     *
107
     * If the table's primary key is auto-incremental and is null during insertion,
108
     * it will be populated with the actual value after insertion.
109
     *
110
     * For example, to insert a customer record:
111
     *
112
     * ```php
113
     * $customer = new Customer;
114
     * $customer->name = $name;
115
     * $customer->email = $email;
116
     * $customer->insert();
117
     * ```
118
     *
119
     * @param  boolean $runValidation whether to perform validation (calling [[validate()]])
120
     * before saving the record. Defaults to `true`. If the validation fails, the record
121
     * will not be saved to the database and this method will return `false`.
122
     * @param  string[]|null $attributes    list of attributes that need to be saved. Defaults to null, meaning all attributes that are loaded from DB will be saved. meaning all attributes that are loaded from DB will be saved.
123
     * meaning all attributes that are loaded from DB will be saved.
124
     * @return boolean whether the attributes are valid and the record is inserted successfully.
125
     * @throws Exception in case insert failed.
126
     */
127
    public function insert($runValidation = true, $attributes = null)
128
    {
129
        if ($runValidation && !$this->validate($attributes)) {
130
            Yii::info('Model not inserted due to validation error.', __METHOD__);
131
            return false;
132
        }
133
134
        return $this->insertInternal($attributes);
135
    }
136
    
137
    /**
138
     * Inserts an ActiveRecord into LDAP without.
139
     *
140
     * @param  string[]|null $attributes list of attributes that need to be saved. Defaults to null,
141
     * meaning all attributes that are loaded will be saved.
142
     * @return boolean whether the record is inserted successfully.
143
     */
144
    protected function insertInternal($attributes = null)
145
    {
146
        if (!$this->beforeSave(true)) {
147
            return false;
148
        }
149
        
150
        $values = $this->getDirtyAttributes($attributes);
151
        $dn = $values[$this->primaryKey];
152
        unset($values[$this->primaryKey]);
153
        
154
        if (static::getDb()->add($dn, $values) === false) {
155
            return false;
156
        }
157
        $this->setAttribute($this->primaryKey, $dn);
158
        $values[$this->primaryKey] = $dn;
159
160
        $changedAttributes = array_fill_keys(array_keys($values), null);
161
        $this->setOldAttributes($values);
162
        $this->afterSave(true, $changedAttributes);
163
164
        return true;
165
    }
166
    
167
    /**
168
     * @see update()
169
     * @param array $attributes attributes to update
170
     * @return integer number of rows updated
171
     * @throws StaleObjectException
172
     */
173
    protected function updateInternal($attributes = null)
174
    {
175
        if (!$this->beforeSave(false)) {
176
            return false;
177
        }
178
        $values = $this->getDirtyAttributes($attributes);
0 ignored issues
show
Bug introduced by
It seems like $attributes defined by parameter $attributes on line 173 can also be of type array; however, yii\db\BaseActiveRecord::getDirtyAttributes() does only seem to accept array<integer,string>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
179
        if (empty($values)) {
180
            $this->afterSave(false, $values);
181
            return 0;
182
        }
183
184
        if(($condition = $this->getOldPrimaryKey(true)) !== $this->getPrimaryKey(true)) {
185
            // TODO Change DN
186
            static::getDb()->rename($condition, $newRdn, $newParent, true);
0 ignored issues
show
Bug introduced by
The variable $newRdn does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $newParent does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
187
            if (!$this->refresh()){
188
                Yii::info('Model not refresh.', __METHOD__);
189
                return false;
190
            }
191
        }
192
        
193
        foreach ($values as $key => $value) {
194
            if(empty ($this->getOldAttribute($key)) && $value === ''){
195
                unset($values[$key]);
196
            } else if($value === ''){
197
                $attributes[] = ['attrib'  => $key, 'modtype' => LDAP_MODIFY_BATCH_REMOVE];
198
            } else if (empty ($this->getOldAttribute($key))) {
199
                $attributes[] = ['attrib'  => $key, 'modtype' => LDAP_MODIFY_BATCH_ADD, 'values' => [$value]];
200
            } else {
201
                $attributes[] = ['attrib'  => $key, 'modtype' => LDAP_MODIFY_BATCH_REPLACE, 'values' => [$value]];
202
            }
203
        }
204
        
205
        if (empty($attributes)) {
206
            $this->afterSave(false, $attributes);
0 ignored issues
show
Bug introduced by
It seems like $attributes defined by parameter $attributes on line 173 can also be of type null; however, yii\db\BaseActiveRecord::afterSave() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
207
            return 0;
208
        }
209
210
        // We do not check the return value of updateAll() because it's possible
211
        // that the UPDATE statement doesn't change anything and thus returns 0.
212
        $rows = static::updateAll($attributes, $condition);
213
214
//        $changedAttributes = [];
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
215
//        foreach ($values as $name => $value) {
216
//            $changedAttributes[$name] = empty($this->getOldAttributes($name)) ? $this->getOldAttributes($name) : null;
217
//            $this->setOldAttributes([$name->$value]);
218
//        }
219
//        $this->afterSave(false, $changedAttributes);
220
221
        return $rows;
222
    }
223
    
224
    /**
225
     * Updates the whole table using the provided attribute values and conditions.
226
     * For example, to change the status to be 1 for all customers whose status is 2:
227
     *
228
     * ```php
229
     * Customer::updateAll(['status' => 1], 'status = 2');
230
     * ```
231
     *
232
     * @param array $attributes attribute values (name-value pairs) to be saved into the table
233
     * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
234
     * Please refer to [[Query::where()]] on how to specify this parameter.
235
     * @return integer the number of rows updated
236
     */
237
    public static function updateAll($attributes, $condition = '')
238
    {
239
        if(is_array($condition)){
240
            $condition = $condition['dn'];
241
        }        
242
        
243
        static::getDb()->modify($condition, $attributes);
244
    }
245
    
246
    /**
247
     * Deletes rows in the table using the provided conditions.
248
     * WARNING: If you do not specify any condition, this method will delete ALL rows in the ldap directory.
249
     *
250
     * For example, to delete all customers whose status is 3:
251
     *
252
     * ```php
253
     * Customer::deleteAll('status = 3');
254
     * ```
255
     *
256
     * @param  string|array $condition the conditions that will be put in the WHERE part of the DELETE SQL.
257
     * Please refer to [[Query::where()]] on how to specify this parameter.
258
     * @return integer the number of rows deleted
259
     */
260
    public static function deleteAll($condition = '')
261
    {
262
        $entries = (new Query())->select(self::primaryKey())->where($condition)->execute()->toArray();
263
        $count = 0;
264
        
265
        foreach ($entries as $entry) {
266
            $params = [
267
                $entry[self::primaryKey()]
268
            ];
269
            static::getDb()->execute('ldap_delete', $params);
0 ignored issues
show
Bug introduced by
The method execute() does not exist on chrmorandi\ldap\Connection. Did you maybe mean executeQuery()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
270
            $count++;
271
        }
272
        return $count;
273
    }
274
}
275