Laravel language switcher, pass additional parameters to a route - php

I really can't think of an elegant solution to fix this problem so here it is:
I followed this tutorial https://www.youtube.com/watch?v=KqzGKg8IxE4 to create a localization option for my website. However, integrating the auth was cancer and now I'm faced with an even greater issue regarding the routing for pages that require a get parameter.
Here is my blade code inside the app.blade.php for the language switcher:
<language-switcher
locale="{{ app()->getLocale() }}"
link-en="{{ route(Route::currentRouteName(), 'en') }}"
link-bg="{{ route(Route::currentRouteName(), 'bg') }}"
></language-switcher>
And here are some of my routes
Route::redirect('/', '/en');
Route::get('email/verify', 'Auth\VerificationController#show')->name('verification.notice');
Route::get('email/verify/{id}/{hash}', 'Auth\VerificationController#verify')->name('verification.verify');
Route::get('email/resend', 'Auth\VerificationController#resend')->name('verification.resend');
Route::group([
"prefix" => '{language}',
'where' => ['language' => '(en||bg)'],
], function () {
//Auth routes
Auth::routes();
//Returns to home
Route::get('/', 'HomeController#index')->name('home');
//Handles the faq routes
Route::resource('faq', 'FaqController');
});
Route::get('password/reset/{token}', 'Auth\ResetPasswordController#showResetForm')->name('password.reset');
Route::post('password/reset', 'Auth\ResetPasswordController#reset')->name('password.update');
});
It works well for pages that don't require any get parameters to work with ex: Login / Register / Home page, however if i use something that requires like, faq.edit, which takes an {id} parameters - the language switcher will throw an error because I haven't passed an {id} parameter to it.
The only solution I can think of is adding the language-switcher inside the child blade view and from there I pass the required parameters, however that implies that I have to add the language-switcher to every child view instead of only once at the parent.

You can achieve it with the following steps:
Implement a URL generator macro which will make much easier to generate your URLs which must be identical except language
Get the current route's parameters
Merge the chosen language into them
<?php
namespace App\Providers;
use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
UrlGenerator::macro('toLanguage', function (string $language) {
$currentRoute = app('router')->current();
$newRouteParameters = array_merge(
$currentRoute->parameters(), compact('language')
);
return $this->route($currentRoute->getName(), $newRouteParameters);
});
}
}
And in your Blade file
#inject('app', 'Illuminate\Contracts\Foundation\Application')
#inject('urlGenerator', 'Illuminate\Routing\UrlGenerator')
<language-switcher
locale="{{ $app->getLocale() }}"
link-en="{{ $urlGenerator->toLanguage('en') }}"
link-bg="{{ $urlGenerator->toLanguage('bg') }}"
></language-switcher>
You'll notice I used contracts injection instead of using facades. More informations here https://laravel.com/docs/7.x/contracts#contracts-vs-facades
And if you don't known Laravel macros, more informations here https://tighten.co/blog/the-magic-of-laravel-macros

Related

Laravel 8 route not found using jetstream nav menu

