How can I prevent CakePHP from escaping data on a save? - php

I am doing some PostGIS work on a CakePHP application.
Because I've been working with some database functions, I've done raw $this->query() calls to do inserts of data. I'm at a point where I need to get the ID of the result of an insert query, but $this->query() returns an empty array.
Here is the query I'm using for inserts:
INSERT INTO locations (title,company_id,state_id,poly,point)
VALUES ('$title',$company_id,$state_id,ST_GeomFromText('$geom',4269),$point);
The problem is Cake is trying to insert 'ST_GeomFromText('$geom',4269)' as a string, and I cannot figure out how to get it to remove the quotes in the SQL insert statement it prepares via $this->Location->save();.
Any suggestions?
Edit:
Before I was doing $this->query(); and my SQL works fine. That's not the issue.
Now when I do:
$this->Location->create();
$this->Location->set('poly',$poly);
... Set all the rest of the model fields ...
$this->Location->save();
The SQL that gets produced looks like this:
INSERT INTO locations (title,company_id,state_id,poly,point)
VALUES ('Some Title',5,23,'ST_GeomFromText('$geom',4269)',POINT(-72.342,102.23455);
Because ST_GeomFromText gets 'quoted', PostgreSQL throws an error and doesn't insert the geometry.

The solution to my problem turned out to NOT be a CakePHP based one, but a PostgreSQL one!
By appending "RETURNING id" to the query like this:
INSERT INTO locations (title,company_id,state_id,poly,point)
VALUES ('$title',$company_id,$state_id,ST_GeomFromText('$geom',4269),$point) RETURNING id;
The query no longer returns an empty array, and now returns the ID of the row it just created!

Via $this->ModelName->getLastInsertID(); you are able to get the last inserted ID. So, after a save you can call this code to get its id.

Related

Clarifications about Multi Queries

I'm trying to execute 2 queries, but whenever I follow the guides online about multi queries, its not doing either of the queries.
What I'm trying to do on the first query is to INSERT or ADD whatever the user inputs on $HISTORY on the record that's currently on colHistory; I.E.:
Current data on colHistory:
A
User inputs 'B' on $HISTORY, the syntax should add 'B' on the 'A' that's currently on record, or 'AB'. Then use the second query to UPDATE all the other records or columns on this particular row.
Here's the code (Please note that the '...' means more code that's unnecessary):
$query = INSERT INTO tbInventory SET colHistory='$HISTORY' WHERE colSerno='$SERIALNUM';";
$query .= "UPDATE tbInventory SET
colImage='$IMAGE',
colSerno='$SERIALNUM',
...
...
colHistory=''
WHERE colSerno='$SERIALNUM'";
mysqli_multi_query($con,$query);
Please note where I declared colHistory as '' before I insert the data from the form. I'm not sure if I'm doing it right on this part. Is there anything that I'm missing?
*Edit:
I have already tried executing the queries one by one as:
mysqli_query($con,"INSERT INTO tbInventory SET colHistory='$HISTORY' ");
mysqli_query($con,"UPDATE tbInventory SET
...
...
colHistory=''
WHERE colSerno='$SERIALNUM'";
Yet it doesn't seem to work either, the whole thing gets ignored.
(** I have a script below the code block above where I could print the results already, and it does run)
Well I can tell you why your first query is broken.
INSERT INTO tbInventory SET colHistory='$HISTORY'
This query is using UPDATE syntax but you are telling the query processor to expect INSERT INTO syntax. So the query is unable to execute.
Decide whether you are needing to UPDATE an existing record or INSERT a new one and alter your query to reflect that. (Change INSERT INTO to UPDATE or change "Set colHistory = '$ History'" to "Values ('$ History', 'col2Val', and so on..")
As for your second query, the syntax looks alright from what you have shown but since you didn't post the entire query its hard to say what is happening there. If you can show more of that query I can update this response.
Here's a good SO question on inserts vs updates.
What are differences between INSERT and UPDATE in MySQL?
I ended up dropping the multi-query method and I did got my intended results by somehow cheating:
I assigned the old data or the data that's currently on the colHistory cell, displayed it, but I disabled the textarea. I then created one more hidden textbox in the script with the old data in it and then hid it to the users view.
I then concatenated both textareas with the new one that I've created that the user could modify, emulating the results wanted.

Codeigniter db class truncating text on insert

I have a codeigniter controller that receives json content from an API and inserts it to a mysql longtext field unchanged.
Everything's been running smoothly for a while, but recently i've been noticing some of the content is being truncated. Here are two examples.
The table being inserted has three fields
id int
data longtext
date_added datetime
I am receiving the data and directly adding it to the database using the active record insert function. It looks like that
$this->db->insert('received', array('data' => $data, 'date_added' => date('Y-m-d H:i:s')));
Using the profiler, i monitored the queries and found two faulty ones:
INSERT INTO `received` (`data`, `date_added`) VALUES ('{\"status\":{\"lastMaintenanceAt\":0000,\"code\":304,\"period\":900},\"items\":[{\"permalinkUrl\":\"http://example.com\",\"updated\":0000,\"postedTime\":0000,\"summary\":\"Man\'s guarantees are like his words.\",\"title\":\"By: John\",\"content\":\"<p>Man’s guarantees are like his words.</p>\",\"id\":\"http://example.com\",\"actor\":{\"permalinkUrl\":\"\",\"displayName\":\"John\"}}],\"title\":\"Comments on: Banker refuses to give ‘Written Guarantee’\"}', '2012-04-08 00:28:29')
and
INSERT INTO `received` (`data`, `date_added`) VALUES ('{\"status\":{\"code\":304,\"period\":900,\"feed\":\"http://example.com\"},\"items\":[],\"title\":\"Comments on: Making her cry…\"}', '2012-04-08 00:49:35')
The queries seems alright. But only part of the JSON is making it to the table in the first case, it is truncated after "[...] refuses to give" and in the second after making her cry.
The queries are not returning an error and the date is being inserted properly. Moreover, if i copy the queries and execute them in a mysql command prompt, they will insert the full text.
These queries a one a few of hundreds of other successful ones.
Any idea what might be the problem?
thank you
All of the faulty queries share one thing in common: they've got "special characters" which MS Word inserts, e.g. ‘, ’ and …. If you can convert / remove these, then your problems should go away.
Update
For a CodeIgniter solution to this, you could load the Text Helper library and then use the ascii_to_entities function on any strings, e.g.
$this->load->helper('text');
$desc = ascii_to_entities($desc);

How to insert to a database and fetch the inserted record in one query?

Let's say you're making a CodeIgniter model's function which creates a user.
function create($data){
$sql = "INSERT INTO people (fname,lname,age,location) VALUES(?,?,?,?)";
$this->db->query($sql, $data);
}
function get($id){
$sql = "SELECT * FROM people WHERE id = ? LIMIT 1";
return $this->db->query($sql, array($id));
}
You want create() to create the user but return the same thing as get() as an associative array containing the user id and the other inserted info.
Obviously this could be done by making an array containing the $data elements. But think about a lot of fields.. it makes the code ugly and unsecure to modify because you'll deal with numerical arrays not associative.
Is there any way for the MySQL to return the inserted record with the id without having to query again using SELECT for better performance and neat looking code?
You can't insert and return the row in the same query with MySQL.
But you can get the last inserted id (I'm guessing the id field is autogenerated) with ->insert_id. You can use that to call get from your create function.
you could wrap the whole thing (INSERT plus SELECT) in a procedure, which would save you a callout to the database, and at the same time ensuring you read exactly what exists in the database (for e.g. autoincrement columns or defaulted values). But procedures aren't everybody's cup of tea.
In MySQL, no, there's no way to do what you want. Some databases (at least PostgreSQL) support a INSERT ... RETURNING syntax, which allows this. Unfortunately, you will either have to re-query the newly inserted row, or mangle a return value out of the input value in your client code.
As #Flimzy said you cannot do it in one query ala Postgres with its RETURNING clause.
Your best bet is to use MySQL's LAST_INSERT_ID() which is guaranteed to return the newly created record's ID and its scoped to the current session so there is no issue of getting a race condition

MSSQL ERROR "Invalid object name" when I add a field to the query?

I have an INSERT query that works fine, but when I add an extra field I get the oh-so-helpful:
Invalid object name 'optimizations'
Optimizations being the table name. The query is below... I am totally lost as to why this wouldn't work. If I cut & paste the query into MSSQL Management Studio, the query works fine.
INSERT INTO [optimizations]
([opt_date],[opt_concept_id],[opt_pallet],[opt_turnable],
[opt_sideupok],[opt_endupok],[opt_flatok],[opt_notstickingout],
[opt_notinoverhang],[opt_itemsloaded],[opt_loadedweight],
[opt_pweightutil],[opt_pvolumeutil],[opt_request_xml],
[opt_response_xml],[opt_images],[opt_primary_stage_id])
VALUES
(GETDATE(),'775','20ft','true','false','false','true',
'true','true','10','5952.6','18.6','48.9',
'<xml><herpa><derpa>xml</derpa></herpa></xml>',
'<xml><herpa><derpa>xml</derpa></herpa></xml>',
'img1.jpg,img2.jpg,img3.jpg','98');
For some reason, if I include the last field in the query [opt_primary_stage_id] it does not work... if I omit this field it works fine. Interestingly enough if I add [opt_primary_stage_id] to the list of fields being passed, and pass it a value of NULL the query also works fine. The [opt_primary_stage_id] field's data type is INT, I have tried including the last entry of the VALUES() section both as a straight number (3) and with quotes ('3')... neither works.
The server executing the code is running PHP 4.3.9, under IIS and with and MSSQL.
This is a version of the query, with the SAME data that will work.
INSERT INTO [optimizations]
([opt_date],[opt_concept_id],[opt_pallet],[opt_turnable],
[opt_sideupok],[opt_endupok],[opt_flatok],[opt_notstickingout],
[opt_notinoverhang],[opt_itemsloaded],[opt_loadedweight],
[opt_pweightutil],[opt_pvolumeutil],[opt_request_xml],
[opt_response_xml],[opt_images])
VALUES
(GETDATE(),'775','20ft','true','false','false','true','true',
'true','10','5952.6','18.6','48.9',
'<xml><herpa><derpa>xml</derpa></herpa></xml>',
'<xml><herpa><derpa>xml</derpa></herpa></xml>',
'img1.jpg,img2.jpg,img3.jpg');
EDIT: Fixed XML to have proper bracketing & added working query.
Adding a field won't cause failure on it's own: there is a valid reason.
You say it runs in SSMS OK then it shows that issue is in PHP or in the call.
First thoughts:
How long is the command. Is it being truncated somewhere in PHP? Is this dynamic SQL IRL?
Is the user context the same from the client? You may have the same table in 2 different schemas for example, with differing permissions.
Wrong database in the connection. What does SELECT DB_NAME() say? Sounds obvious but no object = not there. Don't dismiss this idea and assume it is the correct context: check.

Get the last insert id

Hello I am using cakePHP 1.3 and I am unable to retreive the last inserted row's id. I actually am using $this->Model->id to retreive the last inserted id but I am unable to get the id. When tried to check what is return type, it says as bool(false), which means nothing is returned.
Here I am loading a different model in a different controller, so would that be the issue?? But even though I am loading, I get back nothing!!
$this->loadModel('Contact');
$this->Contact->query("insert into contacts(tblContact_firstName,tblContact_lastName,tblContact_company,tblContact_department,tblContact_address,tblContact_country,tblContact_city,tblContact_state,tblContact_zipcode,tblContact_phone1,tblContact_email1) values('$sanitizedFormData[fname]','$sanitizedFormData[lname]','','$sanitizedFormData[company]','$sanitizedFormData[address]','$sanitizedFormData[country]','$sanitizedFormData[city]','$sanitizedFormData[state]','$sanitizedFormData[zip]','$sanitizedFormData[phone]','$sanitizedFormData[email]');");
$this->loadModel('Contact');
$contactId = $this->Contact->id;
And when I printed the $this->Contact array recursively, I found the value of "id" key empty. So that explains why I was receiving an empty value.
Now given my situation, how would I get the last inserted id, specific to the controller Contact?
I think you just want to do:
$this->getLastInsertID();
http://book.cakephp.org/2.0/en/models/additional-methods-and-properties.html#model-getlastinsertid
When you use query() you loose a lot of automagic cakephp provides. Use save() instead.
In fact, you even do not need to load Contact in this case. You can execute any query from the current controller with query() even saving to any other table.
You can also avoid using loadModel() if your current model is somehow associated with Contact ($this->CurrentModel->AnotherOne->Contact->save(...)).
If this is MySQl you could use "SELECT from contacts LAST_INSERT_ID()" query to get last ID.
or just "SELECT LAST_INSERT_ID()"
For MSSQL it is "SELECT ##IDENTITY".
This bypasses any solution in cakePHP though, so there might be a better solution.
You can get last inserted record id by
echo $this->ModelName->getLastInsertID();
Alternately, you can use:
echo $this->ModelName->getInsertID();
This methods can be found in cake/libs/model/model.php on line 2775
Note: This function doesn't work if you run the insert query manually

Categories