Laravel only saves first item to database why? - php

public function download****()
{
// Downloads new externals (if updateable)
$site_id = 1;
$sinceLastId = $this->lastExternalSiteOrderId($site_id);
$api_version = config('services.*****.api_version');
$path = '/admin/api/' . $api_version . '/orders.json';
// GET request - Shopify.php (orders.json)
$result = $this->basicShopifyApi*****()->rest('GET', $path, ['since_id' => $sinceLastId]);
$orders = $result->body->orders;
if (!isset($orders)) {
return Response::json(['message' => 'No new ***** orders to download!'], 204);
} else {
foreach ($orders as $order) {
// Save to External Log
ExternalLog::create([
'data' => $order,
]);
// Gets updatable status
$updatable = $this->updateable($order->id);
// Saves new externals (create), if updateable
if ($updatable) {
$this->updateOrCreateExternals($order, $site_id);
}
}
}
}
// Updates a external role
public function updateOrCreateExternals($request, $site_id) {
// Saves external updates
$external = External::updateOrCreate(
[
'site_order_id' => ['order_id' => $request->id],
'site_id' => $site_id],
[
// 'order_id' => 0, // not required
'site_order_id' => $request->id,
'site_id' => $site_id, // Shopify is 1 (within ShopifyController this is correct)
'site_sub_id' => 0,
'site_account_id' => 0,
'data' => $request,
'site_order_status' => 0,
'order_created_at' => date("Y-m-d H:i:s", strtotime($request->created_at)),
'order_updated_at' => date("Y-m-d H:i:s", strtotime($request->updated_at)),
]);
echo "ShopifyExternalController#updateOrCreate function called!";
// Creates Customers, Orders, Carts, Payments for external, then set Site_Order_Status to 10
$shopifyOrderController = new shopifyOrderController();
$shopifyOrderController->updateOrCreateOrders($site_id);
}
This code works locally, but on dev (staging) server it only saves the first item?
Any ideas, why it won't work on the server?
It is shared hosting, cent os, PHP 7.2, not set up with Laravel Forge or vapor.
Any tips on writing YAML scripts to deploy a site correctly?
I have tried doing "composer dump-autoload" as I recently changed some of the controller names from lowercase to uppercase.

updateOrCreate
Updates only one model rather than many .

Related

How to create a file relation in TYPO3 v9 in a CLI command from Symfony?

I created a migrate command to import data from another cms to TYPO3 with the new Symfony commands in TYPO3 9.5.4. The only problem is to create a relation between my imported data and their images. The example from the TYPO3 documentation does not work for me.
I create my new entity (e.g. Product) and call my method "addBackendFileRelation" to create a file relation between the uploaded file (record exists in the db) and my new product (record exists in the db).
public function addBackendFileRelation( int $uid, string $table, string $field, File $file ): bool {
$resourceFactory = GeneralUtility::makeInstance( ResourceFactory::class );
try {
$fileObject = $resourceFactory->getFileObject( $file->getUid() );
$objectUid = $fileObject->getUid();
} catch ( FileDoesNotExistException $e ) {
return false;
}
$record = BackendUtility::getRecord( $table, $uid );
$newId = 'NEW1234';
$data = [];
$data['sys_file_reference'][$newId] = [
'table_local' => 'sys_file',
'uid_local' => $objectUid,
'tablenames' => $table,
'uid_foreign' => $uid,
'fieldname' => $field,
'pid' => $record['pid'],
];
$data[$table][$uid] = [
$field => $newId,
'pid' => $record['pid'],
];
$dataHandler = GeneralUtility::makeInstance( DataHandler::class );
$dataHandler->start( $data, [] );
$dataHandler->process_datamap();
foreach ( $dataHandler->errorLog as $log ) {
var_dump( $log );
}
$fileRefUid = $dataHandler->substNEWwithIDs[$newId];
return (int)$fileRefUid > 0;
}
The errorLog from the DataHandler prints this:
[1.2.1]: Attempt to modify table 'sys_file_reference' without permission.
So what can I do to create the relation? Creating a backend user for cli doesn't work for me.
[EDIT] The Solution
With the example of the tutorial (link from the comments) it works. I put the method call before I try to create the relation:
public function execute() {
// authenticate CommandLineUserAuthentication user for DataHandler usage
$GLOBALS['BE_USER']->backendCheckLogin();
// [...]
$this->addBackendFileRelation( $uid, $table, $field, $file );
// [...]
}
The solution is in the comment and the edited question. Just using $GLOBALS['BE_USER']->backendCheckLogin() before trying to use the DataHandler.

