I am trying to do a simple operation: create 2 entries; on each creation, first check if the entity exists, create it, if it does not, and then do the adjustment. In some cases (on first such operation) we need to create the entity in the first entry and work with it in the second. Unfortunately, this does not happen and we end up with two entries that are useless. After this initial issue, everything works as expected (i.e. on next iteration the entity is properly found).
Here is the code for the entries:
// Create first entry
$debitCode = 'bank';
$creditCode = 'equity';
// Create entry
$accountEntry = new AccountEntry();
$accountEntry->setAmount($amount);
$debitAccount = $unit->getAccountByType($debitCode);
if (!$debitAccount) {
// Create debit account
$debitAccountType = $em->getRepository('App:AccountType')->findOneBy(['code' => $debitCode]);
$debitAccount = new Account();
$debitAccount->setType($debitAccountType);
$em->persist($debitAccount);
}
$debitAccount->debit($amount);
$accountEntry->setDebitAccount($debitAccount);
$creditAccount = $unit->getAccountByType($creditCode);
if (!$creditAccount) {
// Create credit account
$creditAccountType = $em->getRepository('App:AccountType')->findOneBy(['code' => $creditCode]);
$creditAccount = new Account();
$creditAccount->setType($creditAccountType);
$em->persist($creditAccount);
}
$creditAccount->credit($amount);
$accountEntry->setCreditAccount($creditAccount);
$em->persist($accountEntry);
$em->flush();
// Create second entry
$debitCode2 = 'accountsPayable';
$creditCode2 = 'bank';
$accountEntry2 = new AccountEntry();
$accountEntry2->setAmount($amount);
$debitAccount = $unit->getAccountByType($debitCode2);
if (!$debitAccount) {
// Create debit account
$debitAccountType = $em->getRepository('App:AccountType')->findOneBy(['code' => $debitCode2]);
$debitAccount = new Account();
$debitAccount->setUnit($unit);
$debitAccount->setType($debitAccountType);
$em->persist($debitAccount);
}
$debitAccount->debit($amount);
$accountEntry2->setDebitAccount($debitAccount);
$creditAccount = $unit->getAccountByType($creditCode2);
if (!$creditAccount) {
// Create credit account
$creditAccountType = $em->getRepository('App:AccountType')->findOneBy(['code' => $creditCode2]);
$creditAccount = new Account();
$creditAccount->setUnit($unit);
$creditAccount->setType($creditAccountType);
$em->persist($creditAccount);
}
$creditAccount->credit($amount);
$accountEntry2->setCreditAccount($creditAccount);
$em->persist($accountEntry2);
$em->flush;
And here is the getAccountByType function:
/**
* Get Account by type of account.
*/
public function getAccountByType($code)
{
$filter = function ($account) use ($code) {
if ($account->getType()->getCode() == $code) {
return $account;
}
};
$accounts = $this->accounts->filter($filter)->getValues();
return isset($accounts[0]) ? $accounts[0] : null;
}
And, of course, 30 minutes after I posted the question, I found the answer myself (despite having been banging my head against the wall for a couple of days before posting).
Basically, we need to refresh the $unit entity after persisting/flushing the initial entry:
$em->refresh($unit);
Otherwise, the getAccountByType method apparently does not take into account the changes. So, it appears that entity methods do not take into account flushed changes to the database, if the entity is not refreshed. Probably basic stuff, but I did not know that. I hope this will save someone lots of trouble.
Related
I have been working around Bit-Wasp/bitcoin-php library for a while now and I encountered problems with it that I cannot resolve.
I have this as my code:
public function bitcoinWalletFromPublicKey($key, $index) {
$adapter = Bitcoin::getEcAdapter();
if (config('market.btc_network') == "mainnet") {
$btc = NetworkFactory::bitcoin();
$bitcoinPrefixes = new BitcoinRegistry();
} else {
$btc = NetworkFactory::bitcoinTestnet();
$bitcoinPrefixes = new BitcoinTestnetRegistry();
}
$slip132 = new Slip132(new KeyToScriptHelper($adapter));
$pubkeytype=substr($key, 0, 4);
if ($pubkeytype=='xpub' || $pubkeytype =='tpub') $pubPrefix = $slip132->p2pkh($bitcoinPrefixes);
if ($pubkeytype=='ypub') $pubPrefix = $slip132->p2shP2wpkh($bitcoinPrefixes);
if ($pubkeytype=='zpub' || $pubkeytype =='vpub') $pubPrefix = $slip132->p2wpkh($bitcoinPrefixes);
$config = new GlobalPrefixConfig([
new NetworkConfig($btc, [$pubPrefix])
]);
$serializer = new Base58ExtendedKeySerializer(
new ExtendedKeySerializer($adapter, $config)
);
$path = '0/' . $index;
$fkey = $serializer->parse($btc, $key);
$child_key = $fkey->derivePath($path);
#$account0Key = $child_key->derivePath("84'/0'/0'");
#$child_key = $fkey->derivePath("0/1");
//dd($child_key->getAddress(new AddressCreator())->getAddress());
return $child_key->getAddress(new AddressCreator())->getAddress();
}
I have two problems with this code:
Problem #1
On the first few lines of the code you will see that I used an If statement to check what network should it use. On my test im using testnet network and I'm sure as well that the code on my If / else { # code } works and it uses NetworkFactory::bitcoinTestnet() and new BitcoinTestnetRegistry() properly;
$key variable represents the Master Public Key of my user from Electrum wallet or whatever with a format of (xpub#########################/vpub#########################) or in my case since its on testnet it uses tpub######################### format. However, it returns an address with a format of bc#########, this means that its passing on mainnet network wherein it should be on testnet network.
Problem #2
On lower part of my code, I'm using $fkey = $serializer->parse($btc, $key); and $child_key = $fkey->derivePath($path) wherein $path = '0/' $index. $index here are just random numbers. It can be 0/1 or 0/99 or whatever 0/random.
Problem here is that somehow related to Problem #1, after it generates the wrong address, when I try to use this address for transaction my rpc returns an invalid address Error. As you can see as well I have a commented code $account0Key = $child_key->derivePath("84'/0'/0'"); wherein i got an error that it needs a private key instead of a public one. Now, my concern is that I do not want the users of the system i'm making to put their private keys whatsoever as it will might just compromise their wallets.
Basically, What I want to achieve using with this library from BitWasp is when a user put in their master public key from their wallet, my system would be able to then generate an address to be used for a btc transaction. Please help.
Passing the network inside the getAddress() method works
return $child_key->getAddress(new AddressCreator())->getAddress($btc);
I can see in the SWF Management Console a Workflow has around 18 events, with the 16th event being my ActivityTaskCompleted event however whenever i poll for decisions i only get up to the 15th event so i never get to call RespondDecisionTaskCompleted with the decision type CompleteWorkflowExecution as such my workflows are always sitting in the Active state until they timeout.
The flow i'm using is from a PHP SWF git i found a while ago, i unfortunately do not have the link to it anymore though.
$response = $this->swf->PollForDecisionTask($opts);
$decision_list = self::_decide(new HistoryEventIterator($this->swf, $opts, $response), $this->swf);
if(count($decision_list) > 0)
{
//Some decisions
}
else
{
//No decisions
}
Where the HistoryEventIterator looks like so
public function __construct(Aws\Swf\SwfClient $swf_client, $original_request, $original_response) {
$this->swf = $swf_client;
$this->events = array();
$this->event_index = 0;
$this->next_page_token = null;
$this->original_poll_request = $original_request;
$this->_process_poll_response($original_response);
$this->workflow_id = $original_response->get('workflowExecution')['workflowId'];
$this->run_id = $original_response->get('workflowExecution')['runId'];
}
protected function _process_poll_response($response) {
if ($response->hasKey("nextPageToken")) {
$this->next_page_token = (string) $response->get("nextPageToken");
} else {
$this->next_page_token = null;
}
$next_events = $response->get("events");
$next_events_object = new ArrayObject($next_events);
$next_events_copy = $next_events_object->getArrayCopy();
$this->events = array_merge($this->events, $next_events);
}
I have omitted error checking and functions of HistoryEventIterator that would not be called in this scenario.
I have output the next_page_token of HistoryEventIterator and found it was always NULL.
Should the RespondDecisionTaskCompleted called from an Activity reach the decider? If so, what could be the cause for mine not? Surely it wouldn't be paging after 15 events, and simply not paging correctly.
I can verify that the Domain, Activity Task List, and Decider Task List are accurate as the Workflow shows up in the SWF Management Console, as does the decisions and the Activity (The Activity even has the status Completed) There is appropriate error checking and Try/Catch blocks and in no cases are there any exceptions.
I'm on an app that retrieve datas (a 7k lines CSV formated string) from an external server to update my own entity. Each row is an item in a stock.
Today the job is nicely done but it's very very very slow: more than 60s (prod env) to retrieve datas, push it in a 2D array, update the BDD, and finally load a page that display the bdd content.
When only displaying the page it's about 20s (still prod).
This the profiler's timeline result while only displaying records : Symfony's profiler timeline
Anymore, i'm not able to profile the "updateAction" cause i't don't appear in the last ten request list.
2 days ago I was checking each row of the CSV file to add it only if needed, I was soft-deleting items to restore it later when back in the stock etc. but with that speed I tried many things to have normal performances.
At the begening everything was in the controler, I moved the function that add/remove in a dedicated service, then in the repository to finally get it back in my controler. To have decent results I tried to empty the database and then refill it without checking. First, using LOAD DATA LOCAL INFILE but it is not compatible with my table pattern (or I mis understood something) and now I'm simply emptying the table before filling it with the CSV (without any control). The time score I gave earlier was with this last try (which is the best one).
But enought talk
here is my controler:
public function majMatosClanAction()
{
$resMaj = $this->majClanCavernes();
if ($resMaj === NULL)
{
$this->get('session')->getFlashBag()->add('alert-danger', 'Unidentified');
return $this->redirect($this->generateUrl('loki_gbl'));
} else if ($resMaj === FALSE)
{
$this->get('session')->getFlashBag()->add('alert-warning','password update required');
return $this->redirect($this->generateUrl('loki_gbl_ST'));
} else
{
$this->get('session')->getFlashBag()->add('alert-success','success');
return $this->redirect($this->generateUrl('loki_gbl_voirMatosClan'));
}
}
here is the function that my controller call:
public function majClanCavernes()
{
$user = $this->get('security.token_storage')->getToken()->getUser();
$outils = $this->container->get('loki_gbl.outils');
if ($user !== NULL)
{
$pwd = $user->getGob()->getPwd();
$num = $user->getGob()->getNum();
if($outils->checkPwd($num, $pwd) !== TRUE) return FALSE;
$em = $this->getDoctrine()->getManager();
//This is a temporary solution
//////////////////////////////////////////////
$connection = $em->getConnection();
$platform = $connection->getDatabasePlatform();
$connection->executeUpdate($platform->getTruncateTableSQL('MatosClan', true ));
//////////////////////////////////////////////
$repository = $em->getRepository('LokiGblBundle:MatosClan');
$urlMatosClan = "http://ie.gobland.fr/IE_ClanCavernes.php?id=".$num."&passwd=".$pwd;
//encode and format the string via a service
$infosBrutes = $outils->fileGetInfosBrutes($urlMatosClan);
//$csv is a 2D array containing the datas
$csv = $outils->getDatasFromCsv($infosBrutes);
foreach($csv as $item)
{
$newItem = new MatosClan;
$newItem->setNum($item[0]);
$newItem->setType($item[1]);
[...]
$em->persist($newItem);
}
$em->flush();
return TRUE;
}
else{
return NULL;
}
}
What is wrong? 7k lines is not that big!
Could it be a lack of hardware issue?
Check out doctrine's batch processing documentation here.
You can also disable logging:
$em->getConnection()->getConfiguration()->setSQLLogger(null);
i am trying to insert new entity using PHP client library into datastore, i am using datastore_connect.php file from this example, https://github.com/amygdala/appengine_php_datastore_example
I want to insert entity with auto id, not the name. I see that there is function setId(), but i dont know how to generate proper id. Whats the best practice in doing so?
Thanks
function createKeyForTestItem () {
$path = new Google_Service_Datastore_KeyPathElement();
$path->setKind("testkind");
$path->setName("testkeyname");
//$path->setId(??)
$key = new Google_Service_Datastore_Key();
$key->setPath([$path]);
return $key;
}
You can have Cloud Datastore generate the ID for you by populating the insertAutoId field on the mutation instead of the upsert field.
Here's a code snippet (adapted from the datastore_connect.php file you posted):
function create_key() {
$path = new Google_Service_Datastore_KeyPathElement();
$path->setKind("testkind");
// Neither name nor ID is set.
$key = new Google_Service_Datastore_Key();
$key->setPath([$path]);
return $key;
}
function create_entity() {
$entity = new Google_Service_Datastore_Entity();
$entity->setKey(create_key());
// Add properties...
return $entity;
}
function create_commit_request() {
$entity = create_entity();
$mutation = new Google_Service_Datastore_Mutation();
$mutation->setInsertAutoId([$entity]); // Causes ID to be allocated.
$req = new Google_Service_Datastore_CommitRequest();
$req->setMode('NON_TRANSACTIONAL');
$req->setMutation($mutation);
return $req;
}
If you're looking for a PHP library to take away most of the headache of Cloud Datastore, you could try my new library, which sits on top of the official google-api-php-client:
https://github.com/tomwalder/php-gds
And here's a sample code snippet to create an Entity with an auto-generated ID
$obj_book = new GDS\Entity();
$obj_book->title = 'Romeo and Juliet';
$obj_book->author = 'William Shakespeare';
$obj_book->isbn = '1840224339';
// Write it to Datastore
$obj_book_store->upsert($obj_book);
More code snippets and documentation on GitHub.
I'm creating a version of an existing symfony php application which is to be used as sandbox, i.e. a sort of demo version of the app.
The two apps will use separate mysql schemas on the same server.
The two schemas are identical and the sandbox schema will be dropped and recreated with data from the main app at the start of each day.
During the day, users may be created/updated in the main app and I want these changes to be reflected in the sandbox app immediately - so I need to copy changes from about three related tables whenever they're changed in the main app.
I've considered creating triggers on the required tables in the main schema, but i'm having little luck finding examples of AFTER INSERT and AFTER UPDATE trigger_body that do something like.
I've considered modifying the Doctrine objects associated with the three tables to save via a separate Doctrine_Connection (for the sandbox dsn).
I've considered extending the sfDoctrineGuardPlugin in the main app to provide authentication for both apps, but this would still require a transfer of data from the three tables.
Is there any method I've not considered here? Which method would be best?
I solved this by doing the following in the sandbox codebase:
Implemented an alternative validator to sfGuardValidatorUser and pointed app.yml to this class with the auth_user_validator key.
Implemented a private validator method _checkMainApp which is called with the user-supplied credentials if the user is not found in the sandbox user table. It does the following:
set-up an ad-hoc connection to the main app db (working around some bugs symfony, doctrine)
query the main db for the user (and then restore the original db connection)
if user is found and the password is good, perform a deep copy of the object
finally, only the user and its profile should be copied and any other related object (available via the deep copied user) should be located in the sandbox db and should replace the existing relations - this is what _fixCopiedRelations does (in a rather cumbersome fashion).
# lib/validator/yiValidatorUserSandbox.class.php
protected function doClean($values)
{
// snip
// don't allow to sign in with an empty username
if ($username)
{
// snip
// user exists?
if ($user) {
// password is ok?
// snip
} else if ($user = $this->_checkMainApp($username, $password)) {
return array_merge($values, array('user' => $user));
}
}
// snip
}
private function _checkMainApp($username, $password)
{
$sandConn = Doctrine_Core::getTable('sfGuardUser')->getConnection();
$readOnlyConn = Doctrine_Manager::connection(
'mysql://root#localhost/maindb', 'readonly' # readonly is only the conn name, not its state
);
$user = Doctrine_Core::getTable('sfGuardUser')
->getAllUserDetailsUsingConnection($username, $readOnlyConn);
Doctrine_Manager::getInstance()->closeConnection($readOnlyConn);
Doctrine_Manager::getInstance()->setCurrentConnection($sandConn->getName());
if ( $user instanceof sfGuardUser && $user->getIsActive()
&& $user->checkPassword($password)
) {
$sandboxUser = $user->copy(true);
$this->_fixCopiedRelations($sandboxUser);
$sandboxUser->setPasswordHash($user['password']);
$sandboxUser->save($sandConn);
return $sandboxUser;
}
return false;
}
private function _fixCopiedRelations(Doctrine_Record $rec)
{
$rel = $rec->getReferences();
foreach ($rel as $name => $related) {
if ($name == 'Responsibilities') {
$coll = new Doctrine_Collection('Client');
foreach ($related as $client) {
$o = Doctrine_Core::getTable('Client')->findOneByCode($client['code']);
if ($o instanceof Client == false) {
throw new UnexpectedValueException(
'Cannot find related object in the sandbox database, therefore not copying sfGuardUser to the sandbox.'
);
}
$coll->add($o);
}
$rec[$name] = $coll;
} else if ($name == 'Groups' || $name == 'Permissions') {
$coll = new Doctrine_Collection(($name == 'Groups' ? 'sfGuardGroup' : 'sfGuardPermission'));
foreach ($related as $instance) {
$o = Doctrine_Core::getTable(($name == 'Groups' ? 'sfGuardGroup' : 'sfGuardPermission'))->findOneByName($instance['name']);
if ($o instanceof Doctrine_Record == false) {
throw new UnexpectedValueException(
'Cannot find related object in the sandbox database, therefore not copying sfGuardUser to the sandbox.'
);
}
$coll->add($o);
}
$rec[$name] = $coll;
} else if ($name == 'Profile') {
$this->_fixCopiedRelations($related);
} else {
throw new UnexpectedValueException(
'Method does not know how to copy this related object to the sandbox, therefore not copying sfGuardUser to the sandbox'
);
}
}
}
It's worth noting that, in order to prevent _fixCopiedRelations from failing, I have to make sure that any related objects that exists in the main db also exist in the sandbox db, but the creation of new such objects is very limited so it's not really a problem in this case.
I'm not particularly enamoured with the solution, but it works in this limited context and it's good enough.