Test Failed
Push — v2 ( c354b7...e39573 )
by Berend
03:33
created
src/ColumnProperty.php 1 patch
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -5,10 +5,10 @@
 block discarded – undo
5 5
 
6 6
 class ColumnProperty
7 7
 {
8
-	const NONE = 0;
9
-	const UNIQUE = 1;
10
-	const NOT_NULL = 2;
11
-	const IMMUTABLE = 4;
12
-	const AUTO_INCREMENT = 8;
13
-	const PRIMARY_KEY = 16;
8
+    const NONE = 0;
9
+    const UNIQUE = 1;
10
+    const NOT_NULL = 2;
11
+    const IMMUTABLE = 4;
12
+    const AUTO_INCREMENT = 8;
13
+    const PRIMARY_KEY = 16;
14 14
 }
15 15
\ No newline at end of file
Please login to merge, or discard this patch.
src/Traits/Pagination.php 1 patch
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -6,5 +6,5 @@
 block discarded – undo
6 6
 
7 7
 trait Pagination
8 8
 {
9
-	// @TODO: This one would most likely only be relevant for api requests (and perhaps supply a public method for searching with a page)
9
+    // @TODO: This one would most likely only be relevant for api requests (and perhaps supply a public method for searching with a page)
10 10
 }
11 11
\ No newline at end of file
Please login to merge, or discard this patch.
test-bootstrap.php 1 patch
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -9,7 +9,7 @@  discard block
 block discarded – undo
9 9
 // Cleanup SQL
10 10
 $user_delete = "DROP USER IF EXISTS $dbuser@'localhost'; ";
11 11
 $database_delete = "DROP DATABASE IF EXISTS $dbname; ";
12
-$sql_cleanup = $user_delete . $database_delete;
12
+$sql_cleanup = $user_delete.$database_delete;
13 13
 
14 14
 // Setup SQL
15 15
 $db_create = "CREATE DATABASE $dbname; ";
@@ -17,7 +17,7 @@  discard block
 block discarded – undo
17 17
 $user_alter = "ALTER USER '$dbuser'@'localhost' IDENTIFIED with mysql_native_password BY '$dbpass'; ";
18 18
 $user_grant = "GRANT ALL PRIVILEGES ON *.* TO '$dbuser'@'localhost';";
19 19
 
20
-$sql_setup = $sql_cleanup . $db_create . $user_create . $user_alter . $user_grant;
20
+$sql_setup = $sql_cleanup.$db_create.$user_create.$user_alter.$user_grant;
21 21
 print $sql_setup;
22 22
 echo "Please enter mysql root password to set up a database environment for testing\n";
23 23
 exec("echo \"$sql_setup\" | mysql -u root -p");
Please login to merge, or discard this patch.
src/Traits/Address.php 1 patch
Indentation   +144 added lines, -144 removed lines patch added patch discarded remove patch
@@ -11,154 +11,154 @@
 block discarded – undo
11 11
 
12 12
 trait Address
13 13
 {
14
-	/** @var string the address line */
15
-	protected $address;
16
-
17
-	/** @var string the zipcode */
18
-	protected $zipcode;
19
-
20
-	/** @var string the city */
21
-	protected $city;
22
-
23
-	/** @var string the country */
24
-	protected $country;
25
-
26
-	/**
27
-	 * Registers the Address trait on the including class
28
-	 * @return void
29
-	 */
30
-	protected function initAddress() 
31
-	{
32
-		$this->extendTableDefinition(TRAIT_ADDRESS_FIELD_ADDRESS, [
33
-			'value' => &$this->address,
34
-			'validate' => null,
35
-			'type' => 'VARCHAR',
36
-			'length' => 1024,
37
-			'properties' => null
38
-		]);
39
-
40
-		$this->extendTableDefinition(TRAIT_ADDRESS_FIELD_ZIPCODE, [
41
-			'value' => &$this->zipcode,
42
-			'validate' => null,
43
-			'type' => 'VARCHAR',
44
-			'length' => 1024,
45
-			'properties' => null
46
-		]);
47
-
48
-		$this->extendTableDefinition(TRAIT_ADDRESS_FIELD_CITY, [
49
-			'value' => &$this->city,
50
-			'validate' => null,
51
-			'type' => 'VARCHAR',
52
-			'length' => 1024,
53
-			'properties' => null
54
-		]);
55
-
56
-		$this->extendTableDefinition(TRAIT_ADDRESS_FIELD_COUNTRY, [
57
-			'value' => &$this->country,
58
-			'validate' => null,
59
-			'type' => 'VARCHAR',
60
-			'length' => 1024,
61
-			'properties' => null
62
-		]);
63
-
64
-		$this->address = null;
65
-		$this->zipcode = null;
66
-		$this->city = null;
67
-		$this->country = null;
68
-	}
69
-
70
-	/**
71
-	 * @return string
72
-	 */
73
-	public function getAddress()
74
-	{
75
-		return $this->address;
76
-	}
14
+    /** @var string the address line */
15
+    protected $address;
16
+
17
+    /** @var string the zipcode */
18
+    protected $zipcode;
19
+
20
+    /** @var string the city */
21
+    protected $city;
22
+
23
+    /** @var string the country */
24
+    protected $country;
25
+
26
+    /**
27
+     * Registers the Address trait on the including class
28
+     * @return void
29
+     */
30
+    protected function initAddress() 
31
+    {
32
+        $this->extendTableDefinition(TRAIT_ADDRESS_FIELD_ADDRESS, [
33
+            'value' => &$this->address,
34
+            'validate' => null,
35
+            'type' => 'VARCHAR',
36
+            'length' => 1024,
37
+            'properties' => null
38
+        ]);
39
+
40
+        $this->extendTableDefinition(TRAIT_ADDRESS_FIELD_ZIPCODE, [
41
+            'value' => &$this->zipcode,
42
+            'validate' => null,
43
+            'type' => 'VARCHAR',
44
+            'length' => 1024,
45
+            'properties' => null
46
+        ]);
47
+
48
+        $this->extendTableDefinition(TRAIT_ADDRESS_FIELD_CITY, [
49
+            'value' => &$this->city,
50
+            'validate' => null,
51
+            'type' => 'VARCHAR',
52
+            'length' => 1024,
53
+            'properties' => null
54
+        ]);
55
+
56
+        $this->extendTableDefinition(TRAIT_ADDRESS_FIELD_COUNTRY, [
57
+            'value' => &$this->country,
58
+            'validate' => null,
59
+            'type' => 'VARCHAR',
60
+            'length' => 1024,
61
+            'properties' => null
62
+        ]);
63
+
64
+        $this->address = null;
65
+        $this->zipcode = null;
66
+        $this->city = null;
67
+        $this->country = null;
68
+    }
69
+
70
+    /**
71
+     * @return string
72
+     */
73
+    public function getAddress()
74
+    {
75
+        return $this->address;
76
+    }
77 77
 	
78
-	/**
79
-	 * @param string $address
80
-	 */
81
-	public function setAddress($address)
82
-	{
83
-		$this->address = $address;
84
-	}
85
-
86
-	/**
87
-	 * @return string
88
-	 */
89
-	public function getZipcode()
90
-	{
91
-		return $this->zipcode;
92
-	}
78
+    /**
79
+     * @param string $address
80
+     */
81
+    public function setAddress($address)
82
+    {
83
+        $this->address = $address;
84
+    }
85
+
86
+    /**
87
+     * @return string
88
+     */
89
+    public function getZipcode()
90
+    {
91
+        return $this->zipcode;
92
+    }
93 93
 	
94
-	/**
95
-	 * @param string $zipcode
96
-	 */
97
-	public function setZipcode($zipcode)
98
-	{
99
-		$this->zipcode = $zipcode;
100
-	}
101
-
102
-	/**
103
-	 * @return string
104
-	 */
105
-	public function getCity()
106
-	{
107
-		return $this->city;
108
-	}
94
+    /**
95
+     * @param string $zipcode
96
+     */
97
+    public function setZipcode($zipcode)
98
+    {
99
+        $this->zipcode = $zipcode;
100
+    }
101
+
102
+    /**
103
+     * @return string
104
+     */
105
+    public function getCity()
106
+    {
107
+        return $this->city;
108
+    }
109 109
 	
110
-	/**
111
-	 * @param string $city
112
-	 */
113
-	public function setCity($city)
114
-	{
115
-		$this->city = $city;
116
-	}
117
-
118
-	/**
119
-	 * @return string
120
-	 */
121
-	public function getCountry()
122
-	{
123
-		return $this->country;
124
-	}
110
+    /**
111
+     * @param string $city
112
+     */
113
+    public function setCity($city)
114
+    {
115
+        $this->city = $city;
116
+    }
117
+
118
+    /**
119
+     * @return string
120
+     */
121
+    public function getCountry()
122
+    {
123
+        return $this->country;
124
+    }
125 125
 	
126
-	/**
127
-	 * @param string $country
128
-	 */
129
-	public function setCountry($country)
130
-	{
131
-		$this->country = $country;
132
-	}
133
-
134
-	/**
135
-	 * @return void
136
-	 */
137
-	abstract protected function extendTableDefinition($columnName, $definition);
126
+    /**
127
+     * @param string $country
128
+     */
129
+    public function setCountry($country)
130
+    {
131
+        $this->country = $country;
132
+    }
133
+
134
+    /**
135
+     * @return void
136
+     */
137
+    abstract protected function extendTableDefinition($columnName, $definition);
138 138
 	
139
-	/**
140
-	 * @return void
141
-	 */
142
-	abstract protected function registerSearchHook($columnName, $fn);
143
-
144
-	/**
145
-	 * @return void
146
-	 */
147
-	abstract protected function registerDeleteHook($columnName, $fn);
148
-
149
-	/**
150
-	 * @return void
151
-	 */
152
-	abstract protected function registerUpdateHook($columnName, $fn);
153
-
154
-	/**
155
-	 * @return void
156
-	 */
157
-	abstract protected function registerReadHook($columnName, $fn);
158
-
159
-	/**
160
-	 * @return void
161
-	 */
162
-	abstract protected function registerCreateHook($columnName, $fn);
139
+    /**
140
+     * @return void
141
+     */
142
+    abstract protected function registerSearchHook($columnName, $fn);
143
+
144
+    /**
145
+     * @return void
146
+     */
147
+    abstract protected function registerDeleteHook($columnName, $fn);
148
+
149
+    /**
150
+     * @return void
151
+     */
152
+    abstract protected function registerUpdateHook($columnName, $fn);
153
+
154
+    /**
155
+     * @return void
156
+     */
157
+    abstract protected function registerReadHook($columnName, $fn);
158
+
159
+    /**
160
+     * @return void
161
+     */
162
+    abstract protected function registerCreateHook($columnName, $fn);
163 163
 	
164 164
 }
165 165
\ No newline at end of file
Please login to merge, or discard this patch.
src/Traits/Password.php 1 patch
Indentation   +156 added lines, -156 removed lines patch added patch discarded remove patch
@@ -13,163 +13,163 @@
 block discarded – undo
13 13
 
14 14
 trait Password
