Laravel Model binding not working after upgrade to 5.3 - laravel

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.

Related

Use Explicit or Implicit Model Binding on Routes in Laravel Package

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');
});
}

Laravel Slugable Route Model Binding weird behaviour

I have a section in my project with the latest news articles. For this I have a:
Post model
Post resource controller and a
Resource Post Route.
Post Model
class Post extends Model
{
use HasFactory, Sluggable;
protected $fillable = [...,...];
public function getRouteKeyName()
{
return 'slug';
}
public function sluggable(): array
{
return [
'slug' => [
'source' => 'title'
]
];
}
}
PostController.php
public function show(Post $post)
{
dd($post);
}
web.php
Route::resource('/posts', App\Http\Controllers\PostController::class)->only(['index','show']);
Index (http://localhost/news) and show (http://localhost/news/{slug}) work as expected!
Now the problem/bug I noticed:
When I change the route from posts to news, the show method no longer works.
Index still works.
the modified route from posts to news
Route::resource('/news', App\Http\Controllers\PostController::class)->only(['index','show']);
http://localhost/news works but http://localhost/news/{slug} shows me only the PostModel Structure.
Do you know the problem and what do I have to do to make it work? I use Laravel 8 and "cviebrock/eloquent-sluggable": "^8.0" packagefor the slugs. Thanks for your time!
Ok. I've figured it out. I'm writing the answer here for anyone who might have the same problem as me. First of all. It is not a bug. If you adjust the route so that the model name is no longer included in the route, then you have to bind the route explicitly. https://laravel.com/docs/8.x/routing#explicit-binding
All you have to do. In the boot() method of RouteServiceProvider.php, add the desired route and bind it with the desired classes. In my case it was news instead of post.
public function boot()
{
....
Route::model('news', \App\Models\Post::class);
}

Laravel - custom id

I'm trying to make my custom ID for table posts. And I found haruncpi laravel id generator and I installed it with this command.
composer require haruncpi/laravel-id-generator.
After that I add use Haruncpi\LaravelIdGenerator\IdGenerator; to my PostsController. And now i need to add this snippet to my model.
$id = IdGenerator::generate(['table' => 'posts', 'length' => 10, 'prefix' =>'INV-']);
But I don't know where and how... Maybe in public function store() or somewhere else? Please help!
I just want my id to be like:
INV-000001
INV-000002
...
Laravel models has a boot() method, where you can register calls related to your model. Therefor you can subscribe to the creating event, which will fire before the model is saved and you can mutate the model in the process.
So in your Post.php model, add the following.
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$model->id= IdGenerator::generate(['table' => 'posts', 'length' => 10, 'prefix' =>'INV-']);
});
}
And as long as you utilize basic Laravel functionality, it will add the id to the model.

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 - middleware on routes in controller

I have a route resource
Route::resource('campaign', 'CampaignController');
I want to restrict some of these routes to the users.
For example the index page lists all the campaigns and they should not see this only their own ones.
I have a custom middleware that simply checks the if the user is an admin
However I cannot apply this to individual methods.
public function index()
{
$this->middleware('checkuser');
}
Just the constructor
public function __construct()
{
$this->middleware('checkuser');
}
How do I get around this and apply to individual route in the controller
Sorry my mistake I should have read the docs you can add exceptions or allowed.
$this->middleware('auth');
$this->middleware('log', ['only' => ['fooAction', 'barAction']]);
$this->middleware('subscribed', ['except' => ['fooAction', 'barAction']]);
}

Resources