laravel Controller error when using Request in function - laravel

I make controller by using php artisan make:controller newsController --resource
And after that when I go to my controller in function index, I want to add Request $request
public function index(Request $request)
{
}
It's return error:
Declaration of
App\Http\Controllers\Admin\NewsController::index(Illuminate\Http\Request
$request) should be compatible with
App\Http\Controllers\Controller::index()
How to fix it? I try many way but it still didn't work!
EDIT — Controller
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
UPDATED — Routes
Route::post('admin/news', 'Admin\NewsController#store');
Route::resource('admin/news', 'Admin\NewsController');

It's quite simple, just create your Resource controller without the index route Or Create new get route, like this:
Route::resource('admin/news', 'Admin\NewsController', ['except' => ['index']]);
Then add your route before the resource declaration, something like this:
Route::post('admin/news', 'Admin\NewsController#index');
Route::resource('admin/news', 'Admin\NewsController', ['except' => ['index']]);
Hope this helps you!!

This doesn't require any Laravel work arounds.
Fix:
a) Remove the index method from the base controller
or
b) Make the index method in the base controller take a Illuminate\Http\Request as an argument and use that same method signature in every controller's index method that inherited from the base in the entire application.
or
c) Figure out why there is an index method defined in the base in the first place and, if needed, move it to a trait to use in child classes instead. (allows you to override the method completely)
b is not a good option, it is just to illustrate a point
Issue demonstrated:
class Foo
{
public function index()
{
//
}
}
class Bar extends Foo
{
public function index(\Illuminate\Http\Request $request)
{
}
}
Declaration of Bar::index(Illuminate\Http\Request $request) should be compatible with Foo::index()

You want to override the index action.
You also want to pass parameters into this index action.
The App\Http\Controllers\Controller::index() does not take parameters.
So they are not "compatible".
Try this "helper-funtion" way:
public function index() {
$request = request() // use the helper function
// ...you code here...
}

You can disable index from resources and define route with different method name before or after resource:
Route::get('resources', 'ResourceController#getResources');
Route::resource('resources', 'ResourceController', $restResource)->except(['index']);

Related

How to know which method is called in Laravel controller

When a request comes to Laravel controller through application router, how can we determine which method is called inside that controller? I mean inside constructor or magic methods of the controller. Is it possible to know?
Consider the method that is called exists. So __call would not be the solution.
I have this Route:
Route::get('exam', [ExamController::class,'index']);
And I want to get index inside ExamController class. maybe in side __construct or ...
public function __construct()
{
// here I want to access the name of called method
}
__call magic method just give the method name if the method is'nt exist:
public function __call($method, $parameters)
{
// I have access to $method name here (index)
}
You can use the __FUNCTION__ or __METHOD__ PHP constants to obtain information about the function or class and function:
class SomeClass
{
public function aFunction()
{
echo __FUNCTION__;
}
public function anotherFunction()
{
echo __METHOD__;
}
}
$obj = new SomeClass();
$obj->aFunction(); // aFunction
$obj->anotherFunction(); // SomeClass::anotherFunction
Update
Let's assume whilst you might not have a function defined for a specific route, you know the name of a route you want to apply a specific middleware to. You can apply middelware to specific functions from the controller constructor:
public function __construct()
{
$this->middleware('auth', ['only' => ['index', 'create']]);
}
Alternatively just specify the middleware required for the route on the route definition.

Laravel - forgetParameter

