How to write SQL query using Doctrine 2's NativeQuery with joins? - mysql

I want to write a query that involves the use of 'UNION' (which is not supported by DQL) therefore I am trying to write a MySQL query using Doctrine 2's NativeQuery. I have been using Doctrine's documentation.
So far my code consists of:
$rsm = new ResultSetMapping();
$rsm->addEntityResult('\Entities\Query', 'q');
$rsm->addFieldResult('q', 'id', 'id');
$rsm->addFieldResult('q', 'title', 'title');
$rsm->addFieldResult('q', 'deadline', 'deadline');
$query = $this->_em->createNativeQuery('SELECT query.id, query.title, query.deadline FROM query', $rsm);
$aQuery = $query->getResult();
$aQuery = $query->getResult();
foreach($aQuery as $o){
echo '<p>';
echo 'id: '.$o->getId().'<br/>';
echo 'title: '.$o->getTitle().'<br/>';
echo 'deadline: '.$o->getDeadline()->getTimestamp().'<br/>';
echo '</p>';die;
}
This returns the expected results. However, I run into problems when I try to introduce a second table/entity in the query:
$rsm->addJoinedEntityResult('\Entities\Status', 's', 'q', 'status');
$rsm->addFieldResult('s', 'id', 'id', '\Entities\Status');
Those lines throw a: Notice: Undefined index: id message.I can't figure out why this is.
The query table has a 'status_id' column, which is a foreign key referring to the 'id' column of the 'status' table. In the query entity class I have mapped the status property as follows:
/**
* #var Status
*
* #ManyToOne(targetEntity="Status")
* #JoinColumns({
* #JoinColumn(name="status_id", referencedColumnName="id")
* })
*/
private $status;
Along with relevant get and set methods.
I have spent quite some time on trying to figure this out. Appreciate it if someone could shed some light on this.

