Laravel: Do one query update and then, if that succeeds, do the next, otherwise, do neither - laravel-5

So, I want to update two different user accounts with the same value.
Scenario: User 1 transfers money to User 2:
$transfer_from = $account->decrement('balance', $amount);
$transfer_to = Account::where('user_wallet_address', $receiver_wallet_address)->increment('balance', $amount);
But, what I want, is something like this:
$account->decrement('balance', $amount)->$transfer_to;
As in if the decrement succeeds, then update the other user's balance, otherwise, both should fail
Thoughts?

Please use database transaction feature
See below code example:
public function readRecord() {
DB::beginTransaction();
try {
// put your code here in try block
$transfer_from = $account->decrement('balance', $amount);
$transfer_to = Account::where('user_wallet_address', $receiver_wallet_address)->increment('balance', $amount);
DB::commit();
} catch (Exception $ex) {
// If any failure case generate, it will rollback the DB operations automatically.
DB::rollBack();
echo $ex->getMessage() . $ex->getLine();
}
I hope this will help you. In this case if any failure case generate it will automatic revert the DB translocation and you don't need to do anything.
Reference URL: https://laravel.com/docs/5.7/database

Related

Laravel 8 - Conditionally remember a value in cache [duplicate]

I'm developing one of my first applications with the Laravel 4 framework (which, by the way, is a joy to design with). For one component, there is an AJAX request to query an external server. The issue is, I want to cache these responses for a certain period of time only if they are successful.
Laravel has the Cache::remember() function, but the issue is there seems to be no "failed" mode (at least, none described in their documentation) where a cache would not be stored.
For example, take this simplified function:
try {
$server->query();
} catch (Exception $e) {
return Response::json('error', 400);
}
I would like to use Cache::remember on the output of this, but only if no Exception was thrown. I can think of some less-than-elegant ways to do this, but I would think that Laravel, being such an... eloquent... framework, would have a better way. Any help? Thanks!
This is what worked for me:
if (Cache::has($key)) {
$data = Cache::get($key);
} else {
try {
$data = longQueryOrProcess($key);
Cache::forever($key, $data); // only stored when no error
} catch (Exception $e) {
// deal with error, nothing cached
}
}
And of course you could use Cache::put($key, $data, $minutes); instead of forever
I found this question, because I was looking for an answer about this topic.
In the meanwhile I found a solution and like to share it here:
(also check out example 2 further on in the code)
<?php
/**
* Caching the query - Example 1
*/
function cacheQuery_v1($server)
{
// Set the time in minutes for the cache
$minutes = 10;
// Check if the query is cached
if (Cache::has('db_query'))
{
return Cache::get('db_query');
}
// Else run the query and cache the data or return the
// error response if an exception was catched
try
{
$query = $server->query(...);
}
catch (Exception $e)
{
return Response::json('error', 400);
}
// Cache the query output
Cache::put('db_query', $query, $minutes);
return $query;
}
/**
* Caching the query - Example 2
*/
function cacheQuery_v2($server)
{
// Set the time in minutes for the cache
$minutes = 10;
// Try to get the cached data. Else run the query and cache the output.
$query = Cache::remember('db_query', $minutes, function() use ($server) {
return $server->query(...);
});
// Check if the $query is NULL or returned output
if (empty($query))
{
return Response::json('error', 400);
}
return $query;
}
I recommend you to use Laravel's Eloquent ORM and/or the Query Builder to operate with the Database.
Happy coding!
We're working around this by storing the last good value in Cache::forever(). If there's an error in the cache update callback, we just pull the last value out of the forever key. If it's successful, we update the forever key.

LockForUpdate causes Deadlock if table is empty

Currently I am trying to upload some images parallel with dropzone.js.
This is part of the code, that handles the upload.
\DB::beginTransaction();
$maxPos = GalleryImage::lockForUpdate()->max('pos');
try {
// some other code
// ....
$galleryImage->pos = $maxPos + 1;
$galleryImage->save();
} catch (\Exception $e) {
\DB::rollBack();
\Log::error($e->getMessage());
return response('An error occured', 500);
}
\DB::commit();
If I do not use the lockForUpdate() I end up with duplicate positions in database.
The problem with above solution ist, that if the table is empty I get the error:
Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction
But only for the second image I am uploading.
Additionally the autoincrement id skips the value 2 and goes like 1,3,4,5,6,7, ...
The Positions in the table are shown correctly 1,2,3,4....
I think the problem has to do with the table beeing empty initially, as I did not notice this problem, when there are already some entries in the table.
Any suggestions what I am doing wrong? Or Maybe using lockForUpdate() in combination with an aggregate function is wrong at all...
I recommend a different approach to transactions.
try {
app('db')->transaction(function ()) {
$maxPos = GalleryImage::lockForUpdate()->max('pos');
// other code (...)
$galleryImage->pos = $maxPos + 1;
$galleryImage->save();
}
} catch (\Exception $e) {
// display an error to user
}
Should any exception occurs inside the callback, the transaction will rollback and free any locks.

WebApi + OData: limit maximum results

I have a WebAPI controller that takes an ODataOptions parameter.
I want to make sure the user can't download the whole database in one swoop.
So I validated the options object:
public IHttpActionResult Get(ODataQueryOptions<ViewModel> options)
{
var oDataValidationSettings = new ODataValidationSettings
{
MaxTop = 100
}
try
{
options.Validate(oDataValidationSettings);
}
catch (ODataException ex)
{
return BadRequest("OData query validation failed: " + ex.Message);
}
//return results
}
This works great for calls like
http://host/api/controller?$filter=...&$top=1000
This returns the expected validation error message.
But it is trivially easy to circumvent by simply making a request to:
http://host/api/controller?
No $top, no nothing. This in effect returns the whole table!
The validator is not triggered if the $top parameter is not specified at all.
I could append a .Take(100) when constructing the query from the oData options, but it seems hacky.
Is there any better way to deal with a missing $top?
You can try to use PageSize which will limit the number of entity been returned.
Refer to this example for how to use it.
https://github.com/OData/ODataSamples/tree/master/WebApi/v4/ODataPagingSample

parse background job each() does not go through all records?

I wrote a cloud job to insert a lowercase version of a user's first name and last name in the user object so that I can perform a search from my client app.
When I did run this job on a dev parse app that had like 40 users it worked great.
When I did run this on my production app however it did not update all of my records. I have a few thousands users at the moment and I was expecting this cloud job to take care of all of them as explained here:
http://blog.parse.com/2013/05/13/launch-and-iterate-parse-data-migrations/
" The new each method on Parse.Query objects allows you to do just that. Even if you have tens of thousands of objects or more in a collection, it will return each one of them, giving you an opportunity to modify them as you see fit."
So then my question is why does this function leave behind over half of my database? It only worked on a bunch of users, maybe a few hundred.
How can I make this function affect my WHOLE DATASET in the User table?
Parse.Cloud.job("migration1", function(request, status) {
// Set up to modify user data
Parse.Cloud.useMasterKey();
// Query for all users
var query = new Parse.Query(Parse.User);
query.each(function(user) {
// Set and save the change
var firstname = user.get("firstname");
if(firstname) {
user.set("searchfirstname", firstname.toLowerCase());
} else {
user.set("searchfirstname", "");
}
var lastname = user.get("lastname");
if(lastname) {
user.set("searchlastname", lastname.toLowerCase());
} else {
user.set("searchlastname", "");
}
return user.save();
}).then(function() {
// Set the job's success status
status.success("search migration completed successfully.");
}, function(error) {
// Set the job's error status
status.error("Uh oh, something went wrong with search migration");
});
});
EDIT:
Ok so when I look at the error logs I see this:
E2014-10-18T15:48:49.984Z] v63: Ran job searchMigration with:
Input: {}
Failed with: TypeError: Cannot call method 'toLowerCase' of undefined
I tried to check for undefined in any way I could think of and I still get the same problem.
What I tried to check for undefined is this:
if(lastname === undefined || lastname === void 0 || typeof lastname == 'undefined') ...
I still get the toLowerCase of undefined error and I think that is why the job does not affect all of my user table since it stops...
Any suggestions?
So, I finally figured out why the job did not go through all the records but it would stop after a few records "randomly"...
I had an AfterSave hook for the user which was triggered at each iteration of the above job...
In that after save it would generate an error at times and make the Job fail.
So, for some reason I thought the after save hook would have not been triggered by a save done on the user while inside of a Job.
That's it. Now it all works.

unassign order status from status

I need to unassign status from state in migration.
$status->setStatus('approved')
->unassignState(Mage_Sales_Model_Order::STATE_NEW)
->assignState(Mage_Sales_Model_Order::STATE_PROCESSING, false)
->save();
If status is assigned to migration, it works. But if state is not assigned, there is exception and
migrations fails.
What is the best way to solve this problem?
The only solution that occured to me, is to create function trapper that catches exception. I this case
it seems that migration works even if status is not assigned to state.
function unAssignStatusFromState($status, $state)
{
try {
$status->unassignState($state);
return true;
} catch(Exception $e) {
return false;
}
}

Resources