Fairly new to laravel 8 with some experience with laravel 7. I'm trying to add some additional pages to the default dashboard nav menu. however after adding the code, as I expect it to be, I get this error:
Symfony\Component\Routing\Exception\RouteNotFoundException
Route [accounts.index] not defined. (View: /home/some/path/resources/views/navigation-menu.blade.php)
So here is what I have done code wise:
in web.php i have the following route:
Route::middleware(['auth:sanctum', 'verified'])
->get('/accounts', [AccountController::class, 'index'])
->name('accounts');
I have a controller /app/Http/Controllers/AccountController.php as follows:
<?php
namespace App\Http\Controllers;
use App\Models\Account;
use Illuminate\Support\Facades\View;
class AccountController extends Controller
{
//
public function index() {
$accounts = Account::all();
return View::make('pages.accounts.index')->with('accounts', $accounts);
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
return View::make('pages.accounts.create');
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store()
{
//
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
//
}
}
I have a Model /app/Models/Account.php:
<?php
namespace App\Models;
use Eloquent;
class Account extends Eloquent
{
}
I also have blade templates for this page which i wont list for brevity as don't think this matters with the issue and at this point if I navigate to {url}/accounts the accounts index page is shown as intended.
However now I want to move this functionality into the jetstream dashboard so I can cut down on some development time and theme it similar to the default laravel concept.
The first thing I need is to add a new nav item next to Dashboard so I amended the default navigation-menu.blade.php file (/resources/views/navigation-menu.blade.php) by copying what it uses for dashboard and updating:
...
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<x-jet-nav-link href="{{ route('dashboard') }}" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }}
</x-jet-nav-link>
</div>
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<x-jet-nav-link href="{{ route('accounts.index') }}" :active="request()->routeIs('accounts.index')">
{{ __('Accounts') }}
</x-jet-nav-link>
</div>
</div>
...
It is at this point when reloading the page throws the error. Anyone know what is causing this? I initially tried without using .index as this is the default anyway right?
I have also tried to follow a few tuts on doing this ((https://eheidi.dev/blog/creating-a-multi-user-to-do-application-with-laravel-jetstream-2p1k)) but I get the same error when i reload my page after editing the navigation-manu.blade.php file so I'm at a loss. I'm developing this on ubuntu 20.04
thanks
Craig
*** EDIT ***
I have updated my route to be a resource for better use moving forward.
Route::middleware(['auth:sanctum', 'verified'])
->resource('/accounts', [AccountController::class, 'index'])
->name('accounts.index');
Tested this without the amend in navigation-menu.blade.php and all still worked added back the amends and same error.
*** EDIT 2 ***
I have I think narrowed this down to what should be in the navigation-menu file and the web routes file. I have further amended my web.php code as per point 2 of an answer below by Chadrack:
Route::middleware(['auth:sanctum', 'verified'])
->resource('/accounts', AccountController::class)
->only( ['index', 'create', 'store', 'update'])
->name('index', 'accounts');
The snippet I added to the navigation-menu.blade.php is:
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<x-jet-nav-link href="{{ route('accounts') }}" :active="request()->routeIs('accounts')">
{{ __('Accounts') }}
</x-jet-nav-link>
</div>
This is still throwing the error, also if i add .index to route function ( route('accounts.index') ) I still get an error (either Route [accounts.index] not defined or Route [accounts] not defined
*** EDIT 3 ***
I repeated the initial tutorial I had tried and failed with (https://eheidi.dev/blog/creating-a-multi-user-to-do-application-with-laravel-jetstream-2p1k) but as with my issue here after i have added the x-jet nav-link section to the blade template and added the route, when i reload i get the same error (with the new defined route) Route [dashboard-todo] not defined. So if the issue I get is the same for both options then I must be missing something here? As pointed out before I have tried every combination of route type. It works using the url [url]/accounts until I add the link into the nav-bar. The Dashboard does work when no changes are made to the navigation-menu.blade.php
you need to call your route using it's name.
in your definition you are define the route with the name accounts while you are calling a route named by accounts.index
replace your route
Route::middleware(['auth:sanctum', 'verified'])
->get('/accounts', [AccountController::class, 'index'])
->name('accounts');
By
Route::middleware(['auth:sanctum', 'verified'])
->get('/accounts', [AccountController::class, 'index'])
->name('accounts.index');
// note this
Your route name is accounts and you called accounts.index, it will not work, you have two possibilities to fix them.
replace :
Route::middleware(['auth:sanctum', 'verified'])->get('/accounts', [AccountController::class, 'index'])->name('accounts')
by :
Route::middleware(['auth:sanctum', 'verified'])
->get('/accounts', [AccountController::class, 'index'])
->name('accounts.index')
You can also use resources as method :
Route::middleware(['auth:sanctum', 'verified'])->resource('/accounts', AccountController::class)->only(['index', 'create','store',update]);
So just in case anyone else had this issue, I managed to fix it as follows:
First i used the following in web.php:
Route::middleware(['auth:sanctum', 'verified'])->resource('/accounts', \App\Http\Controllers\AccountController::class);
Note no name element and the full path to the controller (in addition to it being in a use at the top of web.api.
I then ran the following to clear my routes:
php artisan route:clear
I was then able to use the following in navigation-menu.blade.php:
<x-jet-nav-link href="{{ route('accounts.index') }}" :active="request()->routeIs('accounts.index')">
{{ __('Accounts') }}
</x-jet-nav-link>
Note I call accounts.index here. Now when i go onto my dashboard I see the new menu item and can visit the page as required. I am now also able to add the other pages from this resource (ie create, edit, destroy).
Of note I'm not sure if i need the full path in the route, found this on another post on SO and now it is working as I expected I don't wish to upset the apple-cart.
thanks
Craig

Pass global variable to laravel group route from current url

I have a route group with this structure:
Route::prefix('admin/{w_id}')->middleware(['auth'])->as('weblog.')->group(function () {
Route::get('/dashboard', [HomePageController::class, 'index'])->name('dashboard');
Route::resource('/blogcategory', CategoryController::class);
});
On dashboard route I have w_id in url and when I want to redirect user to blogcategory route (from anywhere) I should pass w_id manully in route helper class, I need some thing to set in globally from current link.
For example when I using this method:
'route' => 'weblog.blogcategory.store'
I got error like :
Missing required parameters for [Route: weblog.blogcategory.store]
And I should pass w_id parameter to all route helper manually, I need set globally w_id from current url of page.
I'm developing fully separated admin area for user's weblog and weblog id is exist in all url.
In order to avoid passing w_id again you will need to use URL::defaults(), it will create a default value for your parameter.
You can use a middleware to pass the default value.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\URL;
class SetDefaultWidForWeblogs
{
public function handle($request, Closure $next)
{
URL::defaults(['w_id' => /* pass the default value here*/]);
return $next($request);
}
}
Now register the middleware in app/Http/Kernel.php class (See more description here)
protected $routeMiddleware = [
...
'pass_wid' => \App\Http\Middleware\SetDefaultWidForWeblogs::class,
];
Then use that middleware
So for your route group
Route::prefix('admin/{w_id}')->middleware(['auth', 'pass_wid'])->as('weblog.')->group(function () {
Route::get('/dashboard', [HomePageController::class, 'index'])->name('dashboard');
Route::resource('/blogcategory', CategoryController::class);
});
See in docs about default values to Url

Laravel - Package can't recognise Auth functions?

I made a package which is counting the visitors on a webpage. Currently I have a single route, controller and view which don't do very much other than display a simple string. I have a separate Laravel application where this package is specifically build for. In this separate application I have a layout file called backend.
layouts/layouts/backend.blade.php.
My package view is extending this template like so: (backend.blade.php does not exists in the package but in the separate laravel application of-course)
#extends('layouts.layouts.backend')
#section('content')
<div class="container-fluid pt-5 ">
<div class="row">
<div class="col-md-6">
<h3>{{ __('Visitors') }}</h3>
</div>
</div>
</div>
#endsection
The package successfully extends this layout but it can't find functions such as Auth::user()->token and it will say
Trying to get property 'token' of non-object (View: /Users/rainierlaan/Sites/rainierlaan/resources/views/layouts/layouts/backend.blade.php)
Why does this happen?
This is my packages service provider
public function register()
{
// Controllers
$this->app->make('Rainieren\Visitors\Http\Controllers\VisitorController');
// Views
$this->loadViewsFrom(__DIR__.'/resources/views', 'visitors');
$this->publishes([
__DIR__.'/resources/views' => resource_path('views/visitors'),
]);
// Migrations
$this->loadMigrationsFrom(__DIR__.'/database/migrations');
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
include __DIR__ . '/routes/routes.php';
}
When I do vendor:publish the view successfully publishes to the right folder but somehow can't recognise functions such as Auth::user()->token or Auth::user()->unreadNotifications->count())
This is my package route:
<?php
Route::get('dashboard/visitors', '\Rainieren\Visitors\Http\Controllers\VisitorController#index')->name('visitors');
And this is the controller
public function index()
{
return view('visitors::index');
}
I would need a little bit more debugging information but my first assumption is the missing AuthenticateSession or Authenticate middleware.
Laravel defines a default middleware group web for your routes inside routes/web.php and this group uses the AuthenticateSession middleware. This is how a new installation looks like:
Route::group([
'middleware' => 'web', <<< this is the magic part
'namespace' => $this->namespace,
], function ($router) {
require base_path('routes/web.php');
});
Here we see that the middleware group web is used.
In your custom module / service provider this is not the case. Your Route::get() definition is added to the Router but not inside this group. Therefore all necessary internals to authenticate the user are not performed.
In this scenario I would try to use ->middleware('auth') or ->middleware('web') which will use the main-projects group middleware.
Route::get('dashboard/visitors', '\Rainieren\Visitors\Http\Controllers\VisitorController#index')
->name('visitors')
->middleware('web');
Here is a different idea:
If you say you're always authenticated. Then you could try to move all web middlewares into the global middlewares within your Kernel (protected $middleware = []).
I did not tested this but I can imagine that this could work as well.
I have seen in all of my active packages that they don't use Auth class at all in the views.
Their workaround is using this in the controller and pass it to the view:
$user = auth()->user();
return view('folder.view' ,['user'=>$user]);
And then in the view:
{{$user->token}} <!-- OR --!> {{$user->unreadNotifications->count()}}
Obviously as #N69S has stated only if a user is authenticated this is going to work.
Hope this works!!
EDIT:(Sorry didn't understand well the first time)
The error is in your service provider class yo have to do all the logic on the boot function, not in the register, because if you do in the register function, you load all before the laravel framework (To say something)
https://laravel.com/docs/5.7/providers#the-register-method
Your serviceprovider should end like this:
public function register()
{
//Nothing
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
// Controllers
$this->app->make('Rainieren\Visitors\Http\Controllers\VisitorController');
// Views
$this->loadViewsFrom(__DIR__.'/resources/views', 'visitors');
$this->publishes([
__DIR__.'/resources/views' => resource_path('views/visitors'),
]);
// Migrations
$this->loadMigrationsFrom(__DIR__.'/database/migrations');
include __DIR__ . '/routes/routes.php';
}
Hope this finally works!

Laravel - How to pass variable to layout partial view

I have a partial view in master layout which is the navigation bar. I have a variable $userApps. This variable checks if the user has enabled apps (true), if enabled then I would like to display the link to the app in the navigation bar.
homepage extends master.layout which includes partials.navbar
My code in the navbar.blade.php is this:
#if ($userApps)
// display link
#endif
However I get an undefined variable error. If I use this in a normal view with a controller it works fine after I declare the variable and route the controller to the view. I dont think I can put a controller to a layout since I cant route a controller to a partial view, so how do I elegantly do this?
What version of Laravel you use? Should be something like this for your case:
#include('partials.navbar', ['userApps' => $userApps])
Just for a test purpose, I did it locally, and it works:
routes.php
Route::get('/', function () {
// passing variable to view
return view('welcome')->with(
['fooVar' => 'bar']
);
});
resources/views/welcome.blade.php
// extanding layout
#extends('layouts.default')
resources/views/layouts/default.blade.php
// including partial and passing variable
#include('partials.navbar', ['fooVar' => $fooVar])
resources/views/partials/navbar.blade.php
// working with variable
#if ($fooVar == 'bar')
<h1>Navbar</h1>
#endif
So the problem must be in something else. Check your paths and variable names.
The other answers did not work for me, or seem to only work for older versions. For newer versions such as Laravel 7.x, the syntax is as follows.
In the parent view:
#include('partial.sub_view', ['var1' => 'this is the value'])
In the sub view:
{{ $var1 }}
I have gone through all the answers but below is the best way to do because you can also run queries in serviceProvider.
You need to create a separate service or you can use AppServiceProvider
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
View::composer('layouts.admin-layout', function ($view) {
$view->with('name', 'John Doe');
});
}
}
In your layout
{{$name}}
This approach is very simple:
In parent view :
#include('partial.sub_view1', ['This is value1' => $var1])
In sub view :
{{ $var1 }}
You can use view composer to send your variable to partial view.
Check the laravel documentation on laravel.com about view composer.
Also you can check the following link that will help you resolve this problem.
https://scotch.io/tutorials/sharing-data-between-views-using-laravel-view-composers

How to use wild card in admin routing in laravel 5

I am using code that is below for admin routing in laravel.
Route::group(['prefix' => 'admin'], function() {
Route::get('/', 'Admin\AdminController#home');
Route::get('/users/userList', 'Admin\UserController#userList');
Route::get('/users/detail', 'Admin\UserController#detail');
Route::get('/posts/view', 'Admin\PostController#view');
Route::get('/posts/edit', 'Admin\PostController#edit');
Route::get('/posts/add', 'Admin\PostController#add');
});
This is working fine for me. But when I add new functions in code for that I have to write routing in routes file. For example: If I want to add edit functionality in users controller, for that I have to add new route like .
Route::get('/users/edit', 'Admin\UserController#edit');
So I have to add routing for each function.
I want to know How to use wild card for admin routing so that I have to write routing only for controller not for each function for example.
Route::group(['prefix' => 'admin'], function() {
Route::get('/', 'Admin\AdminController#home');
Route::get('/users/:any', 'Admin\UserController#:any');
Route::get('/posts/:any', 'Admin\PostsController#:any');
});
wild card replace the function name, and auto ridirect to that function.
You could use implicit controllers that will do what you need.
First declare a route for your implicit controller
Route::controller('users', 'UserController');
Then, on your controller, you have to follow a convention for naming your routes with HTTP verbs used to access them (get for GET, post for POST, any for both)
class UserController extends Controller {
public function getIndex()
{
//
}
public function postProfile()
{
//
}
public function anyLogin()
{
//
}
}
A note about composed method name from documentation
If your controller action contains multiple words, you may access the action using "dash" syntax in the URI. For example, the following controller action on our UserController would respond to the users/admin-profile URI:
public function getAdminProfile() {}

Categories