Laravel Mqtt's subscription doesn't end - laravel

I receive an Mqtt message from Laravel and try to do some action, but if you subscribe, you only get one message and it takes about a minute to delay.
I referred to this at https://github.com/salmanzafar949/MQTT-Laravel.
Implementing Mqtttt motion was made by creating a separate controller.
My code is
<?php
namespace App\Http\Controllers;
use Salman\Mqtt\MqttClass\Mqtt;
use Illuminate\Http\Request;
class MqttController extends Controller{
public $token = "";
public function SendMsgViaMqtt(Request $request)
{
$mqtt = new Mqtt();
//$client_id = Auth::user()->id;/
$topic = $request->topic;
$token = $request->token;
$message = $request->message;
$output = $mqtt->ConnectAndPublish("test", $message, "");
if ($output === true)
{
if($token == "none" || !$token){
return "End";
}else{
$this->SubscribetoTopic($token);
}
}else{
return "Failed";
}
}
public function SubscribetoTopic($token)
{
$topic = 'test';
$this->token = $token;
$message = [];
$mqtt = new Mqtt();
$client_id = "";
$mqtt->ConnectAndSubscribe($topic, function($topic, $msg){
if($msg == "end"){
$message = [
'title' => '魚が釣れました',
'body' => '釣竿を確認してください',
'click_action' => 'Url'
];
}else if($msg == "no"){
$message = [
'title' => '測定できません',
'body' => '波が強すぎると測れません',
'click_action' => 'Url'
];
}else{
return "end";
}
return $this->sendCrul($this->token, $message);
}, "");
}
public function sendCrul($token, $message){
define('SERVER_API_KEY', 'APIKEY');
$tokens = $token;
$header = [
'Authorization: Key=' . SERVER_API_KEY,
'Content-Type: Application/json'
];
$payload = [
'to' => $tokens,
'notification' => $message
];
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://fcm.googleapis.com/fcm/send",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode( $payload ),
CURLOPT_HTTPHEADER => $header
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if($err){
echo "cURL Error #:". $err;
}else{
return $response;
}
// return "ok";
}
}
If you're in trouble like me, let me know how.

Related

Flutter FCM with Laravel

