Skip to content

Commit 5b5859e

Browse files
committed
Adding doc
1 parent 89d7306 commit 5b5859e

File tree

4 files changed

+111
-4
lines changed

4 files changed

+111
-4
lines changed

README.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
What is QueryWritter?
2+
=====================
3+
4+
QueryWritter is a PHP library that parses SQL queries, transforms those into an object representation, stores them in a
5+
dependency injection container, and returns them as string. It is a [Mouf plugin](http://mouf-php.com).
6+
7+
Ok, but why would I use QueryWritter?
8+
-------------------------------------
9+
10+
Because it is **the most effecient way to deal with queries that can have a variable number of parameters**!
11+
Think about a typical datagrid with a bunch of filter (for instance a list of products filtered by name, company, price, ...).
12+
If you have the very common idea to generate the SQL query using no PHP library, your code will look like this:
13+
14+
<div class="alert"><strong>You should not do this!</strong></div>
15+
16+
```php
17+
// People usually write queries like this:
18+
$sql = "SELECT * FROM products p JOIN companies c ON p.company_id = c.id WHERE 1=1 ";
19+
// They keep testing for parameters, and concatenating strings....
20+
if (isset($params['name'])) {
21+
$sql .= "AND p.name LIKE '".addslashes($params['name'])."%'";
22+
}
23+
if (isset($params['company'])) {
24+
$sql .= "AND c.name LIKE '".addslashes($params['company'])."%'";
25+
}
26+
// And so on... for each parameter, we have a "if" statement
27+
```
28+
29+
30+
31+
Concatenating SQL queries is dangerous (especially if you forget to protect parameters).
32+
You can always use parameterized SQL queries, but you will still have to concatenate the filters.
33+
34+
To avoid concatenating strings, frameworks and libraries have used different strategies. Building a full ORM (like
35+
Doctrine or Propel) is a good idea, but it makes writing complex queries even more complex. Other frameworks like
36+
Zend are building queries using function calls. These are valid strategies, but you are no more typing SQL queries
37+
directly, and let's face it, it is always useful to use a query directly.
38+
39+
This is where QueryWritter becomes helpful.
40+
41+
How does it work?
42+
-----------------
43+
// TODO: schema... or even better... video!
44+
45+
###1- Write your query
46+
You start by writing your query, **in plain SQL**. No ORM, no special query language (DQL or HQL anyone?), just plain and simple SQL.
47+
This is cool because everybody knows SQL. In your query, you put absolutely all the parameters you can imagine.
48+
49+
###2- Store your query in Mouf
50+
In Mouf UI, go to **DB** > **SQL queries** > **Create SQL query**.
51+
Here, you can **copy and paste your query**. Since this is Mouf, every query is an "instance", and you have to pick
52+
a name for your query.
53+
54+
Behind the scenes, QueryWritter will parse your query and make sure every piece of the query (each table, each column, each filter...) is transformed
55+
into an object. But you really don't have to care about that right now.
56+
57+
###3- Test your query
58+
Right from Mouf UI, you can test your query! And lo and behold! Because the query was parsed, **QueryWritter will dynamically
59+
add parts of the query depending on the parameters you decide to use**.
60+
61+
###4- Use it in your code
62+
If you are not a Mouf user (if you are using Drupal, Symfony, Zend Framework...), you can directly use the query by fetching the instance from Mouf and calling the <code>toSql</code> method, passing
63+
parameters in... parameter :)
64+
65+
```
66+
$mySelect = Mouf::getMySelectStatement();
67+
$sql = $mySelect->toSql(array("status"=>1, "search"=>"hello"));
68+
```
69+
70+
If you are a Mouf user, you can even directly run the query using the **QueryResult** class that executes
71+
the query directly. Or even better, use the **Evolugrid** module, and display your query result in an HTML
72+
datagrid, directly!

