Distinguish between a call from web route vs API route? - laravel

In my web.php file, I have a route that looks like this:
Route::get('/', 'HomeController#getFeed');
And in my api.php file, I have a route that looks like this:
Route::get('feeds', 'HomeController#getFeed');
Notice that they both call the same method, getFeed().
Is there a way to distinguish whether the call came from the web route vs the API route in the controller's method? I need to be able to return two different responses, one for the web route and one for the API route.
Here is the HomeController.php class:
class HomeController extends Controller
{
public function getFeed() {
$user = Auth::user();
// How to check if call is from web route or API route?
// Need to return two different responses for each scenario.
}
}
Thanks.

All routes from api.php are automatically prefixed with 'api/'
So you can use he below code to check
if (Request::is('api*')) {
echo "request from api route";
exit();
}else{
echo "request from web";
exit();
}

I use \Request::is('api/*')
if(\Request::is('api/*'))
return 'API';
else
return 'Non-API';
Alternatively you can check using the route name;
if(\Request::route()->getName() == 'APIFeed')
return 'API';
else
return 'Non-API';
Make sure to set the route name;
In web.php
Route::get('feeds', 'HomeController#getFeed')->name('WebFeed');
In api.php
Route::get('feeds', 'HomeController#getFeed')->name('APIFeed');

replace this line
Route::get('feeds', 'HomeController#getFeed');
with
Route::get('api/feeds', 'HomeController#getFeed');
means add api prefix in all of your api routes.
it will help you to identify which route come from api and which not.
to check you can use below code
if (Request::is('api*')) {
echo "request from api route";
exit();
}else{
echo "request from web";
exit();
}

You can use
$currentRoute = Illuminate\Routing\Router::getCurrentRoute(); // Returns a Route
$currentRoute->uri(); // returns the uri of the cureent route
https://laravel.com/api/5.4/Illuminate/Routing/Router.html#method_getCurrentRoute
https://laravel.com/api/5.4/Illuminate/Routing/Route.html
Hope this helps. :)

In your shoes, I would have created 3 controllers to handle the requests and like Niraj proposed, separate routes by prefix the api route with /api/
class HomeController extends Controller
{
public function getFeed(entrypoint) {
$user = Auth::user();
// do the common magic here ...
}
}
class WebHomeController extends HomeController
{
public function getFeed() {
feed = this.getFeed();
// do the crazy web magic here ...
}
}
class APIHomeController extends HomeController
{
public function getFeed() {
feed = this.getFeed();
// do the crazy api magic here ...
}
}
Route::get('feeds', 'WebHomeController#getFeed');
Route::get('api/feeds', 'APIHomeController#getFeed');

Here's how you access parent methods from child controller
class WebHomeController extends HomeController
{
//get parent methods and protected variables
public function __construct()
{
parent::__construct();
}
public function getFeed() {
//access parent method
$p = $this->getFeed();
feed = this.getFeed();
// do the crazy web magic here ...
}
}

Related

How to pass data to all views in Laravel 5.6?

