I am developing a web application using Laravel-5.8. Also, I am using guzzlehttp/guzzle-6.3 to consume an external api and save it in my local database.
travelupdate.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp;
use GuzzleHttp\Client;
use App\User;
use App\Activity;
use Avatar;
use Storage;
use App\Travel;
class travelsupdate extends Command {
protected $signature = 'command:travelsupdate';
protected $description = 'travelsupdate';
public function __construct() {
parent::__construct();
}
public function handle()
{
$client = new Client();
$res = $client->request('GET','https://api.abcdef.net/travels/v4/sample');
$trips = json_decode($res->getBody(), true);
foreach($trips as $trip) {
Trip::updateOrCreate([
'trip_id' => $trip->trip_id
],
[
'trip_number' => $trip->trip_no,
'truck_no' => $trip->t_no,
'truck_reg_no' => $trip->reg_no,
'trailer_no' => $trip->trailer_no,
'contract_no' => $trip->contract_no,
'contract' => $trip->contract_name,
'driver_id' => $trip->driver_id,
'driver_name' => $trip->driver_name,
'loading_date' => date_format($trip->loading_date, "Y-m-d"),
'loading_from' => $trip->loading_from
]);
}
}
}
app\Console\Kernel.php
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\User;
use App\Activity;
use Avatar;
use Storage;
use Mail;
use App\Travel;
use App\Audit;
use Carbon\Carbon;
// use \Carbon\Carbon;
class Kernel extends ConsoleKernel
{
protected $commands = [
'App\Console\Commands\travelsupdate',
];
protected function schedule(Schedule $schedule)
{
$schedule->command('command:travelsupdate')
->hourly();
}
/**
* Register the commands for the application.
*
* #return void
*/
protected function commands()
{
// $this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
What I want to achieve is that consume the external api using Guzzle GET request. Then save it into the local database. If data already exists (using trip_id), it updates. I set the cron job to every one hour.
I observe that nothing is happening, and no data is being saved to the local database. When I tested on POSTMAN, it displays the data from the api.
However, Laravel log file is not showing me any error.
How do I resolve this?
NOTE: There is no security setting on the external api.
You are retreiving array from api and then you are using it as an object.
Solution 1:
do not decode it as an array
$trips = json_decode($res->getBody());
Solution 2:
use decoded value as an array
foreach($trips as $trip) {
Trip::updateOrCreate([
'trip_id' => $trip['trip_id']
],
[
'trip_number' => $trip['trip_no'],
'truck_no' => $trip['t_no'],
'truck_reg_no' => $trip['reg_no'],
'trailer_no' => $trip['trailer_no'],
'contract_no' => $trip['contract_no'],
'contract' => $trip['contract_name'],
'driver_id' => $trip['driver_id'],
'driver_name' => $trip['driver_name'],
'loading_date' => date_format($trip['loading_date'], "Y-m-d"),
'loading_from' => $trip['loading_from']
]);
}
Everything else seems to be ok.
If you want to display errors in your log you can do it manually:
try {
...
} catch (\Exception $e){
\Log::error($e->getMessage());
}
Not sure if you are misusing the terminology but you don't set the "cron job" for every hour. You set the "cron job" that calls the Laravel Scheduler to run every minute. The scheduler then decides every time it is ran what needs to be run based on how you setup the calls in the scheduler.
To test this you can adjust your scheduled command to run every minute or 5 minutes lets say and manually call the scheduler yourself, php artisan schedule:run, from the command line.
Related
Event listener that I am trying to perform the same action for 3 different Laravel Spark Next events.
Only the SubscriptionCreated seems to be triggered.
namespace App\Listeners;
use Spark\Events\SubscriptionCreated;
use Spark\Events\SubscriptionUpdated;
use Spark\Events\SubscriptionCancelled;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SparkSubscriptionUpdate
{
public function handle($event)
{
//do something;
}
public function subscribe($events)
{
return [
SubscriptionCreated::class => 'handle',
SubscriptionUpdated::class => 'handle',
SubscriptionCancelled::class => 'handle',
];
}
}
Can someone tell me if I am missing something?
Haven't been able to find any examples on the internet that this wouldn't work.
In a Laravel project in the folder app/mail there is a file called WelcomeDogSchoolManager.php
In this file I can see the text that is being sent when a new user registers himself.
Within this file, I can see the following code:
#component('mail::button', ['url' => $passwordResetUrl])
Registreren
#endcomponent
Unfortunately, the developer left a mistake in the $passwordResetUrl (leaving it at "https://login..{domain}.nl" instead of the required "https://login.{domain}.nl"
This causes all my users being unable to register (unless they manipulate the URL).
Where in the Laravel Project can I search for the option to change the $passwordResetUrl?
I have no working knowledge of Laravel and am basically just searching through all the files on the server using Filezilla, trying to figure it all out. I got to this point, but have no idea how to proceed further. And since I have 7.200 files left, I don't think I will find it quickly :)
Any tips are appreciated!
PS. I have also found this code. Is this helpful?
$this->passwordResetUrl = url(route('password.reset', [ 'token' => $token, 'email' => $this->user->email, ], false));
Is this helpful?
Full code for the file below
<?php
namespace App\Mail;
use App\Models\DogSchool;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Password;
class WelcomeDogSchoolManager extends Mailable
{
use Queueable;
use SerializesModels;
public $subject = 'Welkom bij de Nederlandse Detectie Bond';
public string $passwordResetUrl;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct(public User $user, public DogSchool $dogSchool)
{
$token = Password::getRepository()->create($user);
$this->passwordResetUrl = url(route('password.reset', [
'token' => $token,
'email' => $this->user->email,
], false));
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->markdown('emails.welcome_dog_school_manager');
}
}
this
url(route('password.reset', [ 'token' => $token, 'email' => $this->user->email, ], false));
will generate the correct url provided your APP_URL is correct.
In your question, you repeated the same url for the desired and actual so its hard to say exactly what is wrong.
How to share one method to all controllers with different DI, view and parameters? I need something like this:
public function method(Model $model)
{
$baseData = [
'model' => $model,
'route' => route('$route', [$param => $model]),
];
return view($view);
}
All controllers extend App\Http\Controllers\Controller so you can just place it there
<?php
namespace App\Http\Controllers;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
public function method(Model $model, $route, $param, $view)
{
// Declared but not used
$baseData = [
'model' => $model,
'route' => route($route, [$param => $model]),
];
return view($view);
}
}
And use it with $this->method()
For example in HomeController
<?php
namespace App\Http\Controllers;
use App\User;
class HomeController extends Controller
{
/**
* Show the application dashboard.
*
* #return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
$user = User::first();
return $this->method($user, 'home', 'user', 'welcome');
}
}
Now accessing domain.tld/home will return the welcome view
If you want to share function to all controller best way will make service in service folder of app.
step to make service:-
1.create service using artisan command
php artisan make:service service_name and define function that to share to all controller in your project.
after making service your have to register this service with provider.make a provider using artisan command.
php artisan make provider:provider_name and you will see 2 function register and boot
register function is used to register your created service and boot for call already register service
register service like this
public function register()
{
$this->app->bind('App\Services\servicename', function( $app ){
return new serviceclassname;
});
}
3.Go config folder ,open app.php where you will get providers array. In this provider you have to define you provider like App\Providers\providerclassname::class,
call this service in controllers like use App\Services\serviceclassname;
public function functionname(serviceclassname serviceobject)
{
serviceobject->functionname();
}
I'm currently trying to convert a video into multiple .ts segments (for HTTP video streaming). This is a long task so I'm doing this in a Laravel job.
The problem is that the job run multiple times after a few minutes and because of that, the video is processed multiple times. Here you can see my Job class:
<?php
namespace App\Jobs;
use App\Models\Media\MediaFile;
use FFMpeg\Filters\Video\ResizeFilter;
use FFMpeg\Format\Video\X264;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use FFMpeg;
class ConvertVideoForStreaming implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $mediaFile;
public $deleteWhenMissingModels = true;
public $timeout = 3600;
public $retryAfter = 4000;
public $tries = 3;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct(MediaFile $mediaFile)
{
$this->mediaFile = $mediaFile;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$this->mediaFile->update([
'status' => 'Processing',
'status_message' => null,
]);
$convertingId = Str::random(6);
$lowBitrateFormat = (new X264('aac', 'libx264'))
->setKiloBitrate(500)
->setAudioKiloBitrate(128);
$midBitrateFormat = (new X264('aac', 'libx264'))
->setKiloBitrate(1500)
->setAudioKiloBitrate(192);
$highBitrateFormat = (new X264('aac', 'libx264'))
->setKiloBitrate(3000)
->setAudioKiloBitrate(256);
\Log::info('Started a new converting process with convertingID: ' . $convertingId);
FFMpeg::fromDisk('public')
->open($this->mediaFile->file)
->exportForHLS()
->setSegmentLength(4) // optional
->addFormat($lowBitrateFormat, function($media) {
$media->addFilter(function ($filters) {
$filters->resize(new \FFMpeg\Coordinate\Dimension(640, 480), ResizeFilter::RESIZEMODE_INSET);
});
})
->addFormat($midBitrateFormat, function($media) {
$media->addFilter(function ($filters) {
$filters->resize(new \FFMpeg\Coordinate\Dimension(1280, 720), ResizeFilter::RESIZEMODE_INSET);
});
})
->addFormat($highBitrateFormat, function($media) {
$media->addFilter(function ($filters) {
$filters->resize(new \FFMpeg\Coordinate\Dimension(1920, 1080), ResizeFilter::RESIZEMODE_INSET);
});
})
->onProgress(function ($percentage) use($convertingId) {
\Log::info($this->mediaFile->name . " (ConvertingID: $convertingId) - $percentage % transcoded");
\Redis::set('video-transcoded-' . $this->mediaFile->id, $percentage);
})
->toDisk('public')
->save('livestream/' . Str::slug($this->mediaFile->name) . '/playlist.m3u8');
$this->mediaFile->update([
'status' => 'Active',
'status_message' => null,
]);
}
public function failed(\Exception $exception)
{
$this->mediaFile->update([
'status' => 'Failed',
'status_message' => $exception->getMessage(),
]);
}
}
How can I solve my problem?
Laravel offers a 'lock' feature for scheduled commands. See the 'Prevent Task Overlaps' in this documentation.
In case you'd like to have non-scheduled jobs being processed only once I'd advise you to look into the Symfony Lock Component. This component offers you to lock a certain task for a period of time or until it's unlocked. This way you can do something along the lines of:
At the start of your handle() method
a. check if a lock already exists, otherwise skip this job
b. If the lock does not exist, create it
Execute your long running task
At the end of your handle() logic release the lock
I'm currently developing my personal application that is about private servers (for example, Minecraft servers) and since querying the server takes some time, I decided to implement queued jobs. However, they are not working properly, and they run immediately when called even though they are delayed, causing a massive latency in a page request.
Here's my HomeController's index() which calls the job to update every server with a 30 seconds delay:
public function index()
{
$servers = Server::all();
foreach($servers as $server)
{
// Job Dispatch
$job = (new UpdateServer($server->id))->delay(30);
$this->dispatch($job);
}
return view('serverlist.index', compact('servers'));
}
The job class that updates the servers is the following:
class UpdateServer extends Job implements SelfHandling, ShouldQueue
{
use InteractsWithQueue, SerializesModels;
protected $id;
public function __construct($id)
{
$this->id = $id;
}
public function handle(){
$server = Server::findOrFail($this->id);
// Preparing the packet
$test = new RAGBuffer();
$test->addChar('255');
$test->addChar('1');
$test->addShort(1 | 8);
// Finding the server
$serverGame = new RAGServer($server->server_ip);
// Get server information
$status = $serverGame->sendPacket($test);
$server->onlinePlayers = $status->getOnline();
$server->peakPlayers = $status->getPeak();
$server->maxPlayers = $status->getMax();
if (!$server->save()) {
// Error occurred
}
}
}
Whenever the HomeController's index() is run, there's a massive delay in the page request. I followed the tutorial at Laravel's Official Webpage, and I tried to find answers, but I didn't find anything.
So, what am I doing wrong? Why isn't the job getting delayed 30 seconds and then doing this in background in my server?
Also: The handle() is doing what it is supposed to. It queries the server, sends packets, and updates my database with the correct information.
You have to set up the queue driver you want to use in your project's root dir's .env file.
By default, the queue driver is sync which does exactly what you are describing, executing queues immediately.
You can choose of some different queue drivers, such as beanstalked or redis (which would be my choice). There's an excellent freebie on laracasts.com about setting up a beanstalked queue.
To view all available queue driver options in Laravel, have a look here.
Here's a .env example
APP_ENV=local
APP_DEBUG=true
APP_KEY=SomeRandomString
DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync // <-- Put the desired driver here
MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
This was driving me crazy for ages before I realised that Laravel 5.7 renamed QUEUE_DRIVER to QUEUE_CONNECTION in the .env files
For someone who has made the changes from previous answers and still didn't work, check the default value of the queue file like this: dd(Config::get('queue.default'))
For me it didn't change until flushing the config cache:
php artisan config:clear
To test locally you could set the driver to
QUEUE_DRIVER=database
And run php artisan queue:table.
And then php artisan migrate, so you would get your queue saved into the database, so you visually could see what`s going on.
And to run your queues, simply run php artisan queue:listen ... and leave it running as you do with artisan serve.
If you are running on php artisan serve, restart this and run php artisan serve again. This worked for me after hours trying to wonder what it was.
:)
If you are running tests against the queue service through phpunit, make sure that
<env name="QUEUE_DRIVER" value="X"/>
in phpunit.xml doesn't override your desired queue driver.
Ensure that
'default' => env('QUEUE_DRIVER', 'database'),
in file config/queue.php
And
QUEUE_DRIVER=database
in the .env file to ensure the database driver is used.
It's because the delay function takes an absolute date in the future
UpdateServer::dispatch($server->id)->delay(now()->addSeconds(30))
In my case, I had to implement ShouldQueue and use the Queueable trait:
class CustomNotification extends Notification implements ShouldQueue{
use Queueable;
...
Even if you have configured everything properly this can still happen. We had this problem with Laravel 5.4 where we created a lot of jobs, some delayed, and added them to the queue via Queue:bulk($jobs). This call and also Queue::push($job) completely ignore the delay and cause the job to be processed immediately.
If you want your job to be put on the queue as you configured it you must call dispatch($job).
This is the complete steps to create user API and store its history in jobs table.
In Jobs class:
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Repositories\Eloquent\ApiRepo as ApiRepo;
use Log;
use App\Models\User;
class UserProcess implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The number of times the job may be attempted and override the queue tries.
*
* #var int
*/
public $tries = 3;
/**
* Create a new job instance.
*
* #return void
*/
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
try {
// make api call
Log::info("inside handle".$this->user->id);
$apiRepo = new ApiRepo;
$response = $apiRepo->getUserDetails($this->user->id);
Log::info("Response".$response);
} catch (\Throwable $exception) {
if ($this->attempts() > 3) {
// hard fail after 3 attempts
throw $exception;
}
// requeue this job to be executes
// in 3 minutes (180 seconds) from now
$this->release(180);
return;
}
}
}
In Controller Class:
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Validator;
use App\Models\User;
use App\Jobs\UserProcess;
use App\Models\UserHistory;
use Carbon\Carbon;
class UserController extends Controller
{
public function create(Request $request)
{
$rules = [
'first_name' => 'required|string|max:100',
'last_name' => 'required|string|max:100',
'email' => 'required|string|email|unique:users,email',
'phone_number' => 'required|string|max:10',
'address' => 'string',
'date_of_birth' => 'string|date_format:Y-m-d|before:today',
'is_vaccinated' => 'string|in:YES,NO',
'vaccine_name' => 'string|required_if:is_vaccinated,==,YES|in:COVAXIN,COVISHIELD'
];
$validator = Validator::make(array_map('trim', ($request->all())),$rules);
if($validator->fails()){
return response()->json($validator->errors());
}else{
$user = new User;
$user->first_name = $request->first_name;
$user->last_name = $request->last_name;
$user->email = $request->email;
$user->phone_number = $request->phone_number;
$user->address = $request->address;
$user->date_of_birth = $request->date_of_birth;
$user->is_vaccinated = $request->is_vaccinated;
$user->vaccine_name = $request->vaccine_name;
$user->save();
$token = $user->createToken('auth_token')->plainTextToken;
if($user->save()){
$job = (new UserProcess($user))->delay(Carbon::now()->addMinutes(1));
$this->dispatch($job);
return response()
->json(['data' => $user,'status' => '200','message' => 'User Added Successfully','access_token' => $token, 'token_type' => 'Bearer']);
}else{
return response()
->json(['data' => $user,'status' => '409','message' => 'Something went wrong!']);
}
}
}
}
In ApiRepo Class:
namespace App\Repositories\Eloquent;
use App\Repositories\ApiInterface;
use Illuminate\Http\Request;
use App\Http\Requests;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\UserHistory;
use Log;
class ApiRepo implements ApiInterface {
public function getUserDetails($userid) {
Log::info('User ID - '.#$userid);
$user_history = new UserHistory();
$save_user = User::find($userid);
Log::info('User Json Data - '.#$save_user);
$user_history->user_id = $userid;
$user_history->first_name = $save_user->first_name;
$user_history->last_name = $save_user->last_name;
$user_history->email = $save_user->email ;
$user_history->phone_number = $save_user->phone_number;
$user_history->address = $save_user->address;
$user_history->date_of_birth = $save_user->date_of_birth;
$user_history->is_vaccinated = $save_user->is_vaccinated;
$user_history->vaccine_name = $save_user->vaccine_name;
$user_history->save();
if($user_history->save()){
Log::info('User history Saved!');
}
}
}