I am Using Laravel for my App backend and want to send push notification to my flutter app by topic. Now I implemented firebase messaging into my flutter app. as
_registerOnFirebase() {
_firebaseMessaging.subscribeToTopic('all');
_firebaseMessaging.getToken().then((token) => print(token));
}
void getMessage() {
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print('received message');
}, onResume: (Map<String, dynamic> message) async {
print('on resume $message');
}, onLaunch: (Map<String, dynamic> message) async {
print('on launch $message');
});
}
and I am sending the notification to the app by Postman and It's working.
enter image description here
Now please tell me How can I send the notification from my Laravel Forms(From Views Directory).
I have created a controller Named PushNotification and a views directory in the resource directory as (\resources\views\notification\create.blade).
If you have the controller setup then it won't be that tough to send notification from the frontend/views. Here is my complete example.
Create a form in your view form.blade.php file (resources/views/form.blade.php)
<form method="POST" action="{{route('bulksend')}}">
<label>Title</label>
<input type="text" hint="Title" name="title">
<br>
<label>Body</label>
<input type="text" hint="Body" name="body">
<br>
<label>Image URL</label>
<input type="text" hint="Image URL" name="img">
<br>
<label>ID</label>
<input type="text" hint="Image URL" name="id">
<br>
<input type="submit"/>
</form>
Create a web route (routes/web.php)
Route::get('form', function () {
return view('form');
});
Route::post('send','MyController#bulksend')->name('bulksend');
Create a controller named MyController in app/Http/Controller and add this function to it.
public function bulksend(Request $req){
$url = 'https://fcm.googleapis.com/fcm/send';
$dataArr = array('click_action' => 'FLUTTER_NOTIFICATION_CLICK', 'id' => $req->id,'status'=>"done");
$notification = array('title' =>$req->title, 'text' => $req->body, 'image'=> $req->img, 'sound' => 'default', 'badge' => '1',);
$arrayToSend = array('to' => "/topics/all", 'notification' => $notification, 'data' => $dataArr, 'priority'=>'high');
$fields = json_encode ($arrayToSend);
$headers = array (
'Authorization: key=' . "YOUR_FCM_KEY",
'Content-Type: application/json'
);
$ch = curl_init ();
curl_setopt ( $ch, CURLOPT_URL, $url );
curl_setopt ( $ch, CURLOPT_POST, true );
curl_setopt ( $ch, CURLOPT_HTTPHEADER, $headers );
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $fields );
$result = curl_exec ( $ch );
//var_dump($result);
curl_close ( $ch );
return $result;
}
We do this in Jobs. I share our server side code. you can arrange according to your need.
class SendFCM implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $FcmLog;
protected $regids;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct($token,$payload,$incident_action_id=0)
{
$FcmLog = new FcmLog();
$FcmLog->incident_action_id = $incident_action_id;
$FcmLog->user_fcm_token_ids = $token->pluck('id')->toArray();
$FcmLog->payload = $payload;
$FcmLog->response = '';
$FcmLog->save();
$this->regids = $token->pluck('token')->toArray();
$this->FcmLog = $FcmLog;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
try {
$regids = UserFcmToken::whereIn('id',$this->FcmLog->user_fcm_token_ids)->get();
$targets = [
'android' => [],
'ios' => []
];
foreach($regids as $regid) {
$identifier = $regid->device_info['os'];
if($regid->device_info['os']==='android'&&$regid->device_info['framework']==='flutter') {
$identifier = 'android_flutter';
}
$targets[$identifier][] = $regid->token;
}
$headers = array(
'Authorization'=>'key = ******YOUR FIREBASE KEY*****',
'Content-Type'=>'application/json'
);
$client = new Client([
'base_uri' => 'https://fcm.googleapis.com/',
'timeout' => 30,
'connect_timeout' => 15,
'headers' => $headers
]);
$response = [
'ios'=>null,
'android'=>null,
'android_flutter'=>null
];
if(!empty($targets['ios'])) {
if ($this->FcmLog->payload['notification_type'] == 'incident_action') {
$incident = new Incident();
$incidents = $incident->ofUser([
'request' => (object) [
'resource' => 'pending',
'count' => 10,
'internal_user_id' => $this->FcmLog->payload['notification_body']['user_id']
]
]);
$badgeCount = intval($incidents['total']) ?: 1;
}
$fields = array(
'registration_ids' => $targets['ios'],
'notification' => []
);
if($this->FcmLog->payload['notification_type']=='announcement') {
$fields['notification'] = [
'body'=> $this->FcmLog->payload['notification_body']['announcement']['text'],
'title'=> $this->FcmLog->payload['notification_body']['announcement']['title'],
'sound'=> "default",
'badge'=> $badgeCount,
'id'=> $this->FcmLog->payload['notification_body']['announcement']['id'],
];
} else {
$fields['notification'] = [
'body'=> $this->FcmLog->payload['notification_body']['message'],
'title'=> 'Bildirim!',
'sound'=> "default",
'badge'=> $badgeCount,
'notification_type'=>$this->FcmLog->payload['notification_type'],
'id'=> $this->FcmLog->payload['notification_body']['incident_number'],
'token'=> $this->FcmLog->payload['notification_body']['public_token'],
];
}
$request = $client->post('fcm/send', [
'body' => json_encode($fields),
]);
$response['ios'] = (string) $request->getBody();
}
if(!empty($targets['android'])) {
$fields = array(
'registration_ids' => $targets['android'],
'data' => $this->FcmLog->payload
);
$request = $client->post('fcm/send', [
'body' => json_encode($fields),
]);
$response['android'] = (string) $request->getBody();
}
if(!empty($targets['android_flutter'])) {
if($this->FcmLog->payload['notification_type']=='announcement') {
$notificationBody = $this->FcmLog->payload['notification_body']['announcement']['text'];
$notificationTitle = $this->FcmLog->payload['notification_body']['announcement']['title'];
} else {
$notificationBody = $this->FcmLog->payload['notification_body']['message'];
$notificationTitle = 'Bildirim!';
}
$fields = array(
'registration_ids' => $targets['android_flutter'],
'data' => $this->FcmLog->payload,
'notification' => [
'body'=>$notificationBody,
'title'=>$notificationTitle
]
);
$fields['data']['click_action'] = 'FLUTTER_NOTIFICATION_CLICK';
$request = $client->post('fcm/send', [
'body' => json_encode($fields),
]);
$response['android_flutter'] = (string) $request->getBody();
}
}
catch (\Exception $e) {
$response = [ mb_substr($e->getMessage(),0,200) ];
}
$this->FcmLog->response = $response;
$this->FcmLog->save();
}
}

