From 432ab184e609e82a8ea3d1d14ed2f495a949e986 Mon Sep 17 00:00:00 2001 From: rotimi Date: Wed, 5 Jun 2024 20:02:51 -0600 Subject: [PATCH] Named parameters / place-holder now being used in all LeanORM generated queries --- README.md | 9 +++++--- docs/getting-started.md | 47 ++++++++++++++++++----------------------- src/LeanOrm/Model.php | 14 +++++++----- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 5f4dd99..27ecfd8 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ PHP 7.4+ for versions 2.x & 3.x [Composer](https://getcomposer.org/) -Version 2.x & 3.x of this package have been rigorously tested against sqlite 3.7.11+, MySQL 8.0.29+ & Postgresql 15.1+. +Versions 2.x & 3.x of this package have been rigorously tested against sqlite 3.7.11+, MySQL 8.0.29+ & Postgresql 15.1+. Version 4.x has been rigorously tested against: - Sqlite 3.34.1+ @@ -105,7 +105,9 @@ Documentation for version 2.x version can be found [here](https://github.com/rot Documentation for version 3.x+ can be found [here](https://github.com/rotexsoft/leanorm/blob/3.x/docs/index.md). -Documentation for version 4.x+ can be found [here](https://github.com/rotexsoft/leanorm/blob/master/docs/index.md). +Documentation for version 4.0.x+ can be found [here](https://github.com/rotexsoft/leanorm/blob/4.0.x/docs/index.md). + +Documentation for version 4.1.x+ can be found [here](https://github.com/rotexsoft/leanorm/blob/master/docs/index.md). Please submit an issue (preferably with a pull request) to address mistakes or omissions in the documentation or to propose improvements to the documentation. @@ -123,7 +125,8 @@ New Test files must be manually added to the phpunit.xml.dist file in order for These are the branches in this repository: -- **master:** contains code for the latest major version of this package. +- **master:** contains code for the latest major version (4.1.x) of this package. +- **4.0.x:** contains code for the 4.0.x versions of this package. Only bug fixes should be added to this branch. This branch is feature complete. - **3.x:** contains code for the 3.x versions of this package. Only bug fixes should be added to this branch. This branch is feature complete. - **2.2.x:** contains code for the 2.2.x versions of this package. Only bug fixes should be added to this branch. This branch is feature complete. - **1.X:** contains code for the **1.X** versions of this package. This branch is abandoned. diff --git a/docs/getting-started.md b/docs/getting-started.md index baccdb1..3f35a47 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -324,6 +324,8 @@ The following methods for fetching data from the database are defined in **\GDAO All these fetch methods accept a first argument which is a query object. LeanOrm uses [Aura\SqlQuery](https://github.com/auraphp/Aura.SqlQuery/blob/3.x/docs/select.md) as its query object. You can create a query object to inject into each fetch method using the **getSelect(): \Aura\SqlQuery\Common\Select** method in **\LeanOrm\Model**. Read the documentation for [Aura\SqlQuery](https://github.com/auraphp/Aura.SqlQuery/blob/3.x/docs/select.md) to figure out how to customize the sql queries executed by each fetch method. Some examples will be shown later on below. +> NOTE: Please ALWAYS use named parameters / place-holders in all your queries, [Aura\SqlQuery](https://github.com/auraphp/Aura.SqlQuery/blob/3.x/docs/select.md) version 3.x was designed to work with named parameters / place-holders. DO NOT use question mark parameters / place-holders in your queries. + Some of these fetch methods also accept a second argument called **$relations_to_include**. It is basically an array of relationship names for related data defined in the Model class. When you specify these relationship names in a fetch method, the fetch method will eager load the related data which would eliminate the need to issues N queries to fetch the related data for a specified defined relation for each fetched record which leads to the N+1 problem. For example, when fetching records from the authors table via the AuthorsModel, each author record / row can have one or more posts associated with it. If you do not specify that the posts for the author records be eager fetched during a fetch, then when you loop through the returned author records, additional queries will be issued to fetch the posts for each author. If we have 3 authors in the database, then doing a fetch without eager loading posts will lead to the following queries being issued when you loop through the authors and try to access the posts associated with each of them: ```sql @@ -399,7 +401,7 @@ $colVals = $authorsModel->fetchCol(); $colVals = $authorsModel->fetchCol( $authorsModel->getSelect() ->cols(['name']) - ->where(' author_id <= ? ', [5]) + ->where(' author_id <= :author_id_val ', ['author_id_val' => 5]) ); ``` @@ -420,7 +422,7 @@ $record = $authorsModel->fetchOneRecord(); $record = $authorsModel->fetchOneRecord( $authorsModel->getSelect() ->cols(['author_id', 'name']) - ->where(' author_id = ? ', [5]) + ->where(' author_id = :author_id_val ', ['author_id_val' => 5]) ); // $record will contain the first row of data returned by @@ -431,7 +433,7 @@ $record = $authorsModel->fetchOneRecord( $record = $authorsModel->fetchOneRecord( $authorsModel->getSelect() ->cols(['author_id', 'name']) - ->where(' author_id = ? ', [5]), + ->where(' author_id = :author_id_val ', ['author_id_val' => 5]), ['posts'] // eager fetch posts for the author ); ``` @@ -478,7 +480,7 @@ $keyValPairs = $authorsModel->fetchPairs(); $keyValPairs = $authorsModel->fetchPairs( $authorsModel->getSelect() ->cols(['author_id', 'date_created']) - ->where(' author_id <= ? ', [5]) + ->where(' author_id <= :author_id_val ', ['author_id_val' => 5]) ); // Similar to example above, except that the second specified column is an expression @@ -490,7 +492,7 @@ $keyValPairs = $authorsModel->fetchPairs( $keyValPairs = $authorsModel->fetchPairs( $authorsModel->getSelect() ->cols(['author_id', " concat(author_id, '-', 'name') "]) - ->where(' author_id <= ? ', [5]) + ->where(' author_id <= :author_id_val ', ['author_id_val' => 5]) ); ``` @@ -513,7 +515,7 @@ $records = $authorsModel->fetchRecordsIntoArray(); $records = $authorsModel->fetchRecordsIntoArray( $authorsModel->getSelect() ->cols(['author_id', 'name']) - ->where(' author_id <= ? ', [5]) + ->where(' author_id <= :author_id_val ', ['author_id_val' => 5]) ); // $records is an array containing the all rows of data as record objects returned by @@ -525,7 +527,7 @@ $records = $authorsModel->fetchRecordsIntoArray( $records = $authorsModel->fetchRecordsIntoArray( $authorsModel->getSelect() ->cols(['author_id', 'name']) - ->where(' author_id <= ? ', [5]), + ->where(' author_id <= :author_id_val ', ['author_id_val' => 5]), ['posts'] // eager fetch posts for all the matching authors ); ``` @@ -557,7 +559,7 @@ $records = $authorsModel->fetchRecordsIntoCollection(); $records = $authorsModel->fetchRecordsIntoCollection( $authorsModel->getSelect() ->cols(['author_id', 'name']) - ->where(' author_id <= ? ', [5]) + ->where(' author_id <= :author_id_val ', ['author_id_val' => 5]) ); // $records is a collection object containing the all rows of data as record objects returned by @@ -569,7 +571,7 @@ $records = $authorsModel->fetchRecordsIntoCollection( $records = $authorsModel->fetchRecordsIntoCollection( $authorsModel->getSelect() ->cols(['author_id', 'name']) - ->where(' author_id <= ? ', [5]), + ->where(' author_id <= :author_id_val ', ['author_id_val' => 5]), ['posts'] // eager fetch posts for all the matching authors ); ``` @@ -603,7 +605,7 @@ $records = $authorsModel->fetchRowsIntoArray(); $records = $authorsModel->fetchRowsIntoArray( $authorsModel->getSelect() ->cols(['author_id', 'name']) - ->where(' author_id <= ? ', [5]) + ->where(' author_id <= :author_id_val ', ['author_id_val' => 5]) ); // $records is an array containing the all rows of data as record objects returned by @@ -615,7 +617,7 @@ $records = $authorsModel->fetchRowsIntoArray( $records = $authorsModel->fetchRowsIntoArray( $authorsModel->getSelect() ->cols(['author_id', 'name']) - ->where(' author_id <= ? ', [5]), + ->where(' author_id <= :author_id_val ', ['author_id_val' => 5]), ['posts'] // eager fetch posts for all the matching authors ); ``` @@ -660,7 +662,7 @@ $value = $authorsModel->fetchValue( $value = $authorsModel->fetchValue( $authorsModel->getSelect() ->cols(['max(author_id)']) - ->where(' author_id <= ? ', [5]) + ->where(' author_id <= :author_id_val ', ['author_id_val' => 5]) ); // NOTE: if the database table is empty or the select query returns no row(s) of @@ -719,7 +721,7 @@ $authorsModel = new AuthorsModel('mysql:host=hostname;dbname=blog', 'user', 'pwd /////////////////////////////////////////////////////////////////// $joeBlowRecord = $authorsModel->fetchOneRecord( $authorsModel->getSelect() - ->where(' name = ? ', ['Joe Blow']) + ->where(' name = :name_val ', [ 'name_val' => 'Joe Blow']) ); // - Deletes record from the database table // - Flags the record object as new @@ -733,7 +735,7 @@ $joeBlowRecord->delete(false); /////////////////////////////////////////////////////////////////// $jillBlowRecord = $authorsModel->fetchOneRecord( $authorsModel->getSelect() - ->where(' name = ? ', ['Jill Blow']) + ->where(' name = :name_val ', [ 'name_val' => 'Jill Blow']) ); // - Deletes record from the database table // - Flags the record object as new @@ -741,15 +743,6 @@ $jillBlowRecord = $authorsModel->fetchOneRecord( $jillBlowRecord->delete(true); /////////////////////////////////////////////////////////////////// -$jackAndJaneDoe = $authorsModel->fetchRecordsIntoCollection( - $authorsModel->getSelect() - ->where( - ' name IN (?, ?) ', - [ 'Jack Doe', 'Jane Doe' ] - ) - ); -// OR - $jackAndJaneDoe = $authorsModel->fetchRecordsIntoCollection( $authorsModel->getSelect() ->where( @@ -828,7 +821,7 @@ $authorsModel = new AuthorsModel('mysql:host=hostname;dbname=blog', 'user', 'pwd /////////////////////////////////////////////////////////////////// $joeBlowRecord = $authorsModel->fetchOneRecord( $authorsModel->getSelect() - ->where(' name = ? ', ['Joe Blow']) + ->where(' name = :name_val ', ['name_val' => 'Joe Blow']) ); // Prepend a title to Joe Blow's name @@ -839,8 +832,8 @@ $joeBlowRecord->save(); // update the record $jackAndJaneDoe = $authorsModel->fetchRecordsIntoCollection( $authorsModel->getSelect() ->where( - ' name IN (?, ?) ', - [ 'Jack Doe', 'Jane Doe' ] + ' name IN (:bar) ', + [ 'bar' => ['Jack Doe', 'Jane Doe'] ] ) ); @@ -856,7 +849,7 @@ $jackAndJaneDoe->saveAll(); /////////////////////////////////////////////////////////////////// $jillBlowRecord = $authorsModel->fetchOneRecord( $authorsModel->getSelect() - ->where(' name = ? ', ['Jill Blow']) + ->where(' name = :name_val ', ['name_val' => 'Jill Blow']) ); // reverse the name for this record diff --git a/src/LeanOrm/Model.php b/src/LeanOrm/Model.php index 3582987..b6aeda8 100644 --- a/src/LeanOrm/Model.php +++ b/src/LeanOrm/Model.php @@ -754,8 +754,8 @@ protected function loadHasManyThrough( if ( $parent_data instanceof \GDAO\Model\RecordInterface ) { $query_obj->where( - " {$join_table_name}.{$col_in_join_table_linked_to_my_models_table} = ? ", - [$parent_data->$fkey_col_in_my_table] + " {$join_table_name}.{$col_in_join_table_linked_to_my_models_table} = :leanorm_col_in_join_table_linked_to_my_models_table_val ", + ['leanorm_col_in_join_table_linked_to_my_models_table_val' => $parent_data->$fkey_col_in_my_table] ); } else { @@ -1151,8 +1151,8 @@ protected function getBelongsToOrHasOneOrHasManyData( if ( $parent_data instanceof \GDAO\Model\RecordInterface ) { $query_obj->where( - " {$foreign_table_name}.{$fkey_col_in_foreign_table} = ? ", - [$parent_data->$fkey_col_in_my_table] + " {$foreign_table_name}.{$fkey_col_in_foreign_table} = :leanorm_fkey_col_in_foreign_table_val ", + ['leanorm_fkey_col_in_foreign_table_val' => $parent_data->$fkey_col_in_my_table] ); } else { @@ -1869,7 +1869,11 @@ public function fetchOneRecord(?object $query=null, array $relations_to_include= public function fetchOneByPkey(string|int $id, array $relations_to_include = []): ?\GDAO\Model\RecordInterface { $select = $this->getSelect(); - $select->where(" {$this->getPrimaryCol()} = ? ", [$id]); + $query_placeholder = "leanorm_{$this->getTableName()}_{$this->getPrimaryCol()}_val"; + $select->where( + " {$this->getPrimaryCol()} = :{$query_placeholder} ", + [ $query_placeholder => $id] + ); return $this->fetchOneRecord($select, $relations_to_include); }