15 15
 {
16
-	/** @var string The password hash. */
17
-	protected $password;
18
-
19
-	/** @var string|null The password reset token. */
20
-	protected $passwordResetToken;
21
-
22
-	/**
23
-	 * this method is required to be called in the constructor for each class that uses this trait. 
24
-	 * It adds the fields necessary for the passwords struct to the table definition
25
-	 */
26
-	protected function initPassword()
27
-	{
28
-		$this->extendTableDefinition(TRAIT_PASSWORD_FIELD_PASSWORD, [
29
-			'value' => &$this->password,
30
-			'validate' => [$this, 'validatePassword'],
31
-			'type' => 'VARCHAR',
32
-			'length' => 1024,
33
-			'properties' => null
34
-		]);
35
-
36
-		$this->extendTableDefinition(TRAIT_PASSWORD_FIELD_PASSWORD_RESET_TOKEN, [
37
-			'value' => &$this->passwordResetToken,
38
-			'validate' => null,
39
-			'default' => 0,
40
-			'type' => 'VARCHAR',
41
-			'length' => 1024
42
-		]);
43
-	}
44
-
45
-
46
-	/**
47
-	 * Returns whether the users password has been set
48
-	 * @return boolean true if the user has a password
49
-	 */
50
-	public function hasPasswordBeenSet()
51
-	{
52
-		return $this->password !== null;
53
-	}
54
-
55
-	/**
56
-	 * Returns true if the credentials are correct.
57
-	 *
58
-	 * @param string $password
59
-	 * @return boolean true if the credentials are correct
60
-	 */
61
-	public function isPassword($password)
62
-	{ 
63
-		if (!$this->hasPasswordBeenSet())
64
-		{
65
-			throw new ActiveRecordTraitException("Password field has not been set");
66
-		}
67
-
68
-		if (!password_verify($password, $this->password)) {
69
-			return false;
70
-		}
71
-
72
-		if (password_needs_rehash($this->password, TRAIT_PASSWORD_ENCRYPTION, ['cost' => TRAIT_PASSWORD_STRENTH])) {
73
-			$this->setPassword($password)->sync();
74
-		}
75
-
76
-		return true;
77
-	}
78
-
79
-	public function validatePassword($password) {
80
-		if (strlen($password) < TRAIT_PASSWORD_MIN_LENGTH) {
81
-			$message = sprintf('\'Password\' must be atleast %s characters long. %s characters provied.', TRAIT_PASSWORD_MIN_LENGTH, strlen($password));
82
-			return [false, $message];
83
-		}
84
-		return [true, ''];
85
-	}
86
-
87
-	/**
88
-	 * Set the password.
89
-	 *
90
-	 * @param string $password
91
-	 * @return $this
92
-	 * @throws \Exception
93
-	 */
94
-	public function setPassword($password)
95
-	{
96
-		[$status, $error] = $this->validatePassword($password);
97
-		if (!$status) {
98
-			throw new ActiveRecordTraitException($error);
99
-		}
100
-
101
-		$passwordHash = \password_hash($password, TRAIT_PASSWORD_ENCRYPTION, ['cost' => TRAIT_PASSWORD_STRENTH]);
102
-
103
-		if ($passwordHash === false) {
104
-			throw new ActiveRecordTraitException('\'Password\' hash failed.');
105
-		}
106
-
107
-		$this->password = $passwordHash;
108
-
109
-		return $this;
110
-	}
111
-
112
-	/**
113
-	 * @return string The Hash of the password
114
-	 */
115
-	public function getPasswordHash()
116
-	{
117
-		return $this->password;
118
-	}
119
-
120
-	/**
121
-	 * Returns the currently set password token for the entity, or null if not set
122
-	 * @return string|null The password reset token
123
-	 */
124
-	public function getPasswordResetToken()
125
-	{
126
-		return $this->passwordResetToken;
127
-	}
128
-
129
-	/**
130
-	 * Generates a new password reset token for the user
131
-	 */
132
-	public function generatePasswordResetToken()
133
-	{
134
-		$this->passwordResetToken = md5(uniqid(mt_rand(), true));
135
-	}
136
-
137
-	/**
138
-	 * Clears the current password reset token
139
-	 */
140
-	public function clearPasswordResetToken()
141
-	{
142
-		$this->passwordResetToken = null;
143
-	}
16
+    /** @var string The password hash. */
17
+    protected $password;
18
+
19
+    /** @var string|null The password reset token. */
20
+    protected $passwordResetToken;
21
+
22
+    /**
23
+     * this method is required to be called in the constructor for each class that uses this trait. 
24
+     * It adds the fields necessary for the passwords struct to the table definition
25
+     */
26
+    protected function initPassword()
27
+    {
28
+        $this->extendTableDefinition(TRAIT_PASSWORD_FIELD_PASSWORD, [
29
+            'value' => &$this->password,
30
+            'validate' => [$this, 'validatePassword'],
31
+            'type' => 'VARCHAR',
32
+            'length' => 1024,
33
+            'properties' => null
34
+        ]);
35
+
36
+        $this->extendTableDefinition(TRAIT_PASSWORD_FIELD_PASSWORD_RESET_TOKEN, [
37
+            'value' => &$this->passwordResetToken,
38
+            'validate' => null,
39
+            'default' => 0,
40
+            'type' => 'VARCHAR',
41
+            'length' => 1024
42
+        ]);
43
+    }
44
+
45
+
46
+    /**
47
+     * Returns whether the users password has been set
48
+     * @return boolean true if the user has a password
49
+     */
50
+    public function hasPasswordBeenSet()
51
+    {
52
+        return $this->password !== null;
53
+    }
54
+
55
+    /**
56
+     * Returns true if the credentials are correct.
57
+     *
58
+     * @param string $password
59
+     * @return boolean true if the credentials are correct
60
+     */
61
+    public function isPassword($password)
62
+    { 
63
+        if (!$this->hasPasswordBeenSet())
64
+        {
65
+            throw new ActiveRecordTraitException("Password field has not been set");
66
+        }
67
+
68
+        if (!password_verify($password, $this->password)) {
69
+            return false;
70
+        }
71
+
72
+        if (password_needs_rehash($this->password, TRAIT_PASSWORD_ENCRYPTION, ['cost' => TRAIT_PASSWORD_STRENTH])) {
73
+            $this->setPassword($password)->sync();
74
+        }
75
+
76
+        return true;
77
+    }
78
+
79
+    public function validatePassword($password) {
80
+        if (strlen($password) < TRAIT_PASSWORD_MIN_LENGTH) {
81
+            $message = sprintf('\'Password\' must be atleast %s characters long. %s characters provied.', TRAIT_PASSWORD_MIN_LENGTH, strlen($password));
82
+            return [false, $message];
83
+        }
84
+        return [true, ''];
85
+    }
86
+
87
+    /**
88
+     * Set the password.
89
+     *
90
+     * @param string $password
91
+     * @return $this
92
+     * @throws \Exception
93
+     */
94
+    public function setPassword($password)
95
+    {
96
+        [$status, $error] = $this->validatePassword($password);
97
+        if (!$status) {
98
+            throw new ActiveRecordTraitException($error);
99
+        }
100
+
101
+        $passwordHash = \password_hash($password, TRAIT_PASSWORD_ENCRYPTION, ['cost' => TRAIT_PASSWORD_STRENTH]);
102
+
103
+        if ($passwordHash === false) {
104
+            throw new ActiveRecordTraitException('\'Password\' hash failed.');
105
+        }
106
+
107
+        $this->password = $passwordHash;
108
+
109
+        return $this;
110
+    }
111
+
112
+    /**
113
+     * @return string The Hash of the password
114
+     */
115
+    public function getPasswordHash()
116
+    {
117
+        return $this->password;
118
+    }
119
+
120
+    /**
121
+     * Returns the currently set password token for the entity, or null if not set
122
+     * @return string|null The password reset token
123
+     */
124
+    public function getPasswordResetToken()
125
+    {
126
+        return $this->passwordResetToken;
127
+    }
128
+
129
+    /**
130
+     * Generates a new password reset token for the user
131
+     */
132
+    public function generatePasswordResetToken()
133
+    {
134
+        $this->passwordResetToken = md5(uniqid(mt_rand(), true));
135
+    }
136
+
137
+    /**
138
+     * Clears the current password reset token
139
+     */
140
+    public function clearPasswordResetToken()
141
+    {
142
+        $this->passwordResetToken = null;
143
+    }
144 144
 	
145
-	/**
146
-	 * @return void
147
-	 */
148
-	abstract protected function extendTableDefinition($columnName, $definition);
145
+    /**
146
+     * @return void
147
+     */
148
+    abstract protected function extendTableDefinition($columnName, $definition);
149 149
 	
150
-	/**
151
-	 * @return void
152
-	 */
153
-	abstract protected function registerSearchHook($columnName, $fn);
154
-
155
-	/**
156
-	 * @return void
157
-	 */
158
-	abstract protected function registerDeleteHook($columnName, $fn);
159
-
160
-	/**
161
-	 * @return void
162
-	 */
163
-	abstract protected function registerUpdateHook($columnName, $fn);
164
-
165
-	/**
166
-	 * @return void
167
-	 */
168
-	abstract protected function registerReadHook($columnName, $fn);
169
-
170
-	/**
171
-	 * @return void
172
-	 */
173
-	abstract protected function registerCreateHook($columnName, $fn);
150
+    /**
151
+     * @return void
152
+     */
153
+    abstract protected function registerSearchHook($columnName, $fn);
154
+
155
+    /**
156
+     * @return void
157
+     */
158
+    abstract protected function registerDeleteHook($columnName, $fn);
159
+
160
+    /**
161
+     * @return void
162
+     */
163
+    abstract protected function registerUpdateHook($columnName, $fn);
164
+
165
+    /**
166
+     * @return void
167
+     */
168
+    abstract protected function registerReadHook($columnName, $fn);
169
+
170
+    /**
171
+     * @return void
172
+     */
173
+    abstract protected function registerCreateHook($columnName, $fn);
174 174
 
175 175
 }
176 176
\ No newline at end of file
Please login to merge, or discard this patch.
src/ActiveRecordInterface.php 1 patch
Indentation   +60 added lines, -60 removed lines patch added patch discarded remove patch
@@ -19,72 +19,72 @@
 block discarded – undo
19 19
 interface ActiveRecordInterface
20 20
 {
21 21
 
22
-	public function __construct(\PDO $pdo);
22
+    public function __construct(\PDO $pdo);
23 23
 	
24
-	/**
25
-	 * Returns this active record after creating an entry with the records attributes.
26
-	 *
27
-	 * @return $this
28
-	 * @throws ActiveRecordException on failure.
29
-	 */
30
-	public function create();
24
+    /**
25
+     * Returns this active record after creating an entry with the records attributes.
26
+     *
27
+     * @return $this
28
+     * @throws ActiveRecordException on failure.
29
+     */
30
+    public function create();
31 31
 
32
-	/**
33
-	 * Returns this active record after reading the attributes from the entry with the given identifier.
34
-	 *
35
-	 * @param mixed $id
36
-	 * @return $this
37
-	 * @throws ActiveRecordException on failure.
38
-	 */
39
-	public function read($id);
32
+    /**
33
+     * Returns this active record after reading the attributes from the entry with the given identifier.
34
+     *
35
+     * @param mixed $id
36
+     * @return $this
37
+     * @throws ActiveRecordException on failure.
38
+     */
39
+    public function read($id);
40 40
 
41
-	/**
42
-	 * Returns this active record after updating the attributes to the corresponding entry.
43
-	 *
44
-	 * @return $this
45
-	 * @throws ActiveRecordException on failure.
46
-	 */
47
-	public function update();
41
+    /**
42
+     * Returns this active record after updating the attributes to the corresponding entry.
43
+     *
44
+     * @return $this
45
+     * @throws ActiveRecordException on failure.
46
+     */
47
+    public function update();
48 48
 
49
-	/**
50
-	 * Returns this record after deleting the corresponding entry.
51
-	 *
52
-	 * @return $this
53
-	 * @throws ActiveRecordException on failure.
54
-	 */
55
-	public function delete();
49
+    /**
50
+     * Returns this record after deleting the corresponding entry.
51
+     *
52
+     * @return $this
53
+     * @throws ActiveRecordException on failure.
54
+     */
55
+    public function delete();
56 56
 
57
-	/**
58
-	 * Returns this record after synchronizing it with the corresponding entry.
59
-	 * A new entry is created if this active record does not have a corresponding entry.
60
-	 *
61
-	 * @return $this
62
-	 * @throws ActiveRecordException on failure.
63
-	 */
64
-	public function sync();
57
+    /**
58
+     * Returns this record after synchronizing it with the corresponding entry.
59
+     * A new entry is created if this active record does not have a corresponding entry.
60
+     *
61
+     * @return $this
62
+     * @throws ActiveRecordException on failure.
63
+     */
64
+    public function sync();
65 65
 
66
-	/**
67
-	 * Returns true if this active record has a corresponding entry.
68
-	 *
69
-	 * @return bool true if this active record has a corresponding entry.
70
-	 */
71
-	public function exists();
66
+    /**
67
+     * Returns true if this active record has a corresponding entry.
68
+     *
69
+     * @return bool true if this active record has a corresponding entry.
70
+     */
71
+    public function exists();
72 72
 
73
-	/**
74
-	 * Returns this record after filling it with the given attributes.
75
-	 *
76
-	 * @param array $attributes = []
77
-	 * @return $this
78
-	 * @throws ActiveRecordException on failure.
79
-	 */
80
-	public function fill(array $attributes);
73
+    /**
74
+     * Returns this record after filling it with the given attributes.
75
+     *
76
+     * @param array $attributes = []
77
+     * @return $this
78
+     * @throws ActiveRecordException on failure.
79
+     */
80
+    public function fill(array $attributes);
81 81
 
82
-	/**
83
-	 * Returns the records with the given where, order by, limit and offset clauses.
84
-	 *
85
-	 * @param array $excludedTraits
86
-	 * @return ActiveRecordQuery the query representing the current search.
87
-	 * @throws ActiveRecordException on failure.
88
-	 */
89
-	public function search(Array $excludedTraits);
82
+    /**
83
+     * Returns the records with the given where, order by, limit and offset clauses.
84
+     *
85
+     * @param array $excludedTraits
86
+     * @return ActiveRecordQuery the query representing the current search.
87
+     * @throws ActiveRecordException on failure.
88
+     */
89
+    public function search(Array $excludedTraits);
90 90
 }
