Save inside loop - php

I created a function that has a infinite loop and a delay at the end to make sure it will only execute one time per second.
This function is in a shell class and its goal is to save/update records in three different tables and I invoque it from the console line using the command 'cake className'
My problem is:
I stop the loop at the console (Ctrl + C) and only the last record is saved in the database.
I don't know if there is some problems with transaction, i tried to use begin() before save and commit after, but the problem subsisted.
The code is something like this:
$this->T1->begin();
$this->T2->begin();
$this->T3->begin();
if ($this->T1->save((array (
'Field1' => $val1,
'Field2' => $val2,
'Field3' => $val3)))
&& $this->T2->save(array (
'Field1' => $val4,
'Field2' => $val5,
'Field3' => $val6)))
&& $this->T3->saveField('Field1', $val7))
{
$this->T1->commit();
$this->T2->commit();
$this->T3->commit();
echo 'success message';
}

It could be because the id is still present in each of the models which often happens when saving in a loop because the data gets merged.
Try the following to reset the models so they don't load in the previous data.
$this->Sale->create(false);
$this->Bidagent->create(false);
$this->Licitation->create(false);
From your code snippet though i'm not sure what T1, T2 and T3 are... if they are models then they need the same $this->Model->create(false);
Reference:
http://api.cakephp.org/2.3/class-Model.html#_create

Related

Data not saved in database cakephp

I have an event table with this fields:
I use this code to insert data in this table :
$oneEvent = array(
'trainer_id' => $event['trainer_id'],
'formation_id' => $event['formation_id'],
'title' => $event['title'],
'start' => $event['start'][$i],
'end' => $event['end'][$i],
);
$success = $this->Event->save($oneEvent);
$events_id= $this->Event->inserted_ids;
when I run this code I get true and ID of insert element (showed using debug)
but in database i can't see this field never !!!!.
and when I insert data in phpmyadmin when this request INSERT INTO events(title,start, end, trainer_id, formation_id) VALUES ('DĂ©part B','2016-11-18 10:00:00','2016-11-18 11:00:00','13','1') it worked
I didn't know what happen here !!??
I solved the problem by using $dataSource->commit(); after calling save() function

DynamoDB Count Group By

We are trying to search a dynamodb, and need to get count of objects within a grouping, how can this be done?
I have tried this, but when adding the second number, this doesn't work:
$search = array(
'TableName' => 'dev_adsite_rating',
'Select' => 'COUNT',
'KeyConditions' => array(
'ad_id' => array(
'ComparisonOperator' => 'EQ',
'AttributeValueList' => array(
array('N' => 1039722, 'N' => 1480)
)
)
)
);
$response = $client->query($search);
The sql version would look something like this:
select ad_id, count(*)
from dev_adsite_rating
where ad_id in(1039722, 1480)
group by ad_id;
So, is there a way for us to achieve this? I can not find anything on it.
Trying to perform a query like this on DynamoDB is slightly trickier than in an SQL world. To perform something like this, you'll need to consider a few things
EQ ONLY Hash Key: To perform this kind of query, you'll need to make two queries (i.e. ad_id EQ 1039722 / ad_id EQ 1480)
Paginate through query: Because dynamodb returns your result set in increments, you'll need to paginate through your results. Learn more here.
Running "Count": You can take the "Count" property from the response and add it to the running total as you're paginating through the results of both queries. Query API
You could add a Lambda function triggered by the DynamoDBStream, to aggregate your data on the fly, in your case add +1 to the relevant counters. Your search function would then simply retrieve the aggregated data directly.
Example: if you have a weekly online voting system where you need to store each vote (also to check that no user votes twice), you could aggregate the votes on the fly using something like this:
export const handler: DynamoDBStreamHandler = async (event: DynamoDBStreamEvent) => {
await Promise.all(event.Records.map(async record => {
if (record.dynamodb?.NewImage?.vote?.S && record.dynamodb?.NewImage?.week?.S) {
await addVoteToResults(record.dynamodb.NewImage.vote.S, record.dynamodb.NewImage.week.S)
}
}))
}
where addVoteToResults is something like:
export const addVoteToResults = async (vote: string, week: string) => {
await dynamoDbClient.update({
TableName: 'table_name',
Key: { week: week },
UpdateExpression: 'add #vote :inc',
ExpressionAttributeNames: {
'#vote': vote
},
ExpressionAttributeValues: {
':inc': 1
}
}).promise();
}
Afterwards, when the voting is closed, you can retrieve the aggregated votes per week with a single get statement. This solution also helps spreading the write/read load rather than having a huge increase when executing your search function.

+Testing an artisan command that edits the database