Laravel Loop through Array Value and Update Users

I have an app I am working on. I am supposed to get some data from the external API. I am comparing the email from the database to the email in the response, so as to be able to update the user associated with a particular email.
Here is my full code
public function checkpaysub(Request $request) {
$userss = User::all()->pluck('email');
//dd($userss);
foreach($userss as $users) {
$email = $users;
//$ref= $request->reference_code;
$url= 'https://api.paystack.co/customer/';
$url .= $email;
//dd($url);
$client = new Client();
$response = $client->request('GET', $url, [
'headers' => [
'Authorization' => 'Bearer '.'##################',
],
]);
$statusCode = $response->getStatusCode();
$body = json_decode($response->getBody()->getContents());
$sub_cus = $body->status;
$sub_status = $body->data->subscriptions[0]->status;
$sub_email = $body->data->email;
$user_exist = User::where('email', $sub_email)->exists();
if(($body->status == 'true') && $user_exist && $sub_status){
$user = User::where('email', $sub_email)->first();
$user->sub_status = $sub_status;
$user->save();
$users = new UserResource(User::where('email', $sub_email)->first());
return response()->json(array(
'user' => $users
));
}
elseif (($body->status == 'true') && $user_exist) {
$user = User::where('email', $sub_email)->first();
$user->sub_status = "inactive";
$user->save();
$users = new UserResource(User::where('email', $sub_email)->first());
return response()->json(array(
'user' => $users
));
}
}
}
}
This only updates one user. I want to loop through the email and update each user based on the email.
because you have return in foreach so it's only going to loop once.
public function checkpaysub(Request $request) {
$userss = User::all()->pluck('email');
$returnedUsers = collect([]);
foreach($userss as $users) {
$email = $users;
$url= 'https://api.paystack.co/customer/';
$url .= $email;
$client = new Client();
$response = $client->request('GET', $url, [
'headers' => [
'Authorization' => 'Bearer '.'##################',
],
]);
$statusCode = $response->getStatusCode();
$body = json_decode($response->getBody()->getContents());
$sub_cus = $body->status;
$sub_status = $body->data->subscriptions[0]->status;
$sub_email = $body->data->email;
$user_exist = User::where('email', $sub_email)->exists();
if(($body->status == 'true') && $user_exist && $sub_status){
$user = User::where('email', $sub_email)->first();
$user->sub_status = $sub_status;
$user->save();
$users = new UserResource(User::where('email', $sub_email)->first());
$returnedUsers->push($users);
}
elseif (($body->status == 'true') && $user_exist) {
$user = User::where('email', $sub_email)->first();
$user->sub_status = "inactive";
$user->save();
$users = new UserResource(User::where('email', $sub_email)->first());
$returnedUsers->push($users);
}
}
return response()->json(array(
'user' => $returnedUsers->all()
));
}
This can be refactored in a big way to be much more simple:
$client = new Client();
$usersWithUpdatedSubscriptions = collect();
User::chunk(100, function ($users) use ($client, $usersWithUpdatedSubscriptions) {
foreach($users as $user) {
$response = $client->request('GET', "https://api.paystack.co/customer/$user->email", [
'headers' => [
'Authorization' => 'Bearer '.'##################',
],
]);
$body = json_decode($response->getBody()->getContents());
if ($body->status == 'true') && $user->email === data_get($body->data, 'email', null)) {
$user->update([
'sub_status' => data_get($body->data->subscriptions, '0.email', null) ?? 'inactive'
]);
$usersWithUpdatedSubscriptions->push(new UserResource($user));
}
}
});
return response()->json(array(
'user' => $usersWithUpdatedSubscriptions->toArray()
));
What we're doing here:
chunking to ensure we free some memory.
moved the client instantiation outside of the loop so we're not wasting resources on re-instantiation.
created a simple array to hold reference to our users that we can push into.
reduced redundant queries
leveraged null coalescing to simplify deterministic sub_status logic
moved the return outside of the loop

How would I make the same curl request in something like Guzzle?