You may write your query in any repository like below then you will get expected ans. In This way you use any SQL function of MySQL
public function yourFunction()
{
$em = $this->getEntityManager()->getConnection();
$sql = "YOUR DESIRE SQL"; // like SELECT * FROM users
try {
return $em->query($sql)->fetchAll();
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}

Related

codeigniter 3 $query = $this->db->query($queri_str); return 0 result

Good morning,
I have a problem with mysql and coeigniter 3.
if I request data with
$ query = $ this-> db-> query ($ queri_str);
it does not give me results.
if I enter the query on phpmyadmin it shows me two results.
$ queri_str = 'SELECT * FROM `my_table` WHERE` id_mytable2` = "'. $ id_name. '"';
The database tables were created with mysql workbench and automatically the reference to the main table with a 1: n ratio was added
Try this query
$this->db->select('*');
$this->db->where('id', '58e5j0m5bqrs7hk8suokko28hj7ni0v6');
$result = $this->db->get('ci_sessions')->result_array();
print_r($result);
Try this solution, you want to do a normal select,I don't know the query your wrote but
public fucntion get_data($id){
$this->db->select('*');
$this->db->from('your_table');
$this->db->where('id','=' ,'$id');
$query = $this->db->get();
$data = $query->result_array();
return $data;
}
the problem is back.
I'll explain.
function myfunction($id_myname) {
$this->db->select('*');
$this->db->where('id_myname', $id_myname);
//$query = $this->db->get('my_table');
$query = $this->db->get('my_table');
//print_r($query);
//var_dump($query);
if ( !$query ){
$error = $this->db->error(); // Has keys 'code' and 'message'
}
return $query->result();
}
when I call this function an empty value comes back to me.
While if I enter the value of the query in phpmyadmin I find two values

Symfony4.1 Doctrine ManyToMany Reduce No of Queries

I'm working on a project. Entity are Blog,Category,Tags. Blog and Tags are in ManyToMany Relation. My repository query to fetch data by Tags filter is.
CODE1:
/**
* #return BlogPost[]
*/
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('p')
// ->select('p','t')
->innerJoin('p.blogTags', 't')
->where('t.slug = :val')
->setParameter('val', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
This code works fine. All the tags(No of tags in a post)are displayed correctly. But the No of DB Query is 14. Then When I uncomment select as this,
CODE2:
/**
* #return BlogPost[]
*/
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('p')
->select('p','t')
->innerJoin('p.blogTags', 't')
->where('t.slug = :val')
->setParameter('val', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
No of Query is 9. But The Tags per Post is only one(Not displaying all the tags of a single post).
To be clear info:
It displays entire list of BlogPost.
But not all Tags of a Post.
Only one Tag per Post is shown.
Question: Is code1 is correct (No of DB Query = 14) or Do I have to tweak little bit to reduce no of DB Hits. Please guide me on this.
This is the expected behaviour in both cases.
Case 1) You just select the BlogPost entities. So you tell doctrine to fetch all BlogPosts that have the BlogTag that has slug = value.
The SQL query produced returns only column values from the blog_post table and so only hydrates the BlogPost entities returned, it does not hydrate the collection of BlogTags inside each BlogPost.
When you try to access the tags of a BlogPost a new query is generated to get and hydrate its collection.
That is the reason you get more queries in this case.
Case 2) You select also the filtered BlogTag entities, and doctrine hydrates(puts) only this filtered BlogTag to each BlogPost `s collection.
When you try to access the BlogTags of a BlogPost, you get the filtered one that meets the condition in the querybuilder.
To force doctrine to "reload" the data from the database, you should refresh the blogPost entity:
$em->refresh($blogPost);
and also include refrech option on cascade operations of the relation definition:
#OneToMany(targetEntity="BlogTag", mappedBy="post", cascade={"refresh"})
References:
what cascade refresh means in doctrine 2
refresh objects: different question but same solution
Thanks #Jannes Botis for refresh. But in my case the code itself is wrong. There need a slight change in it.
BlogTags.php
/**
* #ORM\ManyToMany(targetEntity="BlogPost", mappedBy="blogTags")
*/
private $blogPosts;
BlogPost.php
/**
* #var Collection|BlogTags[]
*
* #ORM\ManyToMany(targetEntity="BlogTags", inversedBy="blogPosts", cascade={"refresh"})
* #ORM\JoinTable(
* name="fz__blog_n_tag",
* joinColumns={
* #ORM\JoinColumn(name="blog_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="tag_id", referencedColumnName="id")
* }
* )
* #ORM\OrderBy({"name": "ASC"})
*/
private $blogTags;
This created the join_table. Allready I have a join_table. Although This code is for reference to someone.
Controller.php
// This is my old Code
$bp = $em->getRepository('App:BlogPost')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);
// This is my New Code
$bp = $em->getRepository('App:BlogTags')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);
Repository.php
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('t')
->select('t','p','tx')
->innerJoin('t.blogPosts', 'p')
->innerJoin('p.blogTags', 'tx')
->where('p.isActive = :val1')
->andWhere('t.slug = :val2')
->setParameter('val1', true)
->setParameter('val2', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
I not changed my old twig file completely. As it throws error at many places. Because now i'm using tags repo instead of blog. So i modified the twig with
{% include 'frontend/page/blog_home.html.twig' with { 'bp':bp|first.blogPosts } %}
Help me on this (twig file): There is only one tag, that's why |first twig filter
Clarify me with this twig filter. Do I'm doing right. Give me suggestion to improve on it. I tried bp[0] This trows error.
Finally: By using old code in controller it returns 14 db hits. Now it returns only 8. Even there are more tags in a post (but old one returns more).

How to display a row count result in twig file

I am fetching a row count inside a repository and that query is returning an array as a result. How do i fetch the number count and display the result in my twig file?
This is the query fetching row count:
public function getTrailCount(){
$sql = "SELECT count(`id`) FROM `article` where `publish`='1' AND `catForFilter` like '%trail%' ";
$stmt = $this->connection->prepare($sql);
$stmt->execute();
$trailCount = $stmt->fetchAll(PDO::FETCH_ASSOC);
//$trailCount->getSingleScalarResult();
//echo '<pre>'; print_r($trailCount); exit;
return $trailCount;
}
In controller i am trying to fetch it like this, i know its a wrong procedure though:
foreach($trailCount as $trail){
$data['trailCount'] = $trail->count; //throughing an error
}
How to mend this code, any help is much appreciated. Thanks
In that case, using PDO::FETCH_NUM would be much simpler:
$trailCount = $stmt->fetchAll(PDO::FETCH_ASSOC);
...
foreach($trailCount as $trail){
$data['trailCount'] = $trail[0]; // Using "0" instead of named key
}
But if you still really want to use FETCH_ASSOC you would need:
$sql = "SELECT count(`id`) as cnt FROM `article ...... "
.....
foreach($trailCount as $trail){
$data['trailCount'] = $trail['cnt'];
}
Notice that I'm not using ->cnt but ['cnt'] since data returned from PDO is not object-based but array-bases instead.
Hope this helps a bit...
EDIT:
Given the lack of Twig part, I can only assume what you're trying to do:
/**
* #Route("/foo")
* #Template()
*/
public function fooAction(){
...
... The code above
...
return array('data' => $data);
}
And then, in your twig:
{{ data.trailCount }}
I got the solution with some help of Jovan Perovic, i did like this:
The query Part:
public function getTrailCount(){
$sql = "SELECT count(`id`) as cnt FROM `article` where `publish`='1' AND `catForFilter` like '%trail%' ";
$stmt = $this->connection->prepare($sql);
$stmt->execute();
$trailCount = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $trailCount[0]['cnt']; //instead of an array i passed the single value
}
in controller:
$trailCount = $clicknblog->getTrailCount();
return $this->render('AdventureBiddingBundle:Organiser:editProfileOrganiser.html.twig', array(
'trails' => $trailCount
));
in twig file i displayed the value directly:
<p>{{ trails }}</p>
Hope this helps to someone with same problem

How do I use the between() after find() [duplicate]

Is it possible to do a "BETWEEN ? AND ?" where condition LIKE in cakephp 2.5?
In cakephp 2.5 I write something like
'conditions' => ['start_date BETWEEN ? AND ?' => ['2014-01-01', '2014-12-32']]
how can I migrate that?
additionally I would write something like
'conditions' => [ '? BETWEEN start_date AND end_date'] => '2014-03-31']
Expressions
Between expression are supported out of the box, however they only support the first case without additional fiddling:
$Query = $Table
->find()
->where(function($exp) {
return $exp->between('start_date', '2014-01-01', '2014-12-32', 'date');
});
If you'd wanted to handle the second case via the between method, then you'd have to pass all values as expressions, which can easily go wrong, as they will not be subject to escaping/parameter binding in that case, you'd have to do that on your own (which is anything but recommended! See the security notes in the manual for PDO::quote()), something along the lines of:
use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\Query;
// ...
$Query = $Table
->find()
->where(function(QueryExpression $exp, Query $query) {
return $exp->between(
$query->newExpr(
$query->connection()->driver()->quote(
'2014-03-31',
\PDO::PARAM_STR
)
),
new IdentifierExpression('start_date'),
new IdentifierExpression('end_date')
);
});
That might feel a little inconvenient for such a basic SQL expression that is supported by all SQL dialects that CakePHP ships with, so you may have a reason here to use a raw SQL snippet with value bindig instead.
It should be noted however that expressions are often the better choice when it comes to for example cross dialect support, as they can be (more or less) easily transformed at compile time, see the implementations of SqlDialectTrait::_expressionTranslators(). Also expressions usually support automatic identifier quoting.
Value binding
Via manual value binding you can pretty much create anything you like. It should however be noted that whenever possible, you should use expressions instead, as they are easier to port, which happens out of the box for quite a few expressions already.
$Query = $Table
->find()
->where([
'start_date BETWEEN :start AND :end'
])
->bind(':start', '2014-01-01', 'date')
->bind(':end', '2014-12-31', 'date');
That way the second case can also be solved very easily, like:
$Query = $Table
->find()
->where([
':date BETWEEN start_date AND end_date'
])
->bind(':date', '2014-03-31', 'date');
A mixture of both (safest and most compatible approach)
It's also possible to mix both, ie use an expression that makes use of custom bindings, something along the lines of this:
use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\Query;
// ...
$Query = $Table
->find()
->where(function(QueryExpression $exp, Query $query) {
return $exp->between(
$query->newExpr(':date'),
new IdentifierExpression('start_date'),
new IdentifierExpression('end_date')
);
})
->bind(':date', '2014-03-31', 'date');
That way you could handle the second case using possibly portable expressions, and don't have to worry about quoting/escaping input data and identifiers manually.
Regular comparison using array syntax
All that being said, in the end BETWEEN is just the same as using two separate simple conditions like this:
$Query = $Table
->find()
->where([
'start_date >=' => '2014-01-01',
'start_date <=' => '2014-12-32',
]);
$Query = $Table
->find()
->where([
'start_date >=' => '2014-03-31',
'end_date <=' => '2014-03-31',
]);
But don't be mad, if you read all the way down to here, at least you learned something about the ins and outs of the query builder.
See also
Cookbook > Database Access & ORM > Query Builder > Advanced Conditions
API > \Cake\Database\Query::bind()
Currently there seems to be only two options. The core now supports this out of the box, the following is just kept for reference.
Value binding (via the database query builder)
For now the ORM query builder (Cake\ORM\Query), the one that is being retrived when invoking for example find() on a table object, doesn't support value binding
https://github.com/cakephp/cakephp/issues/4926
So, for being able to use bindings you'd have to use the underlying database query builder (Cake\Database\Query), which can for example be retrived via Connection::newQuery().
Here's an example:
$conn = ConnectionManager::get('default');
$Query = $conn->newQuery();
$Query
->select('*')
->from('table_name')
->where([
'start_date BETWEEN :start AND :end'
])
->bind(':start', new \DateTime('2014-01-01'), 'date')
->bind(':end', new \DateTime('2014-12-31'), 'date');
debug($Query->execute()->fetchAll());
This would result in a query similar to this
SELECT
*
FROM
table_name
WHERE
start_date BETWEEN '2014-01-01' AND '2014-12-31'
A custom expression class
Another option would be a custom expression class that generates appropriate SQL snippets. Here's an example.
Column names should be wrapped into identifier expression objects in order to them be auto quoted (in case auto quoting is enabled), the key > value array syntax is for binding values, where the array key is the actual value, and the array value is the datatype.
Please note that it's not safe to directly pass user input for column names, as they are not being escaped! Use a whitelist or similar to make sure the column name is safe to use!
Field between values
use App\Database\Expression\BetweenComparison;
use Cake\Database\Expression\IdentifierExpression;
// ...
$between = new BetweenComparison(
new IdentifierExpression('created'),
['2014-01-01' => 'date'],
['2014-12-31' => 'date']
);
$TableName = TableRegistry::get('TableName');
$Query = $TableName
->find()
->where($between);
debug($Query->execute()->fetchAll());
This would generate a query similar to the one above.
Value between fields
use App\Database\Expression\BetweenComparison;
use Cake\Database\Expression\IdentifierExpression;
// ...
$between = new BetweenComparison(
['2014-03-31' => 'date'],
new IdentifierExpression('start_date'),
new IdentifierExpression('end_date')
);
$TableName = TableRegistry::get('TableName');
$Query = $TableName
->find()
->where($between);
debug($Query->execute()->fetchAll());
This on the other hand would result in a query similar to this
SELECT
*
FROM
table_name
WHERE
'2014-03-31' BETWEEN start_date AND end_date
The expression class
namespace App\Database\Expression;
use Cake\Database\ExpressionInterface;
use Cake\Database\ValueBinder;
class BetweenComparison implements ExpressionInterface {
protected $_field;
protected $_valueA;
protected $_valueB;
public function __construct($field, $valueA, $valueB) {
$this->_field = $field;
$this->_valueA = $valueA;
$this->_valueB = $valueB;
}
public function sql(ValueBinder $generator) {
$field = $this->_compilePart($this->_field, $generator);
$valueA = $this->_compilePart($this->_valueA, $generator);
$valueB = $this->_compilePart($this->_valueB, $generator);
return sprintf('%s BETWEEN %s AND %s', $field, $valueA, $valueB);
}
public function traverse(callable $callable) {
$this->_traversePart($this->_field, $callable);
$this->_traversePart($this->_valueA, $callable);
$this->_traversePart($this->_valueB, $callable);
}
protected function _bindValue($value, $generator, $type) {
$placeholder = $generator->placeholder('c');
$generator->bind($placeholder, $value, $type);
return $placeholder;
}
protected function _compilePart($value, $generator) {
if ($value instanceof ExpressionInterface) {
return $value->sql($generator);
} else if(is_array($value)) {
return $this->_bindValue(key($value), $generator, current($value));
}
return $value;
}
protected function _traversePart($value, callable $callable) {
if ($value instanceof ExpressionInterface) {
$callable($value);
$value->traverse($callable);
}
}
}
You can use one of following 2 methods.
Method 1 :
$start_date = '2014-01-01 00:00:00';
$end_date = '2014-12-31 23:59:59';
$query = $this->Table->find('all')
->where(function ($exp, $q) use($start_date,$end_date) {
return $exp->between('start_date', $start_date, $end_date);
});
$result = $query->toArray();
Method 2:
$start_date = '2014-01-01 00:00:00';
$end_date = '2014-12-31 23:59:59';
$query = $this->Table->find('all')
->where([
'start_date BETWEEN :start AND :end'
])
->bind(':start', new \DateTime($start_date), 'datetime')
->bind(':end', new \DateTime($end_date), 'datetime');
$result = $query->toArray();
I'm using it like this
$this->Table->find()->where(['data_inicio BETWEEN '.'\''.$data_inicio.'\''.' AND .'\''.$data_final.'\''.' ']);
Hello guys please use this query to get data on the basis of range of value
$query = $this->Leads->find('all',
array('conditions'=>array('postcode BETWEEN '.$postcodeFrom.' and'.$postcodeTo.''), 'recursive'=>-1));
debug($query);
print_r($query->toArray());

Zend Framework - join query

I build a function
public function getBannedByLogin($commentId)
{
$sql = $this->getDbAdapter()->select()
->from(array('comments' => 'comments'), array())
->join(array('users' => 'qengine_users'),
'comments.bannedBy = users.userId',
array())
->where('commentId = ?', $commentId)
;
$row = $this->fetchRow($sql);
return $row['login'];
}
And there are problems, that does'nt work! :D
Let's I explain you. Column 'bannedBy' from comments returns id of user, who give a ban. I need to join this with table users to load a login field. Where i have mistakes?
I assume the code works in the sense of not throwing an exception. If so, your code is OK, you just specifically tell Zend_Db not to select any columns.
public function getBannedByLogin($commentId)
{
$sql = $this->getDbAdapter()->select()
->from(array('comments' => 'comments'))
->join(array('users' => 'qengine_users'),
'comments.bannedBy = users.userId')
->where('commentId = ?', $commentId)
;
$row = $this->fetchRow($sql);
return $row['login'];
}
The last argument to from() and join() functions is an array of columns you wish to select. If you pass in an empty array, no columns are selected. No argument = select everything. You can, of course, specify only the columns you need too.