I'm trying to test an artisan command that is designed to do some database maintenance.
In particular, it searches for records that don't have a column filled, and fill it.
This is a simplified version of the fire() method of the command:
public function fire()
{
$items = Item::all();
$total = $items->count();
$updated = 0;
foreach ($items as $item) {
if ($item->myColumn != 'x') {
$item->myColumn = 'x';
$item->save();
$updated++;
}
}
$this->info("total: $total updated: $updated");
}
My (acceptance) test is very simple and does the following:
insert a record in the db
call the artisan command
check that the inserted record has been updated
This is the code:
public function doTheTest(AcceptanceTester $I)
{
$I->wantTo('setup the myColumn when it is not set');
$id = $I->haveRecord('items', [
'myColumn' => '',
]);
$I->runShellCommand('php artisan items:updater');
$I->seeRecord('items', [
'id' => $id,
'myColumn' => 'x',
]);
}
However the test fails, and I get the following message:
Couldn't see record "items",{"id":101,"myColumn":"x"}:
Couldn't find items with {"id":101,"code":"x"}
As you can see, the id of the new record is 101, because there are already 100 items in the db dump, but what it is strange is that the $this->info() in the command prints
total: 100 updated: 100
as if the database used inside the test and the one used inside artisan are different.
Moreover, if at the end of the test I try to grab the added record, and print it, as shown in the following snippet
public function doTheTest(AcceptanceTester $I)
{
/* ... */
$item = $I->grabRecord('items', [
'id' => $id,
]);
\Codeception\Util\Debug::debug($item);
}
and run the codecept run acceptance --debug command, I get the added record
stdClass Object
(
[id] => 101
[myColumn] =>
)
I'm very confused, because the there is a single database, but I'm surely misunderstanding something important here.
Could anyone give me a help?
Thank you very much,
The issue is that every query using Laravel4 module is run in a transaction that, by default, will be rolled back at the end. If you take a look at the Config section of Laravel4 documentation it states
cleanup: boolean, default true - all db queries will be run in transaction, which will be rolled back at the end of test.
You can check this if you restart the MySQL server (in which case, when you run the tests again you'll still see the id 101), or take a look at MySQL logs that will have an entry like the following for each test:
150417 23:24:24 2 Connect root#localhost on laravel-test
2 Prepare set names 'utf8' collate 'utf8_unicode_ci'
2 Execute set names 'utf8' collate 'utf8_unicode_ci'
2 Close stmt
2 Query START TRANSACTION
2 Prepare insert into items (myColumn) values (?)
2 Execute insert into items (myColumn) values ('')
2 Close stmt
2 Query ROLLBACK
2 Quit
To fix this, you need to configure the option cleanup of Laravel4 module in your codeception.yml file, like this:
modules:
config:
Laravel4:
cleanup: false

Symfony sfWidgetFormDoctrineChoice with multiple option on

I have created a form in which i embed another form. My question is about this embedded form - I'm using a sfWidgetFormDoctrineChoice widget with option multiple set to true. The code for this embedded form's configure method:
public function configure()
{
unset($this['prerequisite_id']);
$this->setWidget('prerequisite_id', new sfWidgetFormDoctrineChoice(array(
'model' => 'Stage',
'query' => Doctrine_Query::create()->select('s.id, s.name')->from('Stage s')->where('s.workflow_id = ?', $this->getOption('workflow_id') ),
'multiple' => true
)));
$this->setValidator('prerequisite_id', new sfValidatorDoctrineChoice(array(
'model' => 'Stage',
'multiple' => true,
'query' => Doctrine_Query::create()->select('s.id, s.name')->from('Stage s')->where('s.workflow_id = ?', $this->getOption('workflow_id') ),
'column' => 'id'
)));
}
I unset the prerequisite_id field because it is included in the base form, but I want it to be a multiple select.
Now, when I added the validator, everything seems to work (it passes the validation), but it seems like it has problems saving the records if there is more than one selection sent.
I get this PHP warning after submitting the form:
Warning: strlen() expects parameter 1 to be string, array given in
D:\Development\www\flow_dms\lib\vendor\symfony\lib\plugins\sfDoctrinePlugin\lib\database\sfDoctrineConnectionProfiler.class.php
on line 198
and more - I know, why - in symfony's debug mode I can see the following in the stack trace:
at Doctrine_Connection->exec('INSERT INTO stage_has_prerequisites
(prerequisite_id, stage_id) VALUES (?, ?)', array(array('12', '79'),
'103'))
So, what Symfony does is send to Doctrine an array of choices - and as I see in the debug sql query, Doctrine cannot render the query correctly.
Any ideas how to fix that? I would need to have two queries generated for two choices:
INSERT INTO stage_has_prerequisites (prerequisite_id, stage_id) VALUES (12, 103);
INSERT INTO stage_has_prerequisites (prerequisite_id, stage_id) VALUES (79, 103);
stage_id is always the same (I mean, it's set outside this form by the form in which it is embedded).
I have spend 4 hours on the problem already, so maybe someone is able to provide some help.
Well, I seem to have found a solution (albeit not the best one, I guess). Hopefully it'll be helpful to somebody.
Finally, after much thinking, I have concluded that if the problem comes from the Doctrine_Record not being able to save the record if it encounters an array instead of a single value, then the easiest solution would be to overwrite the save() method of the Doctrine_Record. And that's what I did:
class StageHasPrerequisites extends BaseStageHasPrerequisites
{
public function save(Doctrine_Connection $conn = null)
{
if( is_array( $this->getPrerequisiteId() ) )
{
foreach( $this->getPrerequisiteId() as $prerequisite_id )
{
$obj = new StageHasPrerequisites();
$obj->setPrerequisiteId( $prerequisite_id );
$obj->setStageId( $this->getStageId() );
$obj->save();
}
}
else
{
parent::save($conn);
}
}
(...)
}
So now if it encounters an array instead of a single value, it just creates a temporary object and saves it for each of this array's values.
Not an elegant solution, definitely, but it works (keep in mind that it is written for the specific structure of the data and it's just the effect of my methodology, namely See What's Wrong In The Debug Mode And Then Try To Correct It Any Way Possible).

does sfWidgetFormSelect provide a string or an int of the selected item?

I'm having an annoying problem. I'm trying to find out what fields of a form were changed, and then insert that into a table. I managed to var_dump in doUpdateObjectas shown in the following
public function doUpdateObject($values)
{
parent::doUpdateObject($values);
var_dump($this->getObject()->getModified(false));
var_dump($this->getObject()->getModified(true));
}
And it seems like $this->getObject()->getModified seems to work in giving me both before and after values by setting it to either true or false.
The problem that I'm facing right now is that, some how, sfWidgetFormSelect seems to be saving one of my fields as a string. before saving, that exact same field was an int. (I got this idea by var_dump both before and after).
Here is what the results on both var dumps showed:
array(1) {["annoying_field"]=> int(3)} array(1) {["annoying_field"]=>string(1)"3"}
This seems to cause doctrine to think that this is a modification and thus gives a false positive.
In my base form, I have
under $this->getWidgets()
'annoying_field' => new sfWidgetFormInputText(),
under $this->setValidators
'annoying_field' => new sfValidatorInteger(array('required' => false)),
and lastly in my configured Form.class.php I have reconfigured the file as such:
$this->widgetSchema['annoying_field'] = new sfWidgetFormSelect(array('choices' => $statuses));
statuses is an array containing values like {""a", "b", "c", "d"}
and I just want the index of the status to be stored in the database.
And also how can I insert the changes into another database table? let's say my Log table?
Any ideas and advice as to why this is happen is appreciated, I've been trying to figure it out and browsing google for various keywords with no avail.
Thanks!
Edit:
ok so I created another field, integer in my schema just for testing.
I created an entry, saved it, and edited it.
this time the same thing happened!
first if you what the status_id to be saved in the database, you should define your status array like this:
{1 => "a", 2 => "b", 3 => "c", 4 => "d"}
So that way he know that 1 should be rendered like "a" and so on. Then, when saving, only the index should be saved.
About saving in another database, my advise is to modify the doSave method defined by the Form class yo match your needs. I only know how Propel deals with it, maybe this could help:
the doSave method dose something like this:
protected function doSave($con = null)
{
if (null === $con)
{
$con = $this->getConnection();
}
$old = $this->getObject()->getModifiedValues($this);//Define this
$new_object = new Log($old);//Create a new log entry
$new_object->save($con));//save it!
$this->updateObject();
$this->getObject()->save($con);
// embedded forms
$this->saveEmbeddedForms($con);
}
Hope this helps!
Edit:
This is an example extracted from a model in one of my applications and its working ok:
Schema:
[...]
funding_source_id:
type: integer
required: true
[...]
Form:
$this->setWidget('funding_source_id', new sfWidgetFormChoice(array(
'choices' => array(1 => 'asdads', 2 => '123123123' , 3 => 'asd23qsdf'),
)));
$this->setValidator('funding_source_id', new sfValidatorChoice(array(
'choices' => array(1 => 'asdads', 2 => '123123123' , 3 => 'asd23qsdf'),
'required' => true
)));
About the log thing, that could be quite more complex, you should read the current implementation of the doSave method in the base form class, currently sfFomrObject on Symfony1.4., and when and how it delegates object dealing with modified values.
Okay,
It turns out I forgot to do a custom validator to use the array key instead.

Categories