zend based where clause disappearing - php

I have a weird problem using socialEngine DB class (based on zend framework).
I wrote something like this:
$statusTable = Engine_Api::_()->getDbtable('actions', 'activity');
$myPosts = $statusTable->fetchAll($statusTable->select()
->where('subject_id = ?',$id)
->where('comment_count > ?',0)
->where('type = ?',$type)
->where('date > ?',$newer_than)
->order('date DESC')
->limit(intval($num_items)));
Its a part of a plugin a made, the problem is the query generated is somthing like this:
SELECT `engine4_activity_actions`.*
FROM `engine4_activity_actions`
WHERE (subject_id = 5) AND (comment_count > 0) AND (type = ) AND (date > )
ORDER BY `date` DESC LIMIT 10
You can see that the $type and the $newer_than have disappeared, even though they have values ($type='status', $newer_than='01/01/2000')
EDIT:
It seems to respond only to integers and not strings, if i replace the 'status' with 0 it shows up in the query.
The server runs on php 5.3.2

There's a third optionnal argument on the where() method which is the type of your argument. Depending on your DB adapter it can maybe get an important thing to tell for the Zend_Db_Select query builder.
So you could try
->where('subject_id=?',$subject,'TEXT')
ZF API indicates as well "Note that it is more correct to use named bindings in your queries for values other than strings", this can help the query builder, to get the real type of your args, so you could try as well this way:
$myPosts = $statusTable->fetchAll($statusTable->select()
->where('subject_id=:psubject')
(...)
,array('psubject'=>$subject));

Related

CakePHP 3 putting un-necessary parentheses in SQL causing error

