I have a controller action that uses a sql query:
$tag = $this->params['tag'];
$this->set('projects', $this->Project->query('SELECT * FROM projects INNER JOIN projects_tags ON projects.id = projects_tags.project_id INNER JOIN tags on projects_tags.tag_id = tags.id WHERE tags.tag LIKE $tag'));
As you can see at the end I want to use a where clause with the $tag variable but I'm not sure how the syntax would go. As I'm getting the error
Unknown column '$tag' in 'where clause'
Can someone steer me in the right direction?
Ta,
Jonesy
I would strongly advise you to use the Cake ORM instead of raw queries, especially if you're going to plug URL parameters into it. Conditions on HABTM tables can be tricky, but you can build your joins using Cake's ORM syntax as well!
Read the manual, section 3.7.6.9 Joining tables.
Should you want to use Cake's ORM, the following code should provide results equivalent to your raw SQL query:
$this->loadModel('ProjectsTag'); // Load the joining table as pseudo-model
// Define temporary belongsTo relationships between the pseudo-model and the two real models
$this->ProjectsTag->bindModel(array(
'belongsTo' => array('Project','Tag')
));
// Retrieve all the join-table records with matching Tag.tag values
$result_set = $this->ProjectsTag->find('all',array(
'conditions' => array('Tag.tag LIKE' => "%{$tag}%")
));
// Extract the associated Project records from the result-set
$projects = Set::extract('/Project', $result_set);
// Make the set of Project records available to the view
$this->set(compact('projects'));
in php there's a difference between single and double quotes... basically, single quotes dont evaluate the variables... use double quotes instead
And i think that LIKE will need also single quotes.. i'm not really sure
"SELECT * FROM projects INNER JOIN projects_tags ON projects.id = projects_tags.project_id INNER JOIN tags on projects_tags.tag_id = tags.id WHERE tags.tag LIKE '$tag'"
i know.. i know.. people will start talkin' about sql injection.. and the need to scape the caracters... that's another question =)
good luck!
I would at least consider using the cakephp sanitize functions on your tag strings if they are user sourced. See http://book.cakephp.org/view/1183/Data-Sanitization or if using mysql as the db at least consider using http://www.php.net/manual/en/function.mysql-escape-string.php or do something to filter your user input. But the best thing is to make use of the CakePHP orm stuff.
Modify your query as:
$this->set('projects',
$this->Project->query("SELECT * FROM projects
INNER JOIN projects_tags
ON projects.id = projects_tags.project_id
INNER JOIN tags ON projects_tags.tag_id = tags.id
WHERE tags.tag LIKE '" . $tag . "'") //problem was here
);
and it will work.
Related
If i give hard coded value inside query it works, but not in case of sub query or column given.
Here is small example of issue i am facing :
Following both query is a type of sub query, like its part of another query, so don't think that where is table 'm' and something else, as it is working already.
So, my query like :
1)
SELECT GROUP_CONCAT(CONCAT(a_u.first_name,' ', a_u.last_name)) AS associated_admin_u
FROM users a_u
WHERE a_u.id IN(m.associated_admin)
GROUP
BY m.id
And m.associated_admin will return a quoted string like '1,10' so this will not work because of its a string.
2)
SELECT GROUP_CONCAT(CONCAT(a_u.first_name,' ', a_u.last_name)) AS associated_admin_u
FROM users a_u
WHERE a_u.id IN(1,10)
GROUP
BY m.id
If i write hard code like 1,10 it works, because it is not a string
So first one is not works because that query is part of another query as a sub query.
And i am sure this question couldn't be duplicate as i am facing it like in this way so any help would be appreciate, thanks reader!
Based on your comments, you need something like:
SELECT GROUP_CONCAT(CONCAT(a_u.first_name,' ', a_u.last_name)) AS associated_admin_u
FROM users a_u
WHERE FIND_IN_SET(a_u.id, TRIM(BOTH '\'' FROM m.associated_admin))
GROUP
BY m.id
This will first trim the quotes from m.associated_admin and then use FIND_IN_SET instead of IN so that you can use a string with comma-separated values.
You can just create a subquery in IN for example:
SELECT group_concat(CONCAT(a_u.first_name,' ', a_u.last_name)) AS associated_admin_u
FROM users a_u WHERE a_u.id IN(
SELECT id FROM mytable WHERE id IN(1,10)
) GROUP BY m.id
I've this code:
public function getAllAccess(){
$this->db->select('accesscode');
$this->db->where(array('chain_code' => '123');
$this->db->order_by('dateandtime', 'desc');
$this->db->limit($this->config->item('access_limit'));
return $this->db->get('accesstable')->result();
}
I need to join it with another table (codenamed table), I've to tell it this. Not really a literal query but what I want to achieve:
SELECT * accesscode, dateandtime FROM access table WHERE chain_code = '123' AND codenames.accselect_lista != 0
So basically accesstable has a column code which is a number, let us say 33, this number is also present in the codenames table; in this last table there is a field accselect_lista.
So I have to select only the accselect_lista != 0 and from there get the corrisponding accesstable rows where codenames are the ones selected in the codenames.
Looking for this?
SELECT *
FROM access_table a INNER JOIN codenames c ON
a.chain_code = c.chain_code
WHERE a.chain_code = '123' AND
c.accselect_lista != 0
It will bring up all columns from both tables for the specified criteria. The table and column names need to be exact, obviously.
Good start! But I think you might be getting a few techniques mixed up here.
Firstly, there are two main ways to run multiple where queries. You can use an associative array (like you've started to do there).
$this->db->where(array('accesstable.chain_code' => '123', 'codenames.accselect_lista !=' => 0));
Note that I've appended the table name to each column. Also notice that you can add alternative operators if you include them in the same block as the column name.
Alternatively you can give each their own line. I prefer this method because I think its a bit easier to read. Both will accomplish the same thing.
$this->db->where('accesstable.chain_code', '123');
$this->db->where('codenames.accselect_lista !=', 0);
Active record will format the query with 'and' etc on its own.
The easiest way to add the join is to use from with join.
$this->db->from('accesstable');
$this->db->join('codenames', 'codenames.accselect_lista = accesstable.code');
When using from, you don't need to include the table name in get, so to run the query you can now just use something like:
$query = $this->db->get();
return $query->result();
Check out Codeigniter's Active Record documentation if you haven't already, it goes into a lot more detail with lots of examples.
I have several queries that have a same filter.
For example:
Filter:
$criteria = "AND id_student = 1 AND id_major = 2 ";
Query 1:
$query1 = "SELECT * FROM student AS t0
LEFT JOIN major AS t1 ON t0.id_major = t1.id
WHERE 1=1 ";
Query 2:
$query2 = "SELECT * FROM lecturer AS t0
LEFT JOIN major AS t1 ON t0.id_major = t1.id
LEFT JOIN student AS t2 ON t2.id_major = t1.id
WHERE 1=1 "
It will be run as
$query1.$criteria
and
$query2.$criteria
As you can see, this will yield a query error, since id_major is ambiguous.
My problem is, that this query is very big in number, so I prefer not to hard-code and change the $criteria and $query individually.
I've tried to change the $criteria into
$criteria = "AND student.id_student = 1 AND major.id_major = 2 ";
But it does no better, it gave me errors of "did you mean t0", "did you mean t2", etc.
So, can I do something like, get an alias of a table? So I can put it in the $criteria
$criteria = "AND (get-alias-code).id_student = 1 AND (get-alias-code).id_major = 2 ";
Or any other method?
I am not the primary developer of this program, just a bug-fixer so I cannot change much of this program (because those queries may affect another page).
Use the aliases you have for your tables already, and set the criteria to be
$criteria = "AND t0.`id_student`='1' AND t1.`id_major`='2' ";
I strongly recommend using backtics and ticks as well, it helps avoiding funny mistakes with table or field names like 'order' :)
Then all you need to ensure is that the table that needs the criteria applied gets the correct alias every time
Edit:
Hm...alternatively, you can remove aliasing from all queries.
I honestly don't think that there's any mysql feature to get the aliases for tables. You could run a string analyser code snippet to find them (knowing all table names it may not even be hard to do) but that looks like a very ugly solution IMHO.
I don't know away to handle it on sql but you can handle it with on array for example
$alias[1]["student"] = "t0"; // it means on query 1 your alias for student is t0
now you can use this variable on your filter
$criteria = "AND ".$alias[1]["student"].".id_student = 1 AND ".$alias[1]["major"].".id_major = 2 ";
I think it's one of fast ways to change your filter without changing all of queries.
hope It helps
Wrap the whole thing in an outer query like this:
"SELECT * FROM (".$query1.") a ".$criteria;
Assuming of course that the original query didn't create multiple columns with the same name, you can now reference them without the alias.
I'm trying to build a query with the doctrine query builder which joins a non related table like this:
$query = $this->createQueryBuilder('gpr')
->select('gpr, p')
->innerJoin('TPost', 'p')
->where('gpr.contentId = p.contentId')
But this doesn't work. I still get an error:
Error: Identification Variable TPost used in join path expression but was not defined before.
I searched for this error message and everybody answered to use the table alias + attribute like p.someAttribute. But the table I want to join isn't related in the table I start my select from.
As a normal mysql query i would write it like this:
SELECT * FROM t_group_publication_rel gpr
INNER JOIN t_post p
WHERE gpr.content_id = p.content_id
Any ideas what i'm doing wrong?
Today I was working on similar task and remembered that I opened this issue. I don't know since which doctrine version it's working but right now you can easily join the child classes in inheritance mapping. So a query like this is working without any problem:
$query = $this->createQueryBuilder('c')
->select('c')
->leftJoin('MyBundleName:ChildOne', 'co', 'WITH', 'co.id = c.id')
->leftJoin('MyBundleName:ChildTwo', 'ct', 'WITH', 'ct.id = c.id')
->orderBy('c.createdAt', 'DESC')
->where('co.group = :group OR ct.group = :group')
->setParameter('group', $group)
->setMaxResults(20);
I start the query in my parent class which is using inheritance mapping. In my previous post it was a different starting point but the same issue if I remember right.
Because it was a big problem when I started this issue I think it could be also interesting for other people which don't know about it.
Joins between entities without associations were not possible until version 2.4, where you can generate an arbitrary join with the following syntax:
$query = $em->createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email');
Reference: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html
Want to improve this post? Add citations from reputable sources by editing the post. Posts with unsourced content may be edited or deleted.
$dql = "SELECT
a, md.fisrtName , md.LastName, mj
FROM MembersBundle:Memberdata md
INNER JOIN MembersBundle:Address a WITH md = a.empID
INNER JOIN MembersBundle:Memberjob mj WITH md = mj.memberData
...
WHERE
a.dateOfChange IS NULL
AND WHERE
md.someField = 'SomeValue'";
return $em->createQuery( $dql )->getResult();
A DQL join only works if an association is defined in your mapping. In your case, I'd say it's much easier to do a native query and use ResultSetMapping to populate your objects.
Query in CodeIgniter:
$this->db->select('comments.created_at, comments.section_id, comments.submittedby_id, users.username, comments.text, sections.name');
$this->db->order_by('comments.created_at', 'desc');
$this->db->where('comments.submittedby_id', 'users.user_id');
$this->db->where('comments.section_id', 'sections.id');
$query = $this->db->get(array('comments', 'users', 'sections'),10);
Produce SQL Request:
SELECT pdb_comments.created_at,
pdb_comments.section_id,
pdb_comments.submittedby_id,
pdb_users.username,
pdb_comments.text,
pdb_sections.name FROM
(pdb_comments, pdb_users,
pdb_sections) WHERE
pdb_comments.submittedby_id =
'users.user_id' AND
pdb_comments.section_id =
'sections.id' ORDER BY
pdb_comments.created_at desc LIMIT
10
The issue is that the database prefix (pdb_) does not get added in the WHERE clause. I can manually insert the prefix by appending $this->db->dbprefix, but this doesn't fix the main problem.
Quotes:
`pdb_comments`.`submittedby_id` = 'pdb_users.user_id'
The quotes on the right side are not accurate, and generate 0 results for me. Is there any way to make CodeIgniter recognize the second half of the where clause as a piece of my table; thereby adding the database prefix, and properly placing the quotes by avoiding two joins? Is there another way to do this? Thanks in advance.
--
Jon
Use:
$this->db->select('comments.created_at, comments.section_id, comments.submittedby_id, users.username, comments.text, sections.name');
$this->db->from('comments');
$this->db->join('users', 'comments.submittedby_id=users.user_id');
$this->db->join('sections', 'comments.section_id=sections.id');
$this->db->order_by('comments.created_at', 'desc');
$query = $this->db->get();
instead.
Possibly a dumb question, but why don't you just write the SQL directly? The interface doesn't look like it's giving you anything but clutter.