Laravel / PHP - Run 3 php functions in parallel - php

I have 3 functions that get json data from external apis and then saves in my database. Each function is its in own class e.g :
Class api1 {
public function fetch()
{
//Do Something
}
}
Class api2 {
public function fetch()
{
//Do Something
}
}
Since its api call might take some time or delay . I want to run all 3 in parallel so that api2 does not have to wait for api1 to complete.
Any way to do that ?
* Note : I'm also going to use laravel scheduler which will run each function every minute or run a single function containing all 3.

To me this looks more of like callback request for data, so to keep your app from not slowing down this should be a background job.
But before that I would implement an interface for those classes:
interface apiService{
public function fetch();
}
Class api1 implements apiService {
public function fetch()
{
//Do Something
}
}
Class api2 implements apiService{
public function fetch()
{
//Do Something
}
}
Create a job class php artisan make:job dataFetcher
Jobs will be structured under App\Jobs\
The job class in Laravel its dead simple, consisting of a constructor to Inject dependencies and handle() to fire the job.
protected $service;
public function __construct(apiService $service)
{
$this->service = $service;
}
public function handle()
{
$this->apiService->fetch();
}
Note that I am injecting the interface instead of concrete class, using a bit more high level code here. So now you can create a command to fire the calls with a cron job, or you can create a custom service provider to fire the commands as soon as app bootstraps.
I would go with a custom artisan command here:
So just create a custom artisan command on handle method
public function handle()
{
Job::dispatch(new FirstApiClass);
Job::dispatch(new SecondApiClass);
}
Handle method will execute first line and Job will be processed in background(doesnt matter if job failed or not), then next call will be fired and so on...
Note the use of the interface in this case, Job class doesnt really care which service you are calling as long as you provide an implmenetation of it.

Related

Run code on custom artisan command after handle()

I'm working on a project running an old version of Laravel (5.6). There's a lot of custom artisan commands in this project. Some of those commands should create a summary on the end of the execution. My first idea was to create a parent class, and all the commands extend from this class, something like:
<?php
abstract class ParentCommand extends Command
{
abstract protected function doYourStuff();
abstract protected function prepareSummaryData();
final public function handle()
{
$this->doYourStuff();
$this->createSummary($this->prepareSummaryData());
}
final protected function createSummary($data)
{
// Summary stuff in here...
}
}
But the problem is that Laravel does DI in the handle method, so the handle method in child classes could have a different signature, which is not allowed.. :(
Any idea of how to run something after the handle() method is executed?
What about using a trait ?
trait Summary
{
protected function createSummary($data)
{
// Summary stuff in here...
}
}
class SomeCommand extends Command
{
use Summary;
public function handle()
{
//do stuff
//prepare data to summarize
$this->createSummary($data);
}
}

Implement a Repository Interface into Job

I am trying to implement an Interface into a Job but having no luck. Is it possible to implement an interface / repository within the public construct and use said interface in the handle() method of the Job?
The error I am getting is as follows:
Argument 1 passed to App\Jobs\OrderCreate::__construct() must be an instance of App\Http\Interfaces\OrderInterface, string given, called in /Users/Panoply/Sites/stock-sync/app/Http/Controllers/StockController.php on line 31
Below is a basic setup of what I am trying to achieve.
Stock Controller:
public function test(){
dispatch(new OrderCreate('hello'));
}
OrderCreate Job:
protected $order;
protected $test;
public function __construct(OrderInterface $order, $test)
{
$this->order = $order;
$this->test = $test;
}
public function handle()
{
$this->order->test($this->test);
}
OrderRepository:
class OrderRepository implements OrderInterface
{
public function test($data) {
error_log($data);
}
}
OrderInterface:
public function test($data);
I have had no troubles implementing this pattern into my controllers and commands but I can't seem to get it working on a Job.
Nevermind, the issue was I shouldn't of been calling the interface within the __construct() but instead within handle()
Editing for more detailed explanation.
The __construct() of a Laravel / Lumen Job from what I can tell only accepts data and thus implementing an interface within the __constuct() will cause my above error to be thrown.
In order to use an Interface within a job, you will need to call your interface within the handle() function.
As an example, the following will NOT work within a Job class:
protected $test;
public function __construct(InterfaceTest $test)
{
$this->test = $test;
}
This is because the Job construct does not take in Interfaces, it only takes in the data you pass in from the dispatch call. In order to use your Interface within a job, you need to call the interface within the handle() function and then it will succeed and work, example:
public function handle(InterfaceTest $test)
{
$test->fn();
}
This seems to only be the case when implementing on a Job. In most cases when you require an interface within a Controller or Command, you will implement within the __construct().

How to run a method of a class as a cron task in laravel 5.1

Look I have a class that looks like this, i want to cache some info every day in a cron job with this method cacheTopFilters in laravel 5.1
<?php
namespace namescape/of/the/class;
class FilterTypeCacheService extends BaseService implements IFilterTypeCacheService
{
private $searchFilterService;
private $filterCacheHandler;
function __construct(SearchFilterService $searchFilterService, IFilterTypeCacheHandler $filterCacheHandler){
$this->searchFilterService = $searchFilterService;
$this->filterCacheHandler = $filterCacheHandler;
}
public function cacheTopFilters($type,$keyValuePair,$limit){
$filters = $this->searchFilterService->getAllFilters($type,$keyValuePair);
$this->filterCacheHandler->deleteFiltersBulkFromCache();
$this->filterCacheHandler->SaveFiltersBulk($filters,$type);
}
public function getTopFilters(){
$topFilters = $this->filterCacheHandler->getCachedTopFilters();
return $topFilters;
}
}
As they have dependency injection how can i accomplished to called that method on the app/console/kernel on the schedule method?
You want to take a look at Task Scheduling:
https://laravel.com/docs/5.1/scheduling#defining-schedules
In the App\Console\Kernel class, inside the schedule function, you can schedule anything at all like this:
$schedule->call(function () {
doSomething();
})->daily();
Assuming you have the scheduler setup (system cron job every minute) you should be all set.
If you have a class you need to instantiate, with dependencies injected, you can always use the global app() helper function that Laravel gives you:
$schedule->call(function () {
app(\namespace\of\the\class::class)->cacheTopFilters(...);
})->daily();
That's assuming that Laravel knows how to handle your dependencies. I see that one of your dependencies is a contract (interface) so I'm assuming you've already bound the concrete instance in the Laravel IoC.

Laravel job calling class from nowhere?

I have a job in my Laravel project (v.4.2). which is used for inserting data into database. The class named "ProductUpdate". I use Amazon SQS for queue service.
What makes me confuse now is, when I changed the code in class "ProductUpdate",
it seems that the job is running by using old version of the class.
I even deleted all lines of code in the class but the jobs can still be able to run ( it stills inserts data).
Following is the job class.
The file of this class is at app/jobs/ProductUpdate.php
In my understanding, job class is the only place that will be called from queue, but why it can still be able to run when I deleted all the codes?
<?php
/**
* Here is a class to run a queued item sent from SQS
* Default method to use is fire()
**/
class ProductUpdate
{
public function fire($job, $data)
{
// Disable query log
DB::connection()->disableQueryLog();
// Set the job as a var so it will be used across functions
$this->job = $job;
$product = Product::find($productID);
if($product->product_type != 18) {
// Call the updater from library
$updater = App::make('Product\PriceUpdater');
$updater->update($product);
}
// Done and delete
$this->success();
}
private function success()
{
$this->job->delete();
}
private function fail($messages = array())
{
Log::error('Job processing fail', $messages);
$this->job->delete();
}
}
Your problem is related to cache.
Run this command in terminal to remove all cached data.
php artisan cache:clear
other way:-
Illuminate\Cache\FileStore has the function flush, so you can also use it:
Cache::flush();
This link will also help you :)