How to update column in table if api results have been modified

My application: My application allows users to predict the scores of all upcoming soccer games for the next matchday. I get this data from an API and store the upcoming games in my database. These upcoming games have a status of Scheduled. Now I want to run a cronjob every few minutes that checks if the status of those matches have been changed to in_play or finished, if this is the case I want to update my status field in my database for the correct match to the status field from the api.
How can I check if the status has been changed and modify the correct match in my database? I have a match_id stored which can maybe be used for this?
My code:
updateStatus job
public function handle()
{
$this->updateStatus();
}
public function updateStatus() {
$this->getMatches();
// check if status has been changed from schedulded to 'in_play' or 'finished'
// update the match status of the right matches in my database
}
public function getMatches() {
$client = new Client();
$uri = 'http://api.football-data.org/v2/competitions/PL/matches/?matchday=12&season=2018&matches';
$header = ['headers' => ['X-Auth-Token' => 'My-token']];
$res = $client->get($uri, $header);
return json_decode($res->getBody()->getContents(), true);
}
getMatches job (this job gets the api data and stores it in the database)
public function handle()
{
$this->saveMatches();
}
public function saveMatches()
{
$matches = $this->getMatches();
collect($matches['matches'])
->each(function ($match, $key) {
$date = new DateTime($match['utcDate']);
Match::create([
'match_id' => $match['id'],
'homeTeam' => $match['homeTeam']['name'],
'awayTeam' => $match['awayTeam']['name'],
'status' => $match['status'],
'date' => $date->format('Y-m-d'),
'time' => $date->format('H:i'),
'matchday' => $match['matchday'],
'homeScore'=> $match['score']['fullTime']['homeTeam'],
'awayScore'=> $match['score']['fullTime']['awayTeam']
]);
});
}
public function getMatches()
{
$client = new Client();
$uri = 'http://api.football-data.org/v2/competitions/PL/matches/?matchday=12&season=2018&matches';
$header = ['headers' => ['X-Auth-Token' => 'My-token']];
$res = $client->get($uri, $header);
return json_decode($res->getBody()->getContents(), true);
}
What you probably want to do is utilize Laravel's updateOrCreate() method on your Match object. The uniquely identifying information appears to be the match id. If this doesn't ever change, then when you are looping through your each statement you can do this:
Match::updateOrCreate([
'id' => $match['id'],
],[
'homeTeam' => $match['homeTeam']['name'],
'awayTeam' => $match['awayTeam']['name'],
'status' => $match['status'],
'date' => $date->format('Y-m-d'),
'time' => $date->format('H:i'),
'matchday' => $match['matchday'],
'homeScore'=> $match['score']['fullTime']['homeTeam'],
'awayScore'=> $match['score']['fullTime']['awayTeam']
]);
What this does is look for an existing match with this same ID. If it already exists, it will simply update it with all of the information provided by the API, including the status of the match and the scores. If it doesn't yet exist, it will instead create it and store it in the database as a new match with all of the provided information. Hope this helps!

Product Variants Data updated with last record Every time in Laravel 5.3

