Phalcon PDO transaction is not working correct - mysql

It nearly took me a whole day to resolve the problems about transactions, but I failed.
My requirement is INSERT a new record both in table topic and table topic_data in one transaction.
I have my code like this :
// database connection
$di->set( 'db', function() use( $conf ) {
return new \Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter( [
'host' => $conf->db->host,
'username' => $conf->db->username,
'password' => $conf->db->password,
'dbname' => $conf->db->dbname,
'options' => [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_PERSISTENT => true,
\PDO::ATTR_AUTOCOMMIT => false
]
] );
} );
// transaction code
public function create( $params ) {
$this->db->begin();
$id = \Idalloc::next();
$topic = new Topic();
$topic->id = $id;
$topic->ctime = $_SERVER[ 'REQUEST_TIME' ];
$tags = $params[ 'tags' ];
$params[ 'tags' ] = implode( ',', $tags );
$topic->assign( $params );
if( $topic->save() === false ) {
$this->db->rollback();
return false;
}
for( $i = 0, $l = count( $tags ); $i < $l; ++$i ) {
$topicTag = new TopicTag();
$topicTag->tag_id = $tags[ $i ];
$topicTag->topic_id = $id;
$topicTag->type = $params[ 'type' ];
if( $topicTag->save() === false ) {
$this->db->rollback();
return false;
}
}
var_dump( $this->db->isUnderTransaction() );
$this->db->commit();
return $id;
}
The fails with :
If I don't set \PDO::ATTR_PERSISTENT => true, method "create" with return $id and var_dump( $this->db->isUnderTransaction() ) is TRUE but NO DATA been inserted into both table topic and table topic_tag
If I set \PDO::ATTR_PERSISTENT => true, I will get Exception with :
[Tue, 21 Jun 16 02:16:21 +0800][ERROR] PDOException: There is no active transaction
And still failed to insert data into table topic, but new record appearing in table topic_tag.
If I only keep one of the two parts, it will be working well.
How can I resolve this problem and is there a simple way to create Manual Transactions ?

I found out the solution for this question. The 'db' set in DI must be shared. So there need another parameter "TRUE" :
// database connection
$di->set( 'db', function() use( $conf ) {
return new \Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter( [
'host' => $conf->db->host,
'username' => $conf->db->username,
'password' => $conf->db->password,
'dbname' => $conf->db->dbname,
'options' => [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_PERSISTENT => true,
\PDO::ATTR_AUTOCOMMIT => false
]
] );
}, true );

Related

Import Json data from url and save it to database laravel

i have this json data come from a url:
[
{
"id": "4070",
"title": "Speedway Racing",
"description": "Drivers start your engines! Professional oval track racing that explodes with stock car racing action!",
"instructions": "Use WASD or Arrow keys to move the car ",
"url": "https://html5.gamemonetize.com/b7dzsckxzmy5z3to54nif435exdiogkz/",
"tags": "Car, Cars, Racing",
"thumb": "https://img.gamemonetize.com/b7dzsckxzmy5z3to54nif435exdiogkz/512x384.jpg",
"width": "960",
"height": "600",
"category": "Driving"
},]
I need to save all data in database but for category i need to create new category from the category fled and save it in post_categry table (cat_id, post_id) if his not exists, also check f this game exists in db if yes ignore if no save it.
My GameController:
the json data come from this lik:
This link
public function importer_start(Request $request)
{
$game_cat = new Game;
$validatedata = $this->validate($request, [
'game_source' => 'required',
'source_url' => 'required|url',
]);
$dataurl = $request->input('source_url');
$sourve_name = $request->input('game_source');
$content = file_get_contents($dataurl);
$data = json_decode($content, true);
$query_ammount = $request->input('source_url');
parse_str($query_ammount, $params);
$game_amount = $params['amount'];
$query_ammount = $request->input('source_url');
parse_str($query_ammount, $params);
$game_amount = $params['amount'];
$url = $request->input('source_url');
$parmt_url = parse_url($url);
parse_str($parmt_url['query'], $params);
if ($params['format'] != 'json') {
toastr()->error('Please select JSON file to import games');
return redirect()->back();
}
if (#strpos($dataurl, Constants::GAMESOURCE_GAMEMONETIZE)) {
$finalarray = array();
foreach ($data as $key=>$value) {
// dd($value);
array_push($finalarray,array(
'game_id' => $value['id'],
'title' => $value['title'],
'description' => $value['description'],
'instructions' => $value['instructions'],
'url' => $value['url'],
'category' => !empty($value['category']) ? $value['category']: 'uncategorized',
'tags' => $value['tags'],
'thumb' => $value['thumb'],
'width' => $value['width'],
'height' => $value['height'],
'source' => Constants::GAMESOURCE_GAMEMONETIZE,
'views' => 15,
)
);
}
$categories[] = $value['category'];
Game::insert($finalarray);
toastr()->success('You have add '.$game_amount.' from '.\Constants::GAMESOURCE_GAMEMONETIZE.'');
return redirect()->route('admin.importer');
}elseif (#strpos($dataurl, Constants::GAMESOURCE_GAMEDISTRIBUTION)) {
$result = json_decode(json_encode($data), true);
$key = array_search('categoryList', array_column($result, 'categoryList'));
$finalarray = array();
foreach ($data as $key=>$value) {
array_push($finalarray,array(
'title' => $value['title'],
'gameType' => $value['gameType'],
'gameMobile' => $value['gameMobile'],
'mobileMode' => $value['mobileMode'],
'instructions' => $value['instructions'],
'description' => $value['description'],
'width' => $value['width'],
'height' => $value['height'],
'url' => $value['url'],
'source' => Constants::GAMESOURCE_GAMEDISTRIBUTION,
'assetList' => json_encode($value['assetList']),
'tagList' => json_encode($value['tagList']),
'category' => $value['categoryList'][0]['name'],
'views'=> 15,
)
);
}
//dd($finalarray);
Game::insert($finalarray);
toastr()->success('You have add '.$game_amount.' from '.\Constants::GAMESOURCE_GAMEDISTRIBUTION.'');
return redirect()->route('admin.importer');
}
}
Laravel version: 7.0
Thank you for your helpe.

