Can't get POST parameters in controller - php

I'm experimenting with Symfony and I am trying to make a POST request. But I can't seem to get the request's POST parameters inside my controller.
/**
* #Route("/messages/comment/post/", methods={"POST"}, name="postComment")
*/
public function postComment($messageId, $comment)
{
$statuscode = 200;
$response = null;
try {
$response = $this->messageModel->postComment($messageId, $comment);
} catch (\PDOException $exception) {
var_dump($exception);
$statuscode = 500;
}
return new JsonResponse($response, $statuscode);
}
How do I define parameters for this?

To get POST parameters inside a controller you should use:
use Symfony\Component\HttpFoundation\Request;
....
/**
* #Route("/messages/comment/post/", methods={"POST"}, name="postComment")
*/
public function postComment(Request $request){
$messageId = $request->request->get('messageId');
$comment = $request->request->get('comment');
...
}
Documentation here

Related

custom exception of Laravel is not working as expected

I am working with the Laravel 9 application. I have created a custom Exception. I want to report the General Exception to the sentry and this custom Exception to another vendor like Papertrail.
The Handler.php is not calling the reportable closure function when the application throws a custom exception i.e ServiceException.
By the way, the Laravel documentation for errors is also not understandable
Handler.php
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Inertia\Inertia;
use Throwable;
use Exception;
use App\Exceptions\ServiceException;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* #var array
*/
protected $dontReport = [
// ServiceException::class
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* #var array
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* #return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
// this block is calling for Exception but not for custom Exception like ServiceException.
if ($this->shouldReport($e) && app()->bound('sentry')) {
app('sentry')->captureException($e);
}
});
$this->reportable(function (ServiceException $e) {
// this block is not calling when I throw ServiceException.
echo "Send this ServiceException to papertrail app."; // this line is never called.
die;
});
}
public function render($request, Throwable $e)
{
$response = parent::render($request, $e);
if ($request->isJson()) {
//prevent error for local and staging.
if (! app()->environment(['local', 'staging']) && in_array($response->status(), [500, 503, 404, 403])) {
\Log::error('API Error Handler', [$response->getOriginalContent()]);
$message = trans('message.error_description_500');
if ($response->status() == 404) {
$message = trans('message.data_not_found');
} elseif ($response->status() == 403) {
$message = trans('message.you_are_not_allowed_perform');
} elseif ($response->status() == 503) {
$message = trans('message.error_description_503');
}
return response()->json([
'message' => $message,
], $response->status());
}
}
return $response;
}
}
ServiceException.php
<?php
namespace App\Exceptions;
use Exception;
class ServiceException extends Exception
{
/**
* Report the exception.
*
* #return void
*/
public function report()
{
//
}
/**
* Render the exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function render($request, $exception)
{
if (! $request->ajax()) {
// view('error_handler', compact('exception'));
}
return response()->json([
'code' => $exception->getCode(),
'status' => 'error',
'message' => $exception->getMessage(),
'data' => 'sample data',
]);
}
}
AnyController.php
public function anyFunction() {
// throw new Exception('Unhandled Exception.'); // It will call the $this->reportable(function (Throwable $e) { block.
// throw new ServiceException('Unhandled ServiceException.'); // It will call the $this->reportable(function (Throwable $e) { block.
try {
$this->service->aFunctionThatThrowException(); // this function will throw ServiceException.
} catch (Exception $e) {
Log::error('controller_fun_error', ['error' => $e->getMessage()]);
report($e);
return $this->failResponse();
}
}
AnyService.php
public function aFunctionThatThrowException() {
try {
throw new ServiceException('Throwing ServiceException...)');
} catch (ServiceException | Exception $e) {
Log::error('service_fun_error', ['error' => $e->getMessage()]);
throw $e;
}
}
I'm not intirely sure, but I don't think the error will be reported if you put a try catch around it yourself.
Create a new helper functions file eg: app/Helpers/Common.php and create a service provider HelperServiceProvider in app/Providers/HelperServiceProvider.php use the code below :
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
require_once __DIR__ . '/../Helpers/Common.php';
}
}
Now copy the below mentioned code to the app/Helpers/Common.php file :
if (! function_exists('throwResponse')) {
function throwResponse($message='Exceptions',$data=[],$statusCode=500){
if(request()->wantsJson()) {
if ((gettype($message) !== 'string') && ($message instanceof \Exception)) {
if($message->getMessage()){
$data = (!empty($message->getTrace())) ? $message->getTrace() : [];
$message = (!empty($message->getMessage())) ? $message->getMessage() : "Something went wrong";
$data = $data?:[$message];
$statusCode = 500;
}else{
throw new \Illuminate\Http\Exceptions\HttpResponseException($message->getResponse());
}
}
$errStatus = (in_array($statusCode,[200,201])) ? false : true;
$response = ['code'=>(int)$statusCode,
'error'=>$errStatus,
'message'=>$message];
if(!empty($data)){
$response['data'] = $data;
}
if($statusCode == 200 && $data == "empty"){
$response['data'] = [];
}
throw new \Illuminate\Http\Exceptions\HttpResponseException(response()->json($response,$statusCode));
} else{
if(is_object($message)){
throw $message;
}else{
return $message;
}
}
}
}
Now you can easily call the throwResponse function from anywhere in the project :
for normal response
throwResponse('Message for the response',['user_id'=>1],200);
{
"code":200,
"error":false,
"message":"Message for the response",
"data":{
"user_id":1
}
}
or
throwResponse('unauthorized',null,401);
{
"code":401,
"error":true,
"message":"unauthroized",
}
or for exceptions simply pass the exception $e
throwResponse($e);
{
"code":500,
"error":true,
"message":"exception message",
"data":{
exception data
}
}
The response will be
for example :
public function exampleFunction(Request $request){
try{
if(empty($request->userId)){
throwResponse('Validation error',null,401);
}
$userData = User::find($request->userId);
if(!empty($userData)){
throwResponse('User data',$userData,401);
}else{
throwResponse('Invalid user id',null,422);
}
}catch(\Exception $e){
throwResponse($e);
}
}
Change responses according to your need

PhP call a function and pass parameters to construct

I have a class social which has:
protected $id;
public function __construct($request, $id)
{
Log::info('Processing...', ['request' => $request, 'id' => $id]);
try {
$client = new Client();
$url = sprintf($request);
$response = $client->get($url);
$json = json_decode((string) $response->getBody(), true);
return $json;
} catch (ClientException $exception) {
$responseBody = $exception->getResponse()->getBody(true);
Log::error($responseBody, ['entity_id' => $id]);
}
}
public function wikipedia($wikipedia_url, $id)
{
dd($json);
try {
$wikipedia_array = $json['parse']['text'];
$wikipedia_array = array_slice($wikipedia_array, 0, 9);
$wikipedia_array = implode($wikipedia_array, ',');
Log::info('Processed Wikipedia for', ['entity_id' => $id]);
return $wikipedia_array;
} catch (Exception $e) {
Log::error('Wikipedia:', ['message' => $e->getMessage(), 'entity_id' => $id]);
}
}
In another function I am calling a facade like this:
$id = $entity->id;
$wikipedia_id = $entity->wikipedia;
if (!empty($wikipedia_id)) {
$wikipedia_url = 'http://en.wikipedia.org/w/api.php?action=parse&prop=text&section=0&disablelimitreport=1&format=json&page='.$wikipedia_id;
$wikipedia_html = Social::wikipedia($wikipedia_url, $id);
Log::info('Wikipedia ok for', ['entity_id' => $id]);
}
However I get this:
Type error: Too few arguments to function App\Helpers\Social::__construct(), 0 passed in /home/vagrant/liveandnow/app/Providers/SocialServiceProvider.php on line 35 and exactly 2 expected
Can anyone explain to me how to call a method, pass parameters to it but also pass them along to construct?
Here's my facade:
<?php
namespace App\Facade;
use Illuminate\Support\Facades\Facade;
class Social extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'social';
}
}
and service provider:
public function register()
{
$this->app->bind('social', function ($app) {
return new Social;
});
}
the error lies in the service provider.
You define a constructor with 2 parameters,
public function __construct($request, $id)
but in your service provider you call it like this:
public function register()
{
$this->app->bind('social', function ($app) {
return new Social;
});
}
You need to add both arguments when instantiating the Social class, for example like
return new Social("http://xyz.de", 1);
Hope this helps.

Pass array from controller to middleware Slim 3 PHP

I'm trying to pass an array with data to middleware and format it based on Accept HTTP header.
The controller gets the data from db and should pass it to the response object. Response object write() method accepts only strings:
public function getData(Request $request, Response $response): Response {
return $response->write($this->getUsers());
# This line of code should be fixed
}
The middleware should get the response and format it correctly:
public function __invoke(Request $request, Response $response, callable $next) {
$response = $next($request, $response);
$body = $response->getBody();
switch ($request->getHeader('Accept')) {
case 'application/json':
return $response->withJson($body);
break;
case 'application/xml':
# Building an XML with the data
$newResponse = new \Slim\Http\Response();
return $newResponse->write($xml)->withHeader('Content-type', 'application/xml');
break;
case 'text/html':
# Building a HTML list with the data
$newResponse = new \Slim\Http\Response();
return $newResponse->write($list)->withHeader('Content-type', 'text/html;charset=utf-8');
break;
}
}
I have a few routes behaves similarly:
$app->get('/api/users', 'UsersController:getUsers')->add($formatDataMiddleware);
$app->get('/api/products', 'UsersController:getProducts')->add($formatDataMiddleware);
By using middlewares I can add such functionality in a declarative way, keeping my controller thin.
How can I pass the original data array to response and implementing this pattern?
The Response-Object doesn't provide this functionallity nor has some extensions to do that. So you need to adjust the Response-Class
class MyResponse extends \Slim\Http\Response {
private $data;
public function getData() {
return $this->data;
}
public function withData($data) {
$clone = clone $this;
$clone->data = $data;
return $clone;
}
}
Then you need to add the new Response to the Container
$container = $app->getContainer();
$container['response'] = function($container) { // this stuff is the default from slim
$headers = new Headers(['Content-Type' => 'text/html; charset=UTF-8']);
$response = new MyResponse(200, $headers); // <-- adjust that to the new class
return $response->withProtocolVersion($container->get('settings')['httpVersion']);
}
Now change the Response type to MyResponse and use the withData method
public function getData(Request $request, \MyResponse $response): Response {
return $response->withData($this->getUsers());
}
At the end you can use the getData method and use its value and process it inside the middleware.
public function __invoke(Request $request, \MyResponse $response, callable $next) {
$response = $next($request, $response);
$data = $response->getData();
// [..]
}
That would be the answer to your question. A better solution in my opinion, would be a helper class which does what your middleware does, then you could something like this:
public function getData(Request $request, Response $response): Response {
$data = $this->getUsers();
return $this->helper->formatOutput($request, $response, $data);
}
For that there is already a lib for: rka-content-type-renderer

ZF2 - Set cookie in dispatch listener

I would like to set a cookie in a listener, if the query parameter "source" is set. I tried the following but the cookie does not exist.
How can I set the cookie correct?
class DispatchListener extends AbstractListenerAggregate {
public function attach(EventManagerInterface $eventManager) {
$this->listeners[] = $eventManager->getSharedManager()->attach(
'Zend\Stdlib\DispatchableInterface',
MvcEvent::EVENT_DISPATCH,
array($this, 'setCookie'),
-80
);
}
/**
* #var \Zend\Stdlib\RequestInterface
*/
protected $request;
/**
* #param \Zend\Stdlib\RequestInterface $request
*/
public function __construct(RequestInterface $request) {
$this->request = $request;
}
public function setCookie(EventInterface $event) {
if ($source = $this->request->getQuery('source')) {
$this->request->setCookies([
'source' => $source
]);
}
}
}
--------------------------------- UPDATE -----------------------------------
class Module implements ConfigProviderInterface, BootstrapListenerInterface {
public function onBootstrap(EventInterface $event) {
$target = $event->getTarget();
$serviceManager = $target->getServiceManager();
$eventManager = $target->getEventManager();
$eventManager->attach($serviceManager->get('Application\Listener\Dispatch'));
}
}
Seems like there are a few issues with your code, which we can rule out first.
You haven't provided code for how you are attaching your listener
Not sure why you are setting the cookie on the request, do you want to do this on the response?
Attaching the event listener:
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$app = $e->getApplication();
$em = $app->getEventManager();
// Attach event to attach listener after routing when query will be populated
$em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($eventManager) {
$request = $e->getRequest();
// attach our listener
$eventManager->attach(new DispatchListener($request));
});
}
Updated setCookie method:
public function setCookie(EventInterface $event) {
if ($source = $this->request->getQuery('source')) {
$this->request->setCookies([
'source' => $source
]);
$request = $this->request;
$cookieData = $request->getCookie('someCookie', 'default');
var_dump($cookieData);
}
}
The var_dump prints the following:
object(Zend\Http\Header\Cookie)[274]
protected 'encodeValue' => boolean true
private 'storage' (ArrayObject) =>
array (size=1)
'source' => string 'test' (length=4)
Do you want a cookie in your request object or do you want to create a cookie for the response. You probably want to create a cookie and set it on the response object.
Check for example this answer.
public function setCookie(EventInterface $event) {
if ($source = $this->request->getQuery('source')) {
$cookie = new \Zend\Http\Header\SetCookie('source', $source);
$headers = $this->getResponse()->getHeaders();
$headers->addHeader($cookie);
}
}

Send multiple requests through a middleware in Guzzle

Is there any way I can send multiple requests with guzzle using a middleware and according to certain parameters?
To be more specific I need:
class MyMiddleware
{
/**
* #param callable $handler
* #return \Closure
*/
public function __invoke(callable $handler)
{
return function (RequestInterface $request, array $options) use ($handler) {
if(!isset($options['token']){
//request the token from a url
}
//after the token is aquired proceed with the original request
return $handler($request, $options);
};
}
}
Is this similar to what you are inquiring about? I have done similar to it.
$handler = GuzzleHttp\HandlerStack::create();
$client = new GuzzleHttp\Client([
'handler' = $handler,
// other options
])
$handler->push(GuzzleHttp\Middleware::mapRequest(
function (Psr\Http\Message\RequestInterface $request) use ($client) {
if ('POST' !== $request->getMethod()) {
return $request;
}
if($RequestIsOkAsIs) {
return $request;
}
$response = $client->get($someUrl, $requestOptions);
// Play with the response to modify the body
$newBody = 'results of stuff';
return new GuzzleHttp\Psr7\Request(
$request->getMethod(),
$request->getUri(),
$request->getHeaders(),
$newBody,
$request->getProtocolVersion()
);
}
);

Categories