I'd like to construct the following SQL using Doctrine's query builder:
select c.*
from customer c
join phone p
on p.customer_id = c.id
and p.phone = :phone
where c.username = :username
First I tried
$qb->select('c')
->innerJoin('c.phones', 'p', Join::ON, $qb->expr()->andx(
$qb->expr()->eq('p.customerId', 'c.id'),
$qb->expr()->eq('p.phone', ':phone')
))
->where('c.username = :username');
But I'm getting the following error
Error: expected end of string, got 'ON'
Then I tried
$qb->select('c')
->innerJoin('c.phones', 'p')
->where('c.username = :username')
->andWhere('p.phone = :phone');
which seems to be working. However, does anyone know what's wrong with the first attempt? I'd like to make the first one work since it resembles more closely to how SQL is structured.
Note: I know we can also write native mysql or dql with Doctrine, but I'd prefer query builder.
EDIT: Below is the entire code
namespace Cyan\CustomerBundle\Repository;
use Cyan\CustomerBundle\Entity\Customer;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join;
class CustomerRepository extends EntityRepository
{
public function findCustomerByPhone($username, $phone)
{
$qb = $this->createQueryBuilder('c');
$qb->select('c')
->innerJoin('c.phones', 'p', Join::ON, $qb->expr()->andx(
$qb->expr()->eq('p.customerId', 'c.id'),
$qb->expr()->eq('p.phone', ':phone')
))
->where('c.username = :username');
// $qb->select('c')
// ->innerJoin('c.phones', 'p')
// ->where('c.username = :username')
// ->andWhere('p.phone = :phone');
$qb->setParameters(array(
'username' => $username,
'phone' => $phone->getPhone(),
));
$query = $qb->getQuery();
return $query->getResult();
}
}
I'm going to answer my own question.
innerJoin should use the keyword "WITH" instead of "ON" (Doctrine's documentation [13.2.6. Helper methods] is inaccurate; [13.2.5. The Expr class] is correct)
no need to link foreign keys in join condition as they're already specified in the entity mapping.
Therefore, the following works for me
$qb->select('c')
->innerJoin('c.phones', 'p', 'WITH', 'p.phone = :phone')
->where('c.username = :username')
->setParameter('phone', $phone)
->setParameter('username', $username);
or
$qb->select('c')
->innerJoin('c.phones', 'p', Join::WITH, $qb->expr()->eq('p.phone', ':phone'))
->where('c.username = :username')
->setParameter('phone', $phone)
->setParameter('username', $username);;
You can explicitly have a join like this:
$qb->innerJoin('c.phones', 'p', Join::ON, 'c.id = p.customerId');
But you need to use the namespace of the class Join from doctrine:
use Doctrine\ORM\Query\Expr\Join;
Or if you prefere like that:
$qb->innerJoin('c.phones', 'p', Doctrine\ORM\Query\Expr\Join::ON, 'c.id = p.customerId');
Otherwise, Join class won't be detected and your script will crash...
Here the constructor of the innerJoin method:
public function innerJoin($join, $alias, $conditionType = null, $condition = null);
You can find other possibilities (not just join "ON", but also "WITH", etc...) here: http://docs.doctrine-project.org/en/2.0.x/reference/query-builder.html#the-expr-class
EDIT
Think it should be:
$qb->select('c')
->innerJoin('c.phones', 'p', Join::ON, 'c.id = p.customerId')
->where('c.username = :username')
->andWhere('p.phone = :phone');
$qb->setParameters(array(
'username' => $username,
'phone' => $phone->getPhone(),
));
Otherwise I think you are performing a mix of ON and WITH, perhaps the problem.
Related
i want to perform a Find operation on Entity Results.
$stores = c2gr($this, 'EducateToolsBundle:Stores')->findBy(['selectOption' => true, 'center_type_id' => 9])
i want to do center_type_id != 9
Just use a DQL or QueryBuilder.
$repository
->createQueryBuilder('s')
->where('s.selectOption = :selectOption')
->andWhere('s.center_type_id <> :center_type_id')
->setParameters([
'selectOption' => true
'center_type_id' => 9,
])
->getQuery()
->getResult();
You should be able to achieve that by using Criteria class. Something like this should work:
use Doctrine\Common\Collections\Criteria;
$criteria = Criteria::create();
$criteria->where($criteria->expr()->neq('center_type_id', 9));
$criteria->andWhere($criteria->expr()->eq('selectOption', true));
$entityRepository = $this->getDoctrine()->getRepository('C2EducateToolsBundle:Stores');
$result = $entityRepository->matching($criteria);
NOTE: above will work if you are in class which extends Symfony\Bundle\FrameworkBundle\Controller\Controller since it is using getDoctrine method. If you are not in such class you should either inject EntityManager or get it from service container.
For more details you should check this answer
I'm trying to create a custom list for my module on Prestashop 1.6 and I need to get data from three different tables. My problem is the $this->_filter variable, how can I do that?
I need to do this:
$query = '
SELECT s.*, pl.*
FROM '._DB_PREFIX_.'`scd_gift` s
INNER JOIN '._DB_PREFIX_.'product_lang pl
ON s.id_product = pl.id_product
WHERE s.`id_gift_type` = '.(int)$id_gift_type.' and id_lang='.$id_lang;
Here is my function:
public function getCustomListHostessGifts() {
$this->table = 'scd_gift';
$this->list_id = 'hostess_gift';
$this->lang = true;
$this->identifier = 'id_scd_gift';
$this->_orderBy = 'id_product';
$this->_orderWay = 'DESC';
$this->addRowAction('delete');
$this->fields_list = (array(
'id_product' => array('title' => $this->l('ID'), 'class' => 'fixed-width-xs',
'align' => 'center'),
'name' => array('title' => $this->l('Name'), 'filter_key' => 'b!name'),
));
$this->clearFilters();
$hostessType = MlmGiftsModule::getGiftTypeIdByGiftTypeName('_HOSTESSGIFT_');
$this->_join = Shop::addSqlAssociation('scd_gift', 'a');
$this->_filter = '
INNER JOIN '._DB_PREFIX_.'product_lang pl
ON s.id_product = pl.id_product
AND a.`id_gift_type` ='.$hostessType ;
$this->toolbar_title = $this->l('Hostess gifts:');
return $this->renderList();
}
_filter is use to set filter parameters passed in backoffice list search fields.
Any JOIN sentence must be set in _join var:
$this->_join = Shop::addSqlAssociation('scd_gift', 'a');
. ' INNER JOIN '._DB_PREFIX_.'product_lang pl
ON s.id_product = pl.id_product
AND a.`id_gift_type` ='.$hostessType ;
Good luck.
PixelWeb's answer is correct, but of course, there is no semicolon after
$this->_join = Shop::addSqlAssociation('scd_gift', 'a')
Additional. You may skip this line anyway, because Prestashop adds the default SQL association "a" for the table in the controller itsself.
How to apply the SQL in keyword in Entity Framework method syntax?
For example, if I want to write the query in Entity Framework
select roleName from Roles where rold_id in (select role_id from UserRoles where user_id = 1);
So, how to apply that in Entity Framework method syntax?
The inner query would be done separately:
var inner = UserRoles.Where(r => r.user_id == 1)
.Select(r => r.role_id);
And then the outer would use the .Contains method of the inner.
var roleNames = Roles.Where(r => inner.Contains(r.role_id))
.Select(r => r.roleName);
You could merge it all into a single query, but this is the sanest way to do it. Entity Framework uses deferred queries so it will still do this efficiently.
Edit: Just for completeness sakes, here's a one-line version:
var roleNames = Roles.Where(r => UserRoles
.Where(ur => ur.user_id == 1)
.Select(ur => ur.role_id)
.Contains(r.role_id))
.Select(r => r.roleName);
In a nut shell, instead of it being 'B is in set A', it's more like 'Set A contains B'.
I have the following SQL
SELECT entry_subject.id, entry_subject.subject_id
FROM entry_subject
INNER JOIN subject
ON entry_subject.subject_id = subject.id
WHERE subject.id = 71
I have arrived at the following which is an almost carbon copy of a number of examples, however I'm getting 1054 Unknown column 'EntrySubject.subject_id' in 'field list'.
$subjectEntries = new CDbCriteria();
$subjectEntries->alias = 'EntrySubject';
$subjectEntries->select = 'EntrySubject.id, EntrySubject.subject_id';
$subjectEntries->join = 'INNER JOIN Subject ON Subject.id = EntrySubject.subject_id';
$subjectEntries->condition = 'Subject.id=71';
$subjectEntriesModel=Subject::model()->findAll($subjectEntries);
My relationships are as follows
return array(
'entrySubject' => array(self::HAS_MANY, 'EntrySubject', 'subject_id'),
'phase' => array(self::BELONGS_TO, 'Phase', 'phase_id'),
'subjectType' => array(self::BELONGS_TO, 'SubjectType', 'subject_type'),
);
What am I doing wrong?
Many thanks
I assume you are trying to get subject entries for subject #71, then you should simply try the following :
$subject = Subject::model()->findByPk(71);
$subjectEntriesModel = $subject->entrySubject;
EDIT : you should, usually, avoid eager loading (as described in miog and Burhan Çetin answers) for a HAS_MANY relation
From soju's answer above
I assume you are trying to get subject entries for subject #71, then
you should simply try the following :
$subject = Subject::model()->findByPk(71);
$subjectEntriesModel = $subject->entrySubject;
I would suggest an addition to this.
$subject = Subject::model()->with('entrySubject')->findByPk(71);
$subjectEntriesModel = $subject->entrySubject;
->with('entrySubject') was added
This will allow yii to use one query instead if it is possible.
$query = Subject::model()->with('entrySubject')->findbypk($id);
I have a code
$command = Yii::app()->db->createCommand()
->update(
'queue q',
array('i.status_id' => $status_id)
)
->join('item i', 'q.item_id = i.item_id')
->where('IN', 'queue_id', $ids);
after I call $command->buildQuery() I get an error:
CDbCommand failed to execute the SQL statement: Invalid parameter number: parameter was not defined. The SQL statement executed was: UPDATE queue q SET i.status_id=:i.status_id
The impression is that it does not see the join and where commands.
What the problem?
Your code is valid with the newest Yii version. This MySQL-specific functionality has been added as of 1.1.14: https://github.com/yiisoft/yii/commit/ed49b77ca059c0895be17df5813ee1e83d4c916d.
The where clause should be in the update() function like this
Yii::app()->db->createCommand()
->update(
'queue q',
array('i.status_id' => $status_id),array('in', 'queue_id', $ids)
);
And regarding the JOIN part there is a open bug at https://github.com/yiisoft/yii/issues/124 (Im not sure. Correct me if Im wrong). Please let me know if there is a workaround.
You have to bind the parameters:
$command = Yii::app()->db->createCommand()
->update(
'queue q',
array('i.status_id' => ':status_id'),
array('in', 'queue_id', $ids),
array(':status_id' => $status_id),
)
->join('item i', 'q.item_id = i.item_id');
Having come across this problem a few times in my projects I have come-up with the following Yii work-around using CDbCriteria which is a little hacky, but gives the security of param count matching.
When applied to your example my code would be (guessing a little bit of your structure):
$ids = array(1,2,3,4,5);
$criteria = new CDbCriteria();
$criteria->addInCondition('i.queue_id',$ids);
$sql = '
UPDATE queue q
JOIN item i
ON q.item_id = i.item_id
SET i.status_id = :status
WHERE '.$criteria->condition;
$command = Yii::app()->db->createCommand($sql);
$command->bindValue('status',$status);
$command->bindValues($criteria->params);
$rows = $command->execute();