Okay, the code below I have created to show the issue.
And yes, I appreciate the fact that it should ALL be doctrine and by using PDO it makes the whole doctrine thing kinda pointless.
More importantly though, I am trying to understand why the two don't match up.
Basically, the first PDO query comes back with: 70 row count and 140 Max ID.
The second PDO query returns the same 70 row count and 140 Max ID.
The problem is, the doctrine insert between the two has made it 71 and 141.
Why is the second PDO query note reflecting this?
If I run the query in a separate PHP page or in Workbench, it returns 71/141. It just doesn't within the executing page.
$userID = $enUser->getId();
$stmt1 = $this->pdoConnection->getConnection()->prepare('
SELECT COUNT(UT.ID) AS UTC,
MAX(UT.ID) AS UTMID
FROM UserThing UT
WHERE UT.User_ID = :userID;
' ) or die ( implode( ':', $stmt1->errorInfo( ) ) );
$stmt1->bindParam( ':userID', $userID, \PDO::PARAM_INT, 11 );
$results = $this->pdoConnection->dbRecSet( $stmt1, false );
$stmt1->closeCursor();
unset($stmt1);
var_dump($results);
$newUserThing = new UserThing();
$newUserThing->setUserThingThings( $enThing );
$newUserThing->setUserThingUsers( $enUser );
$newUserThing->setAttainedDate( new \DateTime() );
$this->em->persist( $newUserThing );
$this->em->flush();
$userThingID = $newUserThing->getId();
echo $userThingID;
$stmt2 = $this->pdoConnection->getConnection()->prepare('
SELECT COUNT(UT.ID) AS UTC,
MAX(UT.ID) AS UTMID
FROM UserThing UT
WHERE UT.User_ID = :userID;
' ) or die ( implode( ':', $stmt2->errorInfo( ) ) );
$stmt2->bindParam( ':userID', $userID, \PDO::PARAM_INT, 11 );
$results = $this->pdoConnection->dbRecSet( $stmt2, false );
$stmt2->closeCursor();
unset($stmt2);
var_dump($results);
Any ideas why the second PDO query doesn't reflect the DB change, even though Doctrine is able to return the newly inserted ID?
The bit which didn't occur to me when making this post, was the fact that my PDO class used a different connection string to the Symfony one.
To be able to support multiple record sets to be returned from a single query, my class used different driver options to the default. So I just ported over the class into Symfony and used it and it worked well for a long time.
That said, doing an insert/update on one connection and reading straight after on another does not work well. That is the issue I found here.
The documentation for Symfony when it comes to PDO driver options is a bit lacking. For anyone facing it, all you need to know is that the PDO constants just need their Integer equivalents in the YML instead.
So you end up with things like this in your app/config/config.yml:
options:
20: true
3: 1
1002: "SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'"
That allowed me to do what I needed to and do it correctly.
Related
There is a problem in inserting to MongoDB database. It is not insert to database in right order.
We read the writing concern in MongoDB:
http://www.php.net/manual/en/mongo.writeconcerns.php
We use mongoDB 2.6 and PHP driver 1.6 with following sample code:
set_message_sample('1');
set_message_sample('2');
set_message_sample($id) {
$connecting_string = sprintf('mongodb://%s:%d/%s', $hosts, $port,$database), // fill with right connection setting
$connection= new Mongo($connecting_string,array('username'=>$username,'password'=>$password)); // fill with proper authentication setting
$dbname = $connection->selectDB('dbname');
$collection = $dbname->selectCollection('collection');
$post = array(
'title' => $id,
'content' => 'test ' . $id,
);
$posts->insert($insert,array("w" => "1"));
Sometimes the result is inserting "2" before "1" to database. We want to inserting in right order (first "1" and next "2") all the times. I also notice that we order the collection with mongoID which automatically set by mongoDB.
We check many options but the problem not solved. How we could solve it? ( How we could disable something like cache or isolate the insert queue to MongoDB.)
So, i think you could insert the second only after the confirmation of the first one. since the insert is asynchronous, you can't be sure who goes first. So you must insert only after the confirmation of the first one.
I have a data table with 7 columns and 400 records. One of them is budget. I want to group the 400 rows by budget so that I get an array like this:
[budget]=>array(
[0]=>array(
[column1]=>'1',
[column2]=>'sand'
),
[1]=>array(
[column1]=>'2',
[column2]=>'clay'
)
)
[budget2]=>array(
[0]=>array(
[column1]=>'3',
[column2]=>'silt'
),
[1]=>array(
[column1]=>'4',
[column2]=>'stone'
)
)
So far I have been playing around with Yii's CdbCommand and CdbDataReader and PHP's PDOStatement but nothing is working right. I tried the following code
public function actionBidCostByBudget(){
$command = Yii::app()->db
->createCommand()
->Select('*')
->From('bid_cost')
# ->Query()
;
echo '<pre>';
echo get_class($command);
$pdostatement=$command->getPdoStatement();
if($pdostatement) echo get_class($pdostatement);
# print_r($statement->fetchall(PDO::FETCH_COLUMN|PDO::FETCH_GROUP));
# print_r($command->readall());
# print_r($statement->fetch());
# $columnsArray = BidCost::attributeLabels();
//print_r($rowsArray);
//$this->layout='\\layout';
}
The attempts to print_r all print out with nothing. getPdoStatement equals nothing. I have been trying to use PDO::FETCH_COLUMN|PDO::FETCH_GROUP as per the Php.net website, but it does not work either because I get nothing.
One of Yii's strengths is it's ActiveRecord, so why not use it?
Make your budget to a separate table (so you can generate a model from it). Reference it from your "datatable".
CREATE TABLE budget (
id INTEGER PRIMARY KEY,
name TEXT
);
CREATE TABLE datatable(
column1 TEXT,
column2 TEXT,
...
budget_id INTEGER,
FOREIGN KEY(budget_id) REFERENCES budget(id)
);
Next generate models with Gii, and now you can use your newly made relations like this:
$budget = Budget::model()->findByAttributes( ["name"=>"budget2"] );
foreach( $budget->datatables as $dt ) {
echo $dt->column1;
echo $dt->column2;
}
(I know. Not the array you asked for. Sorry if I'm way off with this.)
Alright, the bottom line is that I was not able to find a way to do this right thru Yii, so I did it with a more hands-on approach.
The first thing I did was basically initiate a database connection thru Yii.
$command = Yii::app()->db //outputs CDbConnnection
The next thing I did was get a PDO class from the connection:
$pdoinstance = $command->getPdoInstance(); //outputs PDO class
From this point, it was help obtained from PHP.net and another question posted on this forum:
$pdostatement=$pdoinstance->prepare('SELECT BUDGET_CODE,
PAY_ITEM, ITEM, DESCRIPTION FROM bidcost');
$pdostatement->execute();
//default fetch mode could not be set
# $pdostatement->setfetchmode(PDO::FETCH_GROUP|PDO::FETCH_ASSOC);
//returns array
$testarray=$pdostatement->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC);
I have run into a problem I have never seen before. The code is too much and large and too many classes, so pardon me for not going into much detail.
The interesting bit is this:
$copy = clone $this;
$copy->workerid = $new_workerid;
if ( $copy->mysql_create_row( $this->pending_table ) )
return $copy;
This runs as expected. When I go into the database, I see a new entry in table Pending.
When however I run multiple queries in succession:
$copy = clone $this;
$copy->workerid = $new_workerid;
if ( $copy->mysql_create_row( $this->pending_table ) )
if ( $this->mysql_delete_row( $this->pending_table ) )
if ( $this->mysql_create_row( $this->cancelled_table ) )
return $copy;
Then for some unexplainable reason, the wrong id column is inserted at one of the tables.
In specific method $copy->mysql_create_row, among other things, does this:
if ( $con = db_connect() ) {
$res = mysqli_query( $con, "INSERT INTO $table (`workerid`,`time`,`location`,`tasks`) VALUES ('$this->workerid','$this->datetime','$this->location','$this->tasks');" )
or error_log( "mysql_create_row Error: " . mysqli_error( $con ) );
if ( $res ) {
$this->contactid = mysqli_insert_id( $con );
foreach ( $this->attendees as $att ) {
$att->__setContactId( $this->contactid );
if ( !$att->mysql_create_row( $this->attendee_table ) ) {
error_log( " Contact->mysql_create_row Error: Did not create attendee MySQL row" );
return false;
}
Delegation through object method $att->mysql_create_row( $this->attendee_table ) works the same way, it is merely an INSERT query.
Now this is where the things make no sense:
Whereas the newly inserted $copy object inserts the correct id at $copy->mysql_create_row, the $att->mysql_create_row does not !!!
It inserts the old id.
Furthermore, when I echo out the query from $att->mysql_create_row, it echoes out the correct (new id)!!!
But when I go into the actual database, it has inserted the old id. This makes absolutely no sense to me.
I have tried to create custom __clone() methods, try to make sure that I have copies and not references (damn you php) but it still produces the same error.
The problem seems to be with the successive queries, as when I do not do the delete or the new insert, I do not have any problems.
The delete works fine, and the second insertion also works fine, without any errors whatsoever.
What could possibly force the MySQLi api to insert the wrong id, while the query echoes the correct one ?
I could only thing of some form of background/multithreaded corruption, but since I have no control over this sort of thing, I do not even know how to tackle the problem.
When echoing out the query from $att->mysql_create_row, I get:
before query id: 305
'INSERT INTO XXXXXXXX.attendees
(contactid,title,forenames,surname,relationship,gender,age,arrived,left,supervised,ics_number)
VALUES ('305','Mr',
'Alex','Werner','','M','29/08/1981','0','','','0');'
after query id: 305
But when I actually go into the database, the value inserted is the old id value (say 294).
The table column is not Auto-incrementing, is not unique or primary, it is simply a int(11).
What on earth could be causing this ?
Your problem could be that you are using mysql_last_insert_id to get the id of the last inserted row. As you are inserting multiple rows, it could be possible that you don't get the correct id.
Replace mysql_last_insert_id call with a select query that returns the id of your last insertion and look if your problem is solved.
I am a little confused. I actively use PHP RedBean as ORM within my direct mail service and I run into curious situation - I have a table with unique key constraint (i.e. subscriber_id, delivery_id) and two scripts that is writing data into this table.
There is source code that is inserting or updating table:
public static function addOpenPrecedent($nSubscriberId, $nDeliveryId)
{
$oOpenStatBean = \R::findOrDispense('open_stat', 'delivery_id = :did AND subscriber_id = :sid', array(':did' => $nDeliveryId, ':sid' => $nSubscriberId));
$oOpenStatBean = array_values($oOpenStatBean);
if (1 !== count($oOpenStatBean)) {
throw new ModelOpenStatException(
"Ошибка при обновлении статистики открытий: пара (delivery_id,
subscriber_id) не является уникальной: ($nDeliveryId, $nSubscriberId).");
}
$oOpenStatBean = $oOpenStatBean[0];
if (!empty($oOpenStatBean->last_add_dt)) {
$oOpenStatBean->precedent++;
} else {
$oOpenStatBean->delivery_id = $nDeliveryId;
$oOpenStatBean->subscriber_id = $nSubscriberId;
}
$oOpenStatBean->last_add_dt = time('Y-m-d H:i:s');
\R::store($oOpenStatBean);
}
It is called both from two scripts. And I have issues with corruption unique constraint on this table periodically, because race conditions occurs. I know about SQL "INSERT on duplicate key update" feature. But how can I obtain same result purely using my ORM?
Current, that I know if, Redbean will not issue an
INSERT ON DUPLICATE KEY UPDATE
as the discussion of this cited in the comments above indicates that Redbean's developer considers upsert to be a business logic thing that would pollute the ORM's interphase. This being said, it is most likely achievable if one were to extend Redbean with a custom Query Writer or plugin per the Documentation. I haven't tried this because the method below easily achieves this behavior without messing with the internals and plugins of the ORM, however, it does require that you use transactions and models and a couple of extra queries.
Basically, start your transaction with either R::transaction() or R::begin() before your call to R::store(). Then in your "FUSE"d model, use the "update" FUSE method to run a query that checks for duplication and retrieves the existing id while locking the necessary rows (i.e. SELECT FOR UPDATE). If no id is returned, you are good and just let your regular model validation (or lack thereof) continue as usual and return. If an id is found, simply set $this->bean->id to the returned value and Redbean will UPDATE rather than INSERT. So, with a model like this:
class Model_OpenStat extends RedBean_SimpleModel{
function update(){
$sql = 'SELECT * FROM `open_stat` WHERE `delivery_id`=? AND 'subscriber_id'=? LIMIT 1 FOR UPDATE';
$args = array( $this->bean->deliver_id, $this->bean->subscriber_id );
$dupRow = R::getRow( $sql, $args );
if( is_array( $dupRow ) && isset( $dupRow['id'] ) ){
foreach( $this->bean->getProperties() as $property => $value ){
#set your criteria here for which fields
#should be from the one in the database and which should come from this copy
#this version simply takes all unset values in the current and sets them
#from the one in the database
if( !isset( $value ) && isset( $dupRow[$property] ) )
$this->bean->$property = $dupRow[$property];
}
$this->bean->id = $dupId['id']; #set id to the duplicates id
}
return true;
}
}
You would then modify the R::store() call like so:
\R::begin();
\R::store($oOpenStatBean);
\R::commit();
or
\R::transaction( function() use ( $oOpenStatBean ){ R::store( $oOpenStatBean ); } );
The transaction will cause the "FOR UPDATE" clause to lock the found row or, in the event that no row was found, to lock the places in the index where your new row will go so that you don't have concurrency issues.
Now this will not solve one user's update of the record clobbering another, but that is a whole different topic.
I have a weird problem using socialEngine DB class (based on zend framework).
I wrote something like this:
$statusTable = Engine_Api::_()->getDbtable('actions', 'activity');
$myPosts = $statusTable->fetchAll($statusTable->select()
->where('subject_id = ?',$id)
->where('comment_count > ?',0)
->where('type = ?',$type)
->where('date > ?',$newer_than)
->order('date DESC')
->limit(intval($num_items)));
Its a part of a plugin a made, the problem is the query generated is somthing like this:
SELECT `engine4_activity_actions`.*
FROM `engine4_activity_actions`
WHERE (subject_id = 5) AND (comment_count > 0) AND (type = ) AND (date > )
ORDER BY `date` DESC LIMIT 10
You can see that the $type and the $newer_than have disappeared, even though they have values ($type='status', $newer_than='01/01/2000')
EDIT:
It seems to respond only to integers and not strings, if i replace the 'status' with 0 it shows up in the query.
The server runs on php 5.3.2
There's a third optionnal argument on the where() method which is the type of your argument. Depending on your DB adapter it can maybe get an important thing to tell for the Zend_Db_Select query builder.
So you could try
->where('subject_id=?',$subject,'TEXT')
ZF API indicates as well "Note that it is more correct to use named bindings in your queries for values other than strings", this can help the query builder, to get the real type of your args, so you could try as well this way:
$myPosts = $statusTable->fetchAll($statusTable->select()
->where('subject_id=:psubject')
(...)
,array('psubject'=>$subject));