Multi-language site in Laravel 5.4 - laravel

For SEO purposes I'd like to include the locale of my Laravel project pages in the url, such as http://localhost/en or http://localhost/nl/about.
In trying to achieve this I have found a few tutorials and packages, but I'm not entirely happy with them. The most popular suggestion is mcamara's laravel-localization package, but creating URLs requires a very long function call. Other packages are either for older Laravel versions or offer a lot of functionality I don't need, which would only clutter my code and database.
Marwelln's tutorial seems perfect: it modifies URLs the way I want, works with the default URL helpers like action(), url() and route() and doesn't create unnecessary database tables. However, this tutorial seem to be for Laravel 5.0 and not compatible with Laravel 5.4. Could anyone help me to achieve such URL modification in the newest Laravel version in the most basic way?

Try the following:
In your RouteServiceProvider.php add these lines at the beginnig of the map() method:
$locale = $request->segment(1);
if($locale) {
$this->app->setLocale($locale);
$this->prefix = $locale;
}
And the mapWebRoutes() method should look like this:
protected function mapWebRoutes()
{
$parameters = [
'middleware' => 'web',
'namespace' => $this->namespace,
];
if ($this->prefix) {
$parameters['prefix'] = $this->prefix;
}
Route::group($parameters, function ($router) {
require base_path('routes/web.php');
});
}
Then follow the rest of the instructions in the link of the example you provide. I guess that should be all.
NOTE: the line $this->app->setLocale($locale) has not much sense since you are doing that on the middleware.

Related

"ReflectionException Function () does not exist" when trying to setup authentication in Laravel

I'm having some trouble getting authentication working 100% in Laravel (all views seem to work so far except for "home") and was hoping to get some assistance from the Laravel experts out there.
A bit of background info:
PHP Version: 7.3.11
Laravel Version: 8.13.0
Used Composer to build the ui scaffolding (composer require laravel/ui)
Used the Bootstrap ui option (php artisan ui bootstrap --auth)
The issue
As mentioned above, I seem to be able to access all of the generated authentication views so far (login, register & the password reset views), however after registering with a dummy account I get the following error when trying to access the "home" view:
ReflectionException
Function () does not exist
The Stack trace is pointing to the following file:
"vendor/laravel/framework/src/Illuminate/Routing/RouteSignatureParameters.php:23":
<?php
namespace Illuminate\Routing;
use Illuminate\Support\Reflector;
use Illuminate\Support\Str;
use ReflectionFunction;
use ReflectionMethod;
class RouteSignatureParameters
{
/**
* Extract the route action's signature parameters.
*
* #param array $action
* #param string|null $subClass
* #return array
*/
public static function fromAction(array $action, $subClass = null)
{
$parameters = is_string($action['uses'])
? static::fromClassMethodString($action['uses'])
: (new ReflectionFunction($action['uses']))->getParameters();
return is_null($subClass) ? $parameters : array_filter($parameters, function ($p) use ($subClass) {
return Reflector::isParameterSubclassOf($p, $subClass);
});
}
/**
* Get the parameters for the given class / method by string.
*
* #param string $uses
* #return array
*/
protected static function fromClassMethodString($uses)
{
[$class, $method] = Str::parseCallback($uses);
if (! method_exists($class, $method) && Reflector::isCallable($class, $method)) {
return [];
}
return (new ReflectionMethod($class, $method))->getParameters();
}
}
With the following line (line 23) being highlighted as the error:
: (new ReflectionFunction($action['uses']))->getParameters();
And here are the routes used in the "web.php" file:
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| 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('welcome', ['pageTitle' => 'Home']);
});
Route::get('/services', function () {
return view('services', ['pageTitle' => 'Services']);
});
Route::get('/contact', function () {
return view('contact', ['pageTitle' => 'Contact']);
});
Auth::routes();
Route::get('/home', ['pageTitle' => 'Client Dashboard'], [App\Http\Controllers\HomeController::class, 'index'])->name('home');
After a bit of googling I've learnt that the method I'm trying to use to setup authentication has been deprecated and it is now advised to use Jetstream or Fortify, however I also found a few examples of people still managing to use this old method in their projects:
https://www.youtube.com/watch?v=NuGBzmHlINQ
As this is my first ever Laravel project I was really trying to just stick with the basics and not over complicate things for myself which is why I chose not to use Jetstream or Fortify and tried to stick to this older approach of setting up authentication. However I've been stuck on this for a couple of hours now and have not been able to figure out what's going wrong which is why I'm now seeking some help with it.
Happy to provide extra details/project code if needed - any help I can get with this would be really appreciated.
Btw, this also happens to be my first ever post on StackOverflow so any feedback on my question or advice on how I can improve it would also be greatly appreciated.
Cheers,
adb
Adjust your last route to include some type of 'action', by removing that second argument (as it only takes 2 arguments):
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
I have changed the web.php file.
I was getting the same error when using the following route:
Route::post('/delete/{id}',[AdminController::class],'destroyProduct');
Changing the route using this code fixed the problem:
Route::post('/delete/{id}','App\Http\Controllers\AdminController#destroyProduct');
I was getting the same error when making a simple route.
Route::get("/test", [ListPagesController::class, "index"]);
This was my code in the web.php file and I was routing my blade file in views via the controller.
When I later redirected it directly to web.php this way without using a controller, the problem was resolved.
Route::get("/test", function () {return view("main");});
I don't quite understand why but the problem is solved.
You Should add it in line 2 web.php
use App\Http\Controllers\YourControllerName;
For me, I changed:
Route::get('/branch/', [Controller::class, 'get_branch_list'])->name('branch');
To:
Route::get('/branch/', 'Controller#get_branch_list')->name('branch');