I've got this php curl request that I stumbled onto that works but would like to use something like Guzzle instead and cant quite figure out how to make the conversion. Anything I try is giving me status code 400, Bad Authentication data.
Any help or references would be much appreciated.
$header = array($this->oauth, 'Expect:' );
$header['Content-Type'] = $multipart ? 'multipart/form-data' : 'application/x-www-form-urlencoded';
$options = [
CURLOPT_HTTPHEADER => $header,
CURLOPT_HEADER => false,
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => $return,
CURLOPT_TIMEOUT => 30,
];
if (!is_null($this->postFields)){
$options[CURLOPT_postFields] = http_build_query($this->postFields, '', '&');
}
else if ($this->getField !== ''){
$options[CURLOPT_URL] .= $this->getField;
}
$feed = curl_init();
curl_setopt_array($feed, $options);
$json = curl_exec($feed);
// $this->httpStatusCode = curl_getinfo($feed, CURLINFO_HTTP_CODE);
if (($error = curl_error($feed)) !== '')
{
curl_close($feed);
throw new \Exception($error);
}
curl_close($feed)
First install guzzle, and use code like this
$client = new \GuzzleHttp\Client();
$header = array($this->oauth, 'Expect:' );
$header['Content-Type'] = $multipart ? 'multipart/form-data' :
'application/x-www-form-urlencoded';
try
{
$response = $client->request('GET', $url, [ 'headers' => $header]);
}
catch(\GuzzleHttp\Exception\ClientException $e)
{
return response(['status' => 0, 'error' => $e->getMessage()]);
}
catch(\GuzzleHttp\Exception\ServerException $e)
{
return response(['status' => 0, 'error' => 'Something went wrong']);
}

Creating laravel service class

My Uptime.php
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.uptimerobot.com/v2/getMonitors",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "Your Api Key",
CURLOPT_HTTPHEADER => array(
"cache-control: no-cache",
"content-type: application/x-www-form-urlencoded"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
$data = json_decode($response);
$custom_uptime = ($data->monitors[0]->custom_uptime_ratio);
$uptime = explode("-",$custom_uptime);
}
?>
ApiCommand.php
public function handle()
{
//include(app_path() . '/Includes/Uptime.php')
$this->showMonitors();
}
public function showMonitors(UptimeRobotAPI $uptime_api)
{
$monitors = $uptime_api->getMonitors();
return $monitors;
}
Hello everyone. I just want to ask how can I turn this to a service class? Do I need to use service providers or service containers? Thanks in advance.
Someone convert it to service class and here was my command looks like.
In your terminal, require the guzzle package as you will use it as an HTTP client: composer require guzzlehttp/guzzle
Then you can make a class for your UptimeRobotAPI at app/Services/UptimeRobotAPI.php:
<?php
namespace App\Services;
use GuzzleHttp\Client;
class UptimeRobotAPI
{
protected $url;
protected $http;
protected $headers;
public function __construct(Client $client)
{
$this->url = 'https://api.uptimerobot.com/v2/';
$this->http = $client;
$this->headers = [
'cache-control' => 'no-cache',
'content-type' => 'application/x-www-form-urlencoded',
];
}
private function getResponse(string $uri = null)
{
$full_path = $this->url;
$full_path .= $uri;
$request = $this->http->get($full_path, [
'headers' => $this->headers,
'timeout' => 30,
'connect_timeout' => true,
'http_errors' => true,
]);
$response = $request ? $request->getBody()->getContents() : null;
$status = $request ? $request->getStatusCode() : 500;
if ($response && $status === 200 && $response !== 'null') {
return (object) json_decode($response);
}
return null;
}
private function postResponse(string $uri = null, array $post_params = [])
{
$full_path = $this->url;
$full_path .= $uri;
$request = $this->http->post($full_path, [
'headers' => $this->headers,
'timeout' => 30,
'connect_timeout' => true,
'http_errors' => true,
'form_params' => $post_params,
]);
$response = $request ? $request->getBody()->getContents() : null;
$status = $request ? $request->getStatusCode() : 500;
if ($response && $status === 200 && $response !== 'null') {
return (object) json_decode($response);
}
return null;
}
public function getMonitors()
{
return $this->getResponse('getMonitors');
}
}
You can then add more functions beneath, I created getMonitors() as an example.
To use this in a controller, you can simply dependency inject it into your controller methods:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\Promises\UptimeRobotAPI;
class ExampleController extends Controller
{
public function showMonitors(UptimeRobotAPI $uptime_api)
{
$monitors = $uptime_api->getMonitors();
return view('monitors.index')->with(compact('monitors'));
}
}
This is just an example, this does not handle any errors or timeouts that can occur, this is simply for you to understand and extend. I don't know what you want to do with it, but I can't code your whole project, this will definitely answer your question though. :)

