Skip to content
This repository
Browse code

Removed outdated docs for SQLQuery and data model techniques

Some of the SQLQuery recipes are now handled better by DataList,
others should be contrasted with their respective DataList implementation,
pointing out the limitations
  • Loading branch information...
commit 402297ee1f518dff2d6af6080fd1ce9818b0185c 1 parent 19e087d
Ingo Schommer authored June 28, 2012
201  docs/en/reference/sqlquery.md
Source Rendered
@@ -2,11 +2,30 @@
2 2
 
3 3
 ## Introduction
4 4
 
5  
-An object representing a SQL query. It is easier to deal with object-wrappers than string-parsing a raw SQL-query. This object is used by the SilverStripe ORM internally.
  5
+An object representing a SQL query, which can be serialized into a SQL statement. 
  6
+It is easier to deal with object-wrappers than string-parsing a raw SQL-query. 
  7
+This object is used by the SilverStripe ORM internally.
  8
+
6 9
 Dealing with low-level SQL is not encouraged, since the ORM provides
7 10
 powerful abstraction APIs (see [datamodel](/topics/datamodel). 
  11
+Starting with SilverStripe 3, records in collections are lazy loaded,
  12
+and these collections have the ability to run efficient SQL
  13
+such as counts or returning a single column.
  14
+
  15
+For example, if you want to run a simple `COUNT` SQL statement,
  16
+the following three statements are functionally equivalent:
  17
+
  18
+	:::php
  19
+	// Through raw SQL
  20
+	$count = DB::query('SELECT COUNT(*) FROM "Member"')->value();
  21
+	// Through SQLQuery abstraction layer
  22
+	$query = new SQLQuery();
  23
+	$count = $query->setFrom('Member')->setSelect('COUNT(*)')->value();
  24
+	// Through the ORM
  25
+	$count = Member::get()->count();
8 26
 
9  
-You'll run the risk of breaking various assumptions the ORM and code based on it have:
  27
+If you do use raw SQL, you'll run the risk of breaking 
  28
+various assumptions the ORM and code based on it have:
10 29
 
11 30
 *  Custom getters/setters (object property can differ from database column)
12 31
 *  DataObject hooks like onBeforeWrite() and onBeforeDelete()
@@ -17,6 +36,11 @@ You'll run the risk of breaking various assumptions the ORM and code based on it
17 36
 We'll explain some ways to use *SELECT* with the full power of SQL, 
18 37
 but still maintain a connection to the ORM where possible.
19 38
 
  39
+<div class="warning" markdown="1">
  40
+	Please read our ["security" topic](/topics/security) to find out
  41
+	how to sanitize user input before using it in SQL queries.
  42
+</div>
  43
+
20 44
 ## Usage
21 45
 
22 46
 ### SELECT
@@ -25,11 +49,8 @@ but still maintain a connection to the ORM where possible.
25 49
 	$sqlQuery = new SQLQuery();
26 50
 	$sqlQuery->setFrom('Player');
27 51
 	$sqlQuery->selectField('FieldName', 'Name');
28  
-	$sqlQuery->selectField('YEAR("Birthday")', 'BirthYear');
29  
-	$sqlQuery->addLeftJoin(
30  
-		'Team',
31  
-	  '"Player"."TeamID" = "Team"."ID"'
32  
-	);
  52
+	$sqlQuery->selectField('YEAR("Birthday")', 'Birthyear');
  53
+	$sqlQuery->addLeftJoin('Team','"Player"."TeamID" = "Team"."ID"');
33 54
 	$sqlQuery->addWhere('YEAR("Birthday") = 1982');
34 55
 	// $sqlQuery->setOrderBy(...);
35 56
 	// $sqlQuery->setGroupBy(...);
@@ -37,164 +58,86 @@ but still maintain a connection to the ORM where possible.
37 58
 	// $sqlQuery->setLimit(...);
38 59
 	// $sqlQuery->setDistinct(true);
39 60
 	
40  
-	// get the raw SQL
  61
+	// Get the raw SQL (optional)
41 62
 	$rawSQL = $sqlQuery->sql();
42 63
 	
43  
-	// execute and return a Query-object
  64
+	// Execute and return a Query object
44 65
 	$result = $sqlQuery->execute();
45 66
 
  67
+	// Iterate over results
  68
+	foreach($result as $row) {
  69
+	  echo $row['BirthYear'];
  70
+	}
  71
+
  72
+The result is an array lightly wrapped in a database-specific subclass of `[api:Query]`. 
  73
+This class implements the *Iterator*-interface, and provides convenience-methods for accessing the data.
46 74
 
47 75
 ### DELETE
48 76
 
49 77
 	:::php
50  
-	// ...
51 78
 	$sqlQuery->setDelete(true);
52 79
 
53  
-
54 80
 ### INSERT/UPDATE
55 81
 
56  
-(currently not supported -see below for alternative solutions)
57  
-
58  
-## Working with results
59  
-
60  
-The result is an array lightly wrapped in a database-specific subclass of `[api:Query]`. This class implements the
61  
-*Iterator*-interface defined in PHP5, and provides convenience-methods for accessing the data.
62  
-
63  
-### Iterating
  82
+Currently not supported through the `SQLQuery` class, please use raw `DB::query()` calls instead.
64 83
 
65 84
 	:::php
66  
-	foreach($result as $row) {
67  
-	  echo $row['BirthYear'];
68  
-	}
  85
+	DB::query('UPDATE "Player" SET "Status"=\'Active\'');
69 86
 
  87
+### Value Checks
70 88
 
71  
-### Quick value checking
  89
+Raw SQL is handy for performance-optimized calls,
  90
+e.g. when you want a single column rather than a full-blown object representation.
72 91
 
73  
-Raw SQL is handy for performance-optimized calls. 
  92
+Example: Get the count from a relationship.
74 93
 
75 94
 	:::php
76  
-	class Team extends DataObject {
77  
-	  public function getPlayerCount() {
78  
-	    $sqlQuery = new SQLQuery();
79  
-	    $sqlQuery->setFrom('Player');
80  
-	    $sqlQuery->addSelect('COUNT("Player"."ID")');
81  
-	    $sqlQuery->addLeftJoin('Team', '"Team"."ID" = "Player"."TeamID"');
82  
-	    return $sqlQuery->execute()->value();
83  
-	}
  95
+	$sqlQuery = new SQLQuery();
  96
+  $sqlQuery->setFrom('Player');
  97
+  $sqlQuery->addSelect('COUNT("Player"."ID")');
  98
+  $sqlQuery->addWhere('"Team"."ID" = 99');
  99
+  $sqlQuery->addLeftJoin('Team', '"Team"."ID" = "Player"."TeamID"');
  100
+  $count = $sqlQuery->execute()->value();
84 101
 
85  
-Way faster than dealing with `[api:DataObject]`s, but watch out for premature optimisation:
  102
+Note that in the ORM, this call would be executed in an efficient manner as well:
86 103
 
87 104
 	:::php
88  
-	$players = $myTeam->Players();
89  
-	echo $players->Count();
90  
-
  105
+	$count = $myTeam->Players()->count();
91 106
 
92 107
 ### Mapping
93 108
 
94  
-Useful for creating dropdowns.
  109
+Creates a map based on the first two columns of the query result. 
  110
+This can be useful for creating dropdowns.
  111
+
  112
+Example: Show player names with their birth year, but set their birth dates as values.
95 113
 
96 114
 	:::php
97 115
 	$sqlQuery = new SQLQuery();
98 116
 	$sqlQuery->setFrom('Player');
99  
-	$sqlQuery->selectField('YEAR("Birthdate")', 'Birthdate');
  117
+	$sqlQuery->setSelect('Birthdate');
  118
+	$sqlQuery->selectField('CONCAT("Name", ' - ', YEAR("Birthdate")', 'NameWithBirthyear');
100 119
 	$map = $sqlQuery->execute()->map();
101 120
 	$field = new DropdownField('Birthdates', 'Birthdates', $map);
102 121
 
103  
-
104  
-### "Raw" SQL with DB::query()
105  
-
106  
-This is not recommended for most cases, but you can also use the SilverStripe database-layer to fire off a raw query:
107  
-
108  
-	:::php
109  
-	DB::query('UPDATE "Player" SET "Status"=\'Active\'');
110  
-
111  
-<<<<<<< Updated upstream
112  
-### Transforming a result to `[api:ArrayList]`
113  
-=======
114  
-One example for using a raw DB::query is when you are wanting to order twice in the database:
  122
+Note that going through SQLQuery is just necessary here 
  123
+because of the custom SQL value transformation (`YEAR()`). 
  124
+An alternative approach would be a custom getter in the object definition.
115 125
 
116 126
 	:::php
117  
-	$records = DB::query('SELECT *, CASE WHEN "ThumbnailID" = 0 THEN 2 ELSE 1 END AS "HasThumbnail" FROM "TempDoc" ORDER BY "HasThumbnail", "Name" ASC');
118  
-	$items = singleton('TempDoc')->buildDataObjectSet($records);
119  
-
120  
-This CASE SQL creates a second field "HasThumbnail" depending if "ThumbnailID" exists in the database which you can then
121  
-order by "HasThumbnail" to make sure the thumbnails are at the top of the list and then order by another field "Name"
122  
-separately for both the items that have a thumbnail and then for those that don't have thumbnails.
123  
-
124  
-### "Semi-raw" SQL with buildSQL()
125  
-
126  
-You can gain some ground on the datamodel-side when involving the selected class for querying. You don't necessarily
127  
-need to call *buildSQL* from a specific object-instance, a *singleton* will do just fine.
128  
-
129  
-	:::php
130  
-	$sqlQuery = singleton('Player')->buildSQL('YEAR("Birthdate") = 1982');
131  
-
132  
-
133  
-This form of building a query has the following advantages:
134  
-
135  
-*  Respects DataObject::$default_sort
136  
-*  Automatically `LEFT JOIN` on all base-tables (see [database-structure](database-structure))
137  
-*  Selection of *ID*, *ClassName*, *RecordClassName*, which are necessary to use *buildDataObjectSet* later on
138  
-*  Filtering records for correct *ClassName*
139  
-
140  
-### Transforming a result to `[api:DataObjectSet]`
141  
->>>>>>> Stashed changes
142  
-
143  
-This is a commonly used technique inside SilverStripe: Use raw SQL, but transfer the resulting rows back into
144  
-`[api:DataObject]`s.
145  
-
146  
-	:::php
147  
-	$sqlQuery = new SQLQuery();
148  
-	$sqlQuery->setSelect(array(
149  
-	  '"Firstname" AS "Name"',
150  
-	  'YEAR("Birthday") AS "BirthYear"',
151  
-	  // IMPORTANT: Needs to be set after other selects to avoid overlays
152  
-	  '"Player"."ClassName" AS "ClassName"',
153  
-	  '"Player"."ClassName" AS "RecordClassName"',
154  
-	  '"Player"."ID" AS "ID"'
155  
-	));
156  
-	$sqlQuery->setFrom('Player');
157  
-	$sqlQuery->addLeftJoin('Team', '"Player"."TeamID" = "Team"."ID"');
158  
-	$sqlQuery->addWhere("YEAR("Player"."Birthday") = 1982");
159  
-	
160  
-	$result = $sqlQuery->execute();
161  
-	var_dump($result->first()); // array
162  
-	
163  
-	// let Silverstripe work the magic
164  
-	$myList = singleton('Player')->buildDataObjectSet($result);
165  
-	var_dump($myDataObjectSet->First()); // DataObject
166  
-	
167  
-	// this is where it gets tricky
168  
-	$myFirstPlayer = $myDataObjectSet->First();
169  
-	var_dump($myFirstPlayer->Name); // 'John'
170  
-	var_dump($myFirstPlayer->Firstname); // undefined, as it was not part of the SELECT-clause;
171  
-	var_dump($myFirstPlayer->Surname); // undefined, as it was not part of the SELECT-clause
172  
-	
173  
-	// lets assume that class Player extends BasePlayer,
174  
-	// and BasePlayer has a database-column "Status"
175  
-	var_dump($myFirstPlayer->Status); // undefined, as we didn't LEFT JOIN the BasePlayer-table
176  
-
177  
-
178  
-**CAUTION:** Depending on the selected columns in your query, you might get into one of the following scenarios:
179  
-
180  
-*  Not all object-properties accessible: You need to take care of selecting the right stuff yourself
181  
-*  Overlayed object-properties: If you *LEFT JOIN* a table which also has a column 'Birthdate' and do a global select on
182  
-this table, you might not be able to access original object-properties.
183  
-*  You can't create `[api:DataObject]`s where no scalar record-data is available, e.g. when using *GROUP BY*
184  
-*  Naming conflicts with custom getters: A getter like Player->getName() will overlay the column-data selected in the
185  
-above example
186  
-
187  
-Be careful when saving back `[api:DataObject]`s created through *buildDataObjectSet*, you might get strange side-effects due to
188  
-the issues noted above.
189  
-## Using FormFields with custom SQL
190  
-
191  
-Some subclasses of `[api:FormField]` for ways to create sophisticated report-tables based on SQL.
  127
+	class Player extends DataObject {
  128
+		static $db = array(
  129
+			'Name' => 
  130
+			'Birthdate' => 'Date'
  131
+		);
  132
+		function getNameWithBirthyear() {
  133
+			return date('y', $this->Birthdate);
  134
+		}
  135
+	}
  136
+	$players = Player::get();
  137
+	$map = $players->map('Name', 'NameWithBirthyear');
192 138
 
193 139
 ## Related
194 140
 
195 141
 *  [datamodel](/topics/datamodel)
196 142
 *  `[api:DataObject]`
197  
-*  [database-structure](database-structure)
198  
-
199  
-## API Documentation
200  
-`[api:SQLQuery]`
  143
+*  [database-structure](database-structure)
56  docs/en/topics/datamodel.md
Source Rendered
@@ -14,7 +14,8 @@ logic is handled by SilverStripe, you don't need to worry about writing SQL most
14 14
 Most of the ORM customizations are possible through [PHP5 Object
15 15
 Overloading](http://www.onlamp.com/pub/a/php/2005/06/16/overloading.html) handled in the `[api:Object]`-class.
16 16
 
17  
-See [database-structure](/reference/database-structure) for in-depth information on the database-schema.
  17
+See [database-structure](/reference/database-structure) for in-depth information on the database-schema,
  18
+and the ["sql queries" topic](/reference/sqlquery) in case you need to drop down to the bare metal.
18 19
 
19 20
 ## Generating the Database Schema
20 21
 
@@ -523,8 +524,10 @@ accessors available on both ends.
523 524
 
524 525
 ### Adding relations
525 526
 
526  
-Inside SilverStripe it doesn't matter if you're editing a *has_many*- or a *many_many*-relationship. You need to get a
527  
-`[api:ComponentSet]`.
  527
+Adding new items to a relations works the same,
  528
+regardless if you're editing a *has_many*- or a *many_many*. 
  529
+They are encapsulated by `[api:HasManyList]` and `[api:ManyManyList]`,
  530
+both of which provide very similar APIs, e.g. an `add()` and `remove()` method.
528 531
 
529 532
 	:::php
530 533
 	class Team extends DataObject {
@@ -533,20 +536,8 @@ Inside SilverStripe it doesn't matter if you're editing a *has_many*- or a *many
533 536
 	    "Categories" => "Category",
534 537
 	  );
535 538
 		
536  
-	  /**
537  
-	
538  
-	   * @param DataObjectSet
539  
-	   */
540  
-	  public function addCategories($additionalCategories) {
541  
-	    $existingCategories = $this->Categories();
542  
-	    
543  
-	    // method 1: Add many by iteration
544  
-	    foreach($additionalCategories as $category) {
545  
-	      $existingCategories->add($category);
546  
-	    }
547  
-	
548  
-	    // method 2: Add many by ID-List
549  
-	    $existingCategories->addMany(array(1,2,45,745));
  539
+	  public function addCategories(SS_List $cats) {
  540
+	    foreach($cats as $cat) $this->Categories()->add($cat);
550 541
 	  }
551 542
 	}
552 543
 
@@ -554,8 +545,8 @@ Inside SilverStripe it doesn't matter if you're editing a *has_many*- or a *many
554 545
 ### Custom Relations
555 546
 
556 547
 You can use the flexible datamodel to get a filtered result-list without writing any SQL. For example, this snippet gets
557  
-you the "Players"-relation on a team, but only containing active players. (See `[api:DataObject::$has_many]` for more info on
558  
-the described relations).
  548
+you the "Players"-relation on a team, but only containing active players. 
  549
+See `[api:DataObject::$has_many]` for more info on the described relations.
559 550
 
560 551
 	:::php
561 552
 	class Team extends DataObject {
@@ -569,6 +560,9 @@ the described relations).
569 560
 	  }
570 561
 	}
571 562
 
  563
+Note: Adding new records to a filtered `RelationList` like in the example above
  564
+doesn't automatically set the filtered criteria on the added record.
  565
+
572 566
 ## Validation and Constraints
573 567
 
574 568
 Traditionally, validation in SilverStripe has been mostly handled on the controller
@@ -742,19 +736,13 @@ It checks if a member is logged in who belongs to a group containing the permiss
742 736
 	  }
