I am configuring a multilanguage app. The user is visiting the app as a guest (I wonder if this is related).
My default language in config/app.php is pt-BR. I created a database table called languages, where I save the languages available for the app.
Languages model
Schema::create('languages', function (Blueprint $table) {
$table->id();
$table->string('iso2');
$table->string('name');
$table->string('native');
$table->timestamps();
});
I have a dropdown in the navigation bar where the user can select the language. Then, when the page loads, I get the locale from the session to display as the desired locale/language.
<x-dropdown align="right" width="48">
<x-slot name="trigger">
<button >
<div class="flex">
<x-flag :name="app()->getLocale()" />
</div>
<div class="ml-1">
svg for down array
</div>
</button>
</x-slot>
<x-slot name="content">
#foreach ($languages as $lang)
<x-dropdown-link :href="route('language', $lang->iso2)" >
<x-flag :name="$lang->iso2" />
<span class="ml-2">{{ $lang->native }}</span>
</x-dropdown-link>
#endforeach
</x-slot>
</x-dropdown>
Web route for setting the language:
Route::get('lang/{language:iso2}', [LocalizationController::class, 'index'])->name('language');
When the user select the language, a request is sent to this controller.
LocalizationController.php
public function index(Language $language)
{
session()->put('locale', $language->iso2);
return redirect()->back();
}
And I defined this middleware: App\Http\Middleware\Localization:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
class Localization
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* #return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
if (session()->has('locale')) {
Carbon::setLocale(session()->get('locale'));
App::setLocale(session()->get('locale'));
}
return $next($request);
}
}
I also registered the middleware in kernel.php:
protected $middleware = [
\App\Http\Middleware\Localization::class,
...
];
But when I select a locale other than pt-BR, nothing happens. The locale from the session changes, but when I dd on middleware like:
dump("Locale session value in localization middleware: ",session()->get('locale') );
The locale is null, but if I print session()->get('locale') in the view, the locale is the one that I selected in the dropdown, but the app locale is still pt-br. I don't understand why this is happening. Does anyone have a clue?
You come up with some crutches!
Why are you doing some kind of reverse redirect to the same page when choosing a language on the site? In your dropdownlist for each language, a link to the page on which you are located should be generated, taking into account the locale! No redirects!
Why are you saving languages to a database? On Your website will use a thousand languages? Or will there be a new language version of the site every day? Why else load the application with unnecessary queries to the database to get a list of languages? If you need to store lang in the database, then these should be translations!
For localization in config/app You must have one default locale in the locale cell.
Next, you can add an additional cell to this configuration file, for example, locales, where you can set all the languages that you want to use on the site, for example, like this:
// ....
'locales' => ['ru', 'en'],
// ....
Create a simple service. Register it in cell providers in config/app:
php artisan make:provider App\Services\Localization\LocalizationServiceProvider
In the service write the following code:
<?php
namespace App\Services\Localization;
use Illuminate\Support\ServiceProvider;
/**
* Class LocalizationServiceProvider
*
* #package App\Services\Localization
*/
class LocalizationServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* #return void
*/
public function register()
{
$this->app->singleton('Localization', function () {
return new Localization;
});
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
//
}
}
For convenience, we create a facade for the created service provider:
<?php
namespace App\Services\Localization;
use Illuminate\Support\Facades\Facade;
/**
* Class LocalizationService
*
* #package App\Services\Localization
*/
class LocalizationService extends Facade
{
/**
* #return string
*/
public static function getFacadeAccessor(): string
{
return 'Localization';
}
}
And add a additional class Localization, where we create one simple method:
<?php
namespace App\Services\Localization;
/**
* Class Localization
*
* #package App\Services\Localization
*/
class Localization
{
/**
* #return string
*/
public function locale(): string
{
$locale = request()->segment(1);
if($locale && \in_array($locale, config('app.locales'))) {
\App::setLocale($locale);
return $locale;
}
return '';
}
}
Then for the routes you set a group something like this:
Route::group(['prefix' => LocalizationService::locale()], 'middleware' => [/*...*/]], function () {
// YOUR routes here ....
});
Checked on Laravel v9.48. Working 100%!
Related
sorry for my english.
I'm trying to create a laravel route but i just can't make it work.
My project name is "portalRAG". It's a web app. When i access "my.address/PortalRAG"
it works just fine, but i can't make any other route work.
This is a new Laravel Project. It's almost empty and i haven't touched any major configuration other than creating some 1 or 2 views,controllers and model and only created some html code.
Here's my web.php file:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers;
use App\Http\Controllers\ragController\ragHomeController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('login');
});
/* NOT WORKING
Route::get('test', function () {
return view('login');
});
*/
Route::get('test','App\Http\Controllers\ragController\ragHomeController')->name('test');
I simply want to access "test" route. The controller i'm trying to use it's called ragHomeController and it's inside a ragController (a folder inside the basic Controller file).
Here's ragHomeController.
<?php
namespace App\Http\Controllers\ragController;
use App\Http\Controllers\Controller;
use App\Models\ragModel\ragHomeModel;
use Illuminate\Http\Request;
class ragHomeController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
echo("WHATEVER");
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* #param \App\Models\ragModel\ragHomeModel $ragHomeModel
* #return \Illuminate\Http\Response
*/
public function show(ragHomeModel $ragHomeModel)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param \App\Models\ragModel\ragHomeModel $ragHomeModel
* #return \Illuminate\Http\Response
*/
public function edit(ragHomeModel $ragHomeModel)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param \App\Models\ragModel\ragHomeModel $ragHomeModel
* #return \Illuminate\Http\Response
*/
public function update(Request $request, ragHomeModel $ragHomeModel)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param \App\Models\ragModel\ragHomeModel $ragHomeModel
* #return \Illuminate\Http\Response
*/
public function destroy(ragHomeModel $ragHomeModel)
{
//
}
public function __invoke()
{
}
}
What i'm getting wront? i have tried clearing cache, clearing route cache, and nothing works.
How should i access my "test" route? (I have tried every way and i still can't make it work).
"my.address/PortalRAG/test"?
"my.address/test"?
please write you route like below
Route::get('test',[ragHomeController::class,'Your_Method_Name'])->name('test');
and import your controller in top of your web
use App\Http\Controllers\ragHomeController;
Note:sometime it won't work so you have to run
php artisan optimize
if you are using xampp to run your laravel project then you can access your route like below
"my.address/PortalRAG/public/test"
you didn't use the method that located in ragHomeController so write it in this way
Route::get('test',[App\Http\Controllers\ragController\ragHomeController::class, 'index'])->name('test');
now open your route like this:
my.address/PortalRAG/test
I am not sure the title of the question is clear, so I will try to explain it in details.
I want to execute a piece of code in every controller automatically, assign the result to a variable that will be globally accessible everywhere.
So the code that need to be run will be like this:
function getLanguage() {
session('lang') ? session('lang') : 'en';
}
$LANG = getLanguage();
In any controller I need to access that variable like this:
myModel::all($LANG);
Also in the view, it would be helpful to access that variable as well (if possible)
<div> User language: {{$LANG}}</div>
Is there any place where I can execute that piece of code automatically?
Create a middleware
Add new middleware to App\Http\Kernels $middleware property, if you want it to run on every request. You may also put into $middlewareGroups property's web key.
Your middleware's handle method will be like this
public function handle(Request $request, Closure $next)
{
Config::set('some-name.some-sub-name', session('lang') ?: 'en');
return $next($request);
}
You will be updating a config in your middleware. This config has to be set only in this middleware to prevent possible problems of shared global state. (it is also important to be unique)
Then you can use it with config('some-name.some-sub-name')
In your use-case, you should implement a global middleware which sets the locale as you wish
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Session\SessionManager;
use Illuminate\Contracts\Foundation\Application;
class CheckLocale
{
/**
* The application instance.
*
* #var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
/**
* The session manager instance.
*
* #var \Illuminate\Session\SessionManager
*/
protected $sessionManager;
public function __construct(Application $app, SessionManager $sessionManager)
{
$this->app = $app;
$this->sessionManager = $sessionManager;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
$this->app->setLocale($this->sessionManager->get('lang', 'en'));
return $next($request);
}
}
After setting it as a global middleware, you can access it wherever you need it from a controller or view
Controller
public function foo(Application $app)
{
$lang = $app->getLocale();
}
In a Blade view
#inject('app', Illuminate\Contracts\Foundation\Application::class)
{{ $app->getLocale() }}
For any other variable, you may directly use Laravel container
In a service provider register method:
$this->app->singleton('lang', function ($app) {
return $app['session']->get('lang', 'en');
});
And wherever else
app('lang');
I have a table: settings with the model Setting
class Setting extends Model
{
protected $fillable = [
'name', 'value',
];
}
I have created a service provide SettingsServiceProvider and registered in app.php
class SettingsServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot(Factory $cache, Setting $settings)
{
if (\Schema::hasTable('settings')) {
config()->set('settings', Setting::pluck('value', 'name')->all());
}
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
}
}
After adding the name and value to the table settings, I am calling it in the view like this:
{{ config('settings.sitename') }}
Where sitename is the name field which returns the value perfectly.
Problem:
The problem with this method is that with every page request makes a DB call. And as these settings are not meant to be changed frequently, so I was looking for a method to cache it in the laravel cache.
I tried the following:
class SettingsServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot(Factory $cache, Setting $settings)
{
$settings = $cache->remember('settings', 60, function() use ($settings)
{
return $settings->pluck('name', 'value')->all();
});
config()->set('settings', $settings);
/* if (\Schema::hasTable('settings')) {
config()->set('settings', Setting::pluck('value', 'name')->all());
} */
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
}
But when I try to return it to the view like this:
{{ config('settings.sitename') }}
nothing gets return.
I don't have enough reputation to comment on the post but this should solve it
Replace:
return $settings->pluck('name', 'value')->all();
With:
return $settings->pluck('value', 'name')->all();
and then with the usual:
php artisan config:clear
php artisan cache:clear
In command prompt,
just type
php artisan
OR
php artisan list
It gives list of command with Explanation which help u lot
Thank you.
In Drupal there is a simple url rewrite system that stores path aliases and the real route in the database.
For example:
/category/hello => node/5
I would like to imitate this system in Laravel.
I know how to create the database structure. What I would like suggestions for is actually overriding and remapping the incoming request.
I've taken a glance at the router. No events are really sticking out. What I would like to avoid is adding every permutation as a static route. I would like to for this to be completely dynamic.
I was reading middleware with a redirect would work but don't know if that is the best route to go. Keep in mind that the aliases could be anything. There isn't any set pattern.
The actual business case for this is the application has a hierarchy of categories like for a catalog on an ecommerce site. For every path a dynamic page will need to exist and possibly also allow pass-thrus to other pages.
Ex.
/sports/football/nfl => \App\Http\Controllers\Category::lp(2)
Even something like:
/sports/football/nfl/:game/lines => \App\Http\Controllers\Lines::lp(:game)
However, I don't want to have every permutation in the db. Just the base one and allow everything after /sports/football/nfl/* pass thru to a completely different location.
If I do recall in Symfony this could be done with a custom route matcher. However, I don't see anything like that in Laravel. Unless I'm just missing something. It looks like you either add a static route or nothing all but I haven't taken the deep dive into that code yet so could be wrong.
I was able to implement a dynamic routing system by creating my own custom route and adding to the route collection manually.
Custom Route
use Illuminate\Routing\Route as BaseRoute;
use Modules\Catalog\Routing\Matching\CategoryValidator;
use Illuminate\Routing\Matching\MethodValidator;
use Illuminate\Routing\Matching\SchemeValidator;
use Illuminate\Routing\Matching\HostValidator;
use Illuminate\Http\Request;
use Modules\Event\Repositories\CategoryRepository;
use Illuminate\Routing\ControllerDispatcher;
/**
* Special dynamic touting for catalog categories.
*/
class CategoryRoute extends BaseRoute {
protected $validatorOverrides;
/**
* #param CategoryRepository
*/
protected $categoryRepository;
/**
* Create a new Route instance.
*
* #param CategoryRepository $categoryRepository
* The category repository.
*/
public function __construct(CategoryRepository $categoryRepository)
{
$this->categoryRepository = $categoryRepository;
$action = [
'uses'=> function() use ($categoryRepository) {
$path = app('request')->path();
$category = $categoryRepository->findOneByHierarchicalPath($path);
$controller = app()->make('Modules\Catalog\Http\Controllers\Frontend\CategoryController');
return $controller->callAction('getIndex', ['categoryId'=>$category->getId()]);
}
];
$action['uses']->bindTo($this);
parent::__construct(['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'],'_catalog_category',$action);
}
/**
* Determine if the route matches given request.
*
* #param \Illuminate\Http\Request $request
* #param bool $includingMethod
* #return bool
*/
public function matches(Request $request, $includingMethod = true)
{
$this->compileRoute();
$validators = $this->getValidatorOverrides();
foreach ($validators as $validator) {
/*if (! $includingMethod && $validator instanceof MethodValidator) {
continue;
}*/
if (! $validator->matches($this, $request)) {
return false;
}
}
return true;
}
/**
* Get the route validators for the instance.
*
* #return array
*/
public function getValidatorOverrides()
{
if (isset($this->validatorOverrides)) {
return $this->validatorOverrides;
}
$this->validatorOverrides = [
new MethodValidator, new SchemeValidator,
new HostValidator, /*new UriValidator,*/
new CategoryValidator($this->categoryRepository)
];
return $this->validatorOverrides;
}
}
Custom Route Validator
<?php
namespace Modules\Catalog\Routing\Matching;
use Illuminate\Routing\Matching\ValidatorInterface;
use Illuminate\Routing\Route;
use Illuminate\Http\Request;
use Modules\Event\Repositories\CategoryRepository;
class CategoryValidator implements ValidatorInterface
{
protected $categoryRepository;
public function __construct(CategoryRepository $categoryRepository) {
$this->categoryRepository = $categoryRepository;
}
/**
* Validate a given rule against a route and request.
*
* #param \Illuminate\Routing\Route $route
* #param \Illuminate\Http\Request $request
* #return bool
*/
public function matches(Route $route, Request $request)
{
$path = $request->path() == '/' ? '/' : '/'.$request->path();
$category = $this->categoryRepository->findOneByHierarchicalPath($path);
return $category?true:false;
}
}
To satisfy the requirements of the category repository dependency I had to also create a subscriber that adds the route after all the providers had been booted. Simply placing it in the routes.php file would not work because there was no guarantee that all the dependencies for IoC would be configured when that file gets loaded.
Bootstrap Subscriber
use Modules\Catalog\Routing\CategoryRoute;
use Modules\Event\Repositories\CategoryRepository;
use Illuminate\Support\Facades\Route as RouteFacade;
class BootstrapSubscriber {
public function subscribe($events) {
$events->listen(
'bootstrapped: Illuminate\Foundation\Bootstrap\BootProviders',
'Modules\Catalog\Subscribers\BootstrapSubscriber#onBootstrappedBootProviders'
);
}
public function onBootstrappedBootProviders($event) {
$categoryRepository = app(CategoryRepository::class);
RouteFacade::getRoutes()->add(new CategoryRoute($categoryRepository));
}
}
I will probably expand on this but that is the basic way to do it.
I created a website and would like to know the best method to create a page for the global settings of the portal, such as the theme, logo, title, etc.
I would like a page for the setting that works like the dashboard of a Wordpress or any CRM that you can download, of course with much less functions. Things I'd like to change throughout the site could be: logo, the site title, meta tags, links of the portal for social etc.
Create modelless form
Use Configure::dump() to store your settings in php or ini file
Read this part of CakePHP Book, to learn how to read and use stored settings
EXAMPLE: (Note: This part of the code is used in my own Admin plugin)
1: Create modelless form
src/Form/SiteForm.php
<?php
namespace Admin\Form;
use Cake\Form\Form;
use Cake\Form\Schema;
use Cake\Validation\Validator;
use Cake\Core\Configure;
/**
* Site Form.
*/
class SiteForm extends Form
{
/**
* Builds the schema for the modeless form
*
* #param Schema $schema From schema
* #return $this
*/
protected function _buildSchema(Schema $schema)
{
return $schema;
}
/**
* Form validation builder
*
* #param Validator $validator to use against the form
* #return Validator
*/
protected function _buildValidator(Validator $validator)
{
return $validator;
}
/**
* Defines what to execute once the From is being processed
*
* #return bool
*/
protected function _execute(array $data)
{
Configure::write('Site', $data);
// 2. Use Configure::dump(),.. to save your settings as php array to
// config/site_config.php
Configure::dump('site_config', 'default', ['Site']);
return true;
}
}
src/Controller/SettingsController.php
<?php
namespace Admin\Controller;
use Admin\Controller\AppController;
use Admin\Form\SiteForm;
use Cake\Core\Configure;
/**
* Settings Controller
*
*/
class SettingsController extends AppController
{
/**
* Index method
*
* #return void
*/
public function index()
{
$site = new SiteForm();
if ($this->request->is('post')) {
if ($site->execute($this->request->data)) {
$this->Flash->success('Success.');
} else {
$this->Flash->error('There was a problem submitting your form.');
}
} else {
$this->request->data = Configure::read('Site');
}
$this->set('site', $site);
}
src/Template/Settings/index.ctp
<?php
echo $this->Form->create($site);
echo $this->Form->input('title');
echo $this->Form->input('description');
echo $this->Form->input('keywords');
// ... add your input fields
echo $this->Form->button(__('Save'));
$this->Form->end() ?>
in config/bootstrap.php add, to load site_config.php
Configure::load('site_config', 'default');
in your layout, views, controllers,.. use for example
<title>
<?= Configure::read('Site.title') ?>
</title>