Please login to merge, or discard this patch.
src/Traits/Datefields.php 1 patch
Indentation   +97 added lines, -97 removed lines patch added patch discarded remove patch
@@ -10,104 +10,104 @@
 block discarded – undo
10 10
 
11 11
 trait Datefields
12 12
 {
13
-	/** @var string The timestamp representing the moment this record was created */
14
-	protected $created;
15
-
16
-	/** @var string The timestamp representing the moment this record was last updated */
17
-	protected $lastModified;
18
-
19
-	/**
20
-	 * this method is required to be called in the constructor for each class that uses this trait. 
21
-	 * It adds the datefields to the table definition and registers the callback hooks
22
-	 */
23
-	protected function initDatefields()
24
-	{
25
-		$this->extendTableDefinition(TRAIT_DATEFIELDS_CREATED, [
26
-			'value' => &$this->created,
27
-			'validate' => null,
28
-			'type' => 'DATETIME',
29
-			'properties' => ColumnProperty::NOT_NULL | ColumnProperty::IMMUTABLE
30
-		]);
31
-
32
-		$this->extendTableDefinition(TRAIT_DATEFIELDS_LAST_MODIFIED, [
33
-			'value' => &$this->lastModified,
34
-			'validate' => null,
35
-			'type' => 'DATETIME',
36
-			'properties' => ColumnProperty::NOT_NULL | ColumnProperty::IMMUTABLE
37
-		]);
13
+    /** @var string The timestamp representing the moment this record was created */
14
+    protected $created;
15
+
16
+    /** @var string The timestamp representing the moment this record was last updated */
17
+    protected $lastModified;
18
+
19
+    /**
20
+     * this method is required to be called in the constructor for each class that uses this trait. 
21
+     * It adds the datefields to the table definition and registers the callback hooks
22
+     */
23
+    protected function initDatefields()
24
+    {
25
+        $this->extendTableDefinition(TRAIT_DATEFIELDS_CREATED, [
26
+            'value' => &$this->created,
27
+            'validate' => null,
28
+            'type' => 'DATETIME',
29
+            'properties' => ColumnProperty::NOT_NULL | ColumnProperty::IMMUTABLE
30
+        ]);
31
+
32
+        $this->extendTableDefinition(TRAIT_DATEFIELDS_LAST_MODIFIED, [
33
+            'value' => &$this->lastModified,
34
+            'validate' => null,
35
+            'type' => 'DATETIME',
36
+            'properties' => ColumnProperty::NOT_NULL | ColumnProperty::IMMUTABLE
37
+        ]);
38 38
 		
39
-		$this->registerUpdateHook(TRAIT_DATEFIELDS_LAST_MODIFIED, 'DatefieldsUpdateHook');
40
-		$this->registerCreateHook(TRAIT_DATEFIELDS_LAST_MODIFIED, 'DatefieldsCreateHook');
41
-
42
-		$this->created = null;
43
-		$this->lastModified = null;
44
-	}
45
-
46
-	/**
47
-	 * The hook that gets called to set the timestamp whenever a new record is created
48
-	 */
49
-	protected function DatefieldsCreateHook()
50
-	{
51
-		// @TODO: Should this be split up to seperate hooks for "last_modified" and "created" for consistency?
52
-		$this->created = (new \DateTime('now'))->format('Y-m-d H:i:s');
53
-		$this->lastModified = (new \DateTime('now'))->format('Y-m-d H:i:s');
54
-	}
55
-
56
-	/**
57
-	 * The hook that gets called to set the timestamp whenever a record gets updated
58
-	 */
59
-	protected function DatefieldsUpdateHook()
60
-	{
61
-		$this->lastModified = (new \DateTime('now'))->format('Y-m-d H:i:s');
62
-	}
63
-
64
-	/**
65
-	 * Returns the timestamp of last update for this record
66
-	 * @return \DateTime
67
-	 */
68
-	public function getLastModifiedDate()
69
-	{
70
-		return new \DateTime($this->lastModified);
71
-	}
72
-
73
-	/**
74
-	 * Returns the timestamp of when this record was created
75
-	 * @return \DateTime
76
-	 */
77
-	public function getCreationDate()
78
-	{
79
-		return new \DateTime($this->created);
80
-	}
81
-
82
-	/**
83
-	 * @return void
84
-	 */
85
-	abstract protected function extendTableDefinition($columnName, $definition);
39
+        $this->registerUpdateHook(TRAIT_DATEFIELDS_LAST_MODIFIED, 'DatefieldsUpdateHook');
40
+        $this->registerCreateHook(TRAIT_DATEFIELDS_LAST_MODIFIED, 'DatefieldsCreateHook');
41
+
42
+        $this->created = null;
43
+        $this->lastModified = null;
44
+    }
45
+
46
+    /**
47
+     * The hook that gets called to set the timestamp whenever a new record is created
48
+     */
49
+    protected function DatefieldsCreateHook()
50
+    {
51
+        // @TODO: Should this be split up to seperate hooks for "last_modified" and "created" for consistency?
52
+        $this->created = (new \DateTime('now'))->format('Y-m-d H:i:s');
53
+        $this->lastModified = (new \DateTime('now'))->format('Y-m-d H:i:s');
54
+    }
55
+
56
+    /**
57
+     * The hook that gets called to set the timestamp whenever a record gets updated
58
+     */
59
+    protected function DatefieldsUpdateHook()
60
+    {
61
+        $this->lastModified = (new \DateTime('now'))->format('Y-m-d H:i:s');
62
+    }
63
+
64
+    /**
65
+     * Returns the timestamp of last update for this record
66
+     * @return \DateTime
67
+     */
68
+    public function getLastModifiedDate()
69
+    {
70
+        return new \DateTime($this->lastModified);
71
+    }
72
+
73
+    /**
74
+     * Returns the timestamp of when this record was created
75
+     * @return \DateTime
76
+     */
77
+    public function getCreationDate()
78
+    {
79
+        return new \DateTime($this->created);
80
+    }
81
+
82
+    /**
83
+     * @return void
84
+     */
85
+    abstract protected function extendTableDefinition($columnName, $definition);
86 86
 	
87
-	/**
88
-	 * @return void
89
-	 */
90
-	abstract protected function registerSearchHook($columnName, $fn);
91
-
92
-	/**
93
-	 * @return void
94
-	 */
95
-	abstract protected function registerDeleteHook($columnName, $fn);
96
-
97
-	/**
98
-	 * @return void
99
-	 */
100
-	abstract protected function registerUpdateHook($columnName, $fn);
101
-
102
-	/**
103
-	 * @return void
104
-	 */
105
-	abstract protected function registerReadHook($columnName, $fn);
106
-
107
-	/**
108
-	 * @return void
109
-	 */
110
-	abstract protected function registerCreateHook($columnName, $fn);
87
+    /**
88
+     * @return void
89
+     */
90
+    abstract protected function registerSearchHook($columnName, $fn);
91
+
92
+    /**
93
+     * @return void
94
+     */
95
+    abstract protected function registerDeleteHook($columnName, $fn);
96
+
97
+    /**
98
+     * @return void
99
+     */
100
+    abstract protected function registerUpdateHook($columnName, $fn);
101
+
102
+    /**
103
+     * @return void
104
+     */
105
+    abstract protected function registerReadHook($columnName, $fn);
106
+
107
+    /**
108
+     * @return void
109
+     */
110
+    abstract protected function registerCreateHook($columnName, $fn);
111 111
 }
112 112
 
113
-	
114 113
\ No newline at end of file
114
+    
115 115
\ No newline at end of file
Please login to merge, or discard this patch.
src/AbstractActiveRecord.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -268,7 +268,7 @@
 block discarded – undo
268 268
 		}
269 269
 
270 270
 		if ($default !== NULL) {
271
-			$stmnt .= 'DEFAULT ' . var_export($default, true) . ' ';
271
+			$stmnt .= 'DEFAULT '.var_export($default, true).' ';
272 272
 		}
273 273
 