My project is multi language, so in most route I add parameter "locale". But latter I do not need to pass "locale" parameter to my controllers. I try to remove this parameter using forgetParameter() method but when I use this method then I have error "Route not bound".
So what I'm doing wrong.
How to remove "locale" from all routes?
Laravel 5.8
My route file.
Route::group(['prefix' => '{locale}','where' => ['locale' => '[a-zA-Z]{2}']], function() {
Auth::routes([app()->getLocale()]);
Route::get('/', 'HomeController#index')->name('mainPage');
Route::get('/user/{id}','UserController#showUser')->name('getUserProfile')->forgetParameter('locale');
Route::get('/user/{id}/edit','UserController#editUser')->name('editUserProfile')
->middleware('can:editUser,App\User,id')->forgetParameter('locale');
Route::patch('/user/{id}/edit','UserController#updateUser')->name('updateUserProfile')
->middleware('can:updateUser,App\User,id')->forgetParameter('locale');
});
It's not duplicate with this question - Laravel 5.4: How to DO NOT use route parameter in controller
Solution in that question doesn't work in my case. And my question is why "forgotParamter()" throw an error?
Other question is, why I can't use this construction in middleware:
$request->route()->forgetParameter('locale')
I have following error:
Call to a member function is() on null
Thank you.
Try to forget the parameter in an inline middleware in the controller's constructor. Actually, I would use a base controller: 1, get the route value; 2, set to a protected property, so inherited controllers can access to the value; 3, forget the parameter.
<?php
// Localized/Controller.php
namespace App\Http\Controllers\Localized;
use App\Http\Controllers\Controller as ControllerBase;
use Illuminate\Http\Request;
class Controller extends ControllerBase
{
protected string $currentLocale;
public function __construct()
{
$this->middleware(function ($request, $next) {
// If you need to access this later on inherited controllers
$this->currentLocale = $request->route('locale') ?? 'en';
// Other operations, like setting the locale or check if lang is available, etc
$request->route()->forgetParameter('locale');
return $next($request);
});
}
}
<?php
// Localized/UserController.php
namespace App\Http\Controllers\Localized;
use Illuminate\Http\Request;
// Extends the our base controller instead of App\Http\Controllers\Controller
class UserController extends Controller
{
public function show(Request $request)
{
// No need of string $locale arg
dd($this->currentLocale);
}
}

Laravel sharing data with all views

I'm trying to run a user-related query to fetch data to appear in the top bar of my site on every view.
I've created a new BaseController according to the first answer here:
How to pass data to all views in Laravel 5?
and that's working for a simple test (just sharing a typed-out variable), but when I try and use Auth::user()->id in the __construct method of BaseController (which in my other controllers always returns the ID of the currently logged in user), I get Trying to get property 'id' of non-object.
I've tried adding use App\User at the top of BaseController (even though it isn't usually needed) and also tried adding in the bits for Spatie laravel-permission plugin, but neither has any effect.
I tried dd on Auth::user() and just get 'null'. My feeling is that the user details maybe haven't been loaded at this stage, but BaseController extends Controller same as MyWorkingController extends Controller so I'm not sure why Auth::user()->id doesn't work here when it does normally?
Create a Base Controller which has all the information that you want to share too all controllers/pages/views and let your others controllers extend it.
open file AppServiceProvider.php from folder Providers and write below code in boot function
view()->composer('*', function ($view)
{
$view->with('cartItem', $cart );
});
And now go to your view page and write :
{{ $cartItem }}
You cannot access Auth in constructors because middleware has not been run yet. You can use either View composer or give try this way though i haven't tested.
class BaseController extends Controller {
protected $userId;
public function __construct() {
$this->middleware(function ($request, $next) {
$this->userId= Auth::user()->id;
return $next($request);
});
}
}
Write this in AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;
use DB;
use Auth;
use App\Cart;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Schema::defaultStringLength(191);
view()->composer('*', function ($view)
{
view()->composer('*', function($view)
{
if (Auth::check()) {
$cart = Cart::where('user_id', Auth::user()->user_id)->count();
$view->with('cartItem', $cart );
}else {
$view->with('cartItem', 0);
}
});
});
}
}
In you view simply write
{{ $cartItem }}
For anyone interested, I just encountered the same problem and I've solved it with ServiceProvider:
Define your custom ServiceProvider with the command
php artisan make:provider CustomServiceProvider
Add a reference to your service provider in the config\app.php file, specifically in the providers array, adding this item to it:
\App\Providers\CustomServiceProvider::class,
Declare the variables you want to share in your provider's boot() method and share them by using the view()->share method:
public function boot()
{
$shared_variable = "Hello";
view()->share('shared_variable', $shared_variable);
}
You can now reference your variable in your blade files with the standard notation:
{{ $shared_variable }}

