What is the proper way to manipulate final output before sending it to browser? (laravel 5.*)
I have created facade
namespace App\Facades;
use Illuminate\Support\Facades\Response as ResponseFacade;
use Illuminate\Http\Response as ResponseHttp;
class Response extends ResponseFacade
{
public static function viewMod($view, $data = [], $status = 200, array $headers = [])
{
$output = \Response::view($view, $data, $status, $headers);
return some_manipulating_function($output);
}
}
and in the controller action i use
return viewMod("my_view_file", array $view_data);
but i receive corrupted output (http response headers are added to/ prepended to html)
most probably \Response related __toString method behaves strangely
any ideas? (thanks!)
You can use AfterMiddleware like below example from docs,
<?php
namespace App\Http\Middleware;
use Closure;
class AfterMiddleware
{
public function handle($request, Closure $next)
{
$response = $next($request);
// Perform action
return $response;
}
}
Related
I'm using Laravel 8 to build an API, I have a route which for the purposes of the question is... /api/fact which GETs a random fact. I have a FactsController.php file and two traits, one for constructing a JSON response, the other for getting a fact. For some reason when calling the trait's function from within my controller (which made the request) I'm not getting any returned JSON back in Postman...
Traits/GetFact.php
<?php
namespace App\Traits;
use App\Traits\ApiResponse;
trait GetFact {
use ApiResponse;
/**
* Get random fact
*
* get random fact
*/
public function getRandomFact () {
$this->getFact();
$this->apiResponse([
'msg' => "Fact..."
], 200, true);
}
}
Traits/ApiResponse.php
<?php
namespace App\Traits;
trait ApiResponse {
/**
* API Response
*
* Return a JSON response for our API with params
*/
public function apiResponse ($data, $code, $success = false) {
return response()->json(array_merge(['success' => $success], $data), $code);
}
}
Controllers/FactController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Traits\GetFact;
class FactsController extends Controller
{
use GetFact;
/**
* Fact
*
* #param Request $request
*/
public function fact()
{
$this->getRandomFact()
}
}
When I make my request to /api/fact I should be getting the returned JSON listed where I'm calling $this->apiResponse() with the msg, but I'm not? It's like the return doesn't seem to return JSON because it's scoped to the trait and not the controller and that there isn't a return in there? How can I achieve the listed.
You are not "returning" anything from your Controller method. You have to return something for their to be a response with content. Also, fact and getRandomFact do not return anything so calling those isn't doing anything for you. You are also calling a getFact method and not assigning the return to anything (if it even returns anything).
I have the following code in a custom middleware:
public function handle($request, Closure $next)
{
if($request->hasCookie('uuid')) {
return $next($request);
}
$uuid = 99;
$response = $next($request);
return $response->withCookie(cookie()->forever('uuid', $uuid));
}
I have registered the middleware in the app.php file, but cookies is still not being written. Please can anyone help. Additionally can this above be run as a singleton, so that it is executed once on app start?
Thanks
If you are on a local environment, make sure to set 'secure' => env('SESSION_SECURE_COOKIE', false), around lines #165-170 in your config/session.php file.
On a non-SSL domain (like http://localhost/), this directive must be false. Otherwise cookies will not be set in browser.
Here, I'm mention set and get a cookie in laravel simple example following.
First of the create a controller.
1.php artisan make:controller CookieController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class CookieController extends Controller {
**/* below code a set a cookie in browser */**
public function setCookie(Request $request){
$response = new Response('Hello World');
$response->withCookie(cookie('name', 'Anything else'));
return $response;
}
**/* below code a get a cookie in browser */**
public function getCookie(Request $request){
$value = $request->cookie('name');
echo $value;
}
}
Add a following line code in routes/web.php file (Laravel 5.4)
Route::get('/cookie/set','CookieController#setCookie');
Route::get('/cookie/get','CookieController#getCookie');
And all files add-in project than a run program easily sets and get a cookie.
You can obtain the response object in middleware like so:
public function handle($request, Closure $next)
{
$response = $next($request);
// Do something after the request is handled by the application
return $response;
}
So you could do something like this
if($request->hasCookie('uuid')) {
return $next($request);
}
$uuid = Uuid::generate();
$response = $next($request);
return $response->withCookie(cookie()->forever('uuid', $uuid));
You can use the laravel helper function cookie(). This worked for me.
<?php
//Create:
cookie()->queue(cookie($name, $value, $minutes));
// forever
cookie()->queue(cookie()->forever($name, $value));
//get
request()->cookie($name);
//forget
cookie()->queue(cookie()->forget($name));
I'm currently playing around with Exception Handler, and creating my own custom exceptions.
I've been using PHPUnit to run tests on my Controller Resource, but when I throw my custom exceptions, Laravel thinks it's coming from a regular HTTP request rathen than AJAX.
Exceptions return different response based on wether it's an AJAX request or not, like the following:
<?php namespace Actuame\Exceptions\Castings;
use Illuminate\Http\Request;
use Exception;
use Actuame\Exceptions\ExceptionTrait;
class Already_Applied extends Exception
{
use ExceptionTrait;
var $redirect = '/castings';
var $message = 'castings.errors.already_applied';
}
And the ExceptionTrait goes as follows:
<?php
namespace Actuame\Exceptions;
trait ExceptionTrait
{
public function response(Request $request)
{
$type = $request->ajax() ? 'ajax' : 'redirect';
return $this->$type($request);
}
private function ajax(Request $request)
{
return response()->json(array('message' => $this->message), 404);
}
private function redirect(Request $request)
{
return redirect($this->redirect)->with('error', $this->message);
}
}
Finally, my test goes like this (excerpt of the test that's failing)
public function testApplyToCasting()
{
$faker = Factory::create();
$user = factory(User::class)->create();
$this->be($user);
$casting = factory(Casting::class)->create();
$this->json('post', '/castings/apply/' . $casting->id, array('message' => $faker->text(200)))
->seeJsonStructure(array('message'));
}
My logic is like this although I don't think the error is coming from here
public function apply(Request $request, User $user)
{
if($this->hasApplicant($user))
throw new Already_Applied;
$this->get()->applicants()->attach($user, array('message' => $request->message));
event(new User_Applied_To_Casting($this->get(), $user));
return $this;
}
When running PHPUnit, the error I get returned is
1) CastingsTest::testApplyToCasting PHPUnit_Framework_Exception:
Argument #2 (No Value) of PHPUnit_Framework_Assert:
:assertArrayHasKey() must be a array or ArrayAccess
/home/vagrant/Code/actuame2/vendor/laravel/framework/src/Illuminate/Foundation/T
esting/Concerns/MakesHttpRequests.php:304
/home/vagrant/Code/actuame2/tests/CastingsTest.php:105
And my laravel.log is over here http://pastebin.com/ZuaRaxkL (Too large to paste)
I have actually discovered that PHPUnit is not actually sending an AJAX response, because my ExceptionTrait actually changes the response on this. When running the test it takes the request as a regular POST request, and runs the redirect() response rather than ajax(), hence it's not returning the correspond.
Thanks a bunch!
I have finally found the solution!
As I said, response wasn't the right one as it was trying to redirect rathen than return a valid JSON response.
And after going through the Request code, I found out that I need to use also wantsJson(), as ajax() may not be the case always, so I have modified my trait to this:
<?php
namespace Actuame\Exceptions;
trait ExceptionTrait
{
public function response(Request $request)
{
// Below here, I added $request->wantsJson()
$type = $request->ajax() || $request->wantsJson() ? 'ajax' : 'redirect';
return $this->$type($request);
}
private function ajax(Request $request)
{
return response()->json(array('message' => $this->message), 404);
}
private function redirect(Request $request)
{
return redirect($this->redirect)->with('error', $this->message);
}
}
Following is my routes where I am calling my Controller directly in route.
How can I pass Request $request to my getBlog Function.. Or is there any way to directly get $request object in my getblog function of controller ???
$artObj = App::make('App\Http\Controllers\Front\ArticleController');
return $artObj->getBlog($id);
Code in Routes:
Route::get('/{slug}', function($slug) {
// Get Id and Type depending on url
$resultarray = App\Model\UrlAlias::getTypefromUrl($slug);
if(!empty($resultarray)) {
if($resultarray[0]['type'] == 'article') {
$id = $resultarray[0]['ref_id'] ;
$artObj = App::make('App\Http\Controllers\Front\ArticleController');
return $artObj->getBlog($id);
} else {
return Response::view("errors.404", $msg, 400);
}
} else {
return Response::view("errors.404", array(), 400);
}
});
You can do in the head of the routes.php file:
use Illuminate\Http\Request;
And then in the beginning of your route:
Route::get('/{slug}', function($slug, Request $request) {
And the $request will be available to you. But that is extremely bad practice. What you should do - is move the whole logic into the controller like that:
Route::group(['namespace' => 'Front'], function(){
Route::get('/{slug}', 'ArticleController#methodName');
});
and then you can use the Request class in your controller class and the controller method:
<?php
namespace App\Http\Controllers\Front
use Illuminate\Http\Request;
class ArticleController
{ ...
public function methodName(Request $request){
...function implementation...
}
...
}
The Request is a global variable, you can access it anywhere with either php code or even some helper Laravel functions.
Just use request() and it's the same as passing the request as an object inside a variable through a function. (It's equivalent to the Request $request variable received).
It improved readability also. Just remember you can't change request objects directly, you'd better use request()->merge(['key' => 'newValue']);
when i use laravel middleware its routes is not work properly
<?php
namespace App\Http\Controllers;
use Auth;
use App\Article;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Requests\ArticleRequest;
use App\Http\Controllers\Controller;
use Carbon\Carbon;
//use Illuminate\Http\Request;
class ArticlesController extends Controller
{
public function __construct(){
$this->middleware('auth',['only'=>'create']);
}
//
public function index(){
//return \Auth::user();
$articles = Article::latest('published_at')->published()->get();
return view('articles.index',compact('articles'));
}
public function show($id){
$article = Article::findorFail($id);
//dd($article->published_at->addDays(8)->diffForHumans());
return view('articles.show',compact('article'));
}
public function create(){
if(Auth::guest()){
return redirect('articles');
}
return view('articles.create');
}
public function store(ArticleRequest $request){
/*
$input = Request::all();
$input['published_at'] = Carbon::now();
*/
$article = new Article($request->all());
Auth::user()->articles()->save($article);
//Article::create($request->all());
return redirect('articles');
}
public function edit($id){
$article = Article::findorFail($id);
return view('articles.edit', compact('article'));
}
public function update($id, ArticleRequest $request){
$article = Article::findorFail($id);
$article->update($request->all());
return redirect('articles');
}
}
when i go to http://localhost/lernlaravel/public/articles/create it works fine
but when i go to http://localhost/learnlaravel/public/articles it redirect to http://localhost/articles.
index() method is used for listing articles how i can fix it?
The redirect () accepts a URL path so if you want ensure your redirect will work on both testing and production environments, I would pass either action () or route () to all of your applications redirect calls. In your this case I would go with
return redirect(action ('ArticlesController#show', $articles->id));
This way Laravel will automatically generate the proper URL path to the controller you want to handle the request.
If you choose to go with route() you are required to have named the route in your routes file, but I find that with resourceful controllers it's less complicated to go with action.