Is it possible to get the table from a PDOStatement object? - php

Say I have a PDOStatement object generated via PDO->query($query), is it possible to get the table it was executed on?
Something like this:
<?php
$statement = $pdo->query('SELECT * FROM `foo`;');
echo $statement->xyz;
// foo
I'm fully aware you can use $query->getColumnMeta(0)['table'] to do it, but as mentioned by the docs, it's not very safe. This needs to work across all PDO drivers.

You can retreive the name of the table using the PDOStatement that retrieves an associative array. The value ['name'] is the name of the table.
$select = $conn_pdo->query('SELECT * FROM foo');
$meta = $select->getColumnMeta(0);
echo "Name of table: ".$meta['table'];

You can check the query string:
$statement->queryString
Remember that a SQL query may reference multiple tables, or no tables, so there is no "table it was executed on". You could use regular expressions on the query string to find the first table referenced for standard queries (SELECT, INSERT, UPDATE, DELETE), but that may not be perfect.
Alternatively, use models to access your data, and you'll always know which tables are accessed by the model definition.

Can you not simply store the string used in the query and use simple string manipulations to access the table?

Related

How to echo separately data from mySQL avoiding any loops?

In mySQL I have a table with 8 rows, id and status.
$make=mysql_query("SELECT id, status FROM data order by id");
My question is how can I avoid using the foreach or any loop to echo the data, but instead to ave something like
<?php echo $row['status with the id 5']; ?>
and in another place of the page to echo the status with id 8 ?
You can utilize the following pattern:
$contents = [];
foreach($results as $result) {
$contents[$result->id] = $result;
}
$results contains the MySQL result set. $contents will be the associative array. It would be more comfortable if you swapped that to a function or class which works for all the tables you want to access applying this pattern. Depending on which database class you use, it might be necessary to cast the key to an integer, otherwise there will be problems accessing the index if it is passed as a string.
Note that you furthermore should migrate your code to MySQLi or PDO first.
If your table is likely to become very big, you should not implement this. Instead, it would be better if you checked in the first place which entries will be needed and load those explicitly with an IN() query.

Count on Zend_Db_Select

Say I have a random zend_db_select object.
How can I perform a count on that object, so I know the amount of items that meet the query.
I tried the following:
$data->TotalRecords = $select->columns(new Zend_Db_Expr('COUNT(*)'))->query()->fetch();
But this gives me the following error:
Message: No table has been specifiedfor the FROM clause
The query by itself works fine and returns a resultset.
There's a couple of ways of specifying the columns to fetch in a Zend_Db_Select. The following two product the same SQL
$select = $db->select()
->from('myTable', array())
->columns(array('TotalRecords' => new Zend_Db_Expr('COUNT(*)')));
$select = $db->select()
->from('myTable', array('TotalRecords' => new Zend_Db_Expr('COUNT(*)')));
The from method takes a first argument, the table name, and a second argument, an array of columns to fetch. If you're using an expression, you can specify a 'key' => Expr.
It's really easy to convert a Zend_Db_Select into a SQL string for debugging or use with other functions.
echo $select; // prints SELECT COUNT(*) AS `TotalRecords` FROM `myTable`
This uses a toString method, which is called automatically by Zend_Db fetch methods:
$total = $db->fetchOne($select);
echo $total; //prints the number of rows matching the query
Where $db is an instance of Zend_Db.
Use $select->__toString() method to output your generated query and see what is wrong with it.
If u dont have a from clause in your query add From() method to your select object.
If you use Zend_Db_Select, you have to call the from method to set the table name. With a Zend_Db_Table_Select, the table is passed in the constructor, so you don't need to call from.
$select = $db->select();
$select->from(
'table_name',
array('cnt' => 'count(1)')
);
I just encountered the same issue and found out what is going wrong
the Zend_Db_Select::columns functions expects an Array instead of a Object or String (when the first parameter is an String or Object it'll probably use this as main table for the columns you give but Im not sure about that.).
Changing your code to
$data->TotalRecords = $select->columns(array(new Zend_Db_Expr('COUNT(*)')))->query()->fetch();
Will fix your issue

What does this doctrine query do?

$this->facebook_applications = Doctrine::getTable('FacebookApplication')
->createQuery('a')
->execute();
I don't understand how this works at all. Why is the query just 'a' and why does that seem to get a list of the applications?
The static method Doctrine::getTable() gets an object that represents the FacebookApplication table.
That object has a method called createQuery(), which creates a Doctrine_Query object for querying that table. The argment ('a'), specifies an alias for the table in the query.
So essentially Doctrine::getTable('FacebookApplication')->createQuery('a') creates a query that translates to SQL like:
SELECT * FROM FacebookApplication as a
Which, naturally, returns all rows from that table.
You can see it by using :
$this->facebook_applications->getSqlQuery()

Custom object based on database table

Suppose that I have a database query which looks like below -
select name, gender, birthday from person where person_id = 1;
When this query is executed, some records from the database is returned. I want to execute this query inside a function, then make a custom object which will contain the exact attributes as the column names, with the corresponding values. As an example, suppose that the object is X. So it will have three attributes which are X->name, X->gender and X->birthday, with the corresponding values from the records.
As a second example, consider the following query -
select test1, test2, test3 from test where test_id = 1;
I want to execute this query inside the same function and using the same method, then generate a custom-object Y which will contain the attributes Y->test1, Y->test2 and Y->test3.
Is it doable in PHP? If so, then how?
Edit: By custom object, I meant any kind of object. It does not need to be an instance of a specific class or something like that. I just want to return an object from that function so that I can populate the appropriate classes from the returned object's property values.
foreach ($result as $key => $value)
{
$your_object->{$key} = $value;
}
EDIT: this answer assumed Archangel used MySQL, which it turns out is not the case. Sorry for the mistake! I'll leave my answer here in case any MySQL folks come across it, as well as to preserve the comments if necessary.
If you don't care about custom classes and only want your custom attribute names in the row objects, this is all you need:
$row_object = mysql_fetch_object($result);
Otherwise, you'll need to write your own class yourself (I'm not sure whether a constructor is needed or the function populates your object with data automatically):
class X
{
public $name;
public $gender;
public $birthday;
}
Then call the function like this:
$row_object = mysql_fetch_object($result, 'X');
There's no built-in way that I know of to generate classes on-the-fly for you; you'll have to write your own classes.