Why InitializeTenancyByDomain is not applicable for the login process?

This is for Laravel 8.x with Jetstream/Livewire scaffold having Stancl/Tenancy. The initialization of tenant models or session settings not working right. Either I did not do it right or inbuilt problem.
The entire package was built as per instructions of Stencl/tenancy v3.x. I can see the dd(\App\User::all()) as per code outlined below
Route::middleware([
'web',
InitializeTenancyByDomain::class,
PreventAccessFromCentralDomains::class,
])->group(function (){
Route::get('/', function () {
dd(\App\User::all()); //can see all users models in tenants table
return view('welcomeTenant');
});
Route::get('/home', [
'middleware' => ['auth'],
'uses' => '\App\Http\Controllers\HomeController#index'
])->name('home');
});
This meant InitializeTenancyByDomain right to me.
When a login form is requested from tenant's domain eg. from rtbs.example.in, the encrypted session/cookie info is not stored in sessions table of tenant i.e. rtbs.sessions. When a login form is posted, it is looking for users in central domain (example.in) where users table is not present, hence the central.users table not exist error. As a result I get 419 error. I had disabled the csrf token verification temporarily to identify this problem.
This is the issue. Why the InitializeTenancyByDomain is not applicable for the login process? Could there be a fundamental setting wrong with me? Interestingly, the dd(\App\User::all()) if present anywhere else i.e. as show below
Route::middleware([
'web',
InitializeTenancyByDomain::class,
PreventAccessFromCentralDomains::class,
])->group(function (){
dd(\App\User::all()); //central-domain.users does not exist error
Route::get('/', function () {
return view('welcomeTenant');
});
Route::get('/home', [
'middleware' => ['auth'],
'uses' => '\App\Http\Controllers\HomeController#index'
])->name('home');
});
The same sql exception i.e. central-domain.users table does not exist is thrown. Only when present inside the Route::get('/'... i can see the correct models.
I had this problem with Laravel Fortify, which provides the backend to JetStream. It only became a problem when I changed the session driver to database from file - though this will be necessary for my deployment anyway.
The issue is that Fortify makes extensive use of dependency injection inside the constructor methods of their controllers. As described in the documentation for Tenancy, because of the lifecycle of a Laravel request the tenancy will not be available when these constructors run. This means they will default to the central connection and cause failure to login.
Because we can't change the way Fortify uses constructors, the solution is to change the middleware to be global and add a check for central domains into the initialization middleware:
Copy Stancl\Tenancy\Middleware\InitializeTenancyByDomain to App\Middleware\InitializeTenancyByDomain changing:
public function handle($request, Closure $next)
{
//Skip for central domains
if(in_array($request->getHost(), config('tenancy.central_domains'), true)){
return $next($request);
}
return $this->initializeTenancy(
$request, $next, $request->getHost()
);
}
App/Http/Kernel
use App\Http\Middleware\InitializeTenancyByDomain;
protected $middleware = [
// ...
InitializeTenancyByDomain::class,
];
Config/fortify
use Stancl\Tenancy\Middleware\PreventAccessFromCentralDomains;
'middleware' => [
'web',
PreventAccessFromCentralDomains::class,
],
Routes/tenant
Route::middleware([
'web',
PreventAccessFromCentralDomains::class,
])->group(function () {
//Your Routes
}
With this solution Fortify and Stancl Tenancy are now working well together with well separated databases, sesssions and logins.
I got it to work with Rory's answer. However, you need to set the correct namespace in App\Http\Middleware\InitializeTenancyByDomain.php:
namespace App\Http\Middleware; // << use this instead of Stancl\Tenancy\Middleware
// [..]
use Stancl\Tenancy\Middleware\IdentificationMiddleware; // <<
class InitializeTenancyByDomain extends IdentificationMiddleware
That package is very helpful, but it requires a lot of work to get all the pieces lined up and maintained. You've got the right middleware, including 'web' and the two tenancy bits, which will start the session and allow checks for CSRF.
I've had similar problems before. The biggest cause for me was that it was not finding the right 'central' path, and thus not initializing tenancy correctly.
Make sure your tenancy.php config file is showing the right central domain. Something like:
'central_domains' => [
'example.in' // No http:// here
],
Route's like next can issue bugs with database session store
Route::view('some')
You could use a feature called Universal Routes check the documentation here

Laravel Route Controller issue

I am trying to add a new route to my application and can't seem to get it to work. I keep getting a 404 error. It looks like the physical path is looking at the wrong directory. Currently looking at D:\Web\FormMapper\blog\public\forms but should be looking at D:\Web\FormMapper\blog\resources\view\layout\pages\forms.blade.php
My request URL:
http://localhost/FormMapper/ /works fine
http://localhost/FormMapper/forms /doesn't work
http://localhost/FormMapper/forms.php /No input file specified.
my FormsController:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class FormsController extends Controller
{
public function index()
{
return view('layouts.pages.forms');
}
}
My web.php:
Route::get('/', function () {
return view('layouts/pages/login');
});
Route::get('/forms', 'FormsController#index');
My folder structure looks like this:
My config/view.php
return [
'paths' => [
resource_path('views'),
],
'compiled' => env(
'VIEW_COMPILED_PATH',
realpath(storage_path('framework/views'))
),
];
you must use dot for this. In your controller change to this:
return view('layouts.pages.forms');
If your route only needs to return a view, you may use the Route::view method. Like the redirect method, this method provides a simple shortcut so that you do not have to define a full route or controller. The view method accepts a URI as its first argument and a view name as its second argument. In addition, you may provide an array of data to pass to the view as an optional third argument:
Route::view('/', 'layouts.pages.login');
Route::view('/forms', 'layouts.pages.forms', ['foo' => 'bar']);
Check docs
After tracking digging deeper I determined that the issue was that IIS requires URL rewrite rules in place for Laravel to work properly. The index.php and '/' route would work b/c it was the default page but any other pages wouldn't. To test this I used the
php artisan serve
approach to it. and everything worked properly. Unfortunately I am unable to do this in production so I needed to get it to work with IIS.

Laravel Language prefix with additional parameters

Quick question that is already killing me for days.
With Laravel I am trying to use different languages.
English and Japanese
This works in the route like this.
Route::group([
'prefix' => '{lang}',
'where' => ['lang' => '(jp|en)'],
'middleware' => 'Language'
], function() {
Route::get('/blogs', 'BlogController#index')->name('main-blog');
Route::get('/blog/{postId}/{postTitle}', 'BlogController#post');
});
This works when I am visiting the "/blogs" page.
It changes between languages.
Now when I visit the "/blog/{postId}/{postTitle}" page I can't access the posted parameter anymore within my controller.
Somehow it only shows the "lang" parameter.
What would be the right way to access a parameter when using a prefix.
When I don't use the prefix it works like a charm.
My Controller;
public function post($blog_id, $blog_title)
{
// Do something
}
Help is highly appreciated.
I have been banging my head with this for days now.
Wesley
You use the prefix parameter to specify common parameters for your grouped routes. So You need one more parameter $lang for this controller:
public function post($lang, $blog_id, $blog_title)
{
// Do something
}
With prefix parameter the routes look like these:
/{lang}/blogs
/{lang}/blog/{postId}/{postTitle}

How to render a cms page with default theme AND variables from controllers in OctoberCMS?

I'm wondering how I can render a view, or display a page with my default theme in OctoberCMS, via a route that executes a function in a controller.
If I have the following route:
Route::get('bransje', [
'uses' => 'Ekstremedia\Cityportal\CPController#bransje'
]);
And in my controller CPController ive tried several things, like I used to with Laravel:
public function bransje() {
$stuff = Stuff::with('info');
return View::make('cms::bransje')->with('stuff',$stuff);
}
But I cannot seem to get it to work, and I've tried to search the web, but it's hard to find answers. I have found a workaround, and that is to make a plugin component, then I can include that component and do:
public function onRun()
{
$this->eventen = $this->page['stuff'] = $this->stuff();
}
protected function stuff()
{
return ...
}
Is there any way so I can make pages without using the Cms, and that are wrapped in my default theme? I've tried
return View::make('my-theme-name::page');
and a lot of variants but no luck.
I know I can also do a:
==
public function onRun()
{
}
in the start of my page in the cms, but I'm not sure how to call a function from my plugin controller via there.
You can bypass frontend routing by using routes.php file in your plugin.
Full example in this video turotial.
If this answer can still be useful (Worked for October v434).
I have almost the same scenerio.
What I want to achieve is a type of routing like facebook page and profile.
facebook.com/myprofile is the same url structure as facebook.com/mypage
First I create a page in the CMS for each scenario (say catchpage.htm)
Then a created a catchall route at the buttom of routes.php in my plugin that will also not disturb the internal working of octobercms.
if (!Request::is('combine/*') && !Request::is('backend/*') && !Request::is('backend')) {
// Last fail over for looking up slug from the database
Route::get('{slug}/{slug2?}', function ($slug, $slug2 = null) {
//Pretend this are our routes and we can check them against the database
$routes = ["bola", "sade", "bisi", "ade", "tayo"];
if(in_array($slug, $routes)) {
$cmsController = new Cms\Classes\Controller;
return $cmsController->render("/catchpage", ['slug' => $slug]);
}
// Some fallback to 404
return Response::make(View::make('cms::404'), 404);
});
}
The if Request::is check is a list of all the resource that october uses under the hood, please dont remove the combine as it is the combiner route. Remove it and the style and script will not render. Also the backend is the url to the backend, make sure to supply the backend and the backend/*.
Finally don't forget to return Response::make(View::make('cms::404'), 404); if the resource is useless.
You may put all these in a controller though.
If anyone has a better workaround, please let us know.

Resources