From 8a2c97c3ba1e405ad9b7cd7a980b5013e996ab11 Mon Sep 17 00:00:00 2001 From: Austin Collier Date: Tue, 12 Mar 2024 09:55:48 -0600 Subject: [PATCH] Fixed relation protected keyword issue and setDatabaseConnection --- src/ActiveRecord.php | 47 +++++++++++++++++++++++- tests/ActiveRecordPdoIntegrationTest.php | 40 ++++++++++++++++++++ tests/ActiveRecordTest.php | 23 ++++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-) diff --git a/src/ActiveRecord.php b/src/ActiveRecord.php index bf33edf..ee3e4cf 100644 --- a/src/ActiveRecord.php +++ b/src/ActiveRecord.php @@ -78,6 +78,18 @@ abstract class ActiveRecord extends Base implements JsonSerializable */ protected array $sqlExpressions = []; + /** + * @var string SQL that is built to be used by execute() + */ + protected string $built_sql = ''; + + /** + * Captures all the joins that are made + * + * @var Expressions|null + */ + protected ?Expressions $join = null; + /** * Database connection * @@ -361,6 +373,20 @@ public function getDatabaseConnection() return $this->databaseConnection; } + /** + * set the database connection. + * @param DatabaseInterface|mysqli|PDO $databaseConnection + * @return void + */ + public function setDatabaseConnection($databaseConnection): void + { + if (($databaseConnection instanceof DatabaseInterface) === true) { + $this->databaseConnection = $databaseConnection; + } else { + $this->transformAndPersistConnection($databaseConnection); + } + } + /** * function to find one record and assign in to current object. * @param int|string $id If call this function using this param, will find record by using this id. If not set, just find the first record in database. @@ -535,6 +561,12 @@ public function query(string $sql, array $param = [], ActiveRecord $obj = null, */ protected function &getRelation(string $name) { + + // can't set the name of a relation to a protected keyword + if(in_array($name, ['select', 'from', 'join', 'where', 'group', 'having', 'order', 'limit', 'offset'], true) === true) { + throw new Exception($name. ' is a protected keyword and cannot be used as a relation name'); + } + $relation = $this->relations[$name]; if (is_array($relation) === true) { // ActiveRecordData::BELONGS_TO etc @@ -604,8 +636,19 @@ protected function buildSql(array $sql_statements = []): string } //this code to debug info. //echo 'SQL: ', implode(' ', $sql_statements), "\n", "PARAMS: ", implode(', ', $this->params), "\n"; - return implode(' ', $sql_statements); - } + $this->built_sql = implode(' ', $sql_statements); + return $this->built_sql; + } + + /** + * Gets the built SQL after buildSql has been called + * + * @return string + */ + public function getBuiltSql(): string + { + return $this->built_sql; + } /** * make wrap when build the SQL expressions of WHERE. * @param string $op If give this param will build one WrapExpressions include the stored expressions add into WHERE. Otherwise will stored the expressions into array. diff --git a/tests/ActiveRecordPdoIntegrationTest.php b/tests/ActiveRecordPdoIntegrationTest.php index 17b7285..643400c 100644 --- a/tests/ActiveRecordPdoIntegrationTest.php +++ b/tests/ActiveRecordPdoIntegrationTest.php @@ -3,6 +3,7 @@ namespace flight\tests; use flight\ActiveRecord; +use flight\database\pdo\PdoAdapter; use flight\tests\classes\Contact; use flight\tests\classes\User; use PDO; @@ -509,4 +510,43 @@ public function testRelationsCascadingSave() $this->assertGreaterThan(0, $user->contact->id); $this->assertFalse($user->isDirty()); } + + public function testSetDatabaseConnection() + { + $user = new User(); + $user->setDatabaseConnection(new PDO('sqlite:test.db')); + $user->name = 'bob'; + $user->password = 'pass'; + $user->save(); + + $this->assertGreaterThan(0, $user->id); + } + + public function testSetDatabaseConnectionWithAdapter() + { + $user = new User(); + $user->setDatabaseConnection(new PdoAdapter(new PDO('sqlite:test.db'))); + $user->name = 'bob'; + $user->password = 'pass'; + $user->save(); + + $this->assertGreaterThan(0, $user->id); + } + + public function testRelationWithProtectedKeyword() { + $user = new User(new PDO('sqlite:test.db')); + $user->name = 'demo'; + $user->password = md5('demo'); + $user->insert(); + + $contact = new class(new PDO('sqlite:test.db')) extends ActiveRecord { + protected array $relations = [ + 'group' => [self::HAS_ONE, User::class, 'user_id'] + ]; + }; + + $this->expectException(\Exception::class); + $this->expectExceptionMessage('group is a protected keyword and cannot be used as a relation name'); + $contact->group->id; + } } diff --git a/tests/ActiveRecordTest.php b/tests/ActiveRecordTest.php index 5d12204..ee99878 100644 --- a/tests/ActiveRecordTest.php +++ b/tests/ActiveRecordTest.php @@ -117,4 +117,27 @@ public function testCopyFrom() $this->assertEquals('John', $record->name); $this->assertEquals(['name' => 'John'], $record->getData()); } + + public function testIsset() + { + $record = new class (null, 'test_table') extends ActiveRecord { + }; + $record->name = 'John'; + $this->assertTrue(isset($record->name)); + $this->assertFalse(isset($record->email)); + } + + public function testMultipleJoins() + { + $record = new class (null, 'test_table') extends ActiveRecord { + public function query(string $sql, array $param = [], ?ActiveRecord $obj = null, bool $single = false) + { + return $this; + } + }; + $record->join('table1', 'table1.some_id = test_table.id'); + $record->join('table2', 'table2.some_id = table1.id'); + $result = $record->find()->getBuiltSql(); + $this->assertEquals('SELECT test_table.* FROM test_table LEFT JOIN table1 ON table1.some_id = test_table.id LEFT JOIN table2 ON table2.some_id = table1.id LIMIT 1 ', $result); + } }