yii2-formwizard change model after select2 selection

I'm trying to implement a multiple models form using the form-wizard widget, with Profile as main model and few others as linked ones. When I select the entity type field for the main model, I would like to change the linked model for the next step, basing on the value of entity type field.
I have tried with this code:
Create Form Code
$modelUrlReletedModelsCreate = Profile::urlRelatedModelCreate();
$urlLinkedProfile = Url::to(['create']);
echo FormWizard::widget([
'formOptions'=>[
'id'=>'profile_form',
'enableClientValidation'=>true,
'enableAjaxValidation'=>true,
'validationUrl' => Url::to(['profile-models-validation'])
],
'theme' => FormWizard::THEME_MATERIAL_V,
'steps' => [
//step 1
[
'model' => $model,
'title' => \Yii::t('app', 'Profile'),
'fieldConfig' => [
'only' => ['entity_type_id', 'profile_type_id'],
'entity_type_id' => [
'widget' => Select2::class,
'options' => [
'data' => EntityType::arrayNamesList(),
'options' => ['placeholder' => \Yii::t('app','Select an element')],
'pluginEvents' => ['select2:select'=>'function(e){;'
. 'var type = $("#pr-ty-sel").val();'
. 'var profile= "'.($model->profile_id ? : "no").'";'
. '$.ajax({ method: "GET",'
. 'url:"'.$urlLinkedProfile.'",'
. 'data:{ entity_text : e.params.data.text,'
. 'entity_id : e.params.data.id,'
. 'profile_type_id : type,'
. 'profile_id : profile},'
. 'success: function(data){'
. '$("#profile_form").html(data);'
. '}'
. '});}'],
],
],
'profile_type_id' =>[
'widget' => Select2::class,
'options' => [
'data' => \app\models\ProfileType::arrayNamesList(),
'options' => ['placeholder' => \Yii::t('app','Select an element'),'id'=>'pr-ty-sel'],
],
]
],
'description' => \Yii::t('app', 'Add Profile Type Data'),
'formInfoText' => \Yii::t('app', 'Fill all fields'),
],
//step 2 I want ot change here $linkedModel
[
'model' => [$model,$linkedModel],
'title' => \Yii::t('app', 'Personal Data'),
'description' => \Yii::t('app', 'Insert Personal Data'),
],
]
]);
Controller Action Create Code
public function actionCreate($profileId = NULL)
{
if($profileId AND $profileId !== 'no'){
$model = $this->findModel($profileId);
}else{
$model = new Profile();
}
$profileLinkedModel = new ProfilePrivate();
$renderMethod = 'render';
if (Yii::$app->request->isAjax) {
$entityText = \Yii::$app->request->get('entity_text');
$entity_id = \Yii::$app->request->get('entity_id');
$profileTypeId = \Yii::$app->request->get('profile_type_id');
$profileId = \Yii::$app->request->get('profile_id');
//Utility function to clean the entity text (remove number and special characters)
$entityTextCleaned = \app\components\Utility::cleanString($entityText);
if ($entityTextCleaned == 'private') {
$profileLinkedModel = new ProfilePrivate();
} elseif ($entityText == 'company') {
$profileLinkedModel = new ProfileCompany();
} else {
//#TODO
return FALSE;
}
$model->entity_type_id = $entity_id;
$model->profile_type_id = $profileTypeId;
$profileLinkedModel->profile_id = $model->profile_id;
Yii::$app->response->format = Response::FORMAT_JSON;
$renderMethod = 'renderAjax';
}
//extra table field used to enable custom rule in model
$model->createFullProfile = TRUE;
if ($model->load(Yii::$app->request->post())) {
return $this->redirect(['view', 'id' => $model->profile_id]);
}
return $this->$renderMethod('create', [
'model' => $model,
'profileLinkedModel' => $profileLinkedModel,
]);
}
When I select a field for entity type field, the server runs the ajax request on select event, but when it ends the other form field is not more selectable. So, in other words, after the ajax request I'm unable to select the profile type field. If I try to select the profile type field before the entity type field I can go to next step, but load always the default model.