CakePHP 3.7. Trying to use the ORM to write a query which contains a MySQL COALESCE condition.
Followed advice on CakePHP 3 - How to write COALESCE(...) in query builder? and ended up having to write it manually using newExpr() as this was the given solution.
The code I have is as follows:
$TblRegulatoryAlerts = TableRegistry::getTableLocator()->get('TblRegulatoryAlerts');
$subscribed_to = $TblRegulatoryAlerts->getUserRegulations($u_id, $o_id, false);
$query = $this->find()
->contain('Filters.Groups.Regulations')
->select(['id', 'date', 'comment', 'Filters.label', 'Filters.anchor', 'Groups.label']);
$query->select($query->newExpr('COALESCE((SELECT COUNT(*)
FROM revision_filters_substances
WHERE revision_filter_id = RevisionFilters.id), 0) AS count_substances'));
$query->where(['date >=' => $date_start, 'date <=' => $date_end, 'Regulations.id' => $regulation_id, 'Filters.id IN' => $subscribed_to]);
$query->enableHydration(false)->orderDesc('date');
This produces the following SQL (output of debug($query->sql()):
SELECT RevisionFilters.id AS `RevisionFilters__id`, RevisionFilters.date AS `RevisionFilters__date`, RevisionFilters.comment AS `RevisionFilters__comment`, Filters.label AS `Filters__label`, Filters.anchor AS `Filters__anchor`, Groups.label AS `Groups__label`, (COALESCE((SELECT COUNT(*) FROM revision_filters_substances WHERE revision_filter_id = RevisionFilters.id), 0) AS count_substances) FROM revision_filters RevisionFilters INNER JOIN dev_hub_subdb.filters Filters ON Filters.id = (RevisionFilters.filter_id) INNER JOIN dev_hub_subdb.groups Groups ON Groups.id = (Filters.group_id) INNER JOIN dev_hub_subdb.regulations Regulations ON Regulations.id = (Groups.regulation_id) WHERE ...
Unfortunately this doesn't execute because Cake is putting in un-necessary parentheses surrounding the COALESCE statement, which changes the SQL.
In the above code it generates:
(COALESCE((SELECT COUNT(*) FROM revision_filters_substances WHERE revision_filter_id = RevisionFilters.id), 0) AS count_substances)
Whereas it needs to omit the parentheses surrounding COALESCE so it's just:
COALESCE((SELECT COUNT(*) FROM revision_filters_substances WHERE revision_filter_id = RevisionFilters.id), 0) AS count_substances
Is this possible?
Don't specify the alias in the expression, instead specify it using the key => value syntax of the Query::select() method, like this:
$query->select([
'count_substances' => $query->newExpr('...')
]);
It would still wrap the expression in parentheses, but that would then be valid as it doesn't include the alias.
That being said, using the function builders coalesque() method should work fine, the problem described in the linked question can be fixed by using the key => value syntax too, where the value can specify the kind of the argument, like ['Nodes.value' => 'identifier'], without that it would bind the value as a string.
However there shouldn't be any such problem with your example, using the function builders coalesce() method should work fine.
$query->select([
'count_substances' => $query->func()->coalesce($countSubquery, 1, ['integer'])
]);
The type argument is kinda optional, it would work with most DBMS without it, but for maximum compatibility it should be specified so that the integer value is being bound properly, also it will automatically set the return type of the function (the casting type) to integer too.

How to use an expression in select count(...)? (Doctrine)

I have User and UserGroup entities. Each user has a group (UserGroup), and each user has a boss (User).
I would like to get a list of groups, and decide if the current user is the boss of someone in the group. I already implemented this in SQL, and now I tried to solve it using Doctrine. This is what I tried:
$qb = em()->createQueryBuilder()
->select(array('gr.id AS group_id', 'gr.name AS group_name', 'COUNT(us.boss = :current_user_id)>0 AS is_boss'))
->from('\App\Entity\UserGroup', 'ug')
->leftJoin('\App\Entity\User', 'us', 'WITH', 'us.group = ug.id')
->setParameter('current_user_id', $_SESSION['uid'])
->groupby('gr.id')
;
$groups = $qb->getQuery()->getScalarResult();
Unfortunately I get an uncaught QueryException, and I have no idea how to fix that. How is it possible to put an expression inside the COUNT(...) function?
You can use Mysql case function to do this:
$queryBuilder->addSelect('case when COUNT(case when us.boss = :current_user_id then 1 else 0 end)>0 then 1 else 0 end AS is_boss');
Reference
Is it possible to specify condition in Count()?

php zf2 select where clause with SQL function

I need to compare DATE format from a DATETIME.
Prior ZF 2.3.5, the following code was working fine:
$select->where('DATE(arrival_date) <= DATE(NOW())');
$select->where('DATE(departure_date) >= DATE(NOW())');
$select->where('enable = true');
With ZF 2.4.2+ it does not work anymore and produce the following error:
Cannot inherit previously-inherited or override constant TYPE_IDENTIFIER from interface Zend\Db\Sql\Predicate\PredicateInterface
I have tried the following (without error). The problem is that "arrival_date" is a DATETIME format, and I need to compare with a DATE only.
$where = new Zend\Db\Sql\Where();
$where->lessThanOrEqualTo('arrival_date', date('Y-m-d'));
$where->greaterThanOrEqualTo('arrival_date', date('Y-m-d'));
$where->equalTo('enable', true);
$select->where($where);
Ideally, this code should work, but it does not:
$select->where(new Zend\Db\Sql\Predicate\Expression('DATE(arrival_time) <= ?', 'DATE(NOW())'));
I am lost, any idea ?
Just use Expression like this:
$select->where(
array(
new \Zend\Db\Sql\Predicate\Expression("DATE(arrival_date) <= DATE(NOW())")
)
);
No need of ? like you tried. With ? you params will be escaped and it won't work with an expression. Only with params, like a PHP datetime or something like this.
Tested with 2.4.2

addBetweenCondition in YII

SO I want to find that if value x is exits between the values of 2 columns or not, For that i have run the query in phpmyadmin :
Normal Approch :-
SELECT * FROM `traits_versions` WHERE 16 BETWEEN `trait_value_lower` and `trait_value_upper` and `style_id` = 1
and it is giving me fine result.But when the same approach i want to find achieve in YII that it is not running and giving the sql error :
YII apprroch :-
$details = array();
$criteria = new CDbCriteria();
$criteria->addCondition('style_id='.$style_id);
$criteria->addCondition('version='.$version);
$criteria->addBetweenCondition($style_contribution,$this->trait_value_lower,$this->trait_value_upper);
$trait_details= $this->find($criteria);
When i debug the query in log than it shows in case of yii :
SELECT * FROM `traits_versions` `t` WHERE ((style_id=1) AND (version=1)) AND (16 BETWEEN NULL AND NULL) LIMIT 1
Why it is giving NULL value in query while i'm passing the name of the column in it.
So please guide me where i'm going wrong in yii.
Add compare condition like below
$criteria->compare('trait_value_lower', 16, false, '>');
$criteria->compare('trait_value_upper',16, false, '<');
instead of between condition
$criteria->addBetweenCondition($style_contribution,$this->trait_value_lower,$this->trait_value_upper);
because between condition will apply on one column as per Yii doc.
public static addBetweenCondition(string $column, string $valueStart, string $valueEnd, string $operator='AND')

Doctrine createQueryBuilder multiple conditional where statements a bit clunky

I have an itemid and a category id that are both conditional. If none is given then all items are shown newest fist. If itemid is given then only items with an id lower than given id are shown (for paging). If category id is given than only items in a certain category are shown and if both are given than only items from a certain category where item id smaller than itemid are shown (items by category next page).
Because the parameters are conditional you get a lot of if statements depending on the params when building a SQL string (pseudo code I'm wearing out my dollar sign with php stuff):
if itemid ' where i.iid < :itemid '
if catid
if itemid
' and c.id = :catid'
else
' where c.id = :catid'
end if
end if
If more optional parameters are given this will turn very messy so I thought I'd give the createQueryBuilder a try. Was hoping for something like this:
if($itemId!==false){
$qb->where("i.id < :id");
}
if($categoryId!==false){
$qb->where("c.id = :catID");
}
This is sadly not so and the last where will overwrite the first one
What I came up with is this (in Symfony2):
private function getItems($itemId,$categoryId){
$qb=$this->getDoctrine()->getRepository('mrBundle:Item')
->createQueryBuilder('i');
$arr=array();
$qb->innerJoin('i.categories', 'c', null, null);
$itemIdWhere=null;
$categoryIdWhere=null;
if($itemId!==false){
$itemIdWhere=("i.id < :id");
}
if($categoryId!==false){
$categoryIdWhere=("c.id = :catID");
}
if($itemId!==false||$categoryId!==false){
$qb->where($itemIdWhere,$categoryIdWhere);
}
if($itemId!==false){
$qb->setParameter(':id', $itemId);
}
if($categoryId!==false){
$arr[]=("c.id = :catID");
$qb->setParameter(':catID', $categoryId);
}
$qb->add("orderBy", "i.id DESC")
->setFirstResult( 0 )
->setMaxResults( 31 );
I'm not fully trusting the $qb->where(null,null) although it is currently not creating errors or unexpected results. It looks like these parameters are ignored. Could not find anything in the documentation but an empty string would generate an error $qb->where('','').
It also looks a bit clunky to me still, if I could use multiple $qb->where(condition) then only one if statement per optional would be needed $qb->where(condition)->setParameter(':name', $val);
So the question is: Is there a better way?
I guess if doctrine had a function to escape strings I can get rid of the second if statement round (not sure if malicious user could POST something in a different character set that would allow sql injection):
private function getItems($itemId,$categoryId){
$qb=$this->getDoctrine()->getRepository('mrBundle:Item')
->createQueryBuilder('i');
$arr=array();
$qb->innerJoin('i.categories', 'c', null, null);
$itemIdWhere=null;
$categoryIdWhere=null;
if($itemId!==false){
$itemIdWhere=("i.id < ".
someDoctrineEscapeFunction($id));
}
Thank you for reading this far and hope you can enlighten me.
[UPDATE]
I am currently using a dummy where statement so any additional conditional statements can be added with andWhere:
$qb->where('1=1');// adding a dummy where
if($itemId!==false){
$qb->andWhere("i.id < :id")
->setParameter(':id',$itemId);
}
if($categoryId!==false){
$qb->andWhere("c.id = :catID")
->setParameter(':catID',$categoryId);
}
You can create filters if you want to use more generic approach of handling this.
Doctrine 2.2 features a filter system that allows the developer to add SQL to the conditional clauses of queries
Read more about filters however, I'm handling this in similar manner as you showed

Categories