Doctrine QueryBuilder - Struggling with table 'state' variable - doctrine-orm

I have the following query and the last part of it is to check the state of the item which will be 1 or 0;
My api calls:
http://example.com/api/search?keyword=someword&search_for=item&return_product
The query works as expected, except for one thing. Some of the stone items are disabled and I need to ignore where:
->where('S.state=:state')
->setParameter('state' , 1 )
I am not quite sure where to add this to the current query to get it to work:
$qb = $this->stoneRepository->createQueryBuilder('S');
//Get the image for the item
$qb->addSelect('I')
->leftJoin('S.image' , 'I');
//Check if we want products returned
if ( $return_product )
{
$qb->addSelect('P','PI')
->leftJoin('S.product' , 'P')
->leftJoin('P.image' , 'PI');
}
//Check is we want attributes returned
if ( $return_attribute )
{
$qb->addSelect('A','C')
->leftJoin('S.attribute' , 'A')
->leftJoin('A.category' , 'C');
}
//Check the fields for matches
$qb->add('where' , $qb->expr()->orX(
$qb->expr()->like('S.name' , ':keyword'),
$qb->expr()->like('S.description' , ':keyword')
)
);
//Set the search item
$qb->setParameter('keyword', '%'.$keyword.'%');
$qb->add('orderBy', 'S.name ASC');

Just after the createQueryBuilder call:
$qb = $this->stoneRepository
->createQueryBuilder('S')
->where('S.state = :state')
->setParameter('state', 1);
With the query builder the order is not important: you can add SQL pieces in different order and even override pieces already added.

Related

IcCube - Treemap Chart with duplicate names