I'm trying to update my products with dynamic variants. There is no issue in adding or deleting but when I trying to update, every time it updates with the last record on every field.
I'm trying to update dynamic variants where I have inserted...
color - red
shape -square
It's about dynamic form-fields, inserts work properly but when I am trying to update it, it updates the first value with both the filled and the second value with both the field and I won't be able to distinguish the field because of the Blade file returns in form of an array.
When I am trying to update with any value it repeats the last value in both the fields, so the output looks like...
shape-square
shape-square
Controller
<?php
public function updateProducts($id, Request $request)
{
$featured = Input::has('featured') ? true : false;
$product = Product::findOrFail($id);
$product->update(array(
'product_name' => $request->input('product_name'),
'product_qty' => $request->input('product_qty'),
'product_sku' => $request->input('product_sku'),
'price' => $request->input('price'),
'reduced_price' => $request->input('reduced_price'),
'cat_id' => $request->input('cat_id'),
'brand_id' => $request->input('brand_id'),
'featured' => $featured,
'description' => $request->input('description'),
'product_spec' => $request->input('product_spec'),
));
$product->update($request->all());
$variants = VariantsOption::where('products_id', $id)->get();
$test = $request->all();
foreach ($variants as $v) {
$x = $v->id;
foreach ($test['variants_id'] as $key => $attrib) {
$var_name = $test['txt'][$key];
$varid = $attrib;
$variants = new VariantsOption;
$data = array(
'variants_id' => $varid,
'variants_name' => $var_name
);
$variants->where('id', '=', $x)->update($data);
}
}
return redirect('/admin/products/');
}
use following in your class
use Illuminate\Database\Eloquent\ModelNotFoundException;
OR add exception handler within your function
// Will return a ModelNotFoundException if no user with that id
try
{
$product = Product::findOrFail($id);
}
// catch(Exception $e) catch any exception
catch(ModelNotFoundException $e)
{
dd(get_class_methods($e)) // lists all available methods for exception object
dd($e)
}
It looks like product with that $id is not found so on update it is adding new product in database.

Laravel - keeping a running tally of a column through a transformer

