Use Explicit or Implicit Model Binding on Routes in Laravel Package - laravel

I am migrating some app functionality into a Laravel package. The package includes Models, Controllers and routes. I am using Laravel 9
Everything is working except my Models are not biding to the routes so the models are not being automatically resolved.
// Route
Route::get('/medium/{medium}',
[\ArtMoi\Http\Controllers\MediumController::class, 'fetch']
)->name("get-medium");
Model does not automatically load. In the controller below $medium is null. Route worked when part of the app, but fails when included via a package.
// MediumController
use ArtMoi\Models\Medium;
...
public function fetch(Request $request, Medium $medium)
{
$this->authorize('view', $medium);
return response()->json($medium);
}
Everything works if I don't try to automatically load the model
// This works but not the desired approach
use ArtMoi\Models\Medium;
public function fetch(Request $request, $id)
{
$medium = Medium::find($id);
$this->authorize('view', $medium);
return response()->json($medium);
}
In my package ServiceProvider I provide the routes with
public function boot()
{
Route::group(['prefix' => 'api'], function () {
$this->loadRoutesFrom(__DIR__ . '/../routes/api.php');
});
}
I tried to explicitly bind the Model and the route by adding the following to a RouteServiceProvider but it has no effect.
// RouteServiceProvider.php
use ArtMoi\Models\Medium;
...
public function boot()
{
Route::model('medium', Medium::class);
}
This is the first time I've moved resource type routes to a Package and feel like there is something I am missing. The only difference with the package from the original app is moving models from App\Models to the package namespace of ArtMoi\Models. Models function as expected when performing queries or other functions and the routes have no conflicts.

Can you test this solution? Modify your boot method.
public function boot()
{
Route::group([
'middleware'=>['bindings'],
'prefix' => 'api'
], function () {
$this->loadRoutesFrom(__DIR__ . '/../routes/api.php');
});
}
That bindings middleware is here and is registered by default in web and api middleware.
Solution found here
I hope this works, I didn't test it.

Found that I needed to add the SubstitudeBindings middleware to my routes.
Updated my ServiceProvider to load routes with
use Illuminate\Routing\Middleware\SubstituteBindings;
...
public function boot()
{
Route::group(['prefix' => 'api', 'middleware' => [SubstituteBindings::class]], function () {
$this->loadRoutesFrom(__DIR__ . '/../routes/api.php');
});
}

Related

Laravel Global Variable to specific views

I would like assistance with calling a global variable on Laravel app for specific pages or routes.
This is my current code which works on login
App\Providers\AppServiceProvider
public function boot()
{
view()->composer(['auth.login'], function ($view) {
$view->with('settings', AppSetting::where('id',1)->first());
});
}
This is the route for the login page
Route::get('/', function () {
return view('auth.login');
});
[Edit 1]
On the login page , I used this code bellow to get the app version
{{$settings->app_version}}
After digging a little I think a good solution might be caching your AppSetting Model.
Write the given code in App\Providers\RouteServiceProvider
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* Define your route model bindings, pattern filters, etc.
*
* #return void
*/
public function boot()
{
parent::boot();
App::before(function($request) {
App::singleton('settings', function(){
return AppSetting::where('id',1)->first();
});
// If you use this line of code then it'll be available in any view
// as $settings but you may also use app('settings') as well
View::share('settings', app('settings'));
});
}
}
App::singleton will call once AppSetting::where('id',1)->first() and after one call your settings will be cached.
And you can use {{$settings->app_version}} in your view.
Reference: stackoverflow.com/a/25190686/7047493

Why Auth::user() return null in routes of a custom Service Provider?

I'm making a new Service called Factures in App\Services\Factures.
I created the \App\Services\Factures\FacturesServiceProvider:
public function register() {
$this->app->bind('factures', function ($app) {
return new Facture;
});
}
public function boot() {
// laod Routes
$this->loadRoutesFrom(__DIR__ .'/Http/routes.php');
// load Views
$this->loadViewsFrom(__DIR__ . '/views', 'factures');
}
I registered my provider everything works fine expect the Auth::user() in returns me  null in the views and the routes.php.
How can I get access to the Auth() in custom service?
This post resolved my problem: User Auth not persisting within Laravel package
I figure out that Laravel  apply to the default routes/web.php file a middleware called 'web' And doesn't apply this group to external package routes loaded via service provider's.
So my routes in the custom file should be in web middleware:
Route::group(['middleware' => ['web']], function () {
Route::get('testing-services', function(){
dd(Auth::user());
// output is valid
});
});

Auth::user() returns null on Module __construct()