I have two controllers. StudentController and TeacherController. I have a variable $chat which I want to pass in all the views of StudentController and TeacherController. The $chat will contain different data for both these controllers.
I searched and found ways, but I am getting empty data. I am doing it like this.
<?php
namespace App\Http\Controllers;
use View;
class StudentController extends Controller {
public function __construct()
{
$this->middleware('auth')->except(['home']);
$this->middleware('access')->except(['home']);
$chats = studentChat();
View::share('chats', $chats);
}
So, here I am printing and it is returning an empty array, but when I use the same in a function the array contains data. What is wrong here? Can anyone please help?
What I tried:
public function boot()
{
View::composer('*', function ($view) {
$chats = Cache::remember('chats', 60, function () {
if(Auth::user()->user_type() == config('constant.student'))
{
return studentChat();
}
else
{
return teacherChat();
}
});
$view->with('chats', $chats);
});
}
If you use View::share your share data to ALL your view, if you need to add data to few different views you may do this:
Create blade file(chat.blade.php for your case), and put your variables:
<? $chats = studentChat(); ?>
Include this file to the begining of your views where your need this 'global' varables:
//begin of your blade file
#include('chat')
//some code
{{ $chat->channel }}
Sharing Data With All Views
Occasionally, you may need to share a piece of data with all views that are rendered by your application. You may do so using the view facade's share method. Typically, you should place calls to share within a service provider's boot method. You are free to add them to the AppServiceProvider or generate a separate service provider to house them:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
$chats = studentChat();
View::share('chats', $chats);
}
public function register()
{
//
}
}
So, what I did was in the AppServiceProvider class, in the boot function I added this.
View::composer('*', function ($view) {
if(!\Auth::check())
{
return;
}
$userType = \Auth::user()->user_type ;
if($userType == config('constant.student'))
{
$chats = studentChat();
}
else if($userType == config('constant.teacher'))
{
$chats = teacherChat();
}
$view->with('chats', $chats);
});
You can pass data to the view Like.
return View::make('demo')->with('posts', $posts);
For more details visit article : Introduction to Routing in Laravel
write your query in boot method in appServiceProvider like,
View::composer('*', function ($view) {
$share_query = Cache::remember('share_query', 60,function () {
return App\User::all();
});
$view->with('share_query', $share_query);
});
Your final solution is ok, but not the cleanest possible.
Here is what i would do.
Define a class with a single function that contains your logic and return $chats, that way you will encapsulate your logic properly and keep your service provider boot method clean.
Then you have 2 options:
Inject your class in the boot() method of the service provider you use, then call its function and uses View::share. Should looks like :
public function boot(ChatResolver $chatResolver)
{
$chats = $chatResolver->getChats();
View::share(compact('chats));
}
If you only use $chats variable in a signe view or partial (like a part of layout), you can also inject the class you defined directly in the view.
Here is a link to Laravel doc regarding that.
In some cases it might be the easiest solution.

Setting getKeyRouteName dependant on route (web or api)

Tried looking for the answer to this everywhere but having no luck so far...
Basically I want my web route to use a slug for its URL, but I want to use ID for the API route. So...
http://myurl.com/chapter/my-chapter-slug
and
http://myurl.com/api/chapter/1234
Have tried various combinations of things in the getRouteKeyName method (if(Request::route()->named('myapiroute'), if(Request::isJson() etc...) but I think these might be being checked against the page it's running on, rather than the route I'm trying to generate?
I'm thinking maybe I need to extend the base model to have a separate one to use with my API maybe?
So I'd have...
class Chapter extends Model
{
public function getRouteKeyName()
{
return 'slug';
}
....
}
and then...
class ApiChapter extends Chapter
{
public function getRouteKeyName()
{
return 'id';
}
....
}
But not sure how I'd structure this in the most "Laravel" way? Or is there a better/tidier solution?
define your route for example like
Route::get('chapters/{chapter}','ChapterController#show'); // find by slug
Route::get('api/chapters/{chapter}','ApiChapterController#show'); // find by id
for web controller
class ChapterController extends Controller
{
public function show(Request $request,$slug)
{
$instance = Model::whereSlug($slug)->first();
}
}
for api
class ApiChapterController extends Controller
{
public function show(Request $request,$id)
{
$instance = Model::find($id);
}
}
You can define 2 different routes for that but unfortunatelly you will not be able to use model binding and you will have to look for the model like:
public function show(Request $request,$slug) {
$instance = Model::whereSlug($slug)->first();
}
as shown below: https://stackoverflow.com/a/48115385/6525417

Where to put response messages in laravel api?

In my laravel 5.5 api I have a lot of response message like
"You successfully completed some action..".
At the moment I have stored them as constants in the controller they
are used. I want to move all of them to a single location, so if I need to change them later I don't have to hunt for them in each controller.
What is the best approach for this usecase?
What about Laravel localization? Then using it as trans('success-message-key-here')
In your use case, I would make static functions to call the responses the same way.
class ResponseMessage
{
public static function succesfulResponse()
{
return response('successfull', 200);
}
public static function failedResponse()
{
return response('fail', 400);
}
}
Use case:
...
return ResponseMessage::succesfulResponse();
Another way is to take the parent controller, which is often just named Controller in your controller folder, which you extend from.
class controller
{
public function succesfulResponse()
{
return response('successfull', 200);
}
}
Now you are not in a static context, but you can use the functions if you extend from it.
class yourController extends Controller
{
public function get($id) {
...
return $this->succesfulResponse();
}
}
You could make use of the translations files even if you are just supporting one language.
In your controller you would have something like:
$message = \Lang::get('directory/file.str1');
And your translation file:
return [
'str1' => 'You successfully completed some action.',
];
In resource/lang/en directory (in other words Localization you can put your message like this
ApiMessage.php
return [
'success' => 'success message',
];
Then in your controller you can call like this
public function somefunction(Request $request)
{
// your logic
return response()->json(__('apiMessages.success'));
}
here en folder denotes your local language (default is english symbolized as en)

Deciding which controller#action to be called based on a given parameters

I have to build an api.
It has one route. The client is sending a POST request with an XML.
Based on that xml, I have to decide witch controller#action to be called.
And I have a lot of controllers.
Unfortunately I can't modify the client side.
Do you have any suggestion how can i do that in a Laravel way?
For example
POST["body"] =
"...
<controller>content</controller>
<action>index</action>
..."
I want to call a ContentController::index()
Thx!
Thx for the reflection stuff. It is a big magic, worth the effort to look into it deeper.
I have no problem to parse the xml. So here is a simplier example
URL: /api/request/content/show
Routes.php
Route::get('api/request/{controller}/{action}', 'ApiController#request');
ApiController.php
class ApiController extends Controller
{
public function request($controller, $action)
{
//some error check
$controller = 'App\Http\Controllers\\' . ucfirst($controller) . 'Controller';
$params = "Put some params here";
$reflectionMethod = new \ReflectionMethod($controller, $action);
$reflectionMethod->invoke(new $controller, $params);
}
}
ContentController.php
class ContentController extends Controller
{
public function show($params)
{
dd($params);
}
}
And it is working!
Thx a lot!
A better option is to use App::call(); invoking controller with ReflectionMethod might not let you use response() inside your new forwarded controller and other laravel goodies.
Here is my try on this: /api/request/content/show
Routes web.php or api.php
use App\Http\Controllers\ApiController;
Route::get('api/request/{controller}/{action}', [ApiController::class, 'request']);
ApiController.php
class ApiController extends Controller
{
public function request($controller, $action)
{
//some error check
return App::call('App\Http\Controllers\\'.ucfirst($controller).'Controller#'.$action);
}
}
ContentController.php
use Illuminate\Http\Request;
class ContentController extends Controller
{
public function show(Request $request)
{
dd($request);
}
}
This will allow you more freedom.

Code Igniter controller - can't use uri->segment with function index()?

I'm working with code igniter and for some reason, the url http://mysite.com/account/100 gives me a 404 error but http://mysite.com/account actualy works. Here's what my controller account.php looks like.
class account extends Controller {
function account()
{
parent::Controller();
}
function index()
{
echo 'hello';
echo $this->uri->segment(2);
}
}
Any idea what's wrong?
I just tested a simple account class like you have and it is trying to call 100 as a method of account, instead your URL after index.php should be account/index/100 and it works fine.
This can be solved using routing for example.
$route['account/(:num)'] = "accounts/index/$1";
is what you are looking for. You can look over the URI Routing user guide
for more info.
CodeIgniter requires that your controller name be capitalized, and the filename lowercase so try changing your controller to.
class Account extends Controller { }
and your filename account.php
Add this route and you are good
$route['account/(:any)'] = "account/index/$1";
This is not working because when you request http://example.com/account, it looks index() method.
But when you request http://example.com/account/100, is looking for 100() method. Which is not present.
You may tweak the code like this.
class account extends Controller {
public function account()
{
parent::Controller();
}
public function index()
{
echo 'hello';
}
public function id(){
echo $this->uri->segment(2);
}
public function alternative($id){
echo $id;
}
}
you'll call url like this: http://example.com/account/id/100
or you can do like this
http://example.com/account/alternative/100

Resources