I want to run simple bash scripts using laravel command class, but I have not found error.
If I go to /var/www/mysite/storage/app/scripts and run script from there in command line, then everything is OK.
$ sh remove-spaces.sh
What is wrong in my Laravel code?
lowercase.sh
for file in /var/www/mysite/storage/app/img/*;
do mv "$file" "`echo $file | tr '[A-Z]' '[a-z]'`";
done
remove-spaces.sh
for file in /var/www/mysite/storage/app/img/*;
do mv "$file" `echo $file | tr ' ' '-'`;
done
RenamePicturesCommand
namespace App\Console\Commands\Pictures;
use Illuminate\Console\Command;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
class RenamePicturesCommand extends Command
{
protected $removeSpaces;
protected $lowercase;
protected $signature = 'pictures:rename';
public function __construct()
{
parent::__construct();
$this->removeSpaces = new Process(['sh /var/www/mysite/storage/app/scripts/remove-spaces.sh']);
$this->lowercase = new Process(['sh /var/www/mysite/storage/app/scripts/lowercase.sh']);
}
public function handle()
{
$this->removeSpaces->run();
if (!$this->removeSpaces->isSuccessful()) {
throw new ProcessFailedException($this->removeSpaces);
}
echo $this->removeSpaces->getOutput();
$this->lowercase->run();
if (!$this->lowercase->isSuccessful()) {
throw new ProcessFailedException($this->lowercase);
}
echo $this->lowercase->getOutput();
}
}
error output
http#0bb690b74597:/var/www/mysite$ php artisan pictures:rename
Symfony\Component\Process\Exception\ProcessFailedException
The command "'sh /var/www/mysite/storage/app/scripts/remove-spaces.sh'" failed.
Exit Code: 127(Command not found)
Working directory: /var/www/mysite
Output:
================
Error Output:
================
sh: 1: exec: sh /var/www/mysite/storage/app/scripts/remove-spaces.sh: not found
at app/Console/Commands/Pictures/RenamePicturesCommand.php:59
55▕ // execute command
56▕ $this->removeSpaces->run();
57▕ // executes after the command finishes
58▕ if (!$this->removeSpaces->isSuccessful()) {
➜ 59▕ throw new ProcessFailedException($this->removeSpaces);
60▕ }
61▕ echo $this->removeSpaces->getOutput();
62▕
63▕
+13 vendor frames
14 artisan:37
Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
You may try to provide the command and argument as two separate items in the commands array
namespace App\Console\Commands\Pictures;
use Illuminate\Console\Command;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
class RenamePicturesCommand extends Command
{
protected $removeSpaces;
protected $lowercase;
protected $signature = 'pictures:rename';
public function __construct()
{
parent::__construct();
$this->removeSpaces = new Process(['sh', '/var/www/mysite/storage/app/scripts/remove-spaces.sh']);
$this->lowercase = new Process(['sh', '/var/www/mysite/storage/app/scripts/lowercase.sh']);
//Or you can use Process::fromShellCommandline
//$this->removeSpaces =Process::fromShellCommandline('sh /Volumes/samsung-250/Sites/stackoverflow/storage/app/scripts/lowercase.sh');
//$this->lowercase =Process::fromShellCommandline('sh /Volumes/samsung-250/Sites/stackoverflow/storage/app/scripts/remove-spaces.sh');
}
public function handle()
{
$this->removeSpaces->run();
if (!$this->removeSpaces->isSuccessful()) {
throw new ProcessFailedException($this->removeSpaces);
}
echo $this->removeSpaces->getOutput();
$this->lowercase->run();
if (!$this->lowercase->isSuccessful()) {
throw new ProcessFailedException($this->lowercase);
}
echo $this->lowercase->getOutput();
}
}
You can also achieve the same results with Storage facade without any shell script
namespace App\Console\Commands\Pictures;
use Illuminate\Console\Command;
use Illuminate\Support\LazyCollection;
class RenamePicturesCommand extends Command
{
protected $signature = 'pictures:rename';
public function handle()
{
$this->info('Renaming pictures...');
$this->newLine(2);
$files = LazyCollection::make(Storage::disk('local')->files('img'))
//Remove dotfiles
->reject(fn($filePath) => Str::startsWith($filePath, '.'));
$progress = $this->output->createProgressBar($files->count());
$progress->start();
$modified = [];
$files->each(function ($file) use ($progress, &$modified) {
$new = strtolower(preg_replace('/\s+/', '-', $file));
$temp = ['old' => $file, 'new' => $new, 'status' => 'success'];
if(! Storage::disk('local')->move($file, $new)) {
$temp['status'] = 'failed';
$temp['new'] = $file;
}
$modified[] = $temp;
$progress->advance();
});
$this->newLine(2);
$this->info('Finished Renaming pictures');
$this->newLine(2);
$this->table(['Old', 'New', 'Status'], $modified);
}
}
Related
I am using method post to create new data in xml file but The function c_element cannot be used in the function store
$DeTai = c_element('DeTai', $root);
This is my current code:
public function c_element($e_name, $parent)
{
global $xml;
$node = $xml->createElement($e_name);
$parent->appendChild($node);
return $node;
}
public function c_value($value, $parent)
{
global $xml;
$value = $xml->createTextNode($value);
$parent->appendChild($value);
return $value;
}
public function store(Request $request)
{
$xml = new DOMDocument("1.0","UTF-8");
$xml->load('../xml/QuanLyDoAnTotNghiep.xml');
if ($request->isMethod('post'))
{
$madt= $request->madt;
$noidungdetai = $request->noidungdetai;
$root=$xml->getElementsByTagName("QuanLyDoAnTotNghiep")->item(0);
$DeTai = c_element("DeTai", $root); //error in here
$s_madt = c_element('MaDT', $DeTai);
c_value("$madt", $s_madt);
$s_noidungdetai = c_element('NoiDungDeTai', $DeTai);
c_value("$noidungdetai", $s_noidungdetai);
$xml->formatOutput=true;
$xml->save('../xml/QuanLyDoAnTotNghiep.xml');
echo "Thêm mới thành công!!!";
}
}
use this keyword to call one method in different method of same class
$DeTai = $this->c_element('DeTai', $root);
to know more about it please visit this
Thanks..
I try to integrate Paubox with Laravel 5 as custom mail provider.
How can I do it in best way?
I installed https://github.com/Paubox/paubox-php bot there is no description how to connect it with Laravel.
I solve it by creating new service provider and Transport class.
Change default mail service provider in config/app.php:
// Illuminate\Mail\MailServiceProvider::class,
App\Providers\PauboxServiceProvider::class,
Add credentials to config/mail.php:
'pauboxApiKey' => env('PAUBOX_API_KEY'),
'pauboxApiUser' => env('PAUBOX_API_USER')
Changes in .env
MAIL_DRIVER=paubox
PAUBOX_API_KEY=
PAUBOX_API_USER=
Create new service provider app/Providers/PauboxServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Mail\MailServiceProvider;
class PauboxServiceProvider extends MailServiceProvider {
protected function registerSwiftTransport() {
$this->app->singleton('swift.transport', function ($app) {
return new PauboxTransportManager($app);
});
}
}
Create transport manager app/Providers/PauboxTransportManager.php
<?php
namespace App\Providers;
use Illuminate\Mail\TransportManager;
class PauboxTransportManager extends TransportManager {
protected function createPauboxDriver() {
return new PauboxTransport;
}
}
Create transport app/Providers/PauboxTransport.php
<?php
namespace App\Providers;
use Illuminate\Mail\Transport\Transport;
use Swift_Mime_SimpleMessage;
use App\Vendors\Paubox as Paubox;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Illuminate\Support\Facades\Log;
class PauboxTransport extends Transport {
protected $config;
public function __construct() {
$this->config = config('mail');
}
public function send(Swift_Mime_SimpleMessage $msg, &$failedRecipients = null) {
$paubox = new Paubox\Paubox($this->config['pauboxApiUser'], $this->config['pauboxApiKey']);
$message = new Paubox\Mail\Message();
$content = new Paubox\Mail\Content();
$header = new Paubox\Mail\Header();
$content->setHtmlText($msg->getBody());
$header->setSubject($msg->getSubject());
$header->setFrom('"' . $this->config['from']['name'] . '" <' . $this->config['from']['address'] . '>');
$header->setReplyTo($this->config['from']['address']);
$recipients = [];
foreach ($msg->getTo() as $to => $val) {
recipients[] = $to;
}
$message->setHeader($header);
$message->setContent($content);
$message->setRecipients($recipients);
$sendMessageResponse = new Paubox\Mail\SendMessageResponse();
$sendMessageResponse = $paubox->sendMessage($message);
$errorMsg = '';
if (isset($sendMessageResponse->errors)) {
foreach ($sendMessageResponse->errors as $error) {
$errorMsg .= json_encode($error);
}
Log::error(PHP_EOL . "Paubox: " . $errorMsg . PHP_EOL);
throw new UnprocessableEntityHttpException('Error occurred while sending email');
}
return $sendMessageResponse;
}
}
I copied https://github.com/Paubox/paubox-php into App\Vendors\Paubox. I had to do it bacause paubox installed with composer didn't read my .env data. After copy I had to change namespaces in all files, and add Paubox constructor to pass api_key and api_user:
public function __construct($pauboxApiUser, $pauboxApiKey)
{
$this->pauboxApiUser = $pauboxApiUser;
$this->pauboxApiKey = $pauboxApiKey;
}
and below in code:
change \getenv('PAUBOX_API_USER');
into $this->pauboxApiUser;
and change \getenv('PAUBOX_API_KEY');
into $this->pauboxApiKey;
A had to also install composer require nategood/httpful
My command like this :
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\ItemDetail;
class ImportItemDetail extends Command
{
protected $signature = 'sync:item-detail';
protected $description = 'sync item detail';
public function __construct()
{
parent::__construct();
ini_set('max_execution_time', 0);
ini_set('memory_limit', '-1');
}
public function handle()
{
$path = storage_path('json/ItemDetail.json');
$json = json_decode(file_get_contents($path), true);
foreach ($json['value'] as $value) {
ItemDetail::updateOrCreate(
[ 'entry_number' => $value['Entry_Number'] ],
[
'item_number' => $value['Item_Number'],
'description' => $value['Description'],
....
]
);
}
}
}
If I run the commmand, there exist error like this :
Fatal error: Out of memory (allocated 255852544) (tried to allocate 8192 bytes)
Whereas I had set ini_set('memory_limit', '-1'); in construct
How can I solve the error?
Note :
My record in the json file contains hundreds of thousands of data. So it takes a long time to check the field
Update
My ItemDetail.json like this :
{
"#odata.context":"http://myapp-svr01.www.myapp.com:1224/DynamicsNAV100/ODataV4/$metadata#Collection(NAV.ODATAITEM)","value":[
{
"#odata.etag":"W/\"JzE2O0lBQUFBQUNIbXdrQ0FBQAD5OzE4ODQ1MDQ4NjA7Jw==\"","Entry_Number":123,"Item_Number":"9805010101","Variant_Code":"039","Posting_Date":"2018-01-02T00:00:00Z","Entry_Type":"Sale","Description":"MISC BISBAN POLOS 11MM A847","Quantity":-7200,"Source_Type":"Customer","Order_Type":" ","Sales_Amount_Actual":1800000,"ETag":"16;IAAAAACHmwkCAAAA9;1884504860;"
},{
"#odata.etag":"W/\"JzE2O0lBQUFBQUNIbkFrQ0FBQAD5OzE4ODQ1MDQ5MTA7Jw==\"","Entry_Number":124,"Item_Number":"9805010102","Variant_Code":"100","Posting_Date":"2018-01-02T00:00:00Z","Entry_Type":"Sale","Description":"MISC BISBAN POLOS 11MM A915","Quantity":-7200,"Source_Type":"Customer","Order_Type":" ","Sales_Amount_Actual":1800000,"ETag":"16;IAAAAACHnAkCAAAA9;1884504910;"
}
]
}
I only gave 2 records in json above. Actually there are around 150,000 records
The problem is you're reading in the entire file at once, instead you should read it in chunks or line by line. For example:
$items = [];
$file = fopen(storage_path('json/ItemDetail.json'),'r');
while (!feof($file)){
$line = fgets($file);
$obj = json_decode($line);
// $obj is type stdClass
// transform $obj to fit the update or create method
ItemDetail::updateOrCreate( ... )
}
update
You should look into a streaming parser when dealing with large files.
jsonstreamingparser would be a good choice.
I have been banging my head for last 3 days to set cron for my website. In my cpanel I have mentioned a cron which will run every 5 minutes and command is
wget http://www.example.com/artisan cron:run
In my artisan I have added
Artisan::add(new CronRunCommand);
And CronRunCommand.php is
<?php
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class CronRunCommand extends Command {
protected $name = 'cron:run';
protected $description = 'Run the scheduler';
protected $timestamp;
protected $messages = array();
protected $runAt = '03:00';
public function __construct()
{
parent::__construct();
$this->timestamp = time();
}
public function fire()
{
$this->everyFiveMinutes(function()
{
// In the function, you can use anything that you can use everywhere else in Laravel.
$affectedRows = User::where('logged_in', true)->update(array('logged_in' => false)); // Not really useful, but possible
Artisan::call('auth:clear-reminders');
$this->messages[] = $affectedRows . ' users logged out';
});
$this->dailyAt('09:00', function()
{
Mail::send('hello', array(), function($message)
{
$message->to('admin#mydomain.com', 'Cron Job')->subject('I am still running!');
});
});
$this->finish();
}
protected function finish()
{
// Write execution time and messages to the log
$executionTime = round(((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000), 3);
Log::info('Cron: execution time: ' . $executionTime . ' | ' . implode(', ', $this->messages));
}
protected function yearly(callable $callback)
{
if(date('m', $this->timestamp) === '01' && date('d', $this->timestamp) === '01' && date('H:i', $this->timestamp) === $this->runAt) call_user_func($callback);
}
}
And in my email I am getting this message:
404 Not Found
2015-09-06 04:38:02 ERROR 404: Not Found.
--2015-09-06 04:38:02-- ftp://cron/run
=> “run”
Resolving cron... failed: Name or service not known.
wget: unable to resolve host address “cron”
This worked absolutely fine and I have started regular emails
wget http://www.example.com/protected/app/controllers/TestController.php
I have a CustomCommand_1 and a CustomCommand_2.
Any way to create a pipeline of commands and executing CustomCommand_2 right after CustomCommand_1 execution? (without call a command inside the other one).
You can use a callback to decide when something will or won't run, using when() or skip():
$schedule
->call('Mailer#BusinessDayMailer')
->weekdays()
->skip(function(TypeHintedDeciderClass $decider)
{
return $decider->isHoliday();
}
);
Referred: Event Scheduling and Commands & Handlers
You can also read how to add commands in queue here.
See, if that helps.
I could not find any way to do this, so I came up workaround (tested on laravel sync driver).
First, you have to create/adjust base command:
namespace App\Commands;
use Illuminate\Foundation\Bus\DispatchesCommands;
abstract class Command {
use DispatchesCommands;
/**
* #var Command[]
*/
protected $commands = [];
/**
* #param Command|Command[] $command
*/
public function addNextCommand($command) {
if (is_array($command)) {
foreach ($command as $item) {
$this->commands[] = $item;
}
} else {
$this->commands[] = $command;
}
}
public function handlingCommandFinished() {
if (!$this->commands)
return;
$command = array_shift($this->commands);
$command->addNextCommand($this->commands);
$this->dispatch($command);
}
}
Every command has to call $this->handlingCommandFinished(); when they finish execution.
With this, you can chain your commands:
$command = new FirstCommand();
$command->addNextCommand(new SecondCommand());
$command->addNextCommand(new ThirdCommand());
$this->dispatch($command);
Pipeline
Instead of calling handlingCommandFinished in each command, you can use command pipeline!
In App\Providers\BusServiceProvider::boot add:
$dispatcher->pipeThrough([
'App\Commands\Pipeline\ChainCommands'
]);
Add create App\Commands\Pipeline\ChainCommands:
class ChainCommands {
public function handle(Command $command, $next) {
$result = $next($command);
$command->handlingCommandFinished();
return $result;
}
}
What is stopping you from doing the following?
$this->dispatch(new CustomCommand_1);
$this->dispatch(new CustomCommand_2);
// And so on