Laravel controller with multiple beforeFilter's

I need to filter different authorization roles for different HTTP verbs. Attaching one filter in the controller is no problem, as the example shows:
public function __construct()
{
$this->beforeFilter('roleOne', ['on' => ['get', 'post', 'put', 'delete']]);
}
However, attaching multiple filters, as the next example shows, hides all the routes from the caller.
public function __construct()
{
$this->beforeFilter('roleOne', ['on' => ['get', 'post', 'put', 'delete']]);
$this->beforeFilter('roleTwo', ['on' => ['get']]);
}
And combining the filters lets all traffic through.
public function __construct()
{
$this->beforeFilter('roleOne|roleTwo', ['on' => ['get', 'post', 'put', 'delete']]);
}
Both fails.
I've considered using user levels instead of role names, or attaching level values to the user roles. But the best scenario for me would be to avoid having to change the structure.
Edit, here's the general structure for my filters.
Route::filter('roleOne', function ($route, $request){
$token = $request->header('X-Auth-Token');
// If the token doesn't exist the user isn't authenticated
if ($token === null) {
$data = ['Not authenticated'];
$code = 401;
$response = Response::json([
'error' => true,
'data' => $data,
'code' => $code
], $code
);
return $response;
}
// If the token is invalid the user isn't authenticated
else if ( ! isAuthenticated($token) ) {
$data = ['Not authenticated'];
$code = 401;
$response = Response::json([
'error' => true,
'data' => $data,
'code' => $code
], $code
);
return $response;
}
// If the user is not a member of group s/he doesn't have access
if( ! isAuthorized($token, 'roleOne') ){
$data = ['Not authorized'];
$code = 401;
$response = Response::json([
'error' => true,
'data' => $data,
'code' => $code
], $code
);
return $response;
}
});
I ended up introducing a level parameter in the authorized groups. Then I only needed one single beforeFilter.
public function __construct()
{
$this->beforeFilter('userRole', ['on' => ['get', 'post', 'put', 'delete']]);
}
And the filter now lookes something like...
Route::filter('userRole', function ($route, $request){
$token = $request->header('X-Auth-Token');
// If the token doesn't exist the user isn't authenticated
if ($token === null) {
$data = ['Not authenticated'];
$code = 401;
$response = Response::json([
'error' => true,
'data' => $data,
'code' => $code
], $code
);
return $response;
}
// If the token is invalid the user isn't authenticated
else if ( ! isAuthenticated($token) ) {
$data = ['Not authenticated'];
$code = 401;
$response = Response::json([
'error' => true,
'data' => $data,
'code' => $code
], $code
);
return $response;
}
// If the user is not a member of group Admin s/he doesn't have access
if( ! isAuthorized($token, 'Admin') ){
$data = ['Not authorized'];
$code = 401;
$response = Response::json([
'error' => true,
'data' => $data,
'code' => $code
], $code
);
return $response;
}
});
And the isAuthorized method looks something like this.
public function isAuthorized($token, $group) {
$hasPermission = false;
$user = false;
$requiredGroup = false;
// Attempting to find the user will automatically throw errors if unsuccessful
try {
if( $group !== '*' ){
// Get the needed group
$requiredGroup = $this->groupProvider->findByName($group);
}
// Get the user, including the memberships
$user = $this->userProvider->findByToken($token);
} catch (InvalidTokenException $e) {
$hasPermission = false;
}
// If the group is '*' it means everyone has permission, but has to be authenticated!
// That's why we do this check after the token-check
if ( $group === '*' ) {
return true;
}
if( !$user || !$requiredGroup ) {
$hasPermission = false;
} else {
// Compare user permissions to required
foreach ($user['groups'] as $group) {
if( $group['level'] >= $requiredGroup['level'] ){
$hasPermission = true;
}
}
}
return $hasPermission;
}

Resources