Laravel 5 ModelNotFoundException in Builder.php for Routing

I have a Model Class with name Article.php
and use below rout:
Route::get('articles/create','ArticlesController#create');
when input in browser http://localhost:8000/articles/create
i see this error :
ModelNotFoundException in Builder.php line 125: No query results for model [App\Article].
but when i user below every think is ok:(article insted articles)
Route::get('article/create','ArticlesController#create');
this is my controller :
class ArticlesController extends Controller {
public function index()
{
$articles = Article::all();
return view('articles.index',compact('articles'));
}
public function show($id)
{
$article = Article::findOrFail($id);
return view('articles.show',compact('article'));
}
public function create()
{
return view('articles.create');
}
}
what happened really ?!!!
The problem with your code is that in your routes.php your route priority is like this :
Route::get('articles/{id}','ArticlesController#show');
Route::get('articles/create','ArticlesController#create');
and when you go to http://localhost:8000/articles/create in your browser laravel catches create as a variable with {id} request in articles/{id} before articles/create gets an opportunity to resolve the route. To solve your problem you must consider the route priority and make the following changes to your route.php file :
Route::get('articles/create','ArticlesController#create');
Route::get('articles/{id}/edit','ArticlesController#show');
Route::get('articles/{id}','ArticlesController#show');
But if you have a bunch of these in your routes.php file you should really consider using this instead:
Route::resource('articles', 'ArticlesController');
This single line will take care of all 4 get routes (index, create, edit, show) as well as all three post/put/delete routes of (store, update, delete).
But to each their own.
You should include your controller code.
Most likely there's some code there that tries a findOrFail() on the Eloquent model, triggering this error.

global variable for all controller and views