743 737
 	}
744 738
 
745  
-
746  
-
747  
-
748 739
 ### Saving data with forms
749 740
 
750 741
 See [forms](/topics/forms).
751 742
 
752 743
 ### Saving data with custom SQL
753 744
 
754  
-See `[api:SQLQuery]` for custom *INSERT*, *UPDATE*, *DELETE* queries.
755  
-
756  
-
757  
-
  745
+See the ["sql queries" topic](/reference/sqlquery) for custom *INSERT*, *UPDATE*, *DELETE* queries.
758 746
 
759 747
 ## Extending DataObjects
760 748
 
@@ -763,18 +751,14 @@ code or subclassing.
763 751
 Please see `[api:DataExtension]` for a general description, and `[api:Hierarchy]` for our most
764 752
 popular examples.
765 753
 
766  
-
767  
-
768 754
 ## FAQ
769 755
 
770 756
 ### Whats the difference between DataObject::get() and a relation-getter?
771  
-You can work with both in pretty much the same way, but relationship-getters return a special type of collection: 
772  
-A `[api:ComponentSet]` with relation-specific functionality.
773 757
 
774  
-	:::php
775  
-	$myTeam = DataObject::get_by_id('Team',$myPlayer->TeamID); // returns DataObject
776  
-	$myTeam->add(new Player()); // fails
777  
-	
778  
-	$myTeam = $myPlayer->Team(); // returns Componentset
779  
-	$myTeam->add(new Player()); // works
  758
+You can work with both in pretty much the same way, 
  759
+but relationship-getters return a special type of collection: 
  760
+A `[api:HasManyList]` or a `[api:ManyManyList]` with relation-specific functionality.
780 761
 
  762
+	:::php
  763
+	$myTeams = $myPlayer->Team(); // returns HasManyList
  764
+	$myTeam->add($myOtherPlayer);

0 notes on commit 402297e

Please sign in to comment.
Something went wrong with that request. Please try again.