From f128373fa309eef562b6f175f8846029e1a9fac9 Mon Sep 17 00:00:00 2001 From: jhaller <jan.haller@math.uzh.ch> Date: Fri, 7 Jul 2023 10:40:19 +0200 Subject: [PATCH] F8975: Added documentation for report notation 2.0. refs #8975 --- Documentation/Concept.rst | 13 ++-- Documentation/Report.rst | 69 ++++++++++++++++++- Documentation/Variable.rst | 4 +- extension/Classes/Core/BodytextParser.php | 9 ++- extension/Classes/Core/Constants.php | 1 - .../Tests/Unit/Core/BodytextParserTest.php | 6 +- 6 files changed, 84 insertions(+), 18 deletions(-) diff --git a/Documentation/Concept.rst b/Documentation/Concept.rst index 7d3bf589c..aabbc5431 100644 --- a/Documentation/Concept.rst +++ b/Documentation/Concept.rst @@ -165,21 +165,26 @@ QFQ Keywords (Bodytext) | | | See :ref:`syntax-of-report` | +-------------------------+---------------------------------------------------------------------------------+ | <level>.line.count | Current row index. Will be replaced before the query is fired in case of | -| | ``<level>`` is an outer/previous level or it will be replaced after a query is | -| | fired in case ``<level>`` is the current level. | +| <alias>.line.count | ``<level>``/``<alias>`` is an outer/previous level or it will be replaced after | +| | a query is fired in case ``<level>``/``<alias>`` is the current level. | +-------------------------+---------------------------------------------------------------------------------+ | <level>.line.total | Total rows (MySQL ``num_rows`` for *SELECT* and *SHOW*, MySQL ``affected_rows`` | -| | for *UPDATE* and *INSERT*. | +| <alias>.line.total | for *UPDATE* and *INSERT*. | +-------------------------+---------------------------------------------------------------------------------+ | <level>.line.insertId | Last insert id for *INSERT*. | +| <alias>.line.insertId | | +-------------------------+---------------------------------------------------------------------------------+ -| <level>.line.content | Show content of `<level>` (content have to be stored via <level>.content=....) | +| <level>.line.content | Show content of `<level>`/`<alias>` (content have to be stored via | +| <alias>.line.content | <level>.content=... or <alias>.content=...). | +-------------------------+---------------------------------------------------------------------------------+ | <level>.line.altCount | Like 'line.count' but for 'alt' query. | +| <alias>.line.altCount | | +-------------------------+---------------------------------------------------------------------------------+ | <level>.line.altTotal | Like 'line.total' but for 'alt' query. | +| <alias>.line.altTotal | | +-------------------------+---------------------------------------------------------------------------------+ | <level>.line.altInsertId| Like 'line.insertId' but for 'alt' query. | +| <alias>.line.altInsertId| | +-------------------------+---------------------------------------------------------------------------------+ .. _`report-render`: diff --git a/Documentation/Report.rst b/Documentation/Report.rst index 224572221..ed2ca256a 100644 --- a/Documentation/Report.rst +++ b/Documentation/Report.rst @@ -398,8 +398,8 @@ To get the same result, the following is also possible:: '|p:/export', '|t:Download') AS _pdf -Nesting of levels -^^^^^^^^^^^^^^^^^ +Nesting of levels (version 1) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Levels can be nested. E.g.:: @@ -417,6 +417,56 @@ This is equal to:: 10.5.sql = SELECT ... 10.5.head = ... +Nesting of levels (version 2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Levels can be nested without levels. E.g.:: + + { + sql = SELECT ... + { + sql = SELECT ... + head = ... + } + } + +This is equal to:: + + 1.sql = SELECT ... + 1.2.sql = SELECT ... + 1.2.head = ... + +Levels are automatically numbered from top to bottom. + +An alias can be used instead of levels. E.g.:: + + myAlias { + sql = SELECT ... + myAlias2 { + sql = SELECT ... + head = ... + } + } + +This is also equal to:: + + 1.sql = SELECT ... + 1.2.sql = SELECT ... + 1.2.head = ... + +.. important:: + +Allowed characters for an alias: [a-zA-Z0-9]. + +.. important:: + +The first level determines whether report notation version 1 or 2 is used. Using an alias or no level triggers report notation version 2. +It requires the use of delimiters throughout the report. A combination with the notation '10.sql = ...' is not possible. + +.. important:: + +Report notation version 2 does not require that each level be assigned an alias. If an alias is used, it must be on the same line as the opening delimiter. + Leading / trailing spaces ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -528,7 +578,20 @@ Example 'level':: 10.5.20.sql = SELECT '{{10.pId}}' 10.10.sql = SELECT '{{10.pId}}' +Example 'alias':: + myAlias { + sql = SELECT p.id AS _pId, p.name FROM Person AS p + myAlias2 { + sql = SELECT adr.city, 'dummy' AS _pId FROM Address AS adr WHERE adr.pId={{10.pId}} + myAlias3 { + sql = SELECT '{{myAlias.pId}}' + } + } + myAlias4 { + sql = SELECT '{{myAlias.pId}}' + } + } Notes to the level: +-------------+------------------------------------------------------------------------------------------------------------------------+ @@ -542,6 +605,8 @@ Notes to the level: +-------------+------------------------------------------------------------------------------------------------------------------------+ | Child |The level *30* has one child and child child: *30.5* and *30.5.1* | +-------------+------------------------------------------------------------------------------------------------------------------------+ +| Alias |A variable that can be assigned to a level and used to retrieve its values. | ++-------------+------------------------------------------------------------------------------------------------------------------------+ | Example | *10*, *20*, *30*, *50** are root level and will be completely processed one after each other. | | | *30.5* will be executed as many times as *30* has row numbers. | | | *30.5.1* will be executed as many times as *30.5* has row numbers. | diff --git a/Documentation/Variable.rst b/Documentation/Variable.rst index 0f58273e6..7e3e10c01 100644 --- a/Documentation/Variable.rst +++ b/Documentation/Variable.rst @@ -404,11 +404,11 @@ Example:: Row column variables -------------------- -Syntax: *{{<level>.<column>}}* +Syntax: *{{<level>.<column>}}* or *{{<alias>.<column>}}* Only used in report to access outer columns. See :ref:`access-column-values` and :ref:`syntax-of-report`. -There might be name conflicts between VarName / SQL keywords and <line identifier>. QFQ checks first for *<level>*, +There might be name conflicts between VarName / SQL keywords and <line identifier>. QFQ checks first for *<level>* and *<alias>*, than for *SQL keywords* and than for *VarNames* in stores. All types might be nested with each other. There is no limit of nesting variables. diff --git a/extension/Classes/Core/BodytextParser.php b/extension/Classes/Core/BodytextParser.php index 84e15d138..73dc4daa4 100644 --- a/extension/Classes/Core/BodytextParser.php +++ b/extension/Classes/Core/BodytextParser.php @@ -218,7 +218,7 @@ class BodytextParser { // This later determines the notation mode // Possible values for $firstToken: '10', '10.20', '10.sql=...', '10.head=...', 'myAlias {', 'myAlias{' // Values such as 'form={{form:SE}}' are disregarded - if (empty($firstToken) && 1 !== preg_match('/^(' . TOKEN_VALID_LIST . ')\s*=/', $row)) { + if (empty($firstToken) && 1 !== preg_match('/^(' . TOKEN_VALID_LIST . ')\s*=/', $row) && $row !== $nestingOpen && $row !== $nestingClose) { $firstToken = (strpos($row, $nestingOpen) !== false) ? trim(substr($row, 0, strpos($row, $nestingOpen))) : $row; } @@ -399,10 +399,9 @@ class BodytextParser { // $adjustLength is used later while extracting a substring and has to be zero in the first loop $adjustLength = ($firstToken === $level && strpos($pre, PHP_EOL) === false) ? 0 : 1; - // If the $level, from which the $alias is extracted, contains whitespace, $alias is empty - // E.g. no alias or number is used: $level = "1.sql = SELECT ..." - // E.g. alias is used: $level = "myAlias" - $alias = (strpos($level, ' ')) ? '' : $level; + // If the $level, from which the $alias is extracted, nothing gets saved + // Allowed characters: [a-zA-Z0-9] + $alias = (1 === preg_match('/^[a-zA-Z0-9]+$/', $level) || $level === '') ? $level : null; // If no alias is set, then nothing gets saved if (!empty($alias)) { diff --git a/extension/Classes/Core/Constants.php b/extension/Classes/Core/Constants.php index ee0222b7f..496c84bab 100644 --- a/extension/Classes/Core/Constants.php +++ b/extension/Classes/Core/Constants.php @@ -257,7 +257,6 @@ const ERROR_INVALID_SAVE_ZIP_FILENAME = 1413; const ERROR_NUMERIC_ALIAS = 1414; const ERROR_INVALID_LEVEL = 1415; - // Upload const ERROR_UPLOAD = 1500; const ERROR_UPLOAD_TOO_BIG = 1501; diff --git a/extension/Tests/Unit/Core/BodytextParserTest.php b/extension/Tests/Unit/Core/BodytextParserTest.php index f5c6779d1..4d5780b74 100644 --- a/extension/Tests/Unit/Core/BodytextParserTest.php +++ b/extension/Tests/Unit/Core/BodytextParserTest.php @@ -785,9 +785,8 @@ EOF; LIMIT 4 head = <div> $close - 1.tail = </div> EOF; - $expected = "1.sql = SELECT 'Hello World' FROM Person ORDER BY id LIMIT 4\n1.head = <div>\n1.tail = </div>"; + $expected = "1.sql = SELECT 'Hello World' FROM Person ORDER BY id LIMIT 4\n1.head = <div>"; $result = $btp->process($given); $this->assertEquals($expected, $result); @@ -804,9 +803,8 @@ EOF; LIMIT 4 head = <div> $close - 1.tail = </div> EOF; - $expected = "1.sql = SELECT 'Hello World' FROM Person ORDER BY id LIMIT 4\n1.head = <div>\n1.tail = </div>"; + $expected = "1.sql = SELECT 'Hello World' FROM Person ORDER BY id LIMIT 4\n1.head = <div>"; $result = $btp->process($given); $this->assertEquals($expected, $result); -- GitLab