So I have a Contribution model. I have a controller that pulls in all the contributions and sends them to a transformer like so:
My Controller:
public function showContributions (Request $request, $memberId)
{
$perPage = $request->input('per_page', 15);
$contribution = parent::getRepo('Contribution');
$contributions = Cache::tags(['contributions'])->remember("contributions.$memberId.2", 60, function() use ($perPage, $memberId, $contribution){
return $contribution->where('vip_id', $memberId)->where('fund_id', 2)->paginate($perPage);
});
$transformedData = $this->fractal->paginatedCollection($contributions, new ContributionTransformer(), 'contributions');
return $this->sendResponse($transformedData['contributions'], $transformedData['meta']);
}
My transformer:
public function transform(Contribution $contribution)
{
setlocale(LC_MONETARY, 'en_US.UTF-8'); // Set so that money_format uses the dollar sign instead of USD. Consider moving to bootstrap
$report = $contribution->report;
$employer = $report->employer;
$employerHours = $contribution->employerHours;
$contributionLocal = $contribution->local->local_code ?? '';
$employerLocal = $employerHours->local->local_code ?? '';
$reciprocalLocal = $contributionLocal === $employerLocal ? '0000' : $employerLocal;
$response = [
'id' => $contribution->member_hours_id,
'report_number' => $contribution->report_number_id,
'employer_code' => $employer->employer_code,
'employer_name' => $employer->employer_name,
'worked_date' => $report->ending_worked_date,
'received_date' => $report->receipt_date,
'report_local' => $contributionLocal,
'reciprocal_local' => $reciprocalLocal,
'unit_type' => $contribution->unitType->code_description,
'units_worked' => $contribution->units_worked,
'credited_units' => $contribution->units_credited,
'rate' => $contribution->unit_multiplier,
'reciprocal_rate' => $employerHours->reciprocal_multiplier,
'calculated_amount' => money_format('%.2n', $contribution->calculated_amount),
'received_amount' => money_format('%.2n', $contribution->actual_amount),
'owed_amount' => money_format('%.2n', $contribution->owed_amount),
];
return $response;
}
One of the fields in the contributions table is sub_hours. What they want me to do is keep a running tally of said field. In each subsequent row return that tally as hours_to_date. So in first row sub_hours is 32 and in the second row it is 60. In the first row hours_to_date will be 32 but in the second row it will be 92 and the third row it will be 92 + sub_hours of row 3 etc. I can't seem to figure out how I should keep track of this running tally and allow the transformer access to it. Any help would be appreciated.
Can you create a property on the transformer class? I haven't used transformers but something like
class ContributionTransformer{
private $tally;
function __construct(){
$this->tally = 0;
}
public function transform(Contribution $contribution){
...
$this->tally += $contribution->sub_hours;
...
}

Yii SQL Profiling of AJAX calls

Yii offers a setting that profiles all the SQL calls with the execution time of each (CProfileLogRoute). Except it does not work for ajax calls. How can I access this data?
I am trying to find the bottleneck of an ajax call that opens up a login popup...
On a similar note, does the execution time shown in CProfileLogRoute includes the network trip to the SQL server if any? My database is hosted by Amazon's RDS, and I want to know if that's where the bottleneck is (it seems fine locally).
You can try to extend CFileLogRoute in the manner proposed below and enable it in application's configuration like this:
'log'=>array(
'class'=>'CLogRouter',
'routes'=>array(
array(
'class'=>'ProfileLogRoute',
'levels' => "profile"
)
)
)
In this case all profiled queries will be written to a log file located in protected/runtime directory (overriding of processLogs method is implemented likewise summary report of CProfileWebRoute...unfortunately CProfileWebRoute is derived from CWebLogRoute):
<?php
class ProfileLogRoute extends CFileLogRoute
{
protected function processLogs($logs)
{
$logFile=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile();
if(#filesize($logFile)>$this->getMaxFileSize()*1024)
$this->rotateFiles();
$fp=#fopen($logFile,'a');
#flock($fp,LOCK_EX);
$profileStack = array();
$profileResults = array();
foreach ($logs as $log)
{
if ($log[1] === CLogger::LEVEL_PROFILE)
{
$message = $log[0];
if (!strncasecmp($message, 'begin:', 6))
{
$log[0] = substr($message,6);
$profileStack[] = $log;
}
else if(!strncasecmp($message, 'end:', 4))
{
$token = substr($message,4);
if(($last = array_pop($profileStack)) !== null && $last[0] === $token)
{
$info = array(
'delta' => $log[3] - $last[3],
'category' => $last[2],
'time' => $last[3]
);
$this->aggregateResult($token, $info, $profileResults);
}
else
{
throw new CException(Yii::t('yii','CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.',
array('{token}'=>$token)));
}
}
}
else
{
#fwrite($fp,$this->formatLogMessage($log[0],$log[1],$log[2],$log[3]));
}
}
if (!empty($profileResults))
{
$now = microtime(true);
while(($last = array_pop($profileStack)) !== null)
{
$info = array(
'delta' => $now - $last[3],
'category' => $last[2],
'time' => $last[3]
);
$token = $last[0];
$this->aggregateResult($token, $info, $profileResults);
}
$entries = array_values($profileResults);
$func = create_function('$a,$b','return $a["total"]<$b["total"]?1:0;');
usort($entries, $func);
foreach ($entries as $entry)
{
$message = sprintf("Min: % 11.8f Max: % 11.8f Total: % 11.8f Calls: % 3d %s", $entry['min'], $entry['max'], $entry['total'], $entry['calls'], $entry['token']);
#fwrite($fp, $this->formatLogMessage($message, CLogger::LEVEL_PROFILE, $entry['category'], $entry['time']));
}
}
#flock($fp,LOCK_UN);
#fclose($fp);
}
protected function aggregateResult($token, $info, &$results)
{
if (isset($results[$token]))
{
if ($info['delta'] < $results[$token]['min'])
$results[$token]['min'] = $info['delta'];
else if($info['delta'] > $results[$token]['max'])
$results[$token]['max'] = $info['delta'];
$results[$token]['calls']++;
$results[$token]['total'] += $info['delta'];
return;
}
$results[$token] = array(
'token' => $token,
'calls' => 1,
'min' => $info['delta'],
'max' => $info['delta'],
'total' => $info['delta'],
'category' => $info['category'],
'time' => $info['time']
);
}
}
If you want to know how Yii measures a time of SQL execution you can look at the source code of CDbCommand class - queryInternal method, to be more precise. Profiling is between Yii::beginProfile('system.db.CDbCommand.query...') and Yii::endProfile('system.db.CDbCommand.query...') calls. As you can see both these calls are within the same method, as the result the time of profiling doesn't include a network transfer. Probably, the issue is that your remote database server runs under huge load.
There is a property of the CProfileLogRoute class called ignoreAjaxInFireBug which defaults to "true" by setting it to false you can get your profiling info on ajax requests.
http://www.yiiframework.com/doc/api/1.1/CWebLogRoute#ignoreAjaxInFireBug-detail
'log'=>array(
'class'=>'CLogRouter',
'routes'=>array(
array(
'class'=>'CProfileLogRoute',
'ignoreAjaxInFireBug'=>false,
'levels'=>'error, warning, trace, info, profile',
),
),
I don't believe ou need to use firebug for that work (it's kind of a misnomer). If you do however, just download firefox and install firebug and then your problem is still solved.

Categories