Sending data with put / post to a webservice in Laravel - php

I'm trying to create a dynamic method to send / receive my data with my webservice using guzzle and laravel
I have the following structure in my controller
class TipoProjetoController extends Controller
{
private $Tabela = 'tipoprojeto';
public function AppWebService($Who, $Type, $Data)
{
$Client = new Client(['base_uri' => config('constants.caminhoWS').$Who]);
$response = $Client->request($Type, $Data);
return $response->getBody()->getContents();
}
public function index()
{
$Dados = $this->AppWebService($this->Tabela,'GET','');
$Titulo = 'Tipos de Projeto';
$jsonObj = json_decode($Dados);
$Obj = $jsonObj->data;
return view('Painel.TipoProjeto.index',compact('Obj','Titulo') );
}
public function create()
{
return view('Painel.TipoProjeto.create-edit');
}
public function store(Request $request)
{
//
}
public function show($id)
{
//
}
public function edit($id)
{
$Dados = $this->AppWebService($this->Tabela.'/'.$id,'GET','');
if( $Dados == null )
return redirect()->route('TipoProjeto.index');
else
{
$Objeto = json_decode($Dados);
return view('Painel.TipoProjeto.create-edit',compact('Objeto'));
}
}
public function update(Request $request, $id)
{
$Dados = $this->AppWebService($this->Tabela.'/'.$id, 'PUT', '');
dd($Dados);
}
public function destroy($id)
{
//
}
}
Until then everything worked perfectly in my Index and Edit method, because the method of sending is GET. now I am trying to send a request to the server with the PUT method and I am not getting.
The AppWebService method has as the first parameter the name of the route that I am accessing, the type and the information that I am going to pass.
I tried this way
public function update(Request $request, $id)
{
$Dados = $this->AppWebService($this->Tabela.'/'.$id, 'PUT', ['data'=>'$request']);
dd($Dados);
}
and the error was
(1/1) InvalidArgumentException URI must be a string or UriInterface
my request received from my client was
dd($request)
Request {#38 ▼
#json: null
#convertedFiles: null
#userResolver: Closure {#154 ▶}
#routeResolver: Closure {#156 ▶}
+attributes: ParameterBag {#40 ▶}
+request: ParameterBag {#39 ▶}
+query: ParameterBag {#46 ▶}
+server: ServerBag {#42 ▶}
+files: FileBag {#43 ▶}
+cookies: ParameterBag {#41 ▶}
+headers: HeaderBag {#44 ▶}
#content: null
#languages: null
#charsets: null
#encodings: null
#acceptableContentTypes: null
#pathInfo: "/Painel/TipoProjeto/1"
#requestUri: "/index.php/Painel/TipoProjeto/1"
#baseUrl: "/index.php"
#basePath: null
#method: "PUT"
#format: null
#session: Store {#185 ▶}
#locale: null
#defaultLocale: "en"
-isHostValid: true
-isForwardedValid: true
basePath: ""
format: "html"
}
as my application is in laravel and my webservice too, is it any problem that I send the request this way? or do I have to extract and send it?
and how do I send the put and post methods to my dynamic function?
[EDIT] File Web Service, Route and Controller
Route::group(['prefix' => 'api'],function(){
Route::group(['prefix' => 'user'], function(){
Route::group(['prefix' => 'tipoprojeto'], function(){
Route::get('/','Painel\TipoProjetoController#All');
Route::get('{id}','Painel\TipoProjetoController#Get');
Route::post('','Painel\TipoProjetoController#Save');
Route::put('{id}','Painel\TipoProjetoController#Update');
Route::delete('{id}','Painel\TipoProjetoController#Delete');
});
});
});
Webservice method that receives my request
class TipoProjetoController extends Controller
{
public function Update(Request $request, $id){
return 'Change data of id:'.$id;
}
}

Try with ['data'=>$request] instead of ['data'=>'$request']
you're also using $Client->request(); wrong. The second parameter should be the URI, not an array, hence your error.
Also the base URI is not meant to be used that way,
so instead try
$Client = new Client();
$url = config('constants.caminhoWS').$Who;
$response = $Client->request($Type,$url, $Data);
(if you are going to use a base URI, use the second parameter in $Client->request() to pass the rest of the URL, like
$Client = new Client(['base_uri' => config('constants.caminhoWS')]);
$response = $Client->request($Type, $Who, $Data);
Then Guzzle will construct the URL...
http://docs.guzzlephp.org/en/stable/quickstart.html#making-a-request

Related

Symfony notifier attach custom metadata to envelope

I'm using the Symfony notifier and messenger components to asynchronously send SMS messages (and in the future push and email notifications).
Everything works just fine, however once a message is sent, I'd like to log information about it.
I can catch a successful message by subscribing to WorkerMessageHandledEvent which provides me the Message object, along with the containing Envelope and all its Stamp objects inside. From all the available information, I'll be logging this in my database using an entity named MessageLog.
class MessengerSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
WorkerMessageHandledEvent::class => ['onHandled']
];
}
public function onHandled(WorkerMessageHandledEvent $event) {
$log = new MessageLog();
$log->setSentAt(new DateTime());
if($event->getEnvelope()->getMessage() instanceof SmsMessage) {
$log->setSubject($event->getEnvelope()->getMessage()->getSubject());
$log->setRecipient($event->getEnvelope()->getMessage()->getPhone());
}
// Do more tracking
}
}
What I'd like to do, is track the object that "invoked" the message. For example, if I have a news feed, and posting a post sends out a notification, I'd like to attribute each logged message to that post (to display audience reach/delivery stats per post - and from an admin POV auditing and reporting).
I've tried to go about adding a Stamp, or other means of trying to attach custom metadata to the message, but it seems to be abstracted when using the symfony/notifier bundle.
The below is what I'm using to send notifications (more or less WIP):
class PostService {
protected NotifierInterface $notifier;
public function ___construct(NotifierInterface $notifier) {
$this->notifier = $notifier;
}
public function sendNotifications(Post $post) {
$notification = new PostNotification($post);
$recipients = [];
foreach($post->getNewsFeed()->getSubscribers() as $user) {
$recipients[] = new Recipient($user->getEmail(), $user->getMobilePhone());
}
$this->notifier->send($notification, ...$recipients);
}
}
class PostNotification extends Notification implements SmsNotificationInterface {
protected Post $post;
public function __construct(Post $post) {
parent::__construct();
$this->post = $post;
}
public function getChannels(RecipientInterface $recipient): array {
return ['sms'];
}
public function asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage {
if($transport === 'sms') {
return new SmsMessage($recipient->getPhone(), $this->getPostContentAsSms());
}
return null;
}
private function getPostContentAsSms() {
return $post->getTitle()."\n\n".$post->getContent();
}
}
By the time this is all done, this is all I have in the WorkerMessageHandledEvent
^ Symfony\Component\Messenger\Event\WorkerMessageHandledEvent^ {#5590
-envelope: Symfony\Component\Messenger\Envelope^ {#8022
-stamps: array:7 [
"Symfony\Component\Messenger\Stamp\BusNameStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\BusNameStamp^ {#10417
-busName: "messenger.bus.default"
}
]
"Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineReceivedStamp" => array:1 [
0 => Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineReceivedStamp^ {#10419
-id: "2031"
}
]
"Symfony\Component\Messenger\Stamp\TransportMessageIdStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\TransportMessageIdStamp^ {#10339
-id: "2031"
}
]
"Symfony\Component\Messenger\Stamp\ReceivedStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\ReceivedStamp^ {#5628
-transportName: "async"
}
]
"Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp^ {#7306}
]
"Symfony\Component\Messenger\Stamp\AckStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\AckStamp^ {#7159
-ack: Closure(Envelope $envelope, Throwable $e = null)^ {#6205
class: "Symfony\Component\Messenger\Worker"
this: Symfony\Component\Messenger\Worker {#5108 …}
use: {
$transportName: "async"
$acked: & false
}
}
}
]
"Symfony\Component\Messenger\Stamp\HandledStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\HandledStamp^ {#11445
-result: Symfony\Component\Notifier\Message\SentMessage^ {#2288
-original: Symfony\Component\Notifier\Message\NullMessage^ {#6625
-decoratedMessage: Symfony\Component\Notifier\Message\SmsMessage^ {#10348
-transport: null
-subject: ".................................................."
-phone: "0412345678"
}
}
-transport: "null"
-messageId: null
}
-handlerName: "Symfony\Component\Notifier\Messenger\MessageHandler::__invoke"
}
]
]
-message: Symfony\Component\Notifier\Message\SmsMessage^ {#10348}
}
-receiverName: "async"
}
The doco shows me ways to add my own stamps to the envelope, which I'm guessing I can use to attach metadata such as my Post object, but this means I need to use the MessageBusInterface to send notifications. I don't want to do because I would like to route messages through the NotifierInterface to gain all the benefits of channel policies, texter transports, etc.
tl;dr: how do I get some metadata through to a WorkerMessageHandledEvent if I send a message using the NotifierInterface
I've found a way to make it work!
Essentially what happens is that we have two components here, the Symfony notifier and the Symfony messenger. When used together, they create a powerful way to send messages to any number of endpoints.
Firstly what I did was create an interface called NotificationStampsInterface and a trait called NotificationStamps that satisfies the interface (by storing a protected array using the interface methods to read/write to it).
class NotificationStampsInterface {
public function getStamps(): array;
public function addStamp(StampInterface $stamp);
public function removeStamp(StampInterface $stamp);
}
This interface can then be added onto your custom notification object, in this instance PostNotification, alongside with the NotificationStamps trait to satisfy the interface methods.
The trick here is that when sending a notification via the notifier, it ultimately calls on the messenger component to send the message. The bit that handles this is Symfony\Component\Notifier\Channel\SmsChannel. Essentially, if a MessageBus is available, it will push messages through that rather than going straight though the notifier.
We can extend the SmsChannel class to add our own logic inside notify() method.
class SmsNotify extends \Symfony\Component\Notifer\Channel\SmsChannel {
public function notify(Notification $notification, RecipientInterface $recipient, string $transportName = null): void {
$message = null;
if ($notification instanceof SmsNotificationInterface) {
$message = $notification->asSmsMessage($recipient, $transportName);
}
if (null === $message) {
$message = SmsMessage::fromNotification($notification, $recipient);
}
if (null !== $transportName) {
$message->transport($transportName);
}
if (null === $this->bus) {
$this->transport->send($message);
} else {
// New logic
if($notification instanceof NotificationStampsInterface) {
$envelope = Envelope::wrap($message, $notification->getStamps());
$this->bus->dispatch($envelope);
} else {
$this->bus->dispatch($message);
}
// Old logic
// $this->bus->dispatch($message);
}
}
}
Lastly we need to override the service by adding the following in services.yaml
notifier.channel.sms:
class: App\Notifier\Channel\SmsChannel
arguments: ['#texter.transports', '#messenger.default_bus']
tags:
- { name: notifier.channel, channel: sms }
And that's it! We now have a way to append stamps to our Notification object that will carry all the way through to the WorkerMessageHandledEvent.
An example use would be (for my situation at least)
class RelatedEntityStamp implements StampInterface {
private string $className;
private int $classId;
public function __construct(object $entity) {
$this->className = get_class($entity);
$this->classId = $entity->getId();
}
/**
* #return string
*/
public function getClassName(): string {
return $this->className;
}
/**
* #return int
*/
public function getClassId(): int {
return $this->classId;
}
}
class PostService {
protected NotifierInterface $notifier;
public function ___construct(NotifierInterface $notifier) {
$this->notifier = $notifier;
}
public function sendNotifications(Post $post) {
$notification = new PostNotification($post);
$stamp = new RelatedEntityStamp($post); // Solution
$notification->addStamp($stamp); // Solution
$recipients = [];
foreach($post->getNewsFeed()->getSubscribers() as $user) {
$recipients[] = new Recipient($user->getEmail(), $user->getMobilePhone());
}
$this->notifier->send($notification, ...$recipients);
}
}
Once the message is sent, dumping the result shows that we do indeed have our stamp registered at the point where our event fires.
^ Symfony\Component\Messenger\Event\WorkerMessageHandledEvent^ {#1078
-envelope: Symfony\Component\Messenger\Envelope^ {#1103
-stamps: array:8 [
"App\Notification\Stamp\RelatedEntityStamp" => array:1 [
0 => App\Notification\Stamp\RelatedEntityStamp^ {#1062
-className: "App\Entity\Post"
-classId: 207
}
]
"Symfony\Component\Messenger\Stamp\BusNameStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\BusNameStamp^ {#1063
-busName: "messenger.bus.default"
}
]
"Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineReceivedStamp" => array:1 [
0 => Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineReceivedStamp^ {#1066
-id: "2590"
}
]
"Symfony\Component\Messenger\Stamp\TransportMessageIdStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\TransportMessageIdStamp^ {#1067
-id: "2590"
}
]
"Symfony\Component\Messenger\Stamp\ReceivedStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\ReceivedStamp^ {#1075
-transportName: "async"
}
]
"Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp^ {#1076}
]
"Symfony\Component\Messenger\Stamp\AckStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\AckStamp^ {#1077
-ack: Closure(Envelope $envelope, Throwable $e = null)^ {#1074
class: "Symfony\Component\Messenger\Worker"
this: Symfony\Component\Messenger\Worker {#632 …}
use: {
$transportName: "async"
$acked: & false
}
}
}
]
"Symfony\Component\Messenger\Stamp\HandledStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\HandledStamp^ {#1101
-result: Symfony\Component\Notifier\Message\SentMessage^ {#1095
-original: Symfony\Component\Notifier\Message\NullMessage^ {#1091
-decoratedMessage: Symfony\Component\Notifier\Message\SmsMessage^ {#1060
-transport: null
-subject: ".................................................."
-phone: "0412345678"
}
}
-transport: "null"
-messageId: null
}
-handlerName: "Symfony\Component\Notifier\Messenger\MessageHandler::__invoke"
}
]
]
-message: Symfony\Component\Notifier\Message\SmsMessage^ {#1060}
}
-receiverName: "async"
}

