2
votes

In CakePHP 3.x I can do this:

$Substances = TableRegistry::get('Substances');
$query = $Substances->find()->where($where_conditions)->select(['id']);
debug($query); 

This will show me the Query object.

If I want to get the SQL string I can use debug($query->sql());. This will give me the SQL string with placeholders for any parameters, e.g.

SELECT ... WHERE ... in (:c0,:c1,:c2))

When using debug($query) I can see the values for :c0, :c1, etc:

'params' => [
    ':c0' => [
        'value' => (int) 47,
        'type' => 'smallinteger',
        'placeholder' => 'c0'
    ],
    ':c1' => [
        'value' => (int) 93,
        'type' => 'smallinteger',
        'placeholder' => 'c1'
    ],
    ':c2' => [
        'value' => (int) 845,
        'type' => 'smallinteger',
        'placeholder' => 'c2'
    ],
    ':c3' => [
        'value' => (int) 354,
        'type' => 'smallinteger',
        'placeholder' => 'c3'
    ]
]

However, I cannot access them outside the debug statement. For example $query->params() or $query['params'] doesn't give me the parameters - it will error. I want to be able to pass this array to a custom function, so how can I access it?

It's strange because I can use debug($query->sql()) to get the SQL string as above, and params is just another thing in that object, but doesn't seem to be accessible.

I've read How to get params from query object in CakePHP 3 but think that's a different question as it was to do with not seeing the values in the debug statement due to the default depth that debug would provide.

The reason I want to do this is because I want to be able to do a CREATE TABLE AS query that will write the values of the SELECT statement into a new table (Important: see this link for an example of how that works in vanilla MySQL). I can't figure out how to do that with the ORM in Cake, so was planning on writing a custom function. But I need to be able to access both the SQL as well as the parameters bound so that the query can be executed in my own function.

If you know of a solution where I can use the ORM to do the CREATE TABLE AS query, I'm still interested to know about this. However I would like to know if params are accessible outside debug() as well.

2

2 Answers

3
votes

Premise: I did not actually understand why you need the params

anyway. The information you need is stored by the query ValueBinder object

so you could simply do

$params = $query->getValueBinder()->bindings();
debug($params);

but for some reason this will get you an empty array. My guess is that the query need some kind of initialization first.

in fact if you run

debug($query);
$params = $query->getValueBinder()->bindings();
debug($params);

you'll see your params. I think someone more expert than me will come and give a full explanation

edit: I noticed that debugging $query calls $query->sql() which in turns calls conection->compileQuery();

so you can do

$query->sql(); // you need this to compile the query
               // and generate the bindings
$params = $query->getValueBinder()->bindings();
2
votes

CakePHP does not provide specific methods for creating such CREATE TABLE AS statements, so you'll have to build that on your own.

Compiling a query as the one shown in your question is simple enough using the query objects sql() method, and as arilia already mentioned, you'll be able to access the parameters bound to that query after is was compiled.

Having the compiled SQL and the associated value binder, you can combine this with a custom raw query to build your CREATE TABLE AS statement. All you need to do is prepare a new statement with the compiled SQL, and attach the value binder via its own attachTo() method.

One thing you might also have to do, is to define custom aliases in your select(), as otherwise you'd end up with columns selected (and created) in the form of Substances_id.

$Substances = TableRegistry::get('Substances');

$selectQuery = $Substances
    ->find()
    ->where($where_conditions)
    ->select(['id' => 'id']); // < create aliases as required

// compile the ORM query, this will populate the value binder
$selectSql = $selectQuery->sql();

// combine table creation SQL and compiled ORM query in a single statement
$createStatement = $Substances
    ->getConnection()
    ->prepare('CREATE TABLE dynamic_table AS ' . $selectSql);

// attach the ORM querys value binder, binding all its values to the given statement
$selectQuery->getValueBinder()->attachTo($createStatement);

$success = $createStatement->execute();

This should create SQL similar to:

CREATE TABLE dynamic_table AS 
    SELECT 
      id AS id
    FROM 
      substances Substances 
    WHERE 
      ...

See also