Passed
Push — master ( 344c14...9aa841 )
by Christopher
01:46
created

src/ActiveRecord.php (2 issues)

1
<?php
2
/**
3
 * @link      https://github.com/chrmorandi/yii2-ldap for the 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 Yii;
14
use yii\base\InvalidConfigException;
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
 * Or
37
 *
38
 * ```php
39
 * public function attributes() {
40
 *     return \chrmorandi\ldap\schemas\ADUser::getAttributes();
41
 * }
42
 * ```
43
 *
44
 * @since 1.0.0
45
 */
46
class ActiveRecord extends BaseActiveRecord
47
{
48
    /**
49
     * Returns the LDAP connection used by this AR class.
50
     *
51
     * @return Connection the LDAP connection used by this AR class.
52
     */
53
    public static function getDb()
54
    {
55
        return Yii::$app->get('ldap');
0 ignored issues
show
Bug Best Practice introduced by
The expression return Yii::app->get('ldap') also could return the type mixed which is incompatible with the documented return type chrmorandi\ldap\Connection.
Loading history...
56
    }
57
58
    /**
59
     * @inheritdoc
60
     * @return ActiveQuery the newly created [[ActiveQuery]] instance.
61
     */
62
    public static function find()
63
    {
64
        return Yii::createObject(ActiveQuery::class, [get_called_class()]);
65
    }
66
67
    /**
68
     * Returns the primary key name(s) for this AR class.
69
     * This method should be overridden by child classes to define the primary key.
70
     *
71
     * Note that an array should be returned.
72
     *
73
     * @return string[] the primary keys of this record.
74
     */
75
    public static function primaryKey()
76
    {
77
        return ['dn'];
78
    }
79
80
    /**
81
     * Returns the list of attribute names.
82
     * You must override this method to define avaliable attributes.
83
     * @return array list of attribute names.
84
     */
85
    public function attributes()
86
    {
87
        throw new InvalidConfigException('The attributes() method of ldap ActiveRecord has to be implemented by child classes.');
88
    }
89
90
    /**
91
     * Inserts a record in the ldap using the attribute values.
92
     *
93
     * This method performs the following steps in order:
94
     *
95
     * 1. call [[beforeValidate()]] when `$runValidation` is true. If [[beforeValidate()]]
96
     *    returns `false`, the rest of the steps will be skipped;
97
     * 2. call [[afterValidate()]] when `$runValidation` is true. If validation
98
     *    failed, the rest of the steps will be skipped;
99
     * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`,
100
     *    the rest of the steps will be skipped;
101
     * 4. insert the record into LDAP. If this fails, it will skip the rest of the steps;
102
     * 5. call [[afterSave()]];
103
     *
104
     * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],
105
     * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_INSERT]], and [[EVENT_AFTER_INSERT]]
106
     * will be raised by the corresponding methods.
107
     *
108
     * Only the [[dirtyAttributes|changed attribute values]] will be inserted into database.
109
     *
110
     * If the table's primary key is auto-incremental and is null during insertion,
111
     * it will be populated with the actual value after insertion.
112
     *
113
     * For example, to insert a customer record:
114
     *
115
     * ```php
116
     * $customer = new Customer;
117
     * $customer->name = $name;
118
     * $customer->email = $email;
119
     * $customer->insert();
120
     * ```
121
     *
122
     * @param bool $runValidation whether to perform validation (calling [[validate()]])
123
     * before saving the record. Defaults to `true`. If the validation fails, the record
124
     * will not be saved to the LDAP and this method will return `false`.
125
     * @param string[]|null $attributes list of attributes that need to be saved.
126
     * Defaults to null, meaning all attributes that are loaded from LDAP will be saved.
127
     * meaning all attributes that are loaded from DB will be saved.
128
     * @return bool whether the attributes are valid and the record is inserted successfully.
129
     */
130
    public function insert($runValidation = true, $attributes = null)
131
    {
132
        if ($runValidation && !$this->validate($attributes)) {
133
            Yii::info('Model not inserted due to validation error.', __METHOD__);
134
            return false;
135
        }
136
137
        return $this->insertInternal($attributes);
138
    }
139
140
    /**
141
     * Inserts an ActiveRecord into LDAP without.
142
     *
143
     * @param  string[]|null $attributes list of attributes that need to be saved. Defaults to null,
144
     * meaning all attributes that are loaded will be saved.
145
     * @return bool whether the record is inserted successfully.
146
     */
147
    protected function insertInternal($attributes = null)
148
    {
149
        if (!$this->beforeSave(true)) {
150
            return false;
151
        }
152
153
        $primaryKey = static::primaryKey();
154
        $values     = $this->getDirtyAttributes($attributes);
155
        $dn         = $values[$primaryKey[0]];
156
        unset($values[$primaryKey[0]]);
157
158
        static::getDb()->open();
159
160
        if (static::getDb()->add($dn, $values) === false) {
161
            return false;
162
        }
163
        $this->setAttribute($primaryKey[0], $dn);
164
        $values[$primaryKey[0]] = $dn;
165
166
        $changedAttributes = array_fill_keys(array_keys($values), null);
167
        $this->setOldAttributes($values);
168
        $this->afterSave(true, $changedAttributes);
169
170
        static::getDb()->close();
171
172
        return true;
173
    }
174
175
    /**
176
     * @see update()
177
     * @param string[]|null $attributes the names of the attributes to update.
178
     * @return int|false number of rows updated
179
     */
180
    protected function updateInternal($attributes = null)
181
    {
182
        if (!$this->beforeSave(false)) {
183
            return false;
184
        }
185
        $values = $this->getDirtyAttributes($attributes);
186
187
        if ($this->getOldPrimaryKey() !== $this->getPrimaryKey()) {
188
//            static::getDb()->open();
189
//            static::getDb()->rename($this->getOldPrimaryKey(), $this->getPrimaryKey(), $newParent, true);
190
//            static::getDb()->close();
191
//            if (!$this->refresh()) {
192
//                Yii::info('Model not refresh.', __METHOD__);
193
//                return 0;
194
//            }
195
        }
196
197
        $attributes = [];
198
        foreach ($values as $key => $value) {
199
            if ($key == 'dn') {
200
                continue;
201
            }
202
            if (empty($this->getOldAttribute($key)) && $value === '') {
203
                unset($values[$key]);
204
            } elseif ($value === '') {
205
                $attributes[] = ['attrib' => $key, 'modtype' => LDAP_MODIFY_BATCH_REMOVE];
206
            } elseif (empty($this->getOldAttribute($key))) {
207
                $attributes[] = ['attrib' => $key, 'modtype' => LDAP_MODIFY_BATCH_ADD, 'values' => is_array($value) ? array_map('strval', $value) : [(string) $value]];
208
            } else {
209
                $attributes[] = ['attrib' => $key, 'modtype' => LDAP_MODIFY_BATCH_REPLACE, 'values' => is_array($value) ? array_map('strval', $value) : [(string) $value]];
210
            }
211
        }
212
213
        if (empty($attributes)) {
214
            $this->afterSave(false, $attributes);
215
            return 0;
216
        }
217
218
        // We do not check the return value of updateAll() because it's possible
219
        // that the UPDATE statement doesn't change anything and thus returns 0.
220
        $rows = static::updateAll($attributes, $condition);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $condition seems to be never defined.
Loading history...
221
222
//        $changedAttributes = [];
223
//        foreach ($values as $key => $value) {
224
//            $changedAttributes[$key] = empty($this->getOldAttributes($key)) ? $this->getOldAttributes($key) : null;
225
//            $this->setOldAttributes([$key=>$value]);
226
//        }
227
//        $this->afterSave(false, $changedAttributes);
228
229
        return $rows;
230
    }
231
232
    /**
233
     * Updates the whole table using the provided attribute values and conditions.
234
     * For example, to change the status to be 1 for all customers whose status is 2:
235
     *
236
     * ```php
237
     * Customer::updateAll(['status' => 1], 'status = 2');
238
     * ```
239
     *
240
     * @param string[] $attributes attribute values (name-value pairs) to be saved into the table
241
     * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
242
     * Please refer to [[Query::where()]] on how to specify this parameter.
243
     * @return integer the number of rows updated
244
     */
245
    public static function updateAll($attributes, $condition = '')
246
    {
247
        if (is_array($condition)) {
248
            $condition = $condition['dn'];
249
        }
250
251
        static::getDb()->open();
252
        static::getDb()->modify($condition, $attributes);
253
        static::getDb()->close();
254
255
        return count($attributes);
256
    }
257
258
    /**
259
     * Deletes rows in the table using the provided conditions.
260
     * WARNING: If you do not specify any condition, this method will delete ALL rows in the ldap directory.
261
     *
262
     * For example, to delete all customers whose status is 3:
263
     *
264
     * ```php
265
     * Customer::deleteAll('status = 3');
266
     * ```
267
     *
268
     * @param  string|array $condition the conditions that will be put in the WHERE part of the DELETE SQL.
269
     * Please refer to [[Query::where()]] on how to specify this parameter.
270
     * @return integer the number of rows deleted
271
     */
272
    public static function deleteAll($condition = '')
273
    {
274
        $entries = (new Query())->select(self::primaryKey())->where($condition)->execute()->toArray();
275
        $count   = 0;
276
277
        static::getDb()->open();
278
        foreach ($entries as $entry) {
279
            $dn = $entry[self::primaryKey()[0]];
280
            static::getDb()->delete($dn);
281
            $count++;
282
        }
283
        static::getDb()->close();
284
285
        return $count;
286
    }
287
288
}
289