Completed
Push — master ( f0fd1d...defc9d )
by Christopher
40:01 queued 02:21
created

ActiveRecord::init()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 3
eloc 8
nc 3
nop 0
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 ReflectionClass;
14
use ReflectionProperty;
15
use Yii;
16
use yii\db\BaseActiveRecord;
17
18
/**
19
 * ActiveRecord is the base class for classes representing relational data in terms of objects.
20
 *
21
 * This class implements the ActiveRecord pattern for the [ldap] protocol.
22
 *
23
 * For defining a record a subclass should at least implement the [[attributes()]] method to define
24
 * attributes. A primary key can be defined via [[primaryKey()]] which defaults to `cn` if not specified.
25
 *
26
 * The following is an example model called `User`:
27
 *
28
 * ```php
29
 * class User extends \yii\redis\ActiveRecord
30
 * {
31
 *     public function attributes()
32
 *     {
33
 *         return ['cn', 'name', 'email'];
34
 *     }
35
 * }
36
 * ```
37
 *
38
 * @author Christopher mota
39
 * @since 1.0.0
40
 */
41
class ActiveRecord extends BaseActiveRecord
42
{
43
    /**
44
     * The schema class with implement SchemaInterface
45
     * @var string
46
     */
47
    public $schemaClass;
48
    
49
    /**
50
     * Instance of Schema class
51
     */
52
    private $schema;
53
    
54
    /**
55
     * Returns the LDAP connection used by this AR class.
56
     * @return Connection the LDAP connection used by this AR class.
57
     */
58
    public static function getDb()
59
    {
60
        return Yii::$app->get('ldap');
61
    }
62
63
    /**
64
     * @inheritdoc
65
     * @return ActiveQuery the newly created [[ActiveQuery]] instance.
66
     */
67
    public static function find()
68
    {
69
        return Yii::createObject(ActiveQuery::class, [get_called_class()]);
70
    }
71
72
    /**
73
     * Returns the primary key name(s) for this AR class.
74
     * This method should be overridden by child classes to define the primary key.
75
     *
76
     * @return string[] the primary keys of this record.
77
     */
78
    public static function primaryKey()
79
    {
80
        return 'dn';
0 ignored issues
show
Bug Best Practice introduced by
The return type of return 'dn'; (string) is incompatible with the return type declared by the interface yii\db\ActiveRecordInterface::primaryKey of type string[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
81
    }
82
    
83
    /**
84
     * Initializes the object.
85
     * This method is called at the end of the constructor.
86
     * The default implementation will trigger an [[EVENT_INIT]] event.
87
     * If you override this method, make sure you call the parent implementation at the end
88
     * to ensure triggering of the event.
89
     */
90
    public function init()
91
    {
92
        $this->schema = new Schema();
93
        
94
        if($this->schemaClass !== null){
95
            if(class_exists($this->schemaClass)){
96
                Schema::set(new $this->schemaClass);
97
            } else {
98
                throw new InvalidConfigException('"' . $this->schemaClass . '" does not exist.');
99
            }
100
        }
101
102
        parent::init();        
103
    }
104
    
105
        
106
    /**
107
     * Returns the list of attribute names.
108
     * By default, this method returns all public non-static properties of the class.
109
     * You may override this method to change the default behavior.
110
     * @return array list of attribute names.
111
     */
112
    public function attributes()
113
    {
114
        $class = new ReflectionClass($this->schema->get());
115
        $names = [];
116
        foreach ($class->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
117
            if (!$property->isStatic()) {
118
                $names[] = $property->getName();
119
            }
120
        }
121
122
        return $names;
123
    }
124
    
125
    /**
126
     * Inserts a row into the associated database table using the attribute values of this record.
127
     *
128
     * This method performs the following steps in order:
129
     *
130
     * 1. call [[beforeValidate()]] when `$runValidation` is true. If [[beforeValidate()]]
131
     *    returns `false`, the rest of the steps will be skipped;
132
     * 2. call [[afterValidate()]] when `$runValidation` is true. If validation
133
     *    failed, the rest of the steps will be skipped;
134
     * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`,
135
     *    the rest of the steps will be skipped;
136
     * 4. insert the record into database. If this fails, it will skip the rest of the steps;
137
     * 5. call [[afterSave()]];
138
     *
139
     * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],
140
     * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_INSERT]], and [[EVENT_AFTER_INSERT]]
141
     * will be raised by the corresponding methods.
142
     *
143
     * Only the [[dirtyAttributes|changed attribute values]] will be inserted into database.
144
     *
145
     * If the table's primary key is auto-incremental and is null during insertion,
146
     * it will be populated with the actual value after insertion.
147
     *
148
     * For example, to insert a customer record:
149
     *
150
     * ```php
151
     * $customer = new Customer;
152
     * $customer->name = $name;
153
     * $customer->email = $email;
154
     * $customer->insert();
155
     * ```
156
     *
157
     * @param boolean $runValidation whether to perform validation (calling [[validate()]])
158
     * before saving the record. Defaults to `true`. If the validation fails, the record
159
     * will not be saved to the database and this method will return `false`.
160
     * @param array $attributes list of attributes that need to be saved. Defaults to null,
161
     * meaning all attributes that are loaded from DB will be saved.
162
     * @return boolean whether the attributes are valid and the record is inserted successfully.
163
     * @throws \Exception in case insert failed.
164
     */
165
    public function insert($runValidation = true, $attributes = null)
166
    {
167
        if ($runValidation && !$this->validate($attributes)) {
168
            Yii::info('Model not inserted due to validation error.', __METHOD__);
169
            return false;
170
        }
171
172
        return $this->insertInternal($attributes);
173
    }
174
    
175
    /**
176
     * Inserts an ActiveRecord into LDAP without.
177
     * @param array $attributes list of attributes that need to be saved. Defaults to null,
178
     * meaning all attributes that are loaded will be saved.
179
     * @return boolean whether the record is inserted successfully.
180
     */
181
    protected function insertInternal($attributes = null)
182
    {
183
        if (!$this->beforeSave(true)) {
184
            return false;
185
        }
186
        
187
        $values = $this->getDirtyAttributes($attributes);
0 ignored issues
show
Bug introduced by
It seems like $attributes defined by parameter $attributes on line 181 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...
188
        $dn = $values[self::primaryKey()];
189
        unset($values[self::primaryKey()]);
190
        
191
        if (($primaryKeys = static::getDb()->execute('ldap_add', [$dn,$values])) === false) {
192
            return false;
193
        }
194
        $this->setAttribute(self::primaryKey(), $dn);
195
        $values[self::primaryKey()] = $dn;
196
197
        $changedAttributes = array_fill_keys(array_keys($values), null);
198
        $this->setOldAttributes($values);
199
        $this->afterSave(true, $changedAttributes);
200
201
        return true;
202
    }
203
    
204
    /**
205
     * Updates the whole table using the provided attribute values and conditions.
206
     * For example, to change the status to be 1 for all customers whose status is 2:
207
     *
208
     * ```php
209
     * Customer::updateAll(['status' => 1], 'status = 2');
210
     * ```
211
     *
212
     * @param array $attributes attribute values (name-value pairs) to be saved into the table
213
     * @param string|array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
214
     * Please refer to [[Query::where()]] on how to specify this parameter.
215
     * @param array $params the parameters (name => value) to be bound to the query.
216
     * @return integer the number of rows updated
217
     */
218
    public static function updateAll($attributes, $condition = '', $params = [])
219
    {
220
        $command = static::getDb()->createCommand();
0 ignored issues
show
Documentation Bug introduced by
The method createCommand does not exist on object<chrmorandi\ldap\Connection>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
221
        $command->update(static::tableName(), $attributes, $condition, $params);
0 ignored issues
show
Bug introduced by
The method tableName() does not seem to exist on object<chrmorandi\ldap\ActiveRecord>.

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...
222
223
        return $command->execute();
224
    }
225
    
226
    /**
227
     * Deletes rows in the table using the provided conditions.
228
     * WARNING: If you do not specify any condition, this method will delete ALL rows in the ldap directory.
229
     *
230
     * For example, to delete all customers whose status is 3:
231
     *
232
     * ```php
233
     * Customer::deleteAll('status = 3');
234
     * ```
235
     *
236
     * @param string|array $condition the conditions that will be put in the WHERE part of the DELETE SQL.
237
     * Please refer to [[Query::where()]] on how to specify this parameter.
238
     * @return integer the number of rows deleted
239
     */
240
    public static function deleteAll($condition = null)
241
    {
242
        $entries = (new Query())->select(self::primaryKey())->where($condition);
0 ignored issues
show
Bug introduced by
It seems like $condition defined by parameter $condition on line 240 can also be of type null; however, yii\db\QueryTrait::where() does only seem to accept string|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...
243
        $count = 0;
244
        
245
        foreach ($entries as $entry) {
0 ignored issues
show
Bug introduced by
The expression $entries of type object<chrmorandi\ldap\Query> is not traversable.
Loading history...
246
            $params = [
247
                $entry[self::primaryKey()]
248
            ];
249
            static::getDb()->execute('ldap_delete', $params);
250
            $count++;
251
        }
252
        return $count;
253
    }
254
255
}
256