|The command that should be executed on the server.|none |
+-------------+--------------------------------------------------+-----------------+
**Minimal Examples**
::
10.sql = SELECT "ls -s" AS _exec
20.sql = SELECT "./batchfile.sh" AS _exec
..
Column: _F
^^^^^^^^^^
Challenge 1
'''''''''''
Due to the limitations of MySQL, reserved column names can't be further concatenated. Assume you want to display an image:
::
# This is valid:
10.sql = SELECT concat("/static/directory/", p.foto) AS _img FROM person AS p WHERE ...
# Returns:
..
Now assume you want to wrap the image in a div tag:
::
# This is valid:
10.sql = SELECT "", CONCAT("/static/directory/", p.foto) AS _img, "
" FROM person AS p WHERE ...
# Returns:
..
The example above works fine - however, as soon as you want to use *field wrappers*, things get messy:
::
# This is valid:
10.sql = SELECT "", CONCAT("/static/directory/", p.foto) AS _img, "
" FROM person AS p WHERE ...
10.fbeg =
10.fend = |
# Returns:
|  | |
..
To achieve the desired result, one might want to try something like this:
::
# This is NOT valid:
10.sql = SELECT CONCAT("", concat("/static/directory/", p.foto) AS _img, "
") FROM person AS p WHERE ...
10.fbeg =
10.fend = |
# Returns a MySQL error because nesting concat() -functions is not allowed
..
Challenge 2
'''''''''''
Assume you have multiple columns with reserved names in the same query and want to use one of them in a later query:
::
10.sql = SELECT CONCAT("/static/directory/", g.picture) AS _img, CONCAT("/static/preview/", g.thumbnail) AS _img FROM gallery AS g WHERE ...
20.sql = SELECT "{{10.img}}", d.text FROM description AS d ...
..
The example above will fail because there are two img columns which can not be distinguished.
Solution
''''''''
The reserved column 'F'(=Format) can be used to
* further wrap columns with a reserved name
* assign an arbitrary name to a column built through a reserved name to make it accessible in later queries.
Solution for *#Challenge_1*:
::
10.sql = SELECT CONCAT("Q:img|T:div") AS wrappedImg FROM person AS p WHERE ...
10.fbeg =
10.fend = |
# Returns:
|
..
Solution for *#Challenge_2*:
::
10.sql = SELECT CONCAT("Q:img|V:mypic") AS wrappedImg FROM person AS p WHERE ...
20.sql = SELECT "{{10.mypic}}" ...
..
+-------------+--------------------------------------------------------------------+--------+
|**Parameter**|**Description** |Required|
+=============+====================================================================+========+
|Q |Any of the *reserved column names* | |
+-------------+--------------------------------------------------------------------+--------+
|Z |Process the column but don't display it | |
+-------------+--------------------------------------------------------------------+--------+
|X |Strip tags / Remove all tags | |
+-------------+--------------------------------------------------------------------+--------+
|T |Wrap the column with the defined tag. F.e.: T:tdcolspan="2" | |
+-------------+--------------------------------------------------------------------+--------+
|V |Define an unambiguous variable name for this colum. F.e.: V:someName| |
+-------------+--------------------------------------------------------------------+--------+
|* |Add all the parameters required for the column defined with Q: | |
+-------------+--------------------------------------------------------------------+--------+
Include a form via link/url
^^^^^^^^^^^^^^^^^^^^^^^^^^^
In most occasions it is handy if a formname does not have to be hard-coded in report but can be passed to the page as a
parameter. To achieve this, first build a link on page A which includes the required parameters:
**page A**
::
10.sql = SELECT CONCAT("p:pageB|U:form=Person&r=", id) AS _pagee FROM ...
..
The above example builds a link to pageB - refer to the :ref:`column-link`-manual for details. The link tells page B to
render the form with name formname and load the record with id id for editing.
QFQ CSS Classes
---------------
* `qfq-table-50`, `qfq-table-80` - release the default width of 100% and specify minwidth=50% resp. 80%.
* Background Color: `qfq-color-grey-1`, `qfq-color-grey-2` (table, row, cell)
* Table: `table`
* Table > hover: `table-hover`
* Table > condensed: `table-condensed`
E.g.::
10.sql = SELECT id, name, firstName, ...
10.head =
Examples
--------
The following section gives some examples of typical reports
Basic Queries
^^^^^^^^^^^^^
* One simple query
::
10.sql = SELECT "Hello World"
..
Result:
::
Hello World
..
Two simple queries
::
10.sql = SELECT "Hello World"
20.sql = SELECT "Say hello"
..
Result:
::
Hello WorldSay hello
..
Two simple queries, with break
::
10.sql = SELECT "Hello World
"
20.sql = SELECT "Say hello"
..
Result:
::
Hello World
Say hello
..
Accessing the database
^^^^^^^^^^^^^^^^^^^^^^
Real data, one single column
::
10.sql = SELECT p.first_name FROM exp_person AS p
..
Result:
::
BillieElvisLouisDiana
..
Real data, two columns
::
10.sql = SELECT p.first_name, p.last_name FROM exp_person AS p
..
Result:
::
BillieHolidayElvisPresleyLouisArmstrongDianaRoss
..
The result of the SQL query is output row by row and column by column without adding any formatting information. See `Formatting Examples`_ for examples of how the output can be formatted.
Formatting Examples
^^^^^^^^^^^^^^^^^^^
Formating (i.e. wrapping of data with HTML tags etc.) can be achieved in two different ways:
One can add formatting output directly into the SQL by either putting it in a separate column of the output or by using concat to concatenate data and formatting output in a single column.
One can use ?level keys to define formatting information that will be put before/after/between all rows/columns of the actual levels result.
Two columns
::
# Add the formating information as a coloum
10.sql = SELECT p.first_name, " " , p.last_name, "'
" FROM exp_person AS p
..
Result:
::
Billie Holiday
Elvis Presley
Louis Armstrong
Diana Ross
..
One column 'rend'
::
10.sql = SELECT p.name FROM exp_person AS p
10.rend =
..
Result:
::
Billie Holiday
Elvis Presley
Louis Armstrong
Diana Ross
..
More HTML
::
10.sql = SELECT p.name FROM exp_person AS p
10.head =
10.rbeg =
10.rend =
..
Result:
::
o Billie Holiday
o Elvis Presley
o Louis Armstrong
o Diana Ross
The same as above, but with braces::
10 {
sql = SELECT p.name FROM exp_person AS p
head =
rbeg =
rend =
}
Two queries: ::
10.sql = SELECT p.name FROM exp_person AS p
10.rend =
20.sql = SELECT a.street FROM exp_address AS a
20.rend =
Two queries: nested ::
# outer query
10.sql = SELECT p.name FROM exp_person AS p
10.rend =
# inner query
10.10.sql = SELECT a.street FROM exp_address AS a
10.10.rend =
* For every record of '10', all records of 10.10 will be printed.
Two queries: nested with variables ::
# outer query
10.sql = SELECT p.id, p.name FROM exp_person AS p
10.rend =
# inner query
10.10.sql = SELECT a.street FROM exp_address AS a WHERE a.pid='{{10.id}}'
10.10.rend =
* For every record of '10', all assigned records of 10.10 will be printed.
Two queries: nested with hidden variables in a table ::
10.sql = SELECT p.id AS _p_id, p.name FROM exp_person AS p
10.rend =
# inner query
10.10.sql = SELECT a.street FROM exp_address AS a WHERE a.p_id='{{10.p_id}}'
10.10.rend =
Same as above, but written in the nested notation ::
10 {
sql = SELECT p.id AS _p_id, p.name FROM exp_person AS p
rend =
10 {
# inner query
sql = SELECT a.street FROM exp_address AS a WHERE a.p_id='{{10.p_id}}'
rend =
}
}
* Columns starting with a '_' won't be printed but can be accessed as regular columns.
Best practice: Form
===================
Debug Report
------------
Writing "report's" in the nested notation or long queries broken over several lines, might not interpreted as wished.
Best for debugging is to specify in the tt-content record::
debugShowBodyText = 1
Note: Debug information is only display if it's enabled in *config.ini* by
* *SHOW_DEBUG_INFO=yes* or
* *SHOW_DEBUG_INFO=auto* and logged in in the same Browser as a Typo3 backend user.
More detailed error messages
----------------------------
If *SHOW_DEBUG_INFO* is enabled, a full stacktrace and variable contents are displayed in case of an error.
Person search form
------------------
QFQ content record::
# Creates a small form that redirects back to this page
10 {
sql = SELECT '_'
head =
}
# SQL statement will find and list all the relevant forms
20 {
sql = SELECT CONCAT('?detail&form=form&r=', f.id) AS _Pagee, f.id, f.name, f.title
FROM Form AS f
WHERE f.name LIKE '%{{search:CE:all}}%'
head =
rbeg =
rend =
fbeg =
fend = |
}
Form: compute next free 'ord' automatically
-------------------------------------------
Requirement: new records should automatically get the highest number plus 10 for their 'ord' value. Existing records
should not be altered.
Version 1
^^^^^^^^^
Compute the next 'ord' in advance in the subrecord field of the primary form. Submit that value to the new record
via SIP parameter to the secondary form.
On the secondary form: for 'new' records choose the computed value, for existing records leave the value
unchanged.
* Primary form, `subrecord` *FormElement*, field `parameter`: set ::
detail=id:formId,{{SELECT '&', IFNULL(fe.ord,0)+10 FROM Form AS f LEFT JOIN *FormElement* AS fe ON fe.formId=f.id WHERE
f.id={{r:S0}} ORDER BY fe.ord DESC LIMIT 1}}:ord
* Secondary form, `ord` *FormElement*, field `value`: set
::
`{{RS0}}`.
Version 2
^^^^^^^^^
Compute the next 'ord' as default value direct inside the secondary form. No change is needed for the primary form.
* Secondary form, `ord` *FormElement*, field `value`: set `{{SELECT IF({{ord:R0}}=0, MAX(IFNULL(fe.ord,0))+10,{{ord:R0}}) FROM (SELECT 1) AS a LEFT JOIN FormElement AS fe ON fe.formId={{formId:S0}} GROUP BY fe.formId}}`.
Form: Person Wizard - firstname, city
-------------------------------------
Requirement: A form that displays the column 'firstname' from table 'Person' and 'city' from table 'Address'. If the
records not exist, the form should create it.
Form primary table: Person
Form slave table: Address
Relation: `Person.id = Address.personId`
* Form: wizard
* Name: wizard
* Title: Person Wizard
* Table: Person
* Render: bootstrap
* *FormElement*: firstname
* Class: **native**
* Type: **text**
* Name: firstname
* Label: Firstname
* *FormElement*: email, text, 20
* Class: **native**
* Type: **text**
* Name: city
* Label: City
* Value: `{{SELECT city FROM Address WHERE personId={{r}} ORDER BY id LIMIT 1}}`
* *FormElement*: insert/update address record
* Class: **action**
* Type: **afterSave**
* Label: Manage Address
* Parameter:
* `slaveId={{SELECT id FROM Address WHERE personId={{r}} ORDER BY id LIMIT 1}}`
* `sqlInsert={{INSERT INTO Address (personId, city) VALUES ({{r}}, '{{city:F:allbut:s}}') }}`
* `sqlUpdate={{UPDATE Address SET city='{{city:F:allbut:s}}' WHERE id={{slaveId:V}} }}`
* `sqlDelete={{DELETE FROM Address WHERE id={{slaveId:V}} AND ''='{{city:F:allbut:s}}' LIMIT 1}}`
Form: Person Wizard - firstname, single note
--------------------------------------------
Requirement: A form that displays the column 'firstname' from table 'Person' and 'note' from table 'Note'.
If the records don't exist, the form should create it.
Column Person.noteId points to Note.id
Form primary table: Person
Form slave table: Address
Relation: `Person.id = Address.personId`
* Form: wizard
* Name: wizard
* Title: Person Wizard
* Table: Person
* Render: bootstrap
* *FormElement*: firstname
* Class: **native**
* Type: **text**
* Name: firstname
* Label: Firstname
* *FormElement*: email, text, 20
* Class: **native**
* Type: **text**
* Name: note
* Label: Note
* Value: `{{SELECT Note FROM Note AS n, Person AS p WHERE p.id={{r}} AND p.noteId=n.id ORDER BY id }}`
* *FormElement*: insert/update address record
* Class: **action**
* Type: **afterSave**
* Name: noteId
* Label: Manage Note
* Parameter:
* `sqlInsert={{INSERT INTO Note (note) VALUES ('{{note:F:allbut:s}}') }}`
* `sqlUpdate={{UPDATE Note SET note='{{note:F:allbut:s}}' WHERE id={{slaveId:V}} }}`
Best Practise
=============
Central configured values
-------------------------
Any variable in *config.qfq.ini* can be used by *{{:Y}}* in form or report statements.
E.g.
TECHNICAL_CONTACT = jane.doe@example.net
Could be used in an *FormElement.type* = sendmail with *parameter* setting *sendMailFrom={{TECHNICAL_CONTACT:Y}}*.
Icons Template Group
--------------------
* FormElement.parameter
::
tgAddClass=btn alert-success
tgAddText=
tgRemoveClass=btn btn-danger alert-danger
tgRemoveText=
Chart
-----
* QFQ delivers a chart JavaScript lib: https://github.com/nnnick/Chart.js.git. Docs: http://www.chartjs.org/docs/
* The library is not sourced in the HTML page automatically. To do it, either include the lib
`typo3conf/ext/qfq/Resources/Public/JavaScript/Chart.min.js`:
* in the specific tt_content record (shown below in the example) or
* system wide via Typo3 Template record.
* By splitting HTML and JavaScript code over several lines, take care not accidently to create a 'nesting'-end token.
Check the line after `10.tail =`. It's '}' alone on one line. This is a valid 'nesting'-end token!. There are two options
to circumvent this:
* Don't nest the HTML & JavaScript code - bad workaround, this is not human readable.
* Select different nesting token, e.g. '<' / '>' (check the first line on the following example).
::
# <
10.sql = SELECT '_'
10.head =
Distribution of FormElement types over all forms
# Labels
10.10 <
sql = SELECT "'", fe.type, "'" FROM FormElement AS fe GROUP BY fe.type ORDER BY fe.type
head = labels: [
tail = ],
rsep = ,
>
# Data
10.20 <
sql = SELECT COUNT(fe.id) FROM FormElement AS fe GROUP BY fe.type ORDER BY fe.type
head = datasets: [ { data: [
tail = ], backgroundColor: "steelblue", label: "FormElements" } ]
rsep = ,
>
Upload Form Simple
------------------
Table Person
+---------------------+--------------+
| Name | Type |
+=====================+==============+
| id | int |
+---------------------+--------------+
| name | varchar(255) |
+---------------------+--------------+
| pathFileNamePicture | varchar(255) |
+---------------------+--------------+
| pathFileNameAvatar | varchar(255) |
+---------------------+--------------+
* Form:
* Name: UploadSimple
* Table: Person
* FormElements:
* Name: name
* Type: text
* Label: Name
* Name: pathFileNamePicture
* Type: upload
* Label: Picture
* Parameter::
fileDestination=fileadmin/user/{{id:R0}}-picture-{{filename}}
* Name: pathFileNameAvatar
* Type: upload
* Label: Avatar
* Parameter::
fileDestination=fileadmin/user/{{id:R0}}-avatar-{{filename}}
Upload Form Advanced 1
----------------------
Table: Person
+---------------------+--------------+
| Name | Type |
+=====================+==============+
| id | int |
+---------------------+--------------+
| name | varchar(255) |
+---------------------+--------------+
Table: Note
+---------------------+--------------+
| Name | Type |
+=====================+==============+
| id | int |
+---------------------+--------------+
| pId | int |
+---------------------+--------------+
| type | varchar(255) |
+---------------------+--------------+
| pathFileName | varchar(255) |
+---------------------+--------------+
* Form:
* Name: UploadAdvanced1
* Table: Person
* FormElements
* Name: name
* Type: text
* Label: Name
* Name: mypathFileNamePicture
* Type: upload
* Label: Picture
* Value: {{SELECT pathFileName FROM Note WHERE id={{slaveId}} }}
* Parameter::
fileDestination=fileadmin/user/{{id:R0}}-picture-{{filename}}
slaveId={{SELECT id FROM Note WHERE pId={{id:R0}} AND type='picture' LIMIT 1}}
sqlInsert={{INSERT INTO Note (pathFileName, type, pId) VALUE ('{{fileDestination}}', 'picture', {{id:R0}}) }}
sqlUpdate={{UPDATE Note SET pathFileName = '{{fileDestination}}' WHERE id={{slaveId}} LIMIT 1}}
sqlDelete={{DELETE FROM Note WHERE id={{slaveId}} LIMIT 1}}
* Name: mypathFileNameAvatar
* Type: upload
* Label: Avatar
* Value: {{SELECT pathFileName FROM Note WHERE id={{slaveId}} }}
* Parameter::
fileDestination=fileadmin/user/{{id:R0}}-avatar-{{filename}}
slaveId={{SELECT id FROM Note WHERE pId={{id:R0}} AND type='avatar' LIMIT 1}}
sqlInsert={{INSERT INTO Note (pathFileName, type, pId) VALUE ('{{fileDestination}}', 'avatar', {{id:R0}}) }}
sqlUpdate={{UPDATE Note SET pathFileName = '{{fileDestination}}' WHERE id={{slaveId}} LIMIT 1}}
sqlDelete={{DELETE FROM Note WHERE id={{slaveId}} LIMIT 1}}
Upload Form Advanced 2
----------------------
Table: Person
+---------------------+--------------+
| Name | Type |
+=====================+==============+
| id | int |
+---------------------+--------------+
| name | varchar(255) |
+---------------------+--------------+
| noteIdPicture | int |
+---------------------+--------------+
| noteIdAvatar | int |
+---------------------+--------------+
Table: Note
+---------------------+--------------+
| Name | Type |
+=====================+==============+
| id | int |
+---------------------+--------------+
| pathFileName | varchar(255) |
+---------------------+--------------+
* Form:
* Name: UploadAdvanced2
* Table: Person
* FormElements
* Name: name
* Type: text
* Label: Name
* Name: mypathFileNamePicture
* Type: upload
* Label: Picture
* Value: {{SELECT pathFileName FROM Note WHERE id={{slaveId}} }}
* Parameter::
fileDestination=fileadmin/user/{{id:R0}}-picture-{{filename}}
slaveId={{SELECT id FROM Note WHERE id={{noteIdPicture}} LIMIT 1}}
sqlInsert={{INSERT INTO Note (pathFileName) VALUE ('{{fileDestination}}') }}
sqlUpdate={{UPDATE Note SET pathFileName = '{{fileDestination}}' WHERE id={{slaveId}} LIMIT 1}}
sqlDelete={{DELETE FROM Note WHERE id={{slaveId}} LIMIT 1}}
sqlAfter={{UPDATE Person SET noteIdPicture={{slaveId}} WHERE id={{id:R0}} LIMIT 1
* Name: mypathFileNameAvatar
* Type: upload
* Label: Avatar
* Value: {{SELECT pathFileName FROM Note WHERE id={{slaveId}} }}
* Parameter::
fileDestination=fileadmin/user/{{id:R0}}-avatar-{{filename}}
slaveId={{SELECT id FROM Note WHERE id={{noteIdAvatar}} LIMIT 1}}
sqlInsert={{INSERT INTO Note (pathFileName) VALUE ('{{fileDestination}}') }}
sqlUpdate={{UPDATE Note SET pathFileName = '{{fileDestination}}' WHERE id={{slaveId}} LIMIT 1}}
sqlDelete={{DELETE FROM Note WHERE id={{slaveId}} LIMIT 1}}
sqlAfter={{UPDATE Person SET noteIdAvatar={{slaveId}} WHERE id={{id:R0}} LIMIT 1
FAQ
===
* Q: A variable {{}} is shown as empty string, but there should be a value.
* A: The sanatize rule is violeted and therefore the value has been removed. Set {{::all}} as a test.
Only STORE_CLIENT and STORE_FORM will be sanatized.