In Laravel I have a table settings and i have fetched complete data from the table in the BaseController, as following
public function __construct()
{
// Fetch the Site Settings object
$site_settings = Setting::all();
View::share('site_settings', $site_settings);
}
Now i want to access $site_settings. in all other controllers and views so that i don't need to write the same code again and again, so anybody please tell me the solution or any other way so i can fetch the data from the table once and use it in all controllers and view.
Okay, I'm going to completely ignore the ridiculous amount of over engineering and assumptions that the other answers are rife with, and go with the simple option.
If you're okay for there to be a single database call during each request, then the method is simple, alarmingly so:
class BaseController extends \Controller
{
protected $site_settings;
public function __construct()
{
// Fetch the Site Settings object
$this->site_settings = Setting::all();
View::share('site_settings', $this->site_settings);
}
}
Now providing that all of your controllers extend this BaseController, they can just do $this->site_settings.
If you wish to limit the amount of queries across multiple requests, you could use a caching solution as previously provided, but based on your question, the simple answer is a class property.
At first, a config file is appropriate for this kind of things but you may also use another approach, which is as given below (Laravel - 4):
// You can keep this in your filters.php file
App::before(function($request) {
App::singleton('site_settings', function(){
return Setting::all();
});
// If you use this line of code then it'll be available in any view
// as $site_settings but you may also use app('site_settings') as well
View::share('site_settings', app('site_settings'));
});
To get the same data in any controller you may use:
$site_settings = app('site_settings');
There are many ways, just use one or another, which one you prefer but I'm using the Container.
Use the Config class:
Config::set('site_settings', $site_settings);
Config::get('site_settings');
http://laravel.com/docs/4.2/configuration
Configuration values that are set at run-time are only set for the current request, and will not be carried over to subsequent requests.
In Laravel, 5+ you can create a file in the config folder and create variables in that and use that across the app.
For instance, I want to store some information based on the site.
I create a file called site_vars.php,
which looks like this
<?php
return [
'supportEmail' => 'email#gmail.com',
'adminEmail' => 'admin#sitename.com'
];
Now in the routes, controller, views you can access it using
Config::get('site_vars.supportEmail')
In the views if I this
{{ Config::get('site_vars.supportEmail') }}
It will give email#gmail.com
Hope this helps.
EDiT-
You can also define vars in .env file and use them here.
That is the best way in my opinion as it gives you the flexibility to use values that you want on your local machine.
So, you can do something this in the array
'supportEmail' => env('SUPPORT_EMAIL', 'defaultmail#gmail.com')
Important - After you do this, don't forget to do this on production env
php artisan config:cache
In case, there's still some problem, then you can do this (usually it would never happen but still if it ever happens)
php artisan cache:clear
php artisan config:cache
In your local env, always do this after this adding it
php artisan config:clear
It's always a good practice not to cache config vars in local. in case, it was cached, this would remove the cache and would load the new changes.
I see, that this is still needed for 5.4+ and I just had the same problem, but none of the answers were clean enough, so I tried to accomplish the availability with ServiceProviders. Here is what i did:
Created the Provider SettingsServiceProvider
php artisan make:provider SettingsServiceProvider
Created the Model i needed (GlobalSettings)
php artisan make:model GlobalSettings
Edited the generated register method in \App\Providers\SettingsServiceProvider. As you can see, I retrieve my settings using the eloquent model for it with Setting::all().
public function register()
{
$this->app->singleton('App\GlobalSettings', function ($app) {
return new GlobalSettings(Setting::all());
});
}
Defined some useful parameters and methods (including the constructor with the needed Collection parameter) in GlobalSettings
class GlobalSettings extends Model
{
protected $settings;
protected $keyValuePair;
public function __construct(Collection $settings)
{
$this->settings = $settings;
foreach ($settings as $setting){
$this->keyValuePair[$setting->key] = $setting->value;
}
}
public function has(string $key){ /* check key exists */ }
public function contains(string $key){ /* check value exists */ }
public function get(string $key){ /* get by key */ }
}
At last I registered the provider in config/app.php
'providers' => [
// [...]
App\Providers\SettingsServiceProvider::class
]
After clearing the config cache with php artisan config:cache you can use your singleton as follows.
$foo = app(App\GlobalSettings::class);
echo $foo->has("company") ? $foo->get("company") : "Stack Exchange Inc.";
You can read more about service containers and service providers in Laravel Docs > Service Container and Laravel Docs > Service Providers.
This is my first answer and I had not much time to write it down, so the formatting ist a bit spacey, but I hope you get everything.
I forgot to include the boot method of SettingsServiceProvider, to make the settings variable global available in views, so here you go:
public function boot(GlobalSettings $settinsInstance)
{
View::share('globalsettings', $settinsInstance);
}
Before the boot methods are called all providers have been registered, so we can just use our GlobalSettings instance as parameter, so it can be injected by Laravel.
In blade template:
{{ $globalsettings->get("company") }}
View::share('site_settings', $site_settings);
Add to
app->Providers->AppServiceProvider file boot method
it's global variable.
Most popular answers here with BaseController didn't worked for me on Laravel 5.4, but they have worked on 5.3. No idea why.
I have found a way which works on Laravel 5.4 and gives variables even for views which are skipping controllers. And, of course, you can get variables from the database.
add in your app/Providers/AppServiceProvider.php
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
// Using view composer to set following variables globally
view()->composer('*',function($view) {
$view->with('user', Auth::user());
$view->with('social', Social::all());
// if you need to access in controller and views:
Config::set('something', $something);
});
}
}
credit: http://laraveldaily.com/global-variables-in-base-controller/
In Laravel 5+, to set a variable just once and access it 'globally', I find it easiest to just add it as an attribute to the Request:
$request->attributes->add(['myVar' => $myVar]);
Then you can access it from any of your controllers using:
$myVar = $request->get('myVar');
and from any of your blades using:
{{ Request::get('myVar') }}
In Laravel 5.1 I needed a global variable populated with model data accessible in all views.
I followed a similar approach to ollieread's answer and was able to use my variable ($notifications) in any view.
My controller location: /app/Http/Controllers/Controller.php
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use App\Models\Main as MainModel;
use View;
abstract class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
public function __construct() {
$oMainM = new MainModel;
$notifications = $oMainM->get_notifications();
View::share('notifications', $notifications);
}
}
My model location: /app/Models/Main.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use DB;
class Main extends Model
{
public function get_notifications() {...
I have found a better way which works on Laravel 5.5 and makes variables accessible by views. And you can retrieve data from the database, do your logic by importing your Model just as you would in your controller.
The "*" means you are referencing all views, if you research more you can choose views to affect.
add in your app/Providers/AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Contracts\View\View;
use Illuminate\Support\ServiceProvider;
use App\Setting;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
// Fetch the Site Settings object
view()->composer('*', function(View $view) {
$site_settings = Setting::all();
$view->with('site_settings', $site_settings);
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
}
}
If you are worried about repeated database access, make sure that you have some kind of caching built into your method so that database calls are only made once per page request.
Something like (simplified example):
class Settings {
static protected $all;
static public function cachedAll() {
if (empty(self::$all)) {
self::$all = self::all();
}
return self::$all;
}
}
Then you would access Settings::cachedAll() instead of all() and this would only make one database call per page request. Subsequent calls will use the already-retrieved contents cached in the class variable.
The above example is super simple, and uses an in-memory cache so it only lasts for the single request. If you wanted to, you could use Laravel's caching (using Redis or Memcached) to persist your settings across multiple requests. You can read more about the very simple caching options here:
http://laravel.com/docs/cache
For example you could add a method to your Settings model that looks like:
static public function getSettings() {
$settings = Cache::remember('settings', 60, function() {
return Settings::all();
});
return $settings;
}
This would only make a database call every 60 minutes otherwise it would return the cached value whenever you call Settings::getSettings().
You can also use Laravel helper which I'm using.
Just create Helpers folder under App folder
then add the following code:
namespace App\Helpers;
Use SettingModel;
class SiteHelper
{
public static function settings()
{
if(null !== session('settings')){
$settings = session('settings');
}else{
$settings = SettingModel::all();
session(['settings' => $settings]);
}
return $settings;
}
}
then add it on you config > app.php under alliases
'aliases' => [
....
'Site' => App\Helpers\SiteHelper::class,
]
1. To Use in Controller
use Site;
class SettingsController extends Controller
{
public function index()
{
$settings = Site::settings();
return $settings;
}
}
2. To Use in View:
Site::settings()
A global variable for using in controllers; you can set in AppServiceProvider like this :
public function boot()
{
$company=DB::table('company')->where('id',1)->first();
config(['yourconfig.company' => $company]);
}
usage
config('yourconfig.company');
using middlwares
1- create middlware with any name
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\View;
class GlobalData
{
public function handle($request, Closure $next)
{
// edit this section and share what do you want
$site_settings = Setting::all();
View::share('site_settings', $site_settings);
return $next($request);
}
}
2- register your middleware in Kernal.php
protected $routeMiddleware = [
.
...
'globaldata' => GlobalData::class,
]
3-now group your routes with globaldata middleware
Route::group(['middleware' => ['globaldata']], function () {
// add routes that need to site_settings
}
In file - \vendor\autoload.php, define your gobals variable as follows, should be in the topmost line.
$global_variable = "Some value";//the global variable
Access that global variable anywhere as :-
$GLOBALS['global_variable'];
Enjoy :)
I know I am super late to the party, but this was the easiest way I found.
In app/Providers/AppServiceProvider.php, add your variables in the boot method. Here I am retrieving all countries from the DB:
public function boot()
{
// Global variables
view()->composer('*',function($view) {
$view->with('countries', Country::all());
});
}
There are two options:
Create a php class file inside app/libraries/YourClassFile.php
a. Any function you create in it would be easily accessible in all the views and controllers.
b. If it is a static function you can easily access it by the class name.
c. Make sure you inclued "app/libraries" in autoload classmap in composer file.
In app/config/app.php create a variable and you can reference the same using
Config::get('variable_name');
Hope this helps.
Edit 1:
Example for my 1st point:
// app/libraries/DefaultFunctions.php
class DefaultFunctions{
public static function getSomeValue(){
// Fetch the Site Settings object
$site_settings = Setting::all();
return $site_settings;
}
}
//composer.json
"autoload": {
"classmap": [
..
..
..
"app/libraries" // add the libraries to access globaly.
]
}
//YourController.php
$default_functions = new DefaultFunctions();
$default_functions->getSomeValue();

Resources