In the Google Treemap Chart every node has to have a unique id, but two nodes can have the same name (https://groups.google.com/d/msg/google-visualization-api/UDLD-a-0PCM/IwVCGzsWOg8J).
I used the schema from the parent/child demo (http://www.iccube.com/support/documentation/user_guide/schemas_cubes/dim_parentchild.php)
Using the following MDX statement in the treemap works, as long as the names of the nodes are unique:
WITH
MEMBER [parent_name] as IIF( [dim (ALL)].[Hierarchy].currentmember
is [dim (ALL)].[Hierarchy].[ALL],'',
[dim (ALL)]. [Hierarchy].currentmember.parent.name )
SELECT
{[parent_name],[Measures].[value]} on 0,
non empty [dim (ALL)].[Hierarchy].members on 1
FROM
[Cube]
If I added the line to the In-memory table in icCube's schema :
7,4,Spain, 2, 32
but the name Spain is double when rendering the Treemap. To support names a child definition in the GVI table should be something like this:
{v:'uniqueID-Spain', f:'Spain'}
As a workaround you can use the following code that modifies GviTable processing for the google tree widget. Check the example here:
https://drive.google.com/file/d/0B3kSph_LgXizSVhvSm15Q1hIdW8/view?usp=sharing
Report JavaScript:
function consumeEvent( context, event ) {
if (event.name == 'ic3-report-init') {
if(!_.isFunction(ic3.originalProcessGviTable)) {
ic3.originalProcessGviTable = viz.charts.GenericGoogleWidget.prototype.processGviTable
}
viz.charts.GenericGoogleWidget.prototype.processGviTable = function(gviTable){
if(this.props.ic3chartType === "TreeMap") {
gviTable = gviTable || this.gviTable();
var underlying = _.cloneDeep(gviTable.getUnderlyingGviTable());
_.each(underlying.rows, function(row){
// Replace id with parent prefixed
if(_.isObject(row.c[0]) && !_.isString(row.c[0].f)) {
row.c[0].f = row.c[0].v;
if(_.isObject(row.c[0].p) && _.isString(row.c[0].p.mun)) {
row.c[0].v = row.c[0].p.mun;
}
}
});
gviTable = viz.GviTable.fromSnapshot(underlying);
this.startColumnSelection = gviTable.getNumberOfHeaderColumns() - 1;
return viz.charts.toGoogleDataTableOneRowHeader(gviTable);
} else {
return ic3.originalProcessGviTable.apply(this, gviTable);
}
}
}
}
For the query like:
WITH
MEMBER [parent_name] as
IIF( [dim (ALL)].[Hierarchy].currentmember.isAll(),
'',
([dim (ALL)].[Hierarchy].currentmember.parent.uniqueName)
)
SELECT
{[parent_name],[Measures].[value]} on 0,
non empty [dim (ALL)].[Hierarchy].members on 1
FROM
[Cube]
This is a limitation of Google Treemap chart that is using the same column for the id and the label. Besides changing the names to ensure they are unique (e.g. adding the parent) I don't see a workaround to this.
An option would be using another Treemap chart (e.g. one from D3) that has not this limitation.
--- icCube Schema ---
The schema is working (just use , instead of ; as separator )
--- icCube Reporting ---
The issue using Treemap is that you've two rows with the same id (Germany), fiddle
This fiddle is a running example of treemap

Update and Save a record programmatically in vTiger

Currently I'm trying to change the 'Assigned To' user for a lead to the current user whenever a user enters the lead details screen.
I have the following code:
function checkPermission(Vtiger_Request $request) {
$moduleName = $request->getModule();
$recordId = $request->get('record');
$recordModel = Vtiger_Record_Model::getInstanceById($recordId, $moduleName);
$recordModel->set('assigned_user_id',$current_user->id);
$recordModel->save();
...
return true;
}
The problem is, instead of saving the current record with a new assigned user, vTiger duplicates this record into a new record and saves it with the current user as a new assigned user.
Working on vTiger v6.2.0
You need to set mode is edit to recordModel before save
$recordModel->set('mode','edit');
Try something like this:
$recordModel= Vtiger_Record_Model::getInstanceById($recordId,$moduleName);
$recordModel->set(my_fields, my_new_value);
$recordModel->set(my_fields2, my_new_value2);
$recordModel->set('mode', 'edit');
$recordModel->save();
Tray with event Handler;
create a file: /modules/yourmodulename/handlers/RecordUpdater.php
then put the code below in your file RecordUpdater.php :
require_once 'include/events/VTEventHandler.inc';
class yourmodulename_RecordUpdater_Handler extends VTEventHandler {
function handleEvent($eventName, $data) {
global $adb;
$currentUserModel = Users_Record_Model::getCurrentUserModel();
$module = $data->getModuleName();
if ($eventName === 'vtiger.entity.beforesave' AND $module === "yourmodulename") {
require_once 'modules/yourmodulename/yourmodulename.php';
$currentUserId = $currentUserModel->getId();
$data->set('assigned_user_id', $currentUserId);
}
}
}
finally don't forget to insert in vtiger_eventhandlers table:
INSERT INTO `vtigercrm`.`vtiger_eventhandlers` (
`eventhandler_id` ,
`event_name` ,
`handler_path` ,
`handler_class` ,
`cond` ,
`is_active` ,
`dependent_on`
)
VALUES (
NULL , 'vtiger.entity.beforesave', 'modules/yourmodulename/handlers/RecordUpdater.php', 'yourmodulename_RecordUpdater_Handler', NULL , '1', '[]'
);
then increment vtiger_eventhandlers_seq with 1
that's it :)
I hope, that helps you

Doctrine - How to hydrate a collection when using query builder

A previous question I asked was to do with hydrating a result set when using Doctrine and query builder. My issue was how to return an array and their sub-sets:
This was for a single result set and the answer was quite simple:
$qb = $this->stoneRepository->createQueryBuilder('S');
$query = $qb->addSelect('A','P','I','C')
->leftJoin('S.attribute', 'A')
->leftJoin('A.category', 'C')
->innerJoin('S.product' , 'P')
->innerJoin('S.image' , 'I')
->where('S.id = :sid')
->setParameter('sid', (int) $stone_id)
->getQuery();
$resultArray = $query->getOneOrNullResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
return $resultArray;
My next question is how to do this exact same thing for a collection? This is what I have tried:
public function fetchAll()
{
$qb = $this->stoneRepository->createQueryBuilder('S');
$qb->addSelect('A','P','I','C')
->leftJoin('S.attribute', 'A')
->leftJoin('A.category', 'C')
->innerJoin('S.product' , 'P')
->innerJoin('S.image' , 'I')
->where('S.state=:state')
->setParameter('state' , 1 );
$adapter = new DoctrineAdapter( new ORMPaginator( $qb ) );
$collection = new StoneCollection($adapter);
return $collection;
}
The problem I am facing with this solution is that the join tables are not being populated and I am ending up with a collection of empty results.
The StoneCollection class simply extends paginator:
<?php
namespace Api\V1\Rest\Stone;
use Zend\Paginator\Paginator;
class StoneCollection extends Paginator
{
}
I am thinking that perhaps the best mehod is to get an array and to page the array?
EDIT::
I have this working although I am not keen on it as I hit the DB twice. The first time to build the array (Which is the entire result set which could be very big for some applications) and then the second time to page the results which is then returned to HAL in ApiGility for processing...
Ideally this should be done in one go however I am not sure how to hydrate the results in a single instance...
public function fetchAll( $page = 1 )
{
$qb = $this->stoneRepository->createQueryBuilder('S');
$qb->addSelect('A','P','I','C')
->leftJoin('S.attribute', 'A')
->leftJoin('A.category', 'C')
->innerJoin('S.product' , 'P')
->innerJoin('S.image' , 'I')
->where('S.state=:state')
->setParameter('state' , 1 );
$resultArray = $qb->getQuery()->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
$paginator = new \Zend\Paginator\Paginator(new \Zend\Paginator\Adapter\ArrayAdapter($resultArray));
$paginator->setCurrentPageNumber($page);
return $paginator;
}
The Answer to this is as I have above:
I have this working although I am not keen on it as I hit the DB twice. The first time to build the array (Which is the entire result set which could be very big for some applications) and then the second time to page the results which is then returned to HAL in ApiGility for processing...
Ideally this should be done in one go however I am not sure how to hydrate the results in a single instance...
public function fetchAll( $page = 1 )
{
$qb = $this->stoneRepository->createQueryBuilder('S');
$qb->addSelect('A','P','I','C')
->leftJoin('S.attribute', 'A')
->leftJoin('A.category', 'C')
->innerJoin('S.product' , 'P')
->innerJoin('S.image' , 'I')
->where('S.state=:state')
->setParameter('state' , 1 );
$resultArray = $qb->getQuery()->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
$paginator = new \Zend\Paginator\Paginator(new \Zend\Paginator\Adapter\ArrayAdapter($resultArray));
$paginator->setCurrentPageNumber($page);
return $paginator;
}
On the Doctrine documentation for Pagination they state to use $fetchJoinCollection = true, which I believe is the same as the HYDRATE you are trying to use.
Doctrine Pagination
On my pagination code for my QueryBuilder I use it like the following:
public function getAllPaginated($page, $limit){
$query = $this->createQueryBuilder('o')
->select('o')
->getQuery();
$paginator = new Paginator($query, $fetchJoinCollection = true);
$paginator->getQuery()
->setFirstResult($limit * ($page - 1)) // Offset
->setMaxResults($limit);
return $paginator;
}

Insert main table's primary key into child table multiple times

I have MS Access 2007 database with the following schema:
Main table Object< # Object_PK, ... >
Child table Electric_Consumption< $ Object_PK, # Electric_Consumption_PK, ... >
Child table Water_Consumption< $ Object_PK, # Water_Consumption_PK, ... >
Child table Educational_Object< $ Object_PK, # Educational_Object_PK, ... > which has child tables defined like this:
School< $ Educational_Object_PK, # School_PK, ... >
University< $ Educational_Object_PK, # University_PK, ... >
Here is the picture that should make things clearer:
I use ADO and C++ to insert data.
First I need to enter data for main table Object. I can successfully do that with INSERT query.
My problem is following:
After the above operation I need to insert Object's primary key into child tables, since it is their foreign key.
Allow me to describe exactly what I need so community can help me:
As I said, first I insert data into main table Object.
Then I need to insert data and Object's primary key into child tables.
Browsing through Internet I have found ##IDENTITY that might help me but I do not know if it works for my case.
To make things harder, this will be done in for loop ( the value of the Object_PK is the same in every INSERT and is equal to the value of the last inserted record for the Object ) , something like this:
for ( //... )
L"INSERT INTO Electric_Consumption ( Object_PK, field1, field2 ... )
values ( Object_pk // should I use here ##IDENTITY ? );
Then the same thing should be repeated for tables Water_Consumption and Educational_Object.
After I finish this, I need to add data in the Educational_Object's child tables.
The same as above, only instead of Object_PK I need to add Educational_Object_PK.
Here is the pseudo-code to clarify things better:
L"INSERT INTO Object ( ... ) values ( ... ); //this is OK
for ( ... )
L" INSERT INTO Electric_Consumption ( Object_PK, ... )
values ( Object_PK, ... )"; // should I use ##IDENTITY here
// to get Object_PK ??
for ( ... )
L" INSERT INTO Water_Consumption ( Object_PK, ... )
values ( Object_PK, ... )"; // should I use ##IDENTITY here
// to get Object_PK ??
for ( ... )
L" INSERT INTO Educational_Object ( Object_PK, ... )
values ( Object_PK, ... )"; // should I use ##IDENTITY here
// to get Object_PK ??
for ( ... )
L" INSERT INTO School ( Educational_Object_PK, ... )
values ( Educational_Object_PK, ... )";// should I use ##IDENTITY here
// to get Educational_Object_PK ??
for ( ... )
L" INSERT INTO University ( Educational_Object_PK, ... )
values ( Educational_Object_PK, ... )";// should I use ##IDENTITY here
// to get Educational_Object_PK ??
Can you please tell me which SQL statement to use for this, and demonstrate how to use it by providing a small pseudo code?
I understand that my description of the problem might be confusing so if you need further clarification leave a comment and I will edit my post.
Thank you.
Best regards.
Yes, you want to use SELECT ##IDENTITY as a multiuser-safe way to retrieve the most recently-created AutoNumber (sometimes called "IDENTITY") value. The things to remember are:
You execute a SELECT ##IDENTITY query immediately after you perform the INSERT on the parent table.
You store the returned value in a Long Integer variable.
You use the variable to populate the Foreign Key values in the child table(s).
The following is VBA code, but you can treat it as pseudo-code:
Dim lngObject_PK As Long, lngEducational_Object_PK As Long
Set cmd = New ADODB.Command
cmd.ActiveConnection = con
cmd.CommandText = "INSERT INTO [Object] ([Description]) VALUES (?)"
cmd.Parameters.Append cmd.CreateParameter("?", adVarWChar, adParamInput, 255, "my new Object")
cmd.Execute
Set cmd = Nothing
Set rst = New ADODB.Recordset
rst.Open "SELECT ##IDENTITY", con, adOpenStatic, adLockOptimistic
lngObject_PK = rst(0).Value
rst.Close
Set rst = Nothing
Debug.Print "Object_PK of newly-created Object record: " & lngObject_PK
Set cmd = New ADODB.Command
cmd.ActiveConnection = con
cmd.CommandText = "INSERT INTO [Electric_Consumption] ([Object_PK],[Description]) VALUES (?,?)"
cmd.Parameters.Append cmd.CreateParameter("?", adInteger, adParamInput, , lngObject_PK)
cmd.Parameters.Append cmd.CreateParameter("?", adVarWChar, adParamInput, 255, "my new Electric_Consumption")
cmd.Execute
Set cmd = Nothing
Set cmd = New ADODB.Command
cmd.ActiveConnection = con
cmd.CommandText = "INSERT INTO [Educational_Object] ([Object_PK],[Description]) VALUES (?,?)"
cmd.Parameters.Append cmd.CreateParameter("?", adInteger, adParamInput, , lngObject_PK)
cmd.Parameters.Append cmd.CreateParameter("?", adVarWChar, adParamInput, 255, "my new Educational_Object")
cmd.Execute
Set cmd = Nothing
Set rst = New ADODB.Recordset
rst.Open "SELECT ##IDENTITY", con, adOpenStatic, adLockOptimistic
lngEducational_Object_PK = rst(0).Value
rst.Close
Set rst = Nothing
Debug.Print "Educational_Object_PK of newly-created Educational_Object record: " & lngEducational_Object_PK
Set cmd = New ADODB.Command
cmd.ActiveConnection = con
cmd.CommandText = "INSERT INTO [School] ([Educational_Object_PK],[Description]) VALUES (?,?)"
cmd.Parameters.Append cmd.CreateParameter("?", adInteger, adParamInput, , lngEducational_Object_PK)
cmd.Parameters.Append cmd.CreateParameter("?", adVarWChar, adParamInput, 255, "my new School")
cmd.Execute
Set cmd = Nothing
If the Object_PK is predictable, such as you are using an Autonumber field, you could first determine the next key by something like:
SELECT Max([Object_ID]+1) AS NewKey
FROM ObjectTable;
then use that for all of the other tables (or simply retrieve the MAX key value after storing the Object); How is the primary key defined?

Coldfusion: Can't reference var from query result set

Using cfscript, trying to set the ID of the newly inserted question so that I can use it in my answer insert to build the relationship. I've done this a million times outside of cfscript. setName seems to be the proper method to call to create the query name.
I'm receiving the error that "theQuestionID" does not exist in qryQuestion
i = 1;
while ( structKeyExists( form, "question" & i ) )
{
q = new Query();
q.setDatasource("kSurvey");
q.setName("qryQuestion");
q.setSQL("
set nocount on
insert into question (question)
values('#form["question#i#"]#')
select ##IDENTITY AS theQuestionID
set NOCOUNT off
");
q.execute();
writeOutput("Question"&i&"<br>");
j = 1;
while ( structKeyExists( form, "question" & i & "_answer" & j) ) {
q = new Query();
q.setDatasource("kSurvey");
q.setSQL("
insert into answer (answer,questionid)
values('#form["question#i#_answer#j#"]#',#qryQuestion.theQuestionID#)
");
q.execute();
writeOutput("Answer"&j&"<br>");
j++;
}
i++;
}
Theres a better way to accomplish this without having to select ##identity (which in itself isn't the best way to get it from sql server, using scope_identity is the best practice way to do this in sql server. http://msdn.microsoft.com/en-us/library/ms190315.aspx
Fortunately ColdFusion makes this even easier:
<cfscript>
insertQuery = new query();
insertQuery.setDatasource("datasourcename");
insertQuery.setSql("insert into contact(firstname, lastname)
values('ryan','anklam')");
result = insertQuery.Execute();
theKey = result.getPrefix().generatedkey;
</cfscript>