Error message: Creating default object from empty value

Facing this error message but not sure what is wrong with it? Tried the solutions at StackOverflow and other forums but didn't work either.
The error is at line $review->title=$request->title;
public function updateReview(Request $request)
{
// dd($request->all());
$review = Review::find($request->id);
$review->id=$request->id;
$review->title=$request->title;
$review->review=$request->review;
$review->rating=$request->rating;
$review->save();
return redirect('/');
}
A dd($request->all()); returns the following:
array:5 [▼
"id" => "3"
"title" => "User 3 has updated title"
"review" => "User 4 just updated review"
"rating" => "5"
"_token" => "pGFVAzHNg7HmXbkMXylxcM6biqaGnwFmsxjsrTgl"
]
And these are my routes:
Route::get('/edit_review/{id}', 'App\Http\Controllers\UserController#editReview');
Route::post('/update', 'App\Http\Controllers\UserController#updateReview')->middleware('user');
You're getting this error Warning: Creating default object from empty value because $review->title returns false.
find() takes an id and returns a single model. If no matching model exist, it returns null.
You can use findOrFail() instead of find(), if the record not matched then findOrFail() will throw a 404 error :
$review = Review::findOrFail($request->id);
I think you've missed to define a parameter on your function, that passes with your route, probaby its {id} :
public function updateReview(Request $request, $id)
{
$review = Review::findOrFail($id);
//...
}