I created a new Module named Article using laravel-modules. Some backend routes needed authentication and i added auth middleware and an additional permission view_backend. I am using https://github.com/spatie/laravel-permission package for role-permissions.
the issue is when i try to access the route admin/article/posts it prompts me the login as expected. But after login it show null on __construct() method for Auth::user();
I added web middleware as mentioned on #204 but it did not solve the issue. Can you please guide me to resolve this? My project is on Laravel 5.6 and using the latest version of Laravel-Modules
Route::group(['namespace' => 'Modules\Article\Http\Controllers\Backend', 'as' => 'backend.article.', 'middleware' => ['web', 'auth', 'can:view_backend'], 'prefix' => 'admin/article'], function () {
Route::resource("posts", "PostsController");
});
My project is hosted at Github, https://github.com/nasirkhan/laravel-starter/tree/module
First of all, add Spatie Middleware to your kernel:
protected $routeMiddleware = [
// ...
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
];
Then in your controller check for permission or roles:
public function __construct(Request $request)
{
$this->middleware(['permission: order.index']);
}
Now you can access to your authenticated with $request->user() like:
public function create(Request $request)
{
if ($request->user()->hasRole('admin')) {
// return view("carmodel.create", ["manufacturers"=>$manufacturers]);
} else {
return view("admin.error", ['code'=>'001','msg'=>'err']);
}
}
According to the docs:
In previous versions of Laravel, you could access session variables or the authenticated user in your controller's constructor. This was never intended to be an explicit feature of the framework. In Laravel 5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet.
As an alternative, you may define a Closure based middleware directly
in your controller's constructor. Before using this feature, make sure
that your application is running Laravel 5.3.4 or above:
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->projects = Auth::user()->projects;
return $next($request);
});
}
Or you could typehint it:
public function index(Request $request)
{
$projects = $request->user()->projects;
$value = $request->session()->get('key');
}
Docs

Laravel get getCurrentLocale() in AppServiceProvider

I'm trying to get the LaravelLocalization::getCurrentLocale() in the boot() method of the Laravel AppServiceProvider class, and although my default locale is pt I always get the en. The package I'm using is mcamara/laravel-localization. Code I have:
public function boot()
{
Schema::defaultStringLength(191);
// Twitter view share
$twitter = Twitter::getUserTimeline(['screen_name' => env('TWITTER_USER'), 'count' => 3, 'format' => 'object']);
view()->share('twitter', $twitter);
// Current language code view share
$language = LaravelLocalization::getCurrentLocale();
view()->share('lang', $language);
// Practice Areas
view()->share('practice_areas', \App\Models\PracticeArea::with('children')->orderBy('area_name')->where(['parent_id' => 0, 'language' => $language])->get());
}
I'm probably placing this in the wrong place because when I try to share the practice_areas variable it always sets it as en even if the language is switched.
What may I be doing wrong?
Thanks in advance for any help
Faced the exact same problem, solved by using a dedicated Service Provider and a view composer class, like so:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class LocalizationServiceProvider extends ServiceProvider
{
public function boot() {
View::composer(
'*', 'App\Http\ViewComposers\LocalizationComposer'
);
}
}
and then on LocalizationComposer class:
<?php
namespace App\Http\ViewComposers;
use Illuminate\View\View;
use LaravelLocalization;
class LocalizationComposer {
public function compose(View $view)
{
$view->with('currentLocale', LaravelLocalization::getCurrentLocale());
$view->with('altLocale', config('app.fallback_locale'));
}
}
currentLocale and altLocale will be available on all views of your application
From the package docs Usage section:
Laravel Localization uses the URL given for the request. In order to achieve this purpose, a route group should be added into the routes.php file. It will filter all pages that must be localized.
You need to be setting the localization within your route group definitions:
Route::group(['prefix' => LaravelLocalization::setLocale()], function()
{
/** ADD ALL LOCALIZED ROUTES INSIDE THIS GROUP **/
Route::get('/', function()
{
return View::make('hello');
});
Route::get('test',function(){
return View::make('test');
});
});
After several hours trying to work around the issue, I decided not to use the view()->share() with mcamara/laravel-localization package methods here. The reasons seems to be that in the AppServiceProvider::class boot() method the package isn't yet getting the requested language string.
Anyway, thank you all for your help!

Laravel Model binding not working after upgrade to 5.3

I have Bind Model from Route service provider like this
class RouteServiceProvider extends ServiceProvider {
public function boot(){
parent::boot();
Route::model('job_title', \Cgs\Modules\JobTitle\Models\JobTitle::class);
}
}
and route likes this
Route::get('edit/{job_title}', ['as' => 'job.title.edit', 'uses' => 'JobTitleController#edit', 'middleware' => ['permission:job-title-edit']]);
Edit method
public function edit(JobTitle $jobtitle)
{
dd($jobtitle);
}
Above code give error
No query results for model [Cgs\Modules\JobTitle\Models\JobTitle]
When I check in debugger I see it is binding multiple times first correct and second it takes as json. Please help with multiple binding issue.

Resources