"SELECT * FROM users WHERE id IN ( )" == FAIL

I have a function that I use called sqlf(), it emulates prepared statements. For instance I can do things like:
$sql = sqlf("SELECT * FROM Users WHERE name= :1 AND email= :2",'Big "John"','bj#example.com') ;
For various reasons, I cannot use prepared statements, but I would like to emulate them. The problem that I run into is with queries like
$sql = sqlf("SELECT * FROM Users WHERE id IN (:1)",array(1,2,3) );
My code works, but it fails with empty arrays, e.g. the following throws a mysql error:
SELECT * FROM Users WHERE id IN ();
Does anyone have any suggestions? How should I translate and empty array into sql that can be injected into an IN clause? Substituting NULL will not work.
Null is the only value that you can guarantee is not in the set. How come it is not an option? Anything else can be seen as part of the potential set, they are all values.
I would say that passing an empty array as argument for an IN() clause is an error. You have control over the syntax of the query when calling this function, so you should also be responsible for the inputs. I suggest checking for emptiness of the argument before calling the function.
Is there a possibility that you could detect empty arrays withing sqlf and change the SQL to not have the IN clause?
Alteratively, you could postprocess the SQL before passing it to the "real" SQL executor so that "IN ()" sections are removed although you'd have to do all sorts of trickery to see what other elements had to be removed so that:
SELECT * FROM Users WHERE id IN ();
SELECT * FROM Users WHERE a = 7 AND id IN ();
SELECT * FROM Users WHERE id IN () OR a = 9;
would become:
SELECT * FROM Users;
SELECT * FROM Users WHERE a = 7;
SELECT * FROM Users WHERE a = 9;
That could get tricky depending on the complexity of your SQL - you'd basically need a full SQL language interpreter.
If your prepare-like function simply replaces :1 with the equivalent argument, you might try having your query contain something like (':1'), so that if :1 is empty, it resolves to (''), which will not cause a parse error (however it may cause undesirable behavior, if that field can have blank values -- although if it's an int, this isn't a problem). It's not a very clean solution, however, and you're better off detecting whether the array is empty and simply using an alternate version of the query that lacks the "IN (:1)" component. (If that's the only logic in the WHERE clause, then presumably you don't want to select everything, so you would simply not execute the query.)
I would use zero, assuming your "id" column is a pseudokey that is assigned numbers automatically.
As far as I know, automatic key generators in most brands of database begin at 1. This is a convention, not a requirement (auto-numbered fields are not defined in standard SQL). But this convention is common enough that you can probably rely on it.
Since zero probably never appears in your "id" column, you can use this value in the IN() predicate when your input array is empty, and it'll never match.
The only way I can think to do it would be to make your sqlf() function scan to see if a particular substitution comes soon after an "IN (" and then if the passed variable is an empty array, put in something which you know for certain won't be in that column: "m,znmzcb~~1", for example. It's a hack, for sure but it would work.
If you wanted to take it even further, could you change your function so that there are different types of substitutions? It looks like your function scans for a colon followed by a number. Why not add another type, like an # followed by a number, which will be smart to empty arrays (this saves you from having to scan and guess if the variable is supposed to be an array).

Categories