274 274
 		if ($properties & ColumnProperty::AUTO_INCREMENT) {
Please login to merge, or discard this patch.
Indentation   +621 added lines, -621 removed lines patch added patch discarded remove patch
@@ -18,633 +18,633 @@
 block discarded – undo
18 18
  */
19 19
 abstract class AbstractActiveRecord implements ActiveRecordInterface
20 20
 {
21
-	const COLUMN_NAME_ID = 'id';
22
-	const COLUMN_TYPE_ID = 'INT UNSIGNED';
23
-
24
-	/** @var \PDO The PDO object. */
25
-	protected $pdo;
26
-
27
-	/** @var null|int The ID. */
28
-	private $id;
29
-
30
-	/** @var array A map of column name to functions that hook the insert function */
31
-	protected $registeredCreateHooks;
32
-
33
-	/** @var array A map of column name to functions that hook the read function */
34
-	protected $registeredReadHooks;
35
-
36
-	/** @var array A map of column name to functions that hook the update function */
37
-	protected $registeredUpdateHooks;
38
-
39
-	/** @var array A map of column name to functions that hook the update function */
40
-	protected $registeredDeleteHooks;	
41
-
42
-	/** @var array A map of column name to functions that hook the search function */
43
-	protected $registeredSearchHooks;
44
-
45
-	/** @var array A list of table column definitions */
46
-	protected $tableDefinition;
47
-
48
-	/**
49
-	 * Construct an abstract active record with the given PDO.
50
-	 *
51
-	 * @param \PDO $pdo
52
-	 */
53
-	public function __construct(\PDO $pdo)
54
-	{
55
-		$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
56
-		$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
57
-
58
-		$this->setPdo($pdo);
59
-
60
-		$this->registeredCreateHooks = [];
61
-		$this->registeredReadHooks = [];
62
-		$this->registeredUpdateHooks = [];
63
-		$this->registeredDeleteHooks = [];
64
-		$this->registeredSearchHooks = [];
65
-		$this->tableDefinition = $this->getTableDefinition();
66
-
67
-		// Extend table definition with default ID field, throw exception if field already exists
68
-		if (array_key_exists('id', $this->tableDefinition)) {
69
-			$message = "Table definition in record contains a field with name \"id\"";
70
-			$message .= ", which is a reserved name by ActiveRecord";
71
-			throw new ActiveRecordException($message, 0);
72
-		}
73
-
74
-		$this->tableDefinition[self::COLUMN_NAME_ID] =
75
-		[
76
-			'value' => &$this->id,
77
-			'validate' => null,
78
-			'type' => self::COLUMN_TYPE_ID,
79
-			'properties' => ColumnProperty::NOT_NULL | ColumnProperty::IMMUTABLE | ColumnProperty::AUTO_INCREMENT | ColumnProperty::PRIMARY_KEY
80
-		];
81
-	}
82
-
83
-	private function checkHookConstraints($columnName, $hookMap)
84
-	{
85
-		// Check whether column exists
86
-		if (!array_key_exists($columnName, $this->tableDefinition)) 
87
-		{
88
-			throw new ActiveRecordException("Hook is trying to register on non-existing column \"$columnName\"", 0);
89
-		}
90
-
91
-		// Enforcing 1 hook per table column
92
-		if (array_key_exists($columnName, $hookMap)) {
93
-			$message = "Hook is trying to register on an already registered column \"$columnName\", ";
94
-			$message .= "do you have conflicting traits?";
95
-			throw new ActiveRecordException($message, 0);
96
-		}
97
-	}
98
-
99
-	/**
100
-	 * Register a new hook for a specific column that gets called before execution of the create() method
101
-	 * Only one hook per column can be registered at a time
102
-	 * @param string $columnName The name of the column that is registered.
103
-	 * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
104
-	 */
105
-	public function registerCreateHook($columnName, $fn)
106
-	{
107
-		$this->checkHookConstraints($columnName, $this->registeredCreateHooks);
108
-
109
-		if (is_string($fn) && is_callable([$this, $fn])) {
110
-			$this->registeredCreateHooks[$columnName] = [$this, $fn];
111
-		} else if (is_callable($fn)) {
112
-			$this->registeredCreateHooks[$columnName] = $fn;
113
-		} else {
114
-			throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
115
-		}
116
-	}
117
-
118
-	/**
119
-	 * Register a new hook for a specific column that gets called before execution of the read() method
120
-	 * Only one hook per column can be registered at a time
121
-	 * @param string $columnName The name of the column that is registered.
122
-	 * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
123
-	 */
124
-	public function registerReadHook($columnName, $fn)
125
-	{
126
-		$this->checkHookConstraints($columnName, $this->registeredReadHooks);
127
-
128
-		if (is_string($fn) && is_callable([$this, $fn])) {
129
-			$this->registeredReadHooks[$columnName] = [$this, $fn];
130
-		} else if (is_callable($fn)) {
131
-			$this->registeredReadHooks[$columnName] = $fn;
132
-		} else {
133
-			throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
134
-		}
135
-	}
136
-
137
-	/**
138
-	 * Register a new hook for a specific column that gets called before execution of the update() method
139
-	 * Only one hook per column can be registered at a time
140
-	 * @param string $columnName The name of the column that is registered.
141
-	 * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
142
-	 */
143
-	public function registerUpdateHook($columnName, $fn)
144
-	{
145
-		$this->checkHookConstraints($columnName, $this->registeredUpdateHooks);
146
-
147
-		if (is_string($fn) && is_callable([$this, $fn])) {
148
-			$this->registeredUpdateHooks[$columnName] = [$this, $fn];
149
-		} else if (is_callable($fn)) {
150
-			$this->registeredUpdateHooks[$columnName] = $fn;
151
-		} else {
152
-			throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
153
-		}
154
-	}
155
-
156
-	/**
157
-	 * Register a new hook for a specific column that gets called before execution of the delete() method
158
-	 * Only one hook per column can be registered at a time
159
-	 * @param string $columnName The name of the column that is registered.
160
-	 * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
161
-	 */
162
-	public function registerDeleteHook($columnName, $fn)
163
-	{
164
-		$this->checkHookConstraints($columnName, $this->registeredDeleteHooks);
165
-
166
-		if (is_string($fn) && is_callable([$this, $fn])) {
167
-			$this->registeredDeleteHooks[$columnName] = [$this, $fn];
168
-		} else if (is_callable($fn)) {
169
-			$this->registeredDeleteHooks[$columnName] = $fn;
170
-		} else {
171
-			throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
172
-		}
173
-	}
174
-
175
-	/**
176
-	 * Register a new hook for a specific column that gets called before execution of the search() method
177
-	 * Only one hook per column can be registered at a time
178
-	 * @param string $columnName The name of the column that is registered.
179
-	 * @param string|callable $fn Either a callable, or the name of a method on the inheriting object. The callable is required to take one argument: an instance of miBadger\Query\Query; 
180
-	 */
181
-	public function registerSearchHook($columnName, $fn)
182
-	{
183
-		$this->checkHookConstraints($columnName, $this->registeredSearchHooks);
184
-
185
-		if (is_string($fn) && is_callable([$this, $fn])) {
186
-			$this->registeredSearchHooks[$columnName] = [$this, $fn];
187
-		} else if (is_callable($fn)) {
188
-			$this->registeredSearchHooks[$columnName] = $fn;
189
-		} else {
190
-			throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
191
-		}
192
-	}
193
-
194
-	/**
195
-	 * Adds a new column definition to the table.
196
-	 * @param string $columnName The name of the column that is registered.
197
-	 * @param Array $definition The definition of that column.
198
-	 */
199
-	public function extendTableDefinition($columnName, $definition)
200
-	{
201
-		if ($this->tableDefinition === null) {
202
-			throw new ActiveRecordException("tableDefinition is null, most likely due to parent class not having been initialized in constructor");
203
-		}
204
-
205
-		// Enforcing table can only be extended with new columns
206
-		if (array_key_exists($columnName, $this->tableDefinition)) {
207
-			$message = "Table is being extended with a column that already exists, ";
208
-			$message .= "\"$columnName\" conflicts with your table definition";
209
-			throw new ActiveRecordException($message, 0);
210
-		}
211
-
212
-		$this->tableDefinition[$columnName] = $definition;
213
-	}
214
-
215
-	/**
216
-	 * Returns the type string as it should appear in the mysql create table statement for the given column
217
-	 * @return string The type string
218
-	 */
219
-	private function getDatabaseTypeString($colName, $type, $length)
220
-	{
221
-		switch (strtoupper($type)) {
222
-			case '':
223
-				throw new ActiveRecordException(sprintf("Column %s has invalid type \"NULL\"", $colName));
21
+    const COLUMN_NAME_ID = 'id';
22
+    const COLUMN_TYPE_ID = 'INT UNSIGNED';
23
+
24
+    /** @var \PDO The PDO object. */
25
+    protected $pdo;
26
+
27
+    /** @var null|int The ID. */
28
+    private $id;
29
+
30
+    /** @var array A map of column name to functions that hook the insert function */
31
+    protected $registeredCreateHooks;
32
+
33
+    /** @var array A map of column name to functions that hook the read function */
34
+    protected $registeredReadHooks;
35
+
36
+    /** @var array A map of column name to functions that hook the update function */
37
+    protected $registeredUpdateHooks;
38
+
39
+    /** @var array A map of column name to functions that hook the update function */
40
+    protected $registeredDeleteHooks;	
41
+
42
+    /** @var array A map of column name to functions that hook the search function */
43
+    protected $registeredSearchHooks;
44
+
45
+    /** @var array A list of table column definitions */
46
+    protected $tableDefinition;
47
+
48
+    /**
49
+     * Construct an abstract active record with the given PDO.
50
+     *
51
+     * @param \PDO $pdo
52
+     */
53
+    public function __construct(\PDO $pdo)
54
+    {
55
+        $pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
56
+        $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
57
+
58
+        $this->setPdo($pdo);
59
+
60
+        $this->registeredCreateHooks = [];
61
+        $this->registeredReadHooks = [];
62
+        $this->registeredUpdateHooks = [];
63
+        $this->registeredDeleteHooks = [];
64
+        $this->registeredSearchHooks = [];
65
+        $this->tableDefinition = $this->getTableDefinition();
66
+
67
+        // Extend table definition with default ID field, throw exception if field already exists
68
+        if (array_key_exists('id', $this->tableDefinition)) {
69
+            $message = "Table definition in record contains a field with name \"id\"";
70
+            $message .= ", which is a reserved name by ActiveRecord";
71
+            throw new ActiveRecordException($message, 0);
72
+        }
73
+
74
+        $this->tableDefinition[self::COLUMN_NAME_ID] =
75
+        [
76
+            'value' => &$this->id,
77
+            'validate' => null,
78
+            'type' => self::COLUMN_TYPE_ID,
79
+            'properties' => ColumnProperty::NOT_NULL | ColumnProperty::IMMUTABLE | ColumnProperty::AUTO_INCREMENT | ColumnProperty::PRIMARY_KEY
80
+        ];
81
+    }
82
+
83
+    private function checkHookConstraints($columnName, $hookMap)
84
+    {
85
+        // Check whether column exists
86
+        if (!array_key_exists($columnName, $this->tableDefinition)) 
87
+        {
88
+            throw new ActiveRecordException("Hook is trying to register on non-existing column \"$columnName\"", 0);
89
+        }
90
+
91
+        // Enforcing 1 hook per table column
92
+        if (array_key_exists($columnName, $hookMap)) {
93
+            $message = "Hook is trying to register on an already registered column \"$columnName\", ";
94
+            $message .= "do you have conflicting traits?";
95
+            throw new ActiveRecordException($message, 0);
96
+        }
97
+    }
98
+
99
+    /**
100
+     * Register a new hook for a specific column that gets called before execution of the create() method
101
+     * Only one hook per column can be registered at a time
102
+     * @param string $columnName The name of the column that is registered.
103
+     * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
104
+     */
105
+    public function registerCreateHook($columnName, $fn)
106
+    {
107
+        $this->checkHookConstraints($columnName, $this->registeredCreateHooks);
108
+
109
+        if (is_string($fn) && is_callable([$this, $fn])) {
110
+            $this->registeredCreateHooks[$columnName] = [$this, $fn];
111
+        } else if (is_callable($fn)) {
112
+            $this->registeredCreateHooks[$columnName] = $fn;
113
+        } else {
114
+            throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
115
+        }
116
+    }
117
+
118
+    /**
119
+     * Register a new hook for a specific column that gets called before execution of the read() method
120
+     * Only one hook per column can be registered at a time
121
+     * @param string $columnName The name of the column that is registered.
122
+     * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
123
+     */
124
+    public function registerReadHook($columnName, $fn)
125
+    {
126
+        $this->checkHookConstraints($columnName, $this->registeredReadHooks);
127
+
128
+        if (is_string($fn) && is_callable([$this, $fn])) {
129
+            $this->registeredReadHooks[$columnName] = [$this, $fn];
130
+        } else if (is_callable($fn)) {
131
+            $this->registeredReadHooks[$columnName] = $fn;
132
+        } else {
133
+            throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
134
+        }
135
+    }
136
+
137
+    /**
138
+     * Register a new hook for a specific column that gets called before execution of the update() method
139
+     * Only one hook per column can be registered at a time
140
+     * @param string $columnName The name of the column that is registered.
141
+     * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
142
+     */
143
+    public function registerUpdateHook($columnName, $fn)
144
+    {
145
+        $this->checkHookConstraints($columnName, $this->registeredUpdateHooks);
146
+
147
+        if (is_string($fn) && is_callable([$this, $fn])) {
148
+            $this->registeredUpdateHooks[$columnName] = [$this, $fn];
149
+        } else if (is_callable($fn)) {
150
+            $this->registeredUpdateHooks[$columnName] = $fn;
151
+        } else {
152
+            throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
153
+        }
154
+    }
155
+
156
+    /**
157
+     * Register a new hook for a specific column that gets called before execution of the delete() method
158
+     * Only one hook per column can be registered at a time
159
+     * @param string $columnName The name of the column that is registered.
160
+     * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
161
+     */
162
+    public function registerDeleteHook($columnName, $fn)
163
+    {
164
+        $this->checkHookConstraints($columnName, $this->registeredDeleteHooks);
165
+
166
+        if (is_string($fn) && is_callable([$this, $fn])) {
167
+            $this->registeredDeleteHooks[$columnName] = [$this, $fn];
168
+        } else if (is_callable($fn)) {
169
+            $this->registeredDeleteHooks[$columnName] = $fn;
170
+        } else {
171
+            throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
172
+        }
173
+    }
174
+
175
+    /**
176
+     * Register a new hook for a specific column that gets called before execution of the search() method
177
+     * Only one hook per column can be registered at a time
178
+     * @param string $columnName The name of the column that is registered.
179
+     * @param string|callable $fn Either a callable, or the name of a method on the inheriting object. The callable is required to take one argument: an instance of miBadger\Query\Query; 
180
+     */
181
+    public function registerSearchHook($columnName, $fn)
182
+    {
183
+        $this->checkHookConstraints($columnName, $this->registeredSearchHooks);
184
+
185
+        if (is_string($fn) && is_callable([$this, $fn])) {
186
+            $this->registeredSearchHooks[$columnName] = [$this, $fn];
187
+        } else if (is_callable($fn)) {
188
+            $this->registeredSearchHooks[$columnName] = $fn;
189
+        } else {
190
+            throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
191
+        }
192
+    }
193
+
194
+    /**
195
+     * Adds a new column definition to the table.
196
+     * @param string $columnName The name of the column that is registered.
197
+     * @param Array $definition The definition of that column.
198
+     */
199
+    public function extendTableDefinition($columnName, $definition)
200
+    {
201
+        if ($this->tableDefinition === null) {
202
+            throw new ActiveRecordException("tableDefinition is null, most likely due to parent class not having been initialized in constructor");
203
+        }
204
+
205
+        // Enforcing table can only be extended with new columns
206
+        if (array_key_exists($columnName, $this->tableDefinition)) {
207
+            $message = "Table is being extended with a column that already exists, ";
208
+            $message .= "\"$columnName\" conflicts with your table definition";
209
+            throw new ActiveRecordException($message, 0);
210
+        }
211
+
212
+        $this->tableDefinition[$columnName] = $definition;
213
+    }
214
+
215
+    /**
216
+     * Returns the type string as it should appear in the mysql create table statement for the given column
217
+     * @return string The type string
218
+     */
219
+    private function getDatabaseTypeString($colName, $type, $length)
220
+    {
221
+        switch (strtoupper($type)) {
222
+            case '':
223
+                throw new ActiveRecordException(sprintf("Column %s has invalid type \"NULL\"", $colName));
224 224
 			
225
-			case 'BOOL';
226
-			case 'BOOLEAN':
227
-			case 'DATETIME':
228
-			case 'DATE':
229
-			case 'TIME':
230
-			case 'TEXT':
231
-			case 'INT UNSIGNED':
232
-				return $type;
233
-
234
-			case 'VARCHAR':
235
-				if ($length === null) {
236
-					throw new ActiveRecordException(sprintf("field type %s requires specified column field \"LENGTH\"", $colName));
237
-				} else {
238
-					return sprintf('%s(%d)', $type, $length);	
239
-				}
240
-
241
-			case 'INT':
242
-			case 'TINYINT':
243
-			case 'BIGINT':
244
-			default: 	
245
-				// Implicitly assuming that non-specified cases are correct without a length parameter
246
-				if ($length === null) {
247
-					return $type;
248
-				} else {
249
-					return sprintf('%s(%d)', $type, $length);	
250
-				}
251
-		}
252
-	}
253
-
254
-	/**
255
-	 * Builds the part of a MySQL create table statement that corresponds to the supplied column
256
-	 * @param string $colName 	Name of the database column
257
-	 * @param string $type 		The type of the string
258
-	 * @param int $properties 	The set of Column properties that apply to this column (See ColumnProperty for options)
259
-	 * @return string
260
-	 */
261
-	private function buildCreateTableColumnEntry($colName, $type, $length, $properties, $default)
262
-	{
263
-		$stmnt = sprintf('`%s` %s ', $colName, $this->getDatabaseTypeString($colName, $type, $length));
264
-		if ($properties & ColumnProperty::NOT_NULL) {
265
-			$stmnt .= 'NOT NULL ';
266
-		} else {
267
-			$stmnt .= 'NULL ';
268
-		}
269
-
270
-		if ($default !== NULL) {
271
-			$stmnt .= 'DEFAULT ' . var_export($default, true) . ' ';
272
-		}
273
-
274
-		if ($properties & ColumnProperty::AUTO_INCREMENT) {
275
-			$stmnt .= 'AUTO_INCREMENT ';
276
-		}
277
-
278
-		if ($properties & ColumnProperty::UNIQUE) {
279
-			$stmnt .= 'UNIQUE ';
280
-		}
281
-
282
-		if ($properties & ColumnProperty::PRIMARY_KEY) {
283
-			$stmnt .= 'PRIMARY KEY ';
284
-		}
285
-
286
-		return $stmnt;
287
-	}
288
-
289
-	/**
290
-	 * Sorts the column statement components in the order such that the id appears first, 
291
-	 * 		followed by all other columns in alphabetical ascending order
292
-	 * @param   Array $colStatements Array of column statements
293
-	 * @return  Array
294
-	 */
295
-	private function sortColumnStatements($colStatements)
296
-	{
297
-		// Find ID statement and put it first
298
-		$sortedStatements = [];
299
-
300
-		$sortedStatements[] = $colStatements[self::COLUMN_NAME_ID];
301
-		unset($colStatements[self::COLUMN_NAME_ID]);
302
-
303
-		// Sort remaining columns in alphabetical order
304
-		$columns = array_keys($colStatements);
305
-		sort($columns);
306
-		foreach ($columns as $colName) {
307
-			$sortedStatements[] = $colStatements[$colName];
308
-		}
309
-
310
-		return $sortedStatements;
311
-	}
312
-
313
-	/**
314
-	 * Builds the MySQL Create Table statement for the internal table definition
315
-	 * @return string
316
-	 */
317
-	public function buildCreateTableSQL()
318
-	{
319
-		$columnStatements = [];
320
-		foreach ($this->tableDefinition as $colName => $definition) {
321
-			// Destructure column definition
322
-			$type    = $definition['type'] ?? null;
323
-			$default = $definition['default'] ?? null;
324
-			$length  = $definition['length'] ?? null;
325
-			$properties = $definition['properties'] ?? null;
326
-
327
-			if (isset($definition['relation']) && $type !== null) {
328
-				$tableName = $this->getTableName();
329
-				$msg = "Column \"$colName\" on table \"$tableName\": ";
330
-				$msg .= "Relationship columns have an automatically inferred type, so type should be omitted";
331
-				throw new ActiveRecordException($msg);
332
-			} else if (isset($definition['relation'])) {
333
-				$type = self::COLUMN_TYPE_ID;
334
-			}
335
-
336
-			$columnStatements[$colName] = $this->buildCreateTableColumnEntry($colName, $type, $length, $properties, $default);
337
-		}
338
-
339
-		// Sort table (first column is id, the remaining are alphabetically sorted)
340
-		$columnStatements = $this->sortColumnStatements($columnStatements);
341
-
342
-		$sql = sprintf("CREATE TABLE %s (\n%s\n);", 
343
-			$this->getTableName(), 
344
-			implode(",\n", $columnStatements));
345
-
346
-		return $sql;
347
-	}
348
-
349
-	/**
350
-	 * Creates the entity as a table in the database
351
-	 */
352
-	public function createTable()
353
-	{
354
-		$this->pdo->query($this->buildCreateTableSQL());
355
-	}
356
-
357
-	/**
358
-	 * builds a MySQL constraint statement for the given parameters
359
-	 * @param string $parentTable
360
-	 * @param string $parentColumn
361
-	 * @param string $childTable
362
-	 * @param string $childColumn
363
-	 * @return string The MySQL table constraint string
364
-	 */
365
-	protected function buildConstraint($parentTable, $parentColumn, $childTable, $childColumn)
366
-	{
367
-		$template = <<<SQL
225
+            case 'BOOL';
226
+            case 'BOOLEAN':
227
+            case 'DATETIME':
228
+            case 'DATE':
229
+            case 'TIME':
230
+            case 'TEXT':
231
+            case 'INT UNSIGNED':
232
+                return $type;
233
+
234
+            case 'VARCHAR':
235
+                if ($length === null) {
236
+                    throw new ActiveRecordException(sprintf("field type %s requires specified column field \"LENGTH\"", $colName));
237
+                } else {
238
+                    return sprintf('%s(%d)', $type, $length);	
239
+                }
240
+
241
+            case 'INT':
242
+            case 'TINYINT':
243
+            case 'BIGINT':
244
+            default: 	
245
+                // Implicitly assuming that non-specified cases are correct without a length parameter
246
+                if ($length === null) {
247
+                    return $type;
248
+                } else {
249
+                    return sprintf('%s(%d)', $type, $length);	
250
+                }
251
+        }
252
+    }
253
+
254
+    /**
255
+     * Builds the part of a MySQL create table statement that corresponds to the supplied column
256
+     * @param string $colName 	Name of the database column
257
+     * @param string $type 		The type of the string
258
+     * @param int $properties 	The set of Column properties that apply to this column (See ColumnProperty for options)
259
+     * @return string
260
+     */
261
+    private function buildCreateTableColumnEntry($colName, $type, $length, $properties, $default)
262
+    {
263
+        $stmnt = sprintf('`%s` %s ', $colName, $this->getDatabaseTypeString($colName, $type, $length));
264
+        if ($properties & ColumnProperty::NOT_NULL) {
265
+            $stmnt .= 'NOT NULL ';
266
+        } else {
267
+            $stmnt .= 'NULL ';
268
+        }
269
+
270
+        if ($default !== NULL) {
271
+            $stmnt .= 'DEFAULT ' . var_export($default, true) . ' ';
272
+        }
273
+
274
+        if ($properties & ColumnProperty::AUTO_INCREMENT) {
275
+            $stmnt .= 'AUTO_INCREMENT ';
276
+        }
277
+
278
+        if ($properties & ColumnProperty::UNIQUE) {
279
+            $stmnt .= 'UNIQUE ';
280
+        }
281
+
282
+        if ($properties & ColumnProperty::PRIMARY_KEY) {
283
+            $stmnt .= 'PRIMARY KEY ';
284
+        }
285
+
286
+        return $stmnt;
287
+    }
288
+
289
+    /**
290
+     * Sorts the column statement components in the order such that the id appears first, 
291
+     * 		followed by all other columns in alphabetical ascending order
292
+     * @param   Array $colStatements Array of column statements
293
+     * @return  Array
294
+     */
295
+    private function sortColumnStatements($colStatements)
296
+    {
297
+        // Find ID statement and put it first
298
+        $sortedStatements = [];
299
+
300
+        $sortedStatements[] = $colStatements[self::COLUMN_NAME_ID];
301
+        unset($colStatements[self::COLUMN_NAME_ID]);
302
+
303
+        // Sort remaining columns in alphabetical order
304
+        $columns = array_keys($colStatements);
305
+        sort($columns);
306
+        foreach ($columns as $colName) {
307
+            $sortedStatements[] = $colStatements[$colName];
308
+        }
309
+
310
+        return $sortedStatements;
311
+    }
312
+
313
+    /**
314
+     * Builds the MySQL Create Table statement for the internal table definition
315
+     * @return string
316
+     */
317
+    public function buildCreateTableSQL()
318
+    {
319
+        $columnStatements = [];
320
+        foreach ($this->tableDefinition as $colName => $definition) {
321
+            // Destructure column definition
322
+            $type    = $definition['type'] ?? null;
323
+            $default = $definition['default'] ?? null;
324
+            $length  = $definition['length'] ?? null;
325
+            $properties = $definition['properties'] ?? null;
326
+
327
+            if (isset($definition['relation']) && $type !== null) {
328
+                $tableName = $this->getTableName();
329
+                $msg = "Column \"$colName\" on table \"$tableName\": ";
330
+                $msg .= "Relationship columns have an automatically inferred type, so type should be omitted";
331
+                throw new ActiveRecordException($msg);
332
+            } else if (isset($definition['relation'])) {
333
+                $type = self::COLUMN_TYPE_ID;
334
+            }
335
+
336
+            $columnStatements[$colName] = $this->buildCreateTableColumnEntry($colName, $type, $length, $properties, $default);
337
+        }
338
+
339
+        // Sort table (first column is id, the remaining are alphabetically sorted)
340
+        $columnStatements = $this->sortColumnStatements($columnStatements);
341
+
342
+        $sql = sprintf("CREATE TABLE %s (\n%s\n);", 
343
+            $this->getTableName(), 
344
+            implode(",\n", $columnStatements));
345
+
346
+        return $sql;
347
+    }
348
+
349
+    /**
350
+     * Creates the entity as a table in the database
351
+     */
352
+    public function createTable()
353
+    {
354
+        $this->pdo->query($this->buildCreateTableSQL());
355
+    }
356
+
357
+    /**
358
+     * builds a MySQL constraint statement for the given parameters
359
+     * @param string $parentTable
360
+     * @param string $parentColumn
361
+     * @param string $childTable
362
+     * @param string $childColumn
363
+     * @return string The MySQL table constraint string
364
+     */
365
+    protected function buildConstraint($parentTable, $parentColumn, $childTable, $childColumn)
366
+    {
367
+        $template = <<<SQL
368 368
 ALTER TABLE `%s`
369 369
 ADD CONSTRAINT
370 370
 FOREIGN KEY (`%s`)
371 371
 REFERENCES `%s`(`%s`)
372 372
 ON DELETE CASCADE;
373 373
 SQL;
374
-		return sprintf($template, $childTable, $childColumn, $parentTable, $parentColumn);
375
-	}
376
-
377
-	/**
378
-	 * Iterates over the specified constraints in the table definition, 
379
-	 * 		and applies these to the database.
380
-	 */
381
-	public function createTableConstraints()
382
-	{
383
-		// Iterate over columns, check whether "relation" field exists, if so create constraint
384
-		foreach ($this->tableDefinition as $colName => $definition) {
385
-			if (isset($definition['relation']) && $definition['relation'] instanceof AbstractActiveRecord) {
386
-				// Forge new relation
387
-				$target = $definition['relation'];
388
-				$constraintSql = $this->buildConstraint($target->getTableName(), 'id', $this->getTableName(), $colName);
389
-
390
-				$this->pdo->query($constraintSql);
391
-			} else if (isset($definition['relation'])) {
392
-				$msg = sprintf("Relation constraint on column \"%s\" of table \"%s\" does not contain a valid ActiveRecord instance", 
393
-					$colName,
394
-					$this->getTableName());
395
-				throw new ActiveRecordException($msg);
396
-			}
397
-		}
398
-	}
399
-
400
-	/**
401
-	 * Returns the name -> variable mapping for the table definition.
402
-	 * @return Array The mapping
403
-	 */
404
-	protected function getActiveRecordColumns()
405
-	{
406
-		$bindings = [];
407
-		foreach ($this->tableDefinition as $colName => $definition) {
408
-
409
-			// Ignore the id column (key) when inserting or updating
410
-			if ($colName == self::COLUMN_NAME_ID) {
411
-				continue;
412
-			}
413
-
414
-			$bindings[$colName] = &$definition['value'];
415
-		}
416
-		return $bindings;
417
-	}
418
-
419
-	/**
420
-	 * {@inheritdoc}
421
-	 */
422
-	public function create()
423
-	{
424
-		foreach ($this->registeredCreateHooks as $colName => $fn) {
425
-			$fn();
426
-		}
427
-
428
-		// Insert default values for not-null fields
429
-		foreach ($this->tableDefinition as $colName => $colDef) {
430
-			if ($this->tableDefinition[$colName]['value'] === null
431
-				&& isset($this->tableDefinition[$colName]['properties'])
432
-				&& $this->tableDefinition[$colName]['properties'] && ColumnProperty::NOT_NULL > 0
433
-				&& isset($this->tableDefinition[$colName]['default'])) {
434
-				$this->tableDefinition[$colName]['value'] = $this->tableDefinition[$colName]['default'];
435
-			}
436
-		}
437
-
438
-		try {
439
-			(new Query($this->getPdo(), $this->getTableName()))
440
-				->insert($this->getActiveRecordColumns())
441
-				->execute();
442
-
443
-			$this->setId(intval($this->getPdo()->lastInsertId()));
444
-		} catch (\PDOException $e) {
445
-			throw new ActiveRecordException($e->getMessage(), 0, $e);
446
-		}
447
-
448
-		return $this;
449
-	}
450
-
451
-	/**
452
-	 * {@inheritdoc}
453
-	 */
454
-	public function read($id)
455
-	{
456
-		$whereConditions = [
457
-			Query::Equal('id', $id)
458
-		];
459
-		foreach ($this->registeredReadHooks as $colName => $fn) {
460
-			$cond = $fn();
461
-			if ($cond !== null) {
462
-				$whereConditions[] = $cond;
463
-			}
464
-		}
465
-
466
-		try {
467
-			$row = (new Query($this->getPdo(), $this->getTableName()))
468
-				->select()
469
-				->where(Query::AndArray($whereConditions))
470
-				->execute()
471
-				->fetch();
374
+        return sprintf($template, $childTable, $childColumn, $parentTable, $parentColumn);
375
+    }
376
+
377
+    /**
378
+     * Iterates over the specified constraints in the table definition, 
379
+     * 		and applies these to the database.
380
+     */
381
+    public function createTableConstraints()
382
+    {
383
+        // Iterate over columns, check whether "relation" field exists, if so create constraint
384
+        foreach ($this->tableDefinition as $colName => $definition) {
385
+            if (isset($definition['relation']) && $definition['relation'] instanceof AbstractActiveRecord) {
386
+                // Forge new relation
387
+                $target = $definition['relation'];
388
+                $constraintSql = $this->buildConstraint($target->getTableName(), 'id', $this->getTableName(), $colName);
389
+
390
+                $this->pdo->query($constraintSql);
391
+            } else if (isset($definition['relation'])) {
392
+                $msg = sprintf("Relation constraint on column \"%s\" of table \"%s\" does not contain a valid ActiveRecord instance", 
393
+                    $colName,
394
+                    $this->getTableName());
395
+                throw new ActiveRecordException($msg);
396
+            }
397
+        }
398
+    }
399
+
400
+    /**
401
+     * Returns the name -> variable mapping for the table definition.
402
+     * @return Array The mapping
403
+     */
404
+    protected function getActiveRecordColumns()
405
+    {
406
+        $bindings = [];
407
+        foreach ($this->tableDefinition as $colName => $definition) {
408
+
409
+            // Ignore the id column (key) when inserting or updating
410
+            if ($colName == self::COLUMN_NAME_ID) {
411
+                continue;
412
+            }
413
+
414
+            $bindings[$colName] = &$definition['value'];
415
+        }
416
+        return $bindings;
417
+    }
418
+
419
+    /**
420
+     * {@inheritdoc}
421
+     */
422
+    public function create()
423
+    {
424
+        foreach ($this->registeredCreateHooks as $colName => $fn) {
425
+            $fn();
426
+        }
427
+
428
+        // Insert default values for not-null fields
429
+        foreach ($this->tableDefinition as $colName => $colDef) {
430
+            if ($this->tableDefinition[$colName]['value'] === null
431
+                && isset($this->tableDefinition[$colName]['properties'])
432
+                && $this->tableDefinition[$colName]['properties'] && ColumnProperty::NOT_NULL > 0
433
+                && isset($this->tableDefinition[$colName]['default'])) {
434
+                $this->tableDefinition[$colName]['value'] = $this->tableDefinition[$colName]['default'];
435
+            }
436
+        }
437
+
438
+        try {
439
+            (new Query($this->getPdo(), $this->getTableName()))
440
+                ->insert($this->getActiveRecordColumns())
441
+                ->execute();
442
+
443
+            $this->setId(intval($this->getPdo()->lastInsertId()));
444
+        } catch (\PDOException $e) {
445
+            throw new ActiveRecordException($e->getMessage(), 0, $e);
446
+        }
447
+
448
+        return $this;
449
+    }
450
+
451
+    /**
452
+     * {@inheritdoc}
453
+     */
454
+    public function read($id)
455
+    {
456
+        $whereConditions = [
457
+            Query::Equal('id', $id)
458
+        ];
459
+        foreach ($this->registeredReadHooks as $colName => $fn) {
460
+            $cond = $fn();
461
+            if ($cond !== null) {
462
+                $whereConditions[] = $cond;
463
+            }
464
+        }
465
+
466
+        try {
467
+            $row = (new Query($this->getPdo(), $this->getTableName()))
468
+                ->select()
469
+                ->where(Query::AndArray($whereConditions))
470
+                ->execute()
471
+                ->fetch();
472 472
 			
473
-			if ($row === false) {
474
-				throw new ActiveRecordException(sprintf('Can not read the non-existent active record entry %d from the `%s` table.', $id, $this->getTableName()));	
475
-			}
476
-
477
-			$this->fill($row)->setId($id);
478
-		} catch (\PDOException $e) {
479
-			throw new ActiveRecordException($e->getMessage(), 0, $e);
480
-		}
481
-
482
-		return $this;
483
-	}
484
-
485
-	/**
486
-	 * {@inheritdoc}
487
-	 */
488
-	public function update()
489
-	{
490
-		foreach ($this->registeredUpdateHooks as $colName => $fn) {
491
-			$fn();
492
-		}
493
-
494
-		try {
495
-			(new Query($this->getPdo(), $this->getTableName()))
496
-				->update($this->getActiveRecordColumns())
497
-				->where(Query::Equal('id', $this->getId()))
498
-				->execute();
499
-		} catch (\PDOException $e) {
500
-			throw new ActiveRecordException($e->getMessage(), 0, $e);
501
-		}
502
-
503
-		return $this;
504
-	}
505
-
506
-	/**
507
-	 * {@inheritdoc}
508
-	 */
509
-	public function delete()
510
-	{
511
-		foreach ($this->registeredDeleteHooks as $colName => $fn) {
512
-			$fn();
513
-		}
514
-
515
-		try {
516
-			(new Query($this->getPdo(), $this->getTableName()))
517
-				->delete()
518
-				->where(Query::Equal('id', $this->getId()))
519
-				->execute();
520
-
521
-			$this->setId(null);
522
-		} catch (\PDOException $e) {
523
-			throw new ActiveRecordException($e->getMessage(), 0, $e);
524
-		}
525
-
526
-		return $this;
527
-	}
528
-
529
-	/**
530
-	 * {@inheritdoc}
531
-	 */
532
-	public function sync()
533
-	{
534
-		if (!$this->exists()) {
535
-			return $this->create();
536
-		}
537
-
538
-		return $this->update();
539
-	}
540
-
541
-	/**
542
-	 * {@inheritdoc}
543
-	 */
544
-	public function exists()
545
-	{
546
-		return $this->getId() !== null;
547
-	}
548
-
549
-	/**
550
-	 * {@inheritdoc}
551
-	 */
552
-	public function fill(array $attributes)
553
-	{
554
-		$columns = $this->getActiveRecordColumns();
555
-		$columns['id'] = &$this->id;
556
-
557
-		foreach ($attributes as $key => $value) {
558
-			if (array_key_exists($key, $columns)) {
559
-				$columns[$key] = $value;
560
-			}
561
-		}
562
-
563
-		return $this;
564
-	}
565
-
566
-	/**
567
-	 * {@inheritdoc}
568
-	 */
569
-	public function search(array $ignoredTraits = [])
570
-	{
571
-		$clauses = [];
572
-		foreach ($this->registeredSearchHooks as $column => $fn) {
573
-			if (!in_array($column, $ignoredTraits)) {
574
-				$clauses[] = $fn();
575
-			}
576
-		}
577
-
578
-		return new ActiveRecordQuery($this, $clauses);
579
-	}
580
-
581
-	/**
582
-	 * Returns the PDO.
583
-	 *
584
-	 * @return \PDO the PDO.
585
-	 */
586
-	public function getPdo()
587
-	{
588
-		return $this->pdo;
589
-	}
590
-
591
-	/**
592
-	 * Set the PDO.
593
-	 *
594
-	 * @param \PDO $pdo
595
-	 * @return $this
596
-	 */
597
-	protected function setPdo($pdo)
598
-	{
599
-		$this->pdo = $pdo;
600
-
601
-		return $this;
602
-	}
603
-
604
-	/**
605
-	 * Returns the ID.
606
-	 *
607
-	 * @return null|int The ID.
608
-	 */
609
-	public function getId()
610
-	{
611
-		return $this->id;
612
-	}
613
-
614
-	/**
615
-	 * Set the ID.
616
-	 *
617
-	 * @param int $id
618
-	 * @return $this
619
-	 */
620
-	protected function setId($id)
621
-	{
622
-		$this->id = $id;
623
-
624
-		return $this;
625
-	}
626
-
627
-	public function getFinalTableDefinition()
628
-	{
629
-		return $this->tableDefinition;
630
-	}
631
-
632
-	public function newInstance()
633
-	{
634
-		return new static($this->pdo);
635
-	}
636
-
637
-	/**
638
-	 * Returns the active record table.
639
-	 *
640
-	 * @return string the active record table name.
641
-	 */
642
-	abstract public function getTableName();
643
-
644
-	/**
645
-	 * Returns the active record columns.
646
-	 *
647
-	 * @return array the active record columns.
648
-	 */
649
-	abstract protected function getTableDefinition();
473
+            if ($row === false) {
474
+                throw new ActiveRecordException(sprintf('Can not read the non-existent active record entry %d from the `%s` table.', $id, $this->getTableName()));	
475
+            }
476
+
477
+            $this->fill($row)->setId($id);
478
+        } catch (\PDOException $e) {
479
+            throw new ActiveRecordException($e->getMessage(), 0, $e);
480
+        }
481
+
482
+        return $this;
483
+    }
484
+
485
+    /**
486
+     * {@inheritdoc}
487
+     */
488
+    public function update()
489
+    {
490
+        foreach ($this->registeredUpdateHooks as $colName => $fn) {
491
+            $fn();
492
+        }
493
+
494
+        try {
495
+            (new Query($this->getPdo(), $this->getTableName()))
496
+                ->update($this->getActiveRecordColumns())
497
+                ->where(Query::Equal('id', $this->getId()))
498
+                ->execute();
499
+        } catch (\PDOException $e) {
500
+            throw new ActiveRecordException($e->getMessage(), 0, $e);
501
+        }
502
+
503
+        return $this;
504
+    }
505
+
506
+    /**
507
+     * {@inheritdoc}
508
+     */
509
+    public function delete()
510
+    {
511
+        foreach ($this->registeredDeleteHooks as $colName => $fn) {
512
+            $fn();
513
+        }
514
+
515
+        try {
516
+            (new Query($this->getPdo(), $this->getTableName()))
517
+                ->delete()
518
+                ->where(Query::Equal('id', $this->getId()))
519
+                ->execute();
520
+
521
+            $this->setId(null);
522
+        } catch (\PDOException $e) {
523
+            throw new ActiveRecordException($e->getMessage(), 0, $e);
524
+        }
525
+
526
+        return $this;
527
+    }
528
+
529
+    /**
530
+     * {@inheritdoc}
531
+     */
532
+    public function sync()
533
+    {
534
+        if (!$this->exists()) {
535
+            return $this->create();
536
+        }
537
+
538
+        return $this->update();
539
+    }
540
+
541
+    /**
542
+     * {@inheritdoc}
543
+     */
544
+    public function exists()
545
+    {
546
+        return $this->getId() !== null;
547
+    }
548
+
549
+    /**
550
+     * {@inheritdoc}
551
+     */
552
+    public function fill(array $attributes)
553
+    {
554
+        $columns = $this->getActiveRecordColumns();
555
+        $columns['id'] = &$this->id;
556
+
557
+        foreach ($attributes as $key => $value) {
558
+            if (array_key_exists($key, $columns)) {
559
+                $columns[$key] = $value;
560
+            }
561
+        }
562
+
563
+        return $this;
564
+    }
565
+
566
+    /**
567
+     * {@inheritdoc}
568
+     */
569
+    public function search(array $ignoredTraits = [])
570
+    {
571
+        $clauses = [];
572
+        foreach ($this->registeredSearchHooks as $column => $fn) {
573
+            if (!in_array($column, $ignoredTraits)) {
574
+                $clauses[] = $fn();
575
+            }
576
+        }
577
+
578
+        return new ActiveRecordQuery($this, $clauses);
579
+    }
580
+
581
+    /**
582
+     * Returns the PDO.
583
+     *
584
+     * @return \PDO the PDO.
585
+     */
586
+    public function getPdo()
587
+    {
588
+        return $this->pdo;
589
+    }
590
+
591
+    /**
592
+     * Set the PDO.
593
+     *
594
+     * @param \PDO $pdo
595
+     * @return $this
596
+     */
597
+    protected function setPdo($pdo)
598
+    {
599
+        $this->pdo = $pdo;
600
+
601
+        return $this;
602
+    }
603
+
604
+    /**
605
+     * Returns the ID.
606
+     *
607
+     * @return null|int The ID.
608
+     */
609
+    public function getId()
610
+    {
611
+        return $this->id;
612
+    }
613
+
614
+    /**
615
+     * Set the ID.
616
+     *
617
+     * @param int $id
618
+     * @return $this
619
+     */
620
+    protected function setId($id)
621
+    {
622
+        $this->id = $id;
623
+
624
+        return $this;
625
+    }
626
+
627
+    public function getFinalTableDefinition()
628
+    {
629
+        return $this->tableDefinition;
630
+    }
631
+
632
+    public function newInstance()
633
+    {
634
+        return new static($this->pdo);
635
+    }
636
+
637
+    /**
638
+     * Returns the active record table.
639
+     *
640
+     * @return string the active record table name.
641
+     */
642
+    abstract public function getTableName();
643
+
644
+    /**
645
+     * Returns the active record columns.
646
+     *
647
+     * @return array the active record columns.
648
+     */
649
+    abstract protected function getTableDefinition();
650 650
 }
Please login to merge, or discard this patch.
src/ActiveRecordQuery.php 1 patch
Indentation   +266 added lines, -266 removed lines patch added patch discarded remove patch
@@ -20,276 +20,276 @@
 block discarded – undo
20 20
  */
21 21
 class ActiveRecordQuery implements \IteratorAggregate
22 22
 {
23
-	private $instance;
23
+    private $instance;
24 24
 
25
-	private $query;
25
+    private $query;
26 26
 
27
-	private $type;
27
+    private $type;
28 28
 
29
-	private $clauses = [];
29
+    private $clauses = [];
30 30
 
31
-	private $maxresultCount;
31
+    private $maxresultCount;
32 32
 
33
-	private $results;
33
+    private $results;
34 34
 	
35
-	private $whereExpression = null;
36
-
37
-	private $limit;
38
-
39
-	private $offset;
40
-
41
-	private $orderBy;
42
-
43
-	private $orderDirection;
44
-
45
-	/**
46
-	 * Constructs a new Active Record Query
47
-	 */
48
-	public function __construct(AbstractActiveRecord $instance, Array $additionalWhereClauses)
49
-	{
50
-		$this->instance = $instance;
51
-		$this->query = new Query($instance->getPdo(), $instance->getTableName());
52
-		$this->type = $instance;
53
-		$this->clauses = $additionalWhereClauses;
54
-		$this->maxResultCount = null;
55
-		$this->results = null;
56
-		$this->limit = null;
57
-		$this->offset = null;
58
-	}
59
-
60
-	private function getWhereCondition()
61
-	{
62
-		$clauses = $this->clauses;
63
-
64
-		// Optionally add user concatenated where expression
65
-		if ($this->whereExpression !== null) {
66
-			$clauses[] = $this->whereExpression;
67
-		}
68
-
69
-		// Construct where clause
70
-		if (count($clauses) > 0) {
71
-			return Query::AndArray($clauses);
72
-		}
73
-		return null;
74
-	}
75
-
76
-	/**
77
-	 * Executes the query
78
-	 */
79
-	public function execute()
80
-	{
81
-		$whereCondition = $this->getWhereCondition();
82
-		if ($whereCondition !== null) {
83
-			$this->query->where($whereCondition);
84
-		}
85
-
86
-		$this->query->select();
87
-
88
-		$this->results = $this->query->execute();
89
-
90
-		return $this;
91
-	}
92
-
93
-	/**
94
-	 * Returns an iterator for the result set
95
-	 * @return ArrayIterator
96
-	 */
97
-	public function getIterator()
98
-	{
99
-		return new \ArrayIterator($this->fetchAll());
100
-	}
101
-
102
-	/**
103
-	 * returns the result set of ActiveRecord instances for this query
104
-	 * @return Array
105
-	 */
106
-	public function fetchAll()
107
-	{
108
-		try {
109
-			if ($this->results === null) {
110
-				$this->execute();	
111
-			}
112
-
113
-			$entries = $this->results->fetchAll();
114
-			if ($entries === false) {
115
-				return [];
116
-			}
117
-
118
-			$typedResults = [];
119
-			foreach ($entries as $entry) {
120
-				$typedEntry = $this->type->newInstance();
121
-				$typedEntry->fill($entry);
122
-				$typedResults[] = $typedEntry;
123
-			}
124
-
125
-			return $typedResults;
126
-		} catch (\PDOException $e) {
127
-			throw new ActiveRecordException($e->getMessage(), 0, $e);
128
-		}
129
-	}
130
-
131
-	public function fetchAllAsArray($readWhitelist)
132
-	{
133
-		$data = $this->fetchAll();
134
-		$output = [];
135
-		foreach ($data as $entry) {
136
-			$output[] = $entry->toArray($readWhitelist);
137
-		}
138
-		return $output;
139
-	}
140
-
141
-	/**
142
-	 * Fetch one record from the database
143
-	 * @return AbstractActiveRecord 
144
-	 */
145
-	public function fetch()
146
-	{
147
-		try {
148
-			if ($this->results === null) {
149
-				$this->execute();
150
-			}
151
-
152
-			$typedResult = $this->type->newInstance();
153
-
154
-			$entry = $this->results->fetch();
155
-			if ($entry === false) {
156
-				return null;
157
-			}
158
-
159
-			$typedResult->fill($entry);
160
-
161
-			return $typedResult;
162
-		} catch (\PDOException $e) {
163
-			throw new ActiveRecordException($e->getMessage(), 0, $e);
164
-		}
165
-	}
166
-
167
-	/**
168
-	 * Fetch one record from the database and format it as an associative array, 
169
-	 * 	 filtered by the entries in $readwhitelist
170
-	 * @param Array $readWhitelist Array of whitelisted database column keys to be returned in the result
171
-	 * @return Array|Null
172
-	 */
173
-	public function fetchAsArray($readWhitelist)
174
-	{
175
-		$res = $this->fetch();
176
-		if ($res !== null) {
177
-			return $res->toArray($readWhitelist);
178
-		}
179
-		return null;
180
-	}
181
-
182
-	public function countMaxResults()
183
-	{
184
-		if ($this->maxResultCount === null) {
185
-			$query = new Query($this->instance->getPdo(), $this->instance->getTableName());
186
-			$query->select(['count(*) as count']);
187
-
188
-			$whereCondition = $this->getWhereCondition();
189
-			if ($whereCondition !== null) {
190
-				$query->where($whereCondition);
191
-			}
192
-
193
-			$this->maxResultCount = $query->execute()->fetch()['count'];
194
-		}
195
-		return $this->maxResultCount;
196
-	}
197
-
198
-	public function getNumberOfPages()
199
-	{
200
-		if ($this->limit === null) {
201
-			return 1;
202
-		}
203
-
204
-		if ($this->limit === 0) {
205
-			return 0;
206
-		}
207
-
208
-		$resultCount = $this->countMaxResults();
209
-		if ($resultCount % $this->limit > 0) {
210
-			return $resultCount / $this->limit + 1;
211
-		}
212
-		return $resultCount / $this->limit;
213
-	}
214
-
215
-	public function getCurrentPage()
216
-	{
217
-		if ($this->offset === null || $this->offset === 0) {
218
-			return 1;
219
-		}
220
-
221
-		if ($this->limit === null || $this->limit === 0) {
222
-			return 1;
223
-		}
224
-
225
-		return $this->offset / $this->limit;
226
-	}
227
-
228
-	/**
229
-	 * Set the where condition
230
-	 *
231
-	 * @param QueryExpression $expression the query expression
232
-	 * @return $this
233
-	 * @see https://en.wikipedia.org/wiki/SQL#Operators
234
-	 * @see https://en.wikipedia.org/wiki/Where_(SQL)
235
-	 */
236
-	public function where(QueryExpression $expression)
237
-	{
238
-		$this->whereExpression = $expression;
239
-		return $this;
240
-	}
241
-
242
-	/**
243
-	 * Set an additional group by.
244
-	 *
245
-	 * @param string $column
246
-	 * @return $this
247
-	 * @see https://en.wikipedia.org/wiki/SQL#Queries
248
-	 */
249
-	public function groupBy($column)
250
-	{
251
-		$this->query->groupBy($column);
252
-		return $this;
253
-	}
254
-
255
-	/**
256
-	 * Set an additional order condition.
257
-	 *
258
-	 * @param string $column
259
-	 * @param string|null $order
260
-	 * @return $this
261
-	 * @see https://en.wikipedia.org/wiki/SQL#Queries
262
-	 * @see https://en.wikipedia.org/wiki/Order_by
263
-	 */
264
-	public function orderBy($column, $order = null)
265
-	{
266
-		$this->query->orderBy($column, $order);	
267
-		return $this;
268
-	}
269
-
270
-	/**
271
-	 * Set the limit.
272
-	 *
273
-	 * @param mixed $limit
274
-	 * @return $this
275
-	 */
276
-	public function limit($limit)
277
-	{
278
-		$this->limit = $limit;
279
-		$this->query->limit($limit);
280
-		return $this;
281
-	}
282
-
283
-	/**
284
-	 * Set the offset.
285
-	 *
286
-	 * @param mixed $offset
287
- 	 * @return $this
288
-	 */
289
-	public function offset($offset)
290
-	{
291
-		$this->offset = $offset;
292
-		$this->query->offset($offset);
293
-		return $this;
294
-	}
35
+    private $whereExpression = null;
36
+
37
+    private $limit;
38
+
39
+    private $offset;
40
+
41
+    private $orderBy;
42
+
43
+    private $orderDirection;
44
+
45
+    /**
46
+     * Constructs a new Active Record Query
47
+     */
48
+    public function __construct(AbstractActiveRecord $instance, Array $additionalWhereClauses)
49
+    {
50
+        $this->instance = $instance;
51
+        $this->query = new Query($instance->getPdo(), $instance->getTableName());
52
+        $this->type = $instance;
53
+        $this->clauses = $additionalWhereClauses;
54
+        $this->maxResultCount = null;
55
+        $this->results = null;
56
+        $this->limit = null;
57
+        $this->offset = null;
58
+    }
59
+
60
+    private function getWhereCondition()
61
+    {
62
+        $clauses = $this->clauses;
63
+
64
+        // Optionally add user concatenated where expression
65
+        if ($this->whereExpression !== null) {
66
+            $clauses[] = $this->whereExpression;
67
+        }
68
+
69
+        // Construct where clause
70
+        if (count($clauses) > 0) {
71
+            return Query::AndArray($clauses);
72
+        }
73
+        return null;
74
+    }
75
+
76
+    /**
77
+     * Executes the query
78
+     */
79
+    public function execute()
80
+    {
81
+        $whereCondition = $this->getWhereCondition();
82
+        if ($whereCondition !== null) {
83
+            $this->query->where($whereCondition);
84
+        }
85
+
86
+        $this->query->select();
87
+
88
+        $this->results = $this->query->execute();
89
+
90
+        return $this;
91
+    }
92
+
93
+    /**
94
+     * Returns an iterator for the result set
95
+     * @return ArrayIterator
96
+     */
97
+    public function getIterator()
98
+    {
99
+        return new \ArrayIterator($this->fetchAll());
100
+    }
101
+
102
+    /**
103
+     * returns the result set of ActiveRecord instances for this query
104
+     * @return Array
105
+     */
106
+    public function fetchAll()
107
+    {
108
+        try {
109
+            if ($this->results === null) {
110
+                $this->execute();	
111
+            }
112
+
113
+            $entries = $this->results->fetchAll();
114
+            if ($entries === false) {
115
+                return [];
116
+            }
117
+
118
+            $typedResults = [];
119
+            foreach ($entries as $entry) {
120
+                $typedEntry = $this->type->newInstance();
121
+                $typedEntry->fill($entry);
122
+                $typedResults[] = $typedEntry;
123
+            }
124
+
125
+            return $typedResults;
126
+        } catch (\PDOException $e) {
127
+            throw new ActiveRecordException($e->getMessage(), 0, $e);
128
+        }
129
+    }
130
+
131
+    public function fetchAllAsArray($readWhitelist)
132
+    {
133
+        $data = $this->fetchAll();
134
+        $output = [];
135
+        foreach ($data as $entry) {
136
+            $output[] = $entry->toArray($readWhitelist);
137
+        }
138
+        return $output;
139
+    }
140
+
141
+    /**
142
+     * Fetch one record from the database
143
+     * @return AbstractActiveRecord 
144
+     */
145
+    public function fetch()
146
+    {
147
+        try {
148
+            if ($this->results === null) {
149
+                $this->execute();
150
+            }
151
+
152
+            $typedResult = $this->type->newInstance();
153
+
154
+            $entry = $this->results->fetch();
155
+            if ($entry === false) {
156
+                return null;
157
+            }
158
+
159
+            $typedResult->fill($entry);
160
+
161
+            return $typedResult;
162
+        } catch (\PDOException $e) {
163
+            throw new ActiveRecordException($e->getMessage(), 0, $e);
164
+        }
165
+    }
166
+
167
+    /**
168
+     * Fetch one record from the database and format it as an associative array, 
169
+     * 	 filtered by the entries in $readwhitelist
170
+     * @param Array $readWhitelist Array of whitelisted database column keys to be returned in the result
171
+     * @return Array|Null
172
+     */
173
+    public function fetchAsArray($readWhitelist)
174
+    {
175
+        $res = $this->fetch();
176
+        if ($res !== null) {
177
+            return $res->toArray($readWhitelist);
178
+        }
179
+        return null;
180
+    }
181
+
182
+    public function countMaxResults()
183
+    {
184
+        if ($this->maxResultCount === null) {
185
+            $query = new Query($this->instance->getPdo(), $this->instance->getTableName());
186
+            $query->select(['count(*) as count']);
187
+
188
+            $whereCondition = $this->getWhereCondition();
189
+            if ($whereCondition !== null) {
190
+                $query->where($whereCondition);
191
+            }
192
+
193
+            $this->maxResultCount = $query->execute()->fetch()['count'];
194
+        }
195
+        return $this->maxResultCount;
196
+    }
197
+
198
+    public function getNumberOfPages()
199
+    {
200
+        if ($this->limit === null) {
201
+            return 1;
202
+        }
203
+
204
+        if ($this->limit === 0) {
205
+            return 0;
206
+        }
207
+
208
+        $resultCount = $this->countMaxResults();
209
+        if ($resultCount % $this->limit > 0) {
210
+            return $resultCount / $this->limit + 1;
211
+        }
212
+        return $resultCount / $this->limit;
213
+    }
214
+
215
+    public function getCurrentPage()
216
+    {
217
+        if ($this->offset === null || $this->offset === 0) {
218
+            return 1;
219
+        }
220
+
221
+        if ($this->limit === null || $this->limit === 0) {
222
+            return 1;
223
+        }
224
+
225
+        return $this->offset / $this->limit;
226
+    }
227
+
228
+    /**
229
+     * Set the where condition
230
+     *
231
+     * @param QueryExpression $expression the query expression
232
+     * @return $this
233
+     * @see https://en.wikipedia.org/wiki/SQL#Operators
234
+     * @see https://en.wikipedia.org/wiki/Where_(SQL)
235
+     */
236
+    public function where(QueryExpression $expression)
237
+    {
238
+        $this->whereExpression = $expression;
239
+        return $this;
240
+    }
241
+
242
+    /**
243
+     * Set an additional group by.
244
+     *
245
+     * @param string $column
246
+     * @return $this
247
+     * @see https://en.wikipedia.org/wiki/SQL#Queries
248
+     */
249
+    public function groupBy($column)
250
+    {
251
+        $this->query->groupBy($column);
252
+        return $this;
253
+    }
254
+
255
+    /**
256
+     * Set an additional order condition.
257
+     *
258
+     * @param string $column
259
+     * @param string|null $order
260
+     * @return $this
261
+     * @see https://en.wikipedia.org/wiki/SQL#Queries
262
+     * @see https://en.wikipedia.org/wiki/Order_by
263
+     */
264
+    public function orderBy($column, $order = null)
265
+    {
266
+        $this->query->orderBy($column, $order);	
267
+        return $this;
268
+    }
269
+
270
+    /**
271
+     * Set the limit.
272
+     *
273
+     * @param mixed $limit
274
+     * @return $this
275
+     */
276
+    public function limit($limit)
277
+    {
278
+        $this->limit = $limit;
279
+        $this->query->limit($limit);
280
+        return $this;
281
+    }
282
+
283
+    /**
284
+     * Set the offset.
285
+     *
286
+     * @param mixed $offset
287
+     * @return $this
288
+     */
289
+    public function offset($offset)
290
+    {
291
+        $this->offset = $offset;
292
+        $this->query->offset($offset);
293
+        return $this;
294
+    }
295 295
 }
Please login to merge, or discard this patch.