Call a function on each http request

In my Apigility project I have different Rest resources, all of them extends my class ResourseAbstract and in there I extend the AbstractResourceListener as Apigility needs.
So for example my resource User:
<?php
namespace Marketplace\V1\Rest\User;
use ZF\ApiProblem\ApiProblem;
use Marketplace\V1\Abstracts\ResourceAbstract;
class UserResource extends ResourceAbstract
{
public function fetch($id)
{
$result = $this->getUserCollection()->findOne(['id'=>$id]);
return $result;
}
}
And ResourceAbstract:
<?php
namespace Marketplace\V1\Abstracts;
use ZF\Rest\AbstractResourceListener;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use ZF\ApiProblem\ApiProblem;
class ResourceAbstract extends AbstractResourceListener implements ServiceLocatorAwareInterface {
}
Now, I need to run a function each time an http request is made, if I query /user in my browser the UserResource class will get instantiated and so the ResourceAbstract, my "solution" to get something to run on each call was to use a constructor inside ResourceAbstract, and this "works":
function __construct() {
$appKey = isset(getallheaders()['X-App-Key']) ? getallheaders()['X-App-Key'] : null;
$token = isset(getallheaders()['X-Auth-Token']) ? getallheaders()['X-Auth-Token'] : null;
//some code
return new ApiProblem(400, 'The request you made was malformed');
}
The thing is I need to return an ApiProblem in some cases (bad headers on the http request), but as you know constructor function does not return parameters. Another solution will be to thrown an exception but in Apigility you are supposed to ise ApiProblem when there is an api problem. Is the constructor approach correct? How will you solve this?
Throwing an exception would be a solution, as long as you catch it on the parent portion of the code.
Are you using the ZEND MVC with your apigility project ?
If yes, you could consider hooking up a call that will be executed before the MVC does the dispatching.
If you want to look on the feasability of that approach, you can check that question asked on stackoverflow : Zend Framework 2 dispatch event doesn't run before action
I've not used this library, however it looks as if you can attach a listener to 'all' events by either extending the 'dispatch' method or adding your own event listener with high priority. The controller then listens for the returned 'ApiProblem'.
Attaching a listener is probably a better idea, in your custom class extending AbstractResourceListener (or from within it's service factory) you can then attach the event.
abstract class MyAbstractResource extends AbstractResourceListener
{
public function attach(EventManagerInterface $eventManager)
{
parent::attach($eventManager);
$eventManager->attach('*', [$this, 'checkHeaders'], 1000);
}
public function checkHeaders(EventInterface $event)
{
$headers = getallheaders();
if (! isset($headers['X-App-Key'])) {
return new ApiProblem(400, 'The request you made was malformed');
}
if (! isset($headers['X-Auth-Token'])) {
return new ApiProblem(400, 'The request you made was malformed');
}
}
}
The above would mean that any event triggered would first check if the headers are set, if not a new ApiProblem is returned.

Categories