How to add new values to multiple Select2 from Kartik instead of replace old ones?

I use Select2 widget in multiply mode in YII2 framework. "kartik-v/yii2-widget-select2": "#dev" - this one I have downloaded via composer.
kartik-v/yii2-widget-select2 dev-master dd09e46
I added initial values with ajax on widget init ('initSelection'). And added another ajax method to suggest new values on user's typing. When user select one from list, it replaced initial values what were added on init. New values replace initial values, but don't another new.
I want new values to add to initial instead of replace it.
<?= $form->field($model, 'security[]')->widget(Select2::class, [
'attribute' => 'security',
'hideSearch' => true,
'data'=>$security_data,
'options' => [
'placeholder' => 'Security',
'multiple' => true,
],
'pluginOptions' => [
'allowClear' => true,
'minimumInputLength' => 1,
'ajax' => [
'url' => Url::toRoute([ '/admin/security/select-items' ]),
'dataType' => 'json',
'data' => new JsExpression('function(params) { return {q:params.term}; }'),
'results' => new JsExpression('function(data,page) { return {results:data.results}; }'),
],
'initSelection' => new JsExpression('function(element, callback) { var id = '.Yii::$app->request->getQueryParams()['id'].';if(id !== "") {$.ajax("' . \yii\helpers\Url::toRoute([ '/admin/security/init-items' ]) . '", {data: {id: id},dataType: "json"}).done(function(data) {callback(data.results);});}}'),
],
]); ?>
And here is my api methods:
public function actionSelectItems($q = null){
Yii::$app->response->format = Response::FORMAT_JSON;
$out = ['results' => []];
if(!empty($q)){
$items = Security::find()->where(['like', 'title', $q])->all();
foreach ($items as $item){
$out['results'][] = ['id'=>$item->id, 'text'=>$item->title];
}
}
return $out;
}
public function actionInitItems($id = null){
Yii::$app->response->format = Response::FORMAT_JSON;
$adv = Adv::findOne($id);
$security = #json_decode($adv->security, true);
$out = ['results' => []];
foreach ($security as $item){
$text = Security::findOne($item)->title;
$out['results'][] = ['id'=>$item, 'text'=>$text];
}
return $out;
}
Is there some sort of settings or I missed something when handle http result?

Phalcon transaction not working correct

I created a transaction like this :
$this->db->begin();
$topic = new Topic();
$topic->assign( $data );
$topic->save();
var_dump( $this->db->isUnderTransaction() ); // bool(true)
$this->db->rollback();
var_dump( $this->db->isUnderTransaction() ); // bool(false)
But, the database still changed and inserted a new line. The rollback method is not working.
$di->set method will only be shared if been called with the second parameter is "TRUE".
$di->set( 'db', function() use( $conf ) {
return new DbAdapter( [
'host' => $conf->db->host,
'username' => $conf->db->username,
'password' => $conf->db->password,
'dbname' => $conf->db->dbname,
'options' => [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_PERSISTENT => true
]
] );
}, true );

How to generate SqlDataProvider for 2 db connection

I have a 2 DB connection, db and db2. I want to get all data from table claim in db2 connection. Below is my sql and it did not read from db2 connection. How to solved this?
$sql = "SELECT * FROM claim where provider_id = xx ";
$sql_count = "SELECT COUNT FROM ( $sql ) AS count ";
$totalCount = Yii::$app->db2->createCommand($sql_count)->queryScalar();
$dataProvider = new SqlDataProvider([
'sql' => $sql,
'totalCount' => $totalCount,
'sort' =>false,
'pagination' => [
'pageSize' => 10,
],
]);
return $dataProvider;
First you need to configure your databases like below:
return [
'components' => [
'db1' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=db1name', //maybe other dbms such as psql,...
'username' => 'db1username',
'password' => 'db1password',
],
'db2' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=db2name', //maybe other dbms such as psql,...
'username' => 'db2username',
'password' => 'db2password',
],
],
];
Then you can simply :
//to get from db1
Yii::$app->db1->createCommand((new \yii\db\Query)->select('*')->from('tbl_name'))->queryAll()
//to get from db2
Yii::$app->db2->createCommand((new \yii\db\Query)->select('*')->from('tbl_name'))->queryAll()
If you are using an active record model, in your model you can define:
public function getDb() {
return Yii::$app->db1;
}
//Or db2
public function getDb() {
return Yii::$app->db2;
}
Then:
if you have set db1 in getDb() method, result will be fetched from db1 and so on.
ModelName::find()->select('*')->all();