src/Mouf/Database/QueryWriter/Controllers/SelectController.php

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,23 @@ class SelectController extends AbstractMoufInstanceController {
5353
*/
5454
protected $parameters;
5555

56+
protected $parseError = false;
57+
5658
/**
5759
* Admin page used to edit the SQL of a SELECT instance.
5860
*
5961
* @Action
6062
* //@Admin
6163
*/
62-
public function defaultAction($name, $selfedit="false") {
64+
public function defaultAction($name, $sql = null, $selfedit="false") {
6365
$this->initController($name, $selfedit);
6466

6567
$select = MoufManager::getMoufManagerHiddenInstance()->getInstance($name);
66-
$this->sql = $select->toSql(null, array(), 0, true);
68+
if ($sql != null) {
69+
$this->sql = $sql;
70+
} else {
71+
$this->sql = $select->toSql(null, array(), 0, true);
72+
}
6773

6874
$this->content->addFile(dirname(__FILE__)."/../../../../views/parseSql.php", $this);
6975
$this->template->toHtml();
@@ -82,6 +88,13 @@ public function parse($name, $sql,$selfedit="false") {
8288

8389
$parser = new SQLParser();
8490
$parsed = $parser->parse($sql);
91+
92+
if ($parsed == false) {
93+
$this->parseError = true;
94+
$this->defaultAction($name, $sql, $selfedit);
95+
return;
96+
}
97+
8598
//print_r($parsed);
8699
$select = StatementFactory::toObject($parsed);
87100
//var_dump(StatementFactory::toObject($parsed));
@@ -101,7 +114,10 @@ public function parse($name, $sql,$selfedit="false") {
101114
*
102115
* @Action
103116
*/
104-
public function createQuery($selfedit="false") {
117+
public function createQuery($name = null, $sql = null, $selfedit="false") {
118+
$this->instanceName = $name;
119+
$this->sql = $sql;
120+
105121
$this->content->addFile(dirname(__FILE__)."/../../../../views/createQuery.php", $this);
106122
$this->template->toHtml();
107123
}
@@ -117,6 +133,13 @@ public function createQuery($selfedit="false") {
117133
public function doCreateQuery($name, $sql,$selfedit="false") {
118134
$parser = new SQLParser();
119135
$parsed = $parser->parse($sql);
136+
137+
if ($parsed == false) {
138+
$this->parseError = true;
139+
$this->createQuery($name, $sql, $selfedit);
140+
return;
141+
}
142+
120143
$select = StatementFactory::toObject($parsed);
121144

122145
$moufManager = MoufManager::getMoufManagerHiddenInstance();

src/views/createQuery.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
?>
44
<h1>Create a new SQL query</h1>
55

6+
<?php if ($this->parseError) { ?>
7+
<div class="alert">Unable to parse SQL query</div>
8+
<?php } ?>
9+
610
<form action="doCreateQuery" method="post" class="form-horizontal">
711
<div class="control-group">
812
<label class="control-label">Instance name*: </label>

src/views/parseSql.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@
33
?>
44
<h1>Parse SQL</h1>
55

6+
<?php if ($this->parseError) { ?>
7+
<div class="alert">Unable to parse SQL query</div>
8+
<?php } ?>
9+
610
<form action="parse">
711
<input type="hidden" name="name" value="<?php echo plainstring_to_htmlprotected($this->instanceName) ?>" />
812
<label class="control-label">SQL query:</label>
9-
<textarea rows=10 name="sql" class="span10"><?php echo plainstring_to_htmlprotected($this->sql) ?></textarea>
13+
<div class="controls">
14+
<textarea rows=10 name="sql" class="span10"><?php echo plainstring_to_htmlprotected($this->sql) ?></textarea>
15+
<span class="help-block">You can use <strong>parameters</strong> using prepared statement notation. For instance:
16+
<code>select * from users where country_id = :country_id</code></span>
17+
</div>
1018
<div class="control-group">
1119
<div class="controls">
1220
<button name="action" value="parse" type="submit" class="btn btn-danger">Create object from query</button>

0 commit comments

Comments
 (0)