UpdateExistingPivot method doesn't work

I'm trying to update a value in a pivot table. This is my method :
public function updateStatus(Event $event)
{
$this->authorize('updateStatus', $event);
$newStatus = Input::get('status');
$actualPivot = $event->guests()->where('user_id', Auth::id())->first()->pivot;
$id = $actualPivot['id'];
$status = $actualPivot['status'];
if ($newStatus != $status)
{
dd($event->guests()->updateExistingPivot($id, ['status' => $newStatus]));
}
return back();
}
I've checked with HeidiSQL, the row isn't updated how it should be. I've also tried this solution, but it doesn't update the row, it creates a new one. There is the dd() with this method:
array:3 [▼
"attached" => array:1 [▼
0 => 1
]
"detached" => []
"updated" => []
]
This is my guests() relation defined in the Event model:
public function guests()
{
return $this->belongsToMany('App\User')
->using('App\Invitation')
->withPivot('id', 'status')
->withTimestamps();
}
I don't know why the updateExistingPivot() method doesn't work. I hope you can help.
You must use the guest_id or whatever is the name for your foreign key of App\Invitation instead of your pivot id in order to update the existing record, otherwise you have not a relation for the current event that matches your pivot id.
$id = $actualPivot['guest_id']; // change guest_id for your foreig key name of \App\Invitation
$status = $actualPivot['status'];
if ($newStatus != $status)
{
dd($event->guests()->updateExistingPivot($id, ['status' => $newStatus]));
}

