I understand that Response::view actually ends up calling View::make as part of the process, but why would I return one vs the other from a controller ?
Can someone explain what the difference in output is between them ?
They are both the same things.
Looking at the code :
public static function view($view, $data = array(), $status = 200, array $headers = array()) {
$app = Facade::getFacadeApplication();
return static::make($app['view']->make($view, $data), $status, $headers);
}
We can see that Response::view() calls View::make(), uses that to create a response with the default status being 200 OK and no additional HTTP headers.
You'll need to use Response::view() over View::make() when you need to return a non-default status code or additional headers.
Moreover, this post on Laravel.io confirms that Response::view() is just a shortcut for Response::make(View::make()) added after the initial Laravel 4 beta commit.
Related
In Laravel 8, $request->all() is not showing dynamic path parameters.
Steps to reproduce:
Start a new Laravel project with composer, Laravel version 8.61.0.
Define a route in routes/api.php as follows:
Route::get('/first/{first}/second/{second}', function (Request $request) {
return $request->all();
});
Run php artisan serve and direct the browser to http://localhost:8000/api/first/hello/second/world
Expect something like the following response: {"first":"hello","second":"world"}. Instead we simply see [].
Change the route to:
Route::get('/first/{first}/second/{second}', function (Request $request) {
return [
'first'=>$request->first,
'second'=>$request->second,
];
});
Then we see the expected {"first":"hello","second":"world"}
So...why isn't $request->all() giving me this response?
I looked at Laravel doc
Retrieving All Input Data
You may retrieve all of the incoming request's input data as an
array using the all method. This method may be used regardless of
whether the incoming request is from an HTML form or is an XHR
request
Means $request->all() equal to $request->input('name', 'email', ....)
instead of $request->all() you can try this
Route::get('/first/{first}/second/{second}', function (Illuminate\Http\Request $request, $first, $second) {
return [
'first'=> $first,
'second'=> $second,
];
});
UPDATE
You can do it too with (splat operator in PHP)
Route::get('/first/{first}/second/{second}', function (Illuminate\Http\Request $request, ...$all) {
return $all;
});
// returns
[
"hello",
"world"
]
I hope this helped you,
Happy Coding.
Have you tried dd($request->all()) statement in Controller, not in closure. In previous Laravel version (version 5 or version 7), that state works in that versions.
don't know why $request->all() is empty, but I used $request->getContent() and it returned the data in the body of the request I was looking for.
Credit:
Laravel empty request
I have used a code from Internet to call controller action dynamically. Here is the code for that, and is used in web.php. But I dont fully understand what it does.
Route::match(['get', 'post'], '{controller}/{action?}/{params1?}/{params2?}', function ($controller, $action = 'index', $params1 = '',$params2 = '') {
$params = explode('/', $params1);
$params[1] = $params2;
$app = app();
$controller = $app->make("\App\Http\Controllers\\" . ucwords($controller) . 'Controller');
return $controller->callAction($action, $params);
})->middleware('supadminauth');
Can someone explain?
Route::match(['get', 'post'], '{controller}/{action?}/{params1?}/{params2?}', function ($controller, $action = 'index', $params1 = '',$params2 = '') {
The first line looks at the request to see whether it is a get or post request, if it is some other types of request that means it does not match and will not proceed further. Then the url are separated into 4 parts corresponding by their name and passed into variables with the same name i.e. $controller, $action, $param1 and $params2 where the last 3 variables do not need to be present (with ? at the end of the name).
$params = explode('/', $params1);
$params[1] = $params2;
I believe this is a crude way to create an array of parameters as $params where the following would be more appropriate.
$params = [$params1, $params2];
.
$app = app();
$controller = $app->make("\App\Http\Controllers\\" . ucwords($controller) . 'Controller');
Then load the relevant controller.
return $controller->callAction($action, $params);
And run the corresponding action and passing all the parameters with it.
Hope this makes sense.
This is example of use it:
If you have controller like bellow:
class AdminController extends Controller {
public function index(){ //sample 0, sample 1
...
}
public function view($param1){ //sample2 , sample3
...
}
}
There is some sample route for calling them
sample0: yoursite.com/admin
sample1: yoursite.com/admin/index
sample2: yoursite.com/admin/view
sample3: yoursite.com/admin/view/5
Notice in your question ? in {action?} means it can either have value or not. Other things is simple and clear. Do you need more explaination?
I'm using Guzzle version 6.3.3. I want to make multiple HTTP requests from an external API. The code shown below worker perfect for me. This is just one single request.
public function getAllTeams()
{
$client = new Client();
$uri = 'https://api.football-data.org/v2/competitions/2003/teams';
$header = ['headers' => ['X-Auth-Token' => 'MyKey']];
$res = $client->get($uri, $header);
$data = json_decode($res->getBody()->getContents(), true);
return $data['teams'];
}
But now I want to make multiple requests at once. In the documentation of Guzzle I found out how to do it, but it still didn't work properly. This is the code I try to use.
$header = ['headers' => ['X-Auth-Token' => 'MyKey']];
$client = new Client(['debug' => true]);
$res = $client->send(array(
$client->get('https://api.football-data.org/v2/teams/666', $header),
$client->get('https://api.football-data.org/v2/teams/1920', $header),
$client->get('https://api.football-data.org/v2/teams/6806', $header)
));
$data = json_decode($res->getBody()->getContents(), true);
return $data;
I get the error:
Argument 1 passed to GuzzleHttp\Client::send() must implement interface Psr\Http\Message\RequestInterface, array given called in TeamsController.
If I remove the $header after each URI then I get this error:
resulted in a '403 Forbidden' response: {"message": "The resource you are looking for is restricted. Please pass a valid API token and check your subscription fo (truncated...)
I tried several ways to set X-Auth-Token with my API key. But I still get errors and I don't know many other ways with Guzzle to set them.
I hope someone can help me out :)
Guzzle 6 uses a different approach to Guzzle 3, so you should use something like:
use function GuzzleHttp\Promise\all;
$header = ['headers' => ['X-Auth-Token' => 'MyKey']];
$client = new Client(['debug' => true]);
$responses = all([
$client->getAsync('https://api.football-data.org/v2/teams/666', $header),
$client->getAsync('https://api.football-data.org/v2/teams/1920', $header),
$client->getAsync('https://api.football-data.org/v2/teams/6806', $header)
])->wait();
$data = [];
foreach ($responses as $i => $res) {
$data[$i] = json_decode($res->getBody()->getContents(), true);
}
return $data;
Take a look at different questions on the same topic (#1, #2) to see more usage examples.
I am trying to mock a guzzle response from a specific api.
My controller code looks like this (amended for brevity):
class SomeClass
{
private $guzzle;
public function __construct(\GuzzleHttp\Client $guzzle) {
$this->guzzle = new $guzzle();
}
public function makeRequest(){
$client = $this->guzzle;
$url = 'http//somerurl';
$options = [];
$response = $client->request('POST', $url, $options);
return $response;
}
}
And the test looks something like this (again edited)...
public function someTest(){
$mock = $this->createMock(\GuzzleHttp\Client::class);
$mock->method('request')->willReturn([
'response' => 'somedata'
]);
$someClass = new $SomeClass($mock);
$response = $someClass->makeRequest();
$body = $response->getBody();
...
}
At this point the test returns "Call to a member function getBody on null";
How can the getBody response of a guzzle call be tested?
Thank you in advance...
One approach to testing with Guzzle is to configure a MockHandler
http://docs.guzzlephp.org/en/stable/testing.html
So instead of mocking the guzzle client, you create one like so:
public function someTest() {
$mock = new MockHandler([
new Response(200, [], 'The body!'),
// Add more responses for each response you need
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$someClass = new SomeClass($client);
$response = $someClass->makeRequest();
$body = $response->getBody();
$this->assertSame('The body!', $body);
}
The MockHandler requires you to 'queue' responses, meaning you need to know in what order external API calls will be made. I've taken this a step further and wrapped the MockHandler in another handler capable of stuffing a dummy-response into it at the last moment, if one isn't already waiting in the wings. See https://gist.github.com/kmuenkel/d4d473beb7b2297ac2d8cd480089a738
Just use that trait in your test, and call $this->mockGuzzleResponses(); from the test class's setUp() method. At that point, all requests intended to pass through Guzzle will be available for assertions by way of the $guzzleRequestLog property, and all responses can be mocked by calling $this->guzzleHandler->append(RequestInterface); at the beginning of your test.
Just make sure that all implementations of Guzzle in your code are instantiated by way of the app(Client::class) helper and not new Client. Otherwise the binding override won't take effect. That may have been your issue earlier.
Take a look at my composer package https://packagist.org/packages/doppiogancio/mocked-client.
In my opinion, it's a really simple way to mock a Guzzle Client, binding request URLs with responses.
$builder = new HandlerStackBuilder();
// Add a route with a response via callback
$builder->addRoute(
'GET', '/country/IT', static function (ServerRequestInterface $request): Response {
return new Response(200, [], '{"id":"+39","code":"IT","name":"Italy"}');
}
);
// Add a route with a response in a text file
$builder->addRouteWithFile('GET', '/country/IT/json', __DIR__ . '/fixtures/country.json');
// Add a route with a response in a string
$builder->addRouteWithString('GET', '/country/IT', '{"id":"+39","code":"IT","name":"Italy"}');
// Add a route mocking directly the response
$builder->addRouteWithResponse('GET', '/admin/dashboard', new Response(401));
$client = new Client(['handler' => $builder->build()]);
Once you did you will have a fully functional client to use normally
$response = $client->request('GET', '/country/DE/json');
$body = (string) $response->getBody();
$country = json_decode($body, true);
print_r($country);
// will return
Array
(
[id] => +49
[code] => DE
[name] => Germany
)
I'm more or less new to Laravel 4. I've never used routes before but normally what I'm used to is url/controller/action and then the backend routing for me. I've read the documentation for routes and controllers a few times as well as read through some tutorials and so, I'm trying to figure out how to get this to work without writing a route for every controller and action.
I tried something like
Route::get('{controller}/{action}', function($controller, $action = 'index'){
return $controller."#".$action;
});
Now then, I know this is wrong since it doesn't work, but what am I missing? On most tutorials and stuff I'm seeing an route for more or less every controller and action like:
Route::get('/controller/action' , 'ControllerName#Action');
Which seems silly and like a waste of time to me.
Is there anyway to achieve what I want?
If you are looking for a more automated routing, this would be the Laravel 4 way:
Route:
Route::controller('users', 'UsersController');
Controller (in this case UsersController.php):
public function getIndex()
{
// routed from GET request to /users
}
public function getProfile()
{
// routed from GET request to /users/profile
}
public function postProfile()
{
// routed from POST request to /users/profile
}
public function getPosts($id)
{
// routed from GET request to: /users/posts/42
}
As The Shift Exchange mentioned, there are some benefits to doing it the verbose way. In addition to the excellent article he linked, you can create a name for each route, for example:
Route::get("users", array(
"as"=>"dashboard",
"uses"=>"UsersController#getIndex"
));
Then when creating urls in your application, use a helper to generate a link to a named route:
$url = URL::route('dashboard');
Links are then future proofed from changes to controllers/actions.
You can also generate links directly to actions which would still work with automatic routing.
$url = URL::action('UsersController#getIndex');
app\
controllers\
Admin\
AdminController.php
IndexController.php
Route::get('/admin/{controller?}/{action?}', function($controller='Index', $action='index'){
$controller = ucfirst($controller);
$action = $action . 'Action';
return App::make("Admin\\{$controller}Controller")->$action();
});
Route::get('/{controller?}/{action?}', function($controller='Index', $action='index'){
$controller = ucfirst($controller);
$action = $action . 'Action';
return App::make("{$controller}Controller")->$action();
});
I come from .Net world and routing is typically done:
/{Controller}/{action}/{id}
Which looks like:
/Products/Show/1 OR /Products/Show/Beverages
In Laravel I accomplish this routing like so:
Route::get('/{controller?}/{action?}/{id?}', function ($controller='Home', $action='index', $id = null) {
$controller = ucfirst($controller);
return APP::make("{$controller}Controller")->$action($id);
});
The controller would look roughly like so:
class ProductsController extends BaseController {
public function Show($id) {
$products = array( 1 => array("Price" => "$600","Item" => "iPhone 6"),
2 => array("Price" => "$700", "Item" => "iPhone 6 Plus") );
if ($id == null) {
echo $products[1]["Item"];
} else {
echo $products[$id]["Item"];
}
}
}