The title says it all; I need a Typoscript SQL SELECT query with a dynamic WHERE clause.
I get the UID of the current logged in user with:
data = TSFE:fe_user|user|uid
But how can I use that in a database query?
I store the UID in a typoscript variable named {userID}.
That's my code so far. The Problem is Typoscript just merge the two values:
// User One has value: 50
// User Two has value: 32
// With this code the output is : 5032 ????
lib.coins = CONTENT
lib.coins {
table = fe_users
select {
pidInList = 18
where = uid
andWhere.insertData = TSFE:fe_user|user|uid
selectFields = coins
}
renderObj = COA
renderObj {
1 = TEXT
1.field = coins
}
}
You should make use of the select option markers:
lib.coins = CONTENT
lib.coins {
table = fe_users
select {
selectFields = coins
pidInList = 18
where = uid = ###UID###
markers {
UID.data = TSFE:fe_user|user|uid
}
}
renderObj = COA
renderObj {
1 = TEXT
1.field = coins
}
}
Related
Here is the part I am havng problems... I am able to use this code in my search button to search for Banda = 0 or Musica = 1.. but not both.. any idea?
function myFunction() {
var input, filter, table, tr, td, i, txtValue;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
table = document.getElementById("myTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[0];
if (td) {
txtValue = td.textContent || td.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
Lets say I have a table with a list of names, such as "a, b, c" and each name has several other values assigned (some of the other values can be assigned to several/all of names values, example below).
Table example:
( names - other ):
a - aa
a - ab
a - ac
b - ab
b - bb
b - cb
c - ac
c - bc
c - cc
How do I make in birt so that I could select names in one parameter box and get corresponding other values to select for another parameter? I know it's possible to do that with cascading parameter, but that doesn't allow to have multiselect for the first parameter, which in our example would be names values.
Found a solution partly here: https://forums.opentext.com/forums/developer/discussion/61283/allow-multi-value-for-cascading-parameter
and here (thanks google translate ^^): https://www.developpez.net/forums/d1402270/logiciels/solutions-d-entreprise/business-intelligence/birt/birt-4-3-1-multi-value-cascade-parameters/
Steps to do:
change"\birt\webcontent\birt\ajax\ui\dialog\BirtParameterDialog.js" file contents (there is a file to download for replacement in one of the links, but in case it goes dead I'm sharing 2 snippets from it where the changes occur:
__refresh_cascade_select : function( element )
{
var matrix = new Array( );
var m = 0;
for( var i = 0; i < this.__cascadingParameter.length; i++ )
{
for( var j = 0; j < this.__cascadingParameter[i].length; j++ )
{
var paramName = this.__cascadingParameter[i][j].name;
if( paramName == this.__isnull )
paramName = this.__cascadingParameter[i][j].value;
if( paramName == element.id.substr( 0, element.id.length - 10 ) )
{
//CHANGE//
//The two way work (String(selectedValue) or Array(SelectedValueArray))
var l = 0;
var isFirstElement = true;
var selectedValue = "";
var selectedValueArray = new Array();
for(var test = 0; test<element.options.length;test++){
if(element.options[test].selected){
if(isFirstElement){
isFirstElement = false;
selectedValue = element.options[test].value;
}
else{
selectedValue = selectedValue+","+element.options[test].value;
}
selectedValueArray[l] = element.options[test].value;
l++;
}
}
//CHANGE//
var tempText = element.options[element.selectedIndex].text;
var tempValue = element.options[element.selectedIndex].value;
// Null Value Parameter
if ( tempValue == Constants.nullValue )
{
this.__cascadingParameter[i][j].name = this.__isnull;
this.__cascadingParameter[i][j].value = paramName;
}
else if( tempValue == '' )
{
if( tempText == "" )
{
var target = element;
target = target.parentNode;
var oInputs = target.getElementsByTagName( "input" );
if( oInputs.length >0 && oInputs[1].value != Constants.TYPE_STRING )
{
// Only String parameter allows blank value
alert( birtUtility.formatMessage( Constants.error.parameterNotAllowBlank, paramName ) );
this.__clearSubCascadingParameter( this.__cascadingParameter[i], j );
return;
}
else
{
// Blank Value
this.__cascadingParameter[i][j].name = paramName;
this.__cascadingParameter[i][j].value = tempValue;
}
}
else
{
// Blank Value
this.__cascadingParameter[i][j].name = paramName;
this.__cascadingParameter[i][j].value = tempValue;
}
}
else
{
this.__cascadingParameter[i][j].name = paramName;
//CHANGE//
//The two way work (String(selectedValue) or Array(SelectedValueArray))
this.__cascadingParameter[i][j].value = selectedValueArray;
//CHANGE//
}
for( var m = 0; m <= j; m++ )
{
if( !matrix[m] )
{
matrix[m] = {};
}
matrix[m].name = this.__cascadingParameter[i][m].name;
matrix[m].value = this.__cascadingParameter[i][m].value;
}
this.__pendingCascadingCalls++;
birtEventDispatcher.broadcastEvent( birtEvent.__E_CASCADING_PARAMETER, matrix );
}
}
}
},
and this:
// exist select control and input text/password
// compare the parent div offsetTop
if( oFirstITC.parentNode && oFirstST.parentNode )
{
// Bugzilla 265615: need to use cumulative offset for special cases
// where one element is inside a group container
var offsetITC = Position.cumulativeOffset( oFirstITC );
var offsetST = Position.cumulativeOffset( oFirstST );
// compare y-offset first, then x-offset to determine the visual order
if( ( offsetITC[1] > offsetST[1] ) || ( offsetITC[1] == offsetST[1] && offsetITC[0] > offsetST[0] ) )
{
oFirstST.focus( );
}
else
{
oFirstITC.focus( );
}
}
After .js is changed cascading parameters can have multiselect on all levels.
Example:
1st DataSet "DS_country" query:
select CLASSICMODELS.OFFICES.COUNTRY
from CLASSICMODELS.OFFICES
2nd DataSet "DS_office" query:
select CLASSICMODELS.OFFICES.OFFICECODE
from CLASSICMODELS.OFFICES
where CLASSICMODELS.OFFICES.COUNTRY IN ('DS_country')
After datasets are created we can make cascading report parameters CRP_country and CRP_office (keep in mind that UI doesn't let you to choose "Allow multiple values" on the upper levels, but we can change that in property editor after the parameters are made by going to advanced tab and changing "Scalar parameter type" property value to "Multi Value")
The only thing left is to pick lower level cascading parameters (CRP_office in our example) and go to script tab and add this on "beforeOpen":
// Do that if your selections are in String type.
var stringArray = params["CRP_country"].value.toString().split(",");
var result = "";
for(var i =0 ; i < stringArray.length ; i++){
if(i==0){
result = "'"+stringArray[i]+"'";
}
else{
result = result+",'"+stringArray[i]+"'";
}
}
// For String
this.queryText = this.queryText.replace("'DS_country'", result);
//For integer (the first part is useless)
//this.queryText = this.queryText.replace("'DS_country'",
params["CRP_country"].value);
I want create this query with yii2 search model
select * from t1 where (title = 'keyword' or content = 'keyword') AND
(category_id = 10 or term_id = 10 )
But I don't know how to use orFilterWhere and andFilterWhere.
My code in search model:
public function search($params) {
$query = App::find();
//...
if ($this->keyword) {
$query->orFilterWhere(['like', 'keyword', $this->keyword])
->orFilterWhere(['like', 'content', $this->keyword])
}
if ($this->cat) {
$query->orFilterWhere(['category_id'=> $this->cat])
->orFilterWhere(['term_id'=> $this->cat])
}
//...
}
But it creates this query:
select * from t1 where title = 'keyword' or content = 'keyword' or
category_id = 10 or term_id = 10
First, your required sql statement should be something like this:
select *
from t1
where ((title LIKE '%keyword%') or (content LIKE '%keyword%'))
AND ((category_id = 10) or (term_id = 10))
So your query builder should be something like this:
public function search($params) {
$query = App::find();
...
if ($this->keyword) {
$query->andFilterWhere(['or',
['like','title',$this->keyword],
['like','content',$this->keyword]]);
}
if ($this->cat) {
$query->andFilterWhere(['or',
['category_id'=> $this->cat],
['term_id'=> $this->cat]]);
}...
ID Name from to
001-1 ABC 2015/05/01 2015/05/31
001-1 ABC 2015/06/01 2015/07/15
003-2 DEF 2015/05/01 2015/05/11
002-1 LMN 2015/05/01 2015/06/15
002-1 LMN 2015/06/16 2015/07/31
003-2 DEF 2015/06/01 2015/07/15
004-5 GHI 2015/05/11 2015/05/15
I want to have merge the records into one which matching the period from 2015/05/15 to 2015/07/15 like the following result in datable.
ID Name from to
001-1 ABC 2015/05/01 2015/07/15
002-1 LMN 2015/05/01 2015/07/31
003-2 and 004-5 are not in new datatable as they are not in the require range.
How can I get this? I only know very basic knowledge about LINQ and it's very fresh to me. thx.
With this class / data as a mockup:
class Item
{
public string ID { get; set; }
public string Name { get; set; }
public DateTime From { get; set; }
public DateTime To { get; set; }
}
List<Item> items = new List<Item> {
new Item { ID = "001-1", Name = "ABC",
From = DateTime.Parse("2015/05/01"),
To = DateTime.Parse("2015/05/31") },
new Item { ID = "001-1", Name = "ABC",
From = DateTime.Parse("2015/06/01"),
To = DateTime.Parse("2015/07/15") },
new Item { ID = "003-2", Name = "DEF",
From = DateTime.Parse("2015/05/01"),
To = DateTime.Parse("2015/05/11") },
new Item { ID = "002-1", Name = "LMN",
From = DateTime.Parse("2015/05/01"),
To = DateTime.Parse("2015/06/15") },
new Item { ID = "002-1", Name = "LMN",
From = DateTime.Parse("2015/06/16"),
To = DateTime.Parse("2015/07/31") },
new Item { ID = "003-2", Name = "DEF",
From = DateTime.Parse("2015/06/01"),
To = DateTime.Parse("2015/07/15") },
new Item { ID = "004-5", Name = "GHI",
From = DateTime.Parse("2015/05/11"),
To = DateTime.Parse("2015/05/15") }
};
you can use the following linq query to get the desired result set:
var result = from i in items
orderby i.From
group i by new { i.ID, i.Name } into iGroup
where iGroup.First().From <= DateTime.Parse("2015/05/15") &&
iGroup.Last().To >= DateTime.Parse("2015/07/1") &&
(iGroup.Last().To - iGroup.First().From).Days + 1 ==
iGroup.Sum(g => (g.To - g.From).Days + 1)
select new Item
{
ID = iGroup.Key.ID,
Name = iGroup.Key.Name,
From = iGroup.First().From,
To = iGroup.Last().To
};
You can adjust datetime comparisons to fit your actual requirement. In the above linq query I am comparing the smallest From date and the largest To date of each group to the matching period dates.
This comparison:
(iGroup.Last().To - iGroup.First().From).Days + 1 ==
iGroup.Sum(g => (g.To - g.From).Days + 1)
checks for groups that have no gaps in their date ranges.
EDIT:
If the source data is stored in a DataTable such as:
DataTable items = new DataTable();
items.Columns.Add("ID", typeof(string));
items.Columns.Add("Name", typeof(string));
items.Columns.Add("From", typeof(DateTime));
items.Columns.Add("To", typeof(DateTime));
then the linq query becomes a bit more complicated:
var q = from i in items.AsEnumerable()
orderby i.Field<DateTime>("From")
group i by new { ID = i.Field<string>("ID"), Name = i.Field<string>("Name") } into iGroup
where iGroup.First().Field<DateTime>("From") <= DateTime.Parse("2015/05/15") &&
iGroup.Last().Field<DateTime>("To") >= DateTime.Parse("2015/07/1") &&
(iGroup.Last().Field<DateTime>("To") - iGroup.First().Field<DateTime>("From")).Days + 1 ==
iGroup.Sum(g => (g.Field<DateTime>("To") - g.Field<DateTime>("From")).Days + 1)
select new
{
ID = iGroup.Key.ID,
Name = iGroup.Key.Name,
From = iGroup.First().Field<DateTime>("From"),
To = iGroup.Last().Field<DateTime>("To")
};
The above query returns an IEnumerable of anonymous type. It can be converted back to a DataTable using Reflection (see this post for example).
I have a problem with TYPO3 which I encountered several times now.
If i fetch an object with the TYPO3 CONTENT Object i have the possibility to render the fields with the renderObj...
So far so good...
But if i try to fetch an object which i fetched already before i dont get any response..
Following setup:
temp.current = COA
temp.current {
10 = CONTENT
10 {
table = pages
select {
pidInList = 22
max = 1
}
renderObj = COA
renderObj {
10 = CONTENT
10 {
table = tt_content
select {
pidInList.field = uid
where = colPos = 9
max = 1
languageField = sys_language_uid
}
renderObj = COA
renderObj {
5 = TEXT
5 {
value = here
typolink {
parameter.field = pid
title {
cObject = RECORDS
cObject {
tables = pages
source.field = pid
conf.pages = TEXT
conf.pages.field = title
}
}
}
}
20 = IMAGE
20 {
required = 1
file{
import = uploads/pics/
import.field = image
import.data = levelmedia: -1, slide
import.listNum = 0
width = 300c
height = 300c
}
titleText.field = titleText // altText
altText.field = altText // titleText
imageLinkWrap = 1
imageLinkWrap {
enable = 1
typolink {
parameter.data = field:pid
}
}
}
}
}
}
}
}
This is my current setup which i need to get a current project... Whatever..
The important part is:
5 = TEXT
5 {
value = here
typolink {
parameter.field = pid
title {
cObject = RECORDS
cObject {
tables = pages
source.field = pid
conf.pages = TEXT
conf.pages.field = title
}
}
}
}
I've already debugged the result of source... The value is 92, which is the correct uid from the page from where I need the title field...
Also I know that the code should be okay, because I use this snippet on many pages.
I think the problem is, that I try to fetch a content which i already fetched before..
Right here:
temp.current = COA
temp.current {
10 = CONTENT
10 {
table = pages
select {
pidInList = 22
max = 1
}
}
}
Many thanks!
// EDIT
I found a very good solution for my problem..
5 = TEXT
5 {
value = hier
typolink {
parameter.field = pid
title.cObject = TEXT
title.cObject {
data.dataWrap = DB:pages:{field:pid}:title
}
}
}
I found a solution!
5 = TEXT
5 {
value = hier
typolink {
parameter.field = pid
title.cObject = TEXT
title.cObject {
data.dataWrap = DB:pages:{field:pid}:title
}
}
}
According to http://forge.typo3.org/issues/20541 you are right and this has not been viewed as a bug but a feature ("recursion prevention").