Get Route parameter from group route in SLIM 3 PHP

I am having issue getting the group route parameter in middleware here's what i am doing
I am using [PHP - SLIM 3 Framework]
route.php
$app->group('/{lang}', function() use ($container){
//routes ... (ignore the other routes)
})->add(new Middleware($container));
Middleware.php
//middleware.php
class Middleware {
public function __invoke($req, $res, $next)
{
//here is the part which is confusing about how can i get
// {lang} parameter
$req->getAttribute('route')
}
}
You can do this with the getArguments()-method
public function __invoke($req, $res, $next)
{
$route = $req->getAttribute('route');
$args = $route->getArguments();
$lang = $args['lang'];
return $res;
}
Note: you also need to set the slim setting determineRouteBeforeAppMiddleware to true. Otherwise the argument is not set in the middleware.
$container = [
'settings' => [
'determineRouteBeforeAppMiddleware' => true
]
]
$app = new \Slim\App($container);

Symfony, twig transmit data

In symfony I have twig and I want to transmit data for action but in url for action I dont want see 'role', only transmit for action, how do this?
routing:
artel_admin_index:
path: /{ida}/edit/{id}/submit
defaults: { _controller: ArtelProfileBundle:Dashboard:edit }
requirements: { _method: POST|GET }
twig:
<td>
{{ developer.firstname }} {{ developer.lastname }}
</td>
action:
public function editAction($ida, $id)
{
$request = $this->get('request');
$value = $request->getSession()->get('role');
dump($request, $value);exit;
And I see:
DashboardController.php on line 138:
Request {#7 ▼
+attributes: ParameterBag {#10 ▶}
+request: ParameterBag {#8 ▶}
+query: ParameterBag {#9 ▶}
+server: ServerBag {#13 ▶}
+files: FileBag {#12 ▶}
+cookies: ParameterBag {#11 ▶}
+headers: HeaderBag {#14 ▶}
#content: null
#languages: null
#charsets: null
#encodings: null
#acceptableContentTypes: null
#pathInfo: "/39/edit/116/submit"
#requestUri: "/app_dev.php/39/edit/116/submit"
#baseUrl: "/app_dev.php"
#basePath: null
#method: "GET"
#format: null
#session: Session {#151 ▶}
#locale: null
#defaultLocale: "en"
}
DashboardController.php on line 138:
"ROLE_COMPANY"
I try transmit $role indexAction -> editAction in indexAction I set:
public function indexAction($username)
{
$user_role = $user->getRoles();
$request->getSession()->set('role', $user_role[0]);
and in form I have action hoe in this action transmit this $role but this role not add in routing:
<td>
{{ developer.firstname }} {{ developer.lastname }}
</td>
in indexAction
$user_role = $user->getRoles();
$request->getSession()->set('role', $user_role[0]);
$role = $request->getSession()->get('role');
dump($user_role, $role);exit;
And I see
UserProfileController.php on line 69:
array:1 [▼
0 => "ROLE_COMPANY"
]
UserProfileController.php on line 69:
"ROLE_COMPANY"
Now in index action I render in template ? Because now in indexAction I render some data and in template I have
If you want to pass a variable from a view to the receiving controller you should use POST method.
You can achieve this, for example, submitting a form.
If you knew this data when executing the previous action, then you can store this information in session:
$request->getSession()->set('VARIABLE_NAME', $value);
And then get it back with:
$value = $request->getSession()->get('VARIABLE_NAME');

Categories