How to find started transaction? - laravel

Controller code:
public function update(ContractTypeStoreAndUpdateRequest $request, ContractType $contractType): ContractTypeResource
{
$contractType->is_active = $request->input('is_active');
$contractType->name = $request->input('name');
$contractType->short_name = $request->input('short_name');
$contractType->description = $request->input('description');
$pdo = DB::connection()->getPdo();
$pdo->exec('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
DB::beginTransaction();
if ($request->has('feature')) {
if ($contractType->contracts()->count() !== 0) {
throw ValidationException::withMessages(
[
'feature' => 'Тип договора имеет привязанный договор изменить невозможно',
]
);
}
$contractType->feature = $request->input('feature');
}
$contractType->save();
DB::commit();
return ContractTypeResource::make($contractType);
}
When I run test on this enpoint I am getting the following error:
Active sql transaction: 1568 Transaction characteristics can't be
changed while a transaction is in progress
I understand the error message but I cannot assume where transaction might start before?
Do Laravel under the hood do something with transaction (validation, authentication)?
How to find the place where transaction has started before?

I got the reason.
My tests use this trait \Illuminate\Foundation\Testing\DatabaseTransactions
It covers test in transaction. In the end does rollback to keep database clean.

Related

Laravel queues Call to a member function storeAs() on string

I am trying to store heavy image through the laravel queue as follows because i dont want my user to wait until file gets stored:
This code is in controller
if($request->hasfile('featuringPhoto'))
{
$prefixFilename = date("Y-m-d-H-i-s");
$coverFilename = $prefixFilename.$request->featuringPhoto->getClientOriginalName();
ProceessFiles::dispatch($request->featuringPhoto->getRealPath(),$coverFilename);
}
else
{
$coverFilename4 = NULL;
}
Below code is in job
protected $files, $filename;
public function __construct($files,$filename)
{
$this->files= $files;
$this->filename = $filename;
}
public function handle()
{
if($this->files){
$coverFilename = $prefixFilename.$this->filename;
$img = file_get_contents($this->files);
$img->storeAs('images/photosUploadedByUser', $coverFilename, 'public');
}
}
It Gives me an error saying Call to a member function storeAs() on string
I tried this solution from stack but didnt work
Any suggestion will be appreciated.Thank you.
I think it is wrong to assume you are gonna save a lot time by executing the save operation in a queue, also because you are already fetching it from the web server. Queues will with scaling often be moved to worker servers and with this approach this will not work.
In the spirit of the question, stackoverflow and to explain to you what is not working. file_get_contents() returns the content of the file as a string. So to fix your problem, you should just store the results of that. You obviously can not call methods on strings.
$coverFilename = $prefixFilename.$this->filename;
$img = file_get_contents($this->files);
Storage::put('images/photosUploadedByUser/' . $coverFilename, $img);

Laravel notify user with results when batched jobs have finished

I want to email a potentially large number of clients, so I am using Batches and pushing each email send as a batched job, like this:
public function __construct(RunReport $runReport, User $run_by) {
$this->runReport = $runReport;
$this->run_by = $run_by;
}
public function handle()
{
$company_detail = CompanyDetail::first();
$jobs = $this->runReport->runReportReportees
->map(fn(RunReportReportee $runReportReportee) => new EmailStatementJob($runReportReportee, $company_detail))
->toArray();
$batch = Bus
::batch($jobs)
->then(function(Batch $batch) {
// All completed
$completed = ($batch->totalJobs - $batch->failedJobs);
$message = "foo";
$type = "bar";
$this->run_by->notify(new GenericNotification($message, $type, 'envelope'));
})
->allowFailures()
->name('Batch name here trust me')
->dispatch();
return $batch->id;
}
However the notify line causes an error Serialization of 'Doctrine\DBAL\Driver\PDOConnection' is not allowed.
How can I notify the user that initiated the batch when the batch is finished, with the results of the included email send attempts? Alternatively, how else should I email a few hundred clients and notify the user of the results?
I figured it out eventually with help - I was using $this in the ->then() callback which was my first mistake. Then instead of the notification in the job, I have instead stored the user ID and passed it to the then() callback like this
->then(function(Batch $batch) use ($run_by_id) { ... }
In the callback instead of notifying the user, I call an event
event(new HcpStatementsBatchFinishedEvent($batch, $id));
The event simply stores the information
public function __construct(Batch $batch, int $run_by_sysid)
{
$this->run_by = User::find($run_by_id);
$this->batch = $batch;
}
And the listener for the event builds the message and notifies the user.

Multiple db transaction in one queue laravel 6.5.0

Anyone have experience using laravel job with multiple database transaction in one queue?
does laravel support it.
I have an issue, i have 2 different database connection which is database A and database B, when i dispatch my first job using database A everything is working, all my data insert correctly, but when i dispatch my second job using database B , the transaction does not commit and no data insert at all
i need to restart the worker then it will work again.
im using redis as queue driver.
Database A Transaction
$valid = true;
DB::beginTransaction();
try {
//some logic happen here if got error $valid will be false
//if valid true commit else rollback
$valid ? DB::commit() : DB::rollback();
} catch (Exception $e) {
$valid = false;
DB::rollback();
}
Database B Transaction
$valid = true;
DB::beginTransaction('b-connection');
try {
//some logic happen here if got error $valid will be false
//if valid true commit else rollback
$valid ? DB::connection('b-connection')->commit() : DB::connection('b-connection')->rollback();
} catch (Exception $e) {
$valid = false;
DB::connection('b-connection')->rollback();
}
How I dispatch job
DeliveryOrderJob::dispatch($data)->onConnection('swift')->onQueue('do');
Basically both using the same logic, the only different is database connection. Please help.
You don't need to write rollback function in try{}. If that logic will fail or any other error occur then the catch Exception will rollback it.
\DB::connection('b-connection')->beginTransaction();
try {
\DB::connection('b-connection')->commit();
}catch (Exception $e) {
$valid = false;
DB::connection('b-connection')->rollback();
}

Writing Logs from Console Shell

I have been using CakePHP 2.4.4 to build the interactive web part of my app and that is going very well. CakePHP is awesome.
I am now doing some supporting background processes. The Console and Shell seems to be the way to do it as it has access to the Models.
I have written the code and have it working but I am trying to write to the same log that I use for the Models. In the models I have an afterSave function to log all the database changes and I just used the $this->log("$model $logEntry", 'info'); to write to the log.
That doesn't work in the Shell but I thought the CakeLog::write('info', "$model $logEntry"); might work but it doesn't either.
Do I need to initialise the CakeLog to point to the correct log files?
<?php
App::uses('CakeTime', 'Utility');
App::uses('CakeLog', 'Utility');
class ProcessRequestShell extends AppShell {
//Need to access the request and monitor tables
public $uses = array('Request');
private function updateRequest($data){
$model = 'Request';
$result = $this->Request->save($data);
$logEntry = "UPDATE ProcessRequestShell ";
foreach ($data[$model] AS $k => $v){$logEntry .= "$k='$v' ";}
if ($result){
//$this->log("$model $logEntry", 'info');
CakeLog::write('info', "$model $logEntry");
} else {
//$this->log("$model FAILED $logEntry", 'error');
CakeLog::write('error', "$model FAILED $logEntry");
}
return($result);
}
public function main() {
$options = array('conditions' => array('state' => 0, 'next_state' => 1));
$this->Request->recursive = 0;
$requests = $this->Request->find('all', $options);
//See if the apply_changes_on date/time is past
foreach ($requests AS $request){
$this->out("Updating request ".$request['Request']['id'], 1, Shell::NORMAL);
//Update the next_state to "ready"
$request['Request']['state'] = 1;
$request['Request']['next_state'] = 2;
$this->updateRequest($request);
}
}
}
?>
Did you set up a default listener/scope for those log types?
If not, they will not get logged.
// Setup a 'default' cache configuration for use in the application.
Cache::config('default', array('engine' => 'File'));
In your bootstrap.php for example
See http://book.cakephp.org/2.0/en/appendices/2-2-migration-guide.html#log
You need to setup default log stream writing to file, eventually, in app/Config/bootstrap.php.
CakeLog does not auto-configure itself anymore. As a result log files
will not be auto-created anymore if no stream is listening. Make sure
you got at least one default stream set up, if you want to listen to
all types and levels. Usually, you can just set the core FileLog class
to output into app/tmp/logs/:
CakeLog::config('default', array(
'engine' => 'File'
));
See Logging → Writing to logs section of the CookBook 2.x

Code Igniter - error when trying to config database.php to use PDO driver

I am trying to get the new PDO driver running in Code Igniter 2.1.1 in (to start with) the local (Mac OS 10.7) copy of my app.
I initially coded it using Active Record for all db operations, and I am now thinking I want to use PDO prepared statements in my model files, going forward.
I modified 'application/config/database.php' like so:
(note a couple minor embedded questions)
[snip]
$active_group = 'local_dev';
$active_record = TRUE;//<---BTW, will this need to stay TRUE to make CI sessions work? For better security, don't we want db-based CI sessions to use PDO too?
//http://codeigniter.com/user_guide/database/configuration.html:
//Note: that some CodeIgniter classes such as Sessions require Active Records be enabled to access certain functionality.
//this is the config setting that I am guessing (?) is my main problem:
$db['local_dev']['hostname'] = 'localhost:/tmp/mysql.sock';
// 1.) if $db['local_dev']['dbdriver']='mysql', then here ^^^ 'localhost:/tmp/mysql.sock' works, 2.) but if $db['local_dev']['dbdriver']='pdo', then it fails with error msg. shown below.
$db['local_dev']['username'] = 'root';
$db['local_dev']['password'] = '';
$db['local_dev']['database'] = 'mydbname';
$db['local_dev']['dbdriver'] = 'pdo';
$db['local_dev']['dbprefix'] = '';
$db['local_dev']['pconnect'] = TRUE;
$db['local_dev']['db_debug'] = TRUE;//TRUE
$db['local_dev']['cache_on'] = FALSE;
$db['local_dev']['cachedir'] = '';
$db['local_dev']['char_set'] = 'utf8';
$db['local_dev']['dbcollat'] = 'utf8_general_ci';
$db['local_dev']['swap_pre'] = '';
$db['local_dev']['autoinit'] = TRUE;
$db['local_dev']['stricton'] = FALSE;
[snip]
With the above config., as soon as I load a controller, I get this error message:
Fatal error: Uncaught exception 'PDOException' with message 'could not find driver' in
/Library/WebServer/Documents/system/database/drivers/pdo/pdo_driver.php:114 Stack trace: #0
/Library/WebServer/Documents/system/database/drivers/pdo/pdo_driver.php(114): PDO->__construct('localhost:/tmp/...', 'root', '', Array) #1 /Library/WebServer/Documents/system/database/DB_driver.php(115): CI_DB_pdo_driver->db_pconnect() #2
/Library/WebServer/Documents/system/database/DB.php(148): CI_DB_driver->initialize() #3
/Library/WebServer/Documents/system/core/Loader.php(346): DB('', NULL) #4
/Library/WebServer/Documents/system/core/Loader.php(1171): CI_Loader->database() #5
/Library/WebServer/Documents/system/core/Loader.php(152): CI_Loader->_ci_autoloader() #6
/Library/WebServer/Documents/system/core/Con in
/Library/WebServer/Documents/system/database/drivers/pdo/pdo_driver.php on line 114
I tried swapping out the 'pdo_driver.php' file from the one on github, as per this:
http://codeigniter.com/forums/viewthread/206124/
...but that just generates other errors, not to mention is disturbing to a newbie who does not want to touch the system files if at all possible.
This thread also seems to imply the need to be hacking the 'pdo_driver.php' system file:
CodeIgniter PDO database driver not working
It seems odd to me, though, that (someone thought that) a hack to a system file is needed to make PDO work in CI v.2.1.1, huh?
Thanks for any suggestions I can try.
I don't know if this might be helpful for you since you already started using the CI functions, but I made my own library for PDO with sqlite and just auto load it. My needs were simple, so it serves its purpose.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* CodeIgniter PDO Library
*
*
* #author Michael Cruz
* #version 1.0
*/
class Sqlite_pdo
{
var $DB;
public function connect($path) {
try {
$this->DB = new PDO('sqlite:' . $path);
}
catch(PDOException $e) {
print "Error: " . $e->getMessage();
die();
}
}
public function simple_query($SQL) {
$results = $this->DB->query($SQL)
or die('SQL Error: ' . print_r($this->DB->errorInfo()));
return $results;
}
public function prepared_query($SQL, $bind = array()) {
$q = $this->DB->prepare($SQL)
or die('Prepare Error: ' . print_r($this->DB->errorInfo()));
$q->execute($bind)
or die('Execute Error: ' . print_r($this->DB->errorInfo()));
$q->setFetchMode(PDO::FETCH_BOTH);
return $q;
}
public function my_prepare($SQL) {
$q = $this->DB->prepare($SQL)
or die('Error: ' . print_r($this->DB->errorInfo()));
return $q;
}
public function my_execute($q, $bind) {
$q->execute($bind)
or die('Error: ' . print_r($this->DB->errorInfo()));
$q->setFetchMode(PDO::FETCH_BOTH);
return $q;
}
public function last_insert_id() {
return $this->DB->lastInsertId();
}
}
/* End of file Sqlite_pdo.php */
thanks to the noob thread http://codeigniter.com/forums/viewthread/180277/ (InsiteFX’s answer)..
I figured out the below seems to work (need to test more to be 100%... but at least the error messages are gone:
$db['local_dev']['hostname'] = 'mysql:host=127.0.0.1';

Resources