diff --git a/src/ActiveRecord.php b/src/ActiveRecord.php index bf33edf..3b2a06f 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. @@ -474,7 +500,7 @@ public function save(): ActiveRecord } } - return $record; + return $record; } /** @@ -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,7 +636,18 @@ 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. diff --git a/tests/ActiveRecordPdoIntegrationTest.php b/tests/ActiveRecordPdoIntegrationTest.php index 17b7285..a124ce1 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; @@ -491,22 +492,62 @@ public function testIsHydratedGoodFindAll() $this->assertTrue($users[0]->isHydrated()); } - public function testRelationsCascadingSave() + public function testRelationsCascadingSave() { $user = new User(new PDO('sqlite:test.db')); $user->name = 'demo'; $user->password = md5('demo'); $user->insert(); - $user->name = 'bobby'; - $user->contact->user_id = $user->id; - $user->contact->email = 'test@amail.com'; - $user->contact->address = 'test address'; - $user->save(); + $user->name = 'bobby'; + $user->contact->user_id = $user->id; + $user->contact->email = 'test@amail.com'; + $user->contact->address = 'test address'; + $user->save(); + + $this->assertEquals($user->id, $user->contact->user_id); + $this->assertFalse($user->contact->isDirty()); + $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->assertEquals($user->id, $user->contact->user_id); - $this->assertFalse($user->contact->isDirty()); - $this->assertGreaterThan(0, $user->contact->id); - $this->assertFalse($user->isDirty()); + $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..9a92e46 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); + } }