I want to provide my customer with an option to configure their subdomain names in .env and config/app.php file. My Laravel route configuration is as follows:
Route::group(['namespace' => 'PublicSite',
'domain' => config('app.app_domain_public')], function () {
// public routes here
});
Route::group(['namespace' => 'AdminSite',
'domain' => config('app.app_domain_admin')], function () {
// admin routes here
});
It works fine and reacts to .env changes. But when I prepare my application package for deployment, I run php artisan route:cache and this makes the values of config('app.app_domain_x') frozen somewhere in bootstrap/cache/routes.php.
As the result, the application no more reacts to route domain changes in .env unless I run php artisan route:cache on the server. But there are situations when it's not possible to run artisan on the server.
Of course, I could configure .env as necessary before deployment but this breaks the idea of being able to modify .env settings without rewriting other application files (route cache, to be more specific).
Is there any workaround to make Laravel to cache at least some routes but still provide dynamic domain configuration?
Related
This is my first time asking questions in StackOverflow so sorry for the mistakes.
I have a problem with laravel routes.
<?php
use Illuminate\Support\Facades\Route;
//start :: login
Route::group(['namespace' => 'auth'], function () {
Route::get('/', 'AuthController#index')->name('index');
Route::get('/showResult', 'AuthController#Result')->name('Result');
Route::post('/showResultds', 'AuthController#showResult')->name('showResult');
Route::post('/dologin', 'AuthController#doLogin')->name('doLogin');
Route::get('/logout', 'AuthController#logout')->name('logout');
So these are my routes.
I have determined the app URL correctly in .env file.
My problem is that when my website is on the server (windows server) the route becomes the server's local IP address and not my domain or public IP address.
When I click on my links it becomes 172.30.30.4/login for example. and not domainname.com/login
Thanks for your help
When changing anything in any of the config files or the .env you should always run
php artisan config:cache
This will clear your current cache and cache your new settings
change the url in your config/app.php
'url' => env('APP_URL', 'http://localhost'),
and dont forget to run php artisan config:cache
What is the proper way to add a subdomain into your routes? I have a laravel/homestead project working on my local computer but when I move it to HostGator, a shared host server, it no longer works. Well the home page works but the links to the sub-pages don't.
For example, the following route works fine on my local version:
Route::get('/appointments', 'AppointmentController#index');
But if I use that route on the remote server, it takes it to tekknow.net/appointments instead of tekknow.net/medaverter/appointments.
So I followed instructions from here:
https://laravel.com/docs/5.6/routing#route-group-sub-domain-routing
and added a prefix like this:
Route::prefix('MedAverter')->group(function() {
Route::get('/appointments', 'AppointmentController#index');
});
But that doesn't work either. It also goes to /tekknow.net/appointments
I also tried changing the route to this:
Route::get('/MedAverter/appointments', 'AppointmentController#index');
But that also went to tekknow.net/appointments
Anybody know what I'm doing wrong?
EDIT: I went onto my HostGator cPanel and looked at all my subdomains and saw that my subdomain root was medaverter.tekknow.net which is linked to Document root of medaverter.tekknow.net/MedAverter which gets redirected to http://www.tekknow.net/MedAverter. So I renamed my folder from medaverter to MedAverter to match the subdomain redirection.
Here is a screenshot showing what I see in cPanel for columns Subdomains.Root Domain, Document Root, and Redirection
When you try php artisan route:list | grep appointments,
it prints:
[~/www/MedAverter]# php artisan route:list | grep appointments
| | GET|HEAD | MedAverter/appointments | | App\Http\Controllers\AppointmentController#index | web |
That means your route MedAverter/appointments is working for laravel.
Error 404 means route cannot be found.
So I think that's something wrong with your nginx configuration.
When I try http://tekknow.net/MedAverter/MedAverter/appointments.
It has really found the route with error 500.
So, I think you have defined this code in your nginx configuration:
location = / {
rewrite ^ https://tekknow.net/MedAverter;
}
Solution 1:
Change back to rewrite ^ https://tekknow.net/; in nginx configuration.
(I'm not familiar with hostGatar cPanel, but I think you can change medaverter.tekknow.net/MedAverter redirected to http://www.tekknow.net/`)
And in your laravel project, you need to keep prefix MedAverter to appointments.
Solution 2:
Keep the rewrite code. It means you don't need to change the cPanel redirect.
And remove prefix MedAverter in laravel routes. HostGatar(Nginx) will automatically redirect with this prefix MedAverter for appointments.
Clear all caches
The problem you're getting can be due to cache. So, make sure that's not the problem by running
php artisan route:clear && php artisan view:clear && php artisan config:clear && php artisan cache:clear && php artisan clear-compiled
Base URL
If you want to change the base URL of your Laravel app, that's something already asked.
Multiple Path Prefixes
In this case you have a more than one route with the same prefix in their path. If that's the case, you can use route groups to simplify the structure.
Route::prefix('medaverter')->group(function () {
Route::get('/', function () {
// Path /medaverter
});
Route::get('appointments', function () {
// Path /medaverter/appointments
});
});
This would go to, respectively, tekknow.net/medaverter and tekknow.net/medaverter/appointments. You could also create another prefix, like testing, configure in the same way and go to tekknow.net/testing or tekknow.net/testing/whatever.
Namespace prefixes
When you’re grouping routes by route prefix, it’s likely their controllers have a similar PHP namespace. In the medaverter example, all of the medaverter routes’ controllers might be under a Medaverter namespace.
Route::namespace('Medaverter')->group(function () {
// App\Http\Controllers\Medaverter\AppointmentController
Route::get('medaverter/appointments', 'AppointmentController#index');
});
u can use route groups for that here is a sample try this:
Route::group(['prefix' => 'medaverter'], function () {
Route::get('/appointments', [AppointmentController::class, 'index'])->name('index');
});
or
Route::group(['prefix' => 'medaverter'], function () {
Route::get('/appointments', 'AppointmentController#index');
});
I'm running a laravel project behind a reversed proxy which is why I need to force the root url and scheme:
URL::forceRootUrl($proxy_url);
URL::forceScheme($proxy_schema);
I've added this to the top of my /routes/web.php and it's working fine until I run:
php artisan optimize
It caches the routes in /bootstrap/cache without the forced url and scheme, so now all my urls are pointing to the wrong root url.
I've tried to move the code to /Providers/AppServiceProvider.php (both register and boot) in order to make it take effect when caching the routes but no luck.
I have to manually delete the routes cache file in /bootstrap/cache to make my routes work again.
Have do I make it take effect when caching the routes?
Edit:
I have also tried to create a global middleware where I do the force url and schema. Again it works fine before caching the routes, but when running php artisan optimize the routes are once again incorrect.
php artisan optimize removed since laravel 5.6 (source, source2)
Using URL::forceRootUrl and URL::forceScheme seems like a work-around for working with reverse proxies. The clean solution for it would be to add a trusted proxies in your configuration. This post explains the feature in full. But it comes down to:
Use the App\Http\Middleware\TrustProxies middleware
Edit the middleware $proxies property with the IP(s) of your load balancer
protected $proxies = [
'192.168.1.1',
'192.168.1.2',
];
Remove the following code from /routes/web.php
URL::forceRootUrl($proxy_url);
URL::forceScheme($proxy_schema);
I would like to access single laravel application by multiple domain.
For routing i have used laravel route pattern for eg.
$appRoutes = function() {
Route::group(['namespace'=>"Interface1"], function(){
/** route for index page and User authentication pages **/
Route::get('/', 'LoginController#showLoginForm')->name('login');
});
};
Route::group(array('domain' => 'example1.com'), $appRoutes);
Route::group(array('domain' => 'example2.com'), $appRoutes);
Now .env for each domain i have replaced config variable value inside AppServiceProvider.php register method:
if(isset($_SERVER['HTTP_HOST']) && !empty($_SERVER['HTTP_HOST']) ){
if($_SERVER['HTTP_HOST']=='example1.com'){
$configArray = [
'APP_NAME' => 'Application 1',
'APP_URL' => 'http://example1.com',
];
}else{
$configArray = [
'APP_NAME' => 'Application 2',
'APP_URL' => 'http://example2.com,
];
}
config($configArray);
}
but still i am not getting correct domain url using url(config('app.url'))
How to load all .env variable overwrite for each domain?
For the current Laravel 7.x. at the time of writing this, you can add the following code in your bootstrap/app.php file just before return $app statement.
// bootstrap/app.php
// ...
/*
|-----------------------------------------------
| Load domain-specific .env file if it exists
|-----------------------------------------------
*/
if(isset($_SERVER['HTTP_HOST']) && !empty($_SERVER['HTTP_HOST'])){
$domain = $_SERVER['HTTP_HOST'];
}
if (isset($domain)) {
$dotenv = Dotenv\Dotenv::createImmutable(base_path(), '.env.'.$domain);
try {
$dotenv->load();
} catch (\Dotenv\Exception\InvalidPathException $e) {
// No custom .env file found for this domain
}
}
// ...
return $app;
Next, you should have a .env file for each domain in the following format .env.example1.com .env.example2.com. You can leave the original .env as a fallback for domains that lack an explicit .env file.
For Laravel 5.x or Laravel 6.x, you can find more from the original solution.
config() sets the configuration settings but not the environment settings.
You have to do the following:
if(isset($_SERVER['HTTP_HOST']) && !empty($_SERVER['HTTP_HOST']) ){
if($_SERVER['HTTP_HOST']=='example1.com'){
$configArray = [
'app.name' => 'Application 2',
'app.url' => 'http://example2.com',
]; ;
}else{
$configArray = [
'app.name' => 'Application 2',
'app.url' => 'http://example2.com',
];
}
config($configArray);
}
You can then retrieve the values via:
config("app.name");
config("app.url");
As far as I know there is no way to modify the environment variables before the configuration is actually loaded, because the configuration is loaded and reads the environment variables before the service providers are registered.
Also betware of using HTTP_HOST as it's a client-set header and may not be reliable.
A simple solution is here Laravel Multi Domain Package
This package allows a single Laravel installation to work with multiple HTTP domains.
There are many cases in which different customers use the same application in terms of code but not in terms of database, storage and configuration.
This package gives a very simple way to get a specific env file, a specific storage path and a specific database for each such customer.
Installation steps
Add gecche/laravel-multidomain as a requirement to composer.json:
{
"require": {
"gecche/laravel-multidomain": "4.*"
}
}
Update your packages with composer update or install with composer install.
You can also add the package using composer require gecche/laravel-multidomain and later specify the version you want (for now, dev-v1.1.* is your best bet).
This package needs to override the detection of the HTTP domain in a minimal set of Laravel core functions at the very start of the bootstrap process in order to get the specific environment file. So this package needs a few more configuration steps than most Laravel packages.
Installation steps:
replace the whole Laravel container by modifying the following lines at the very top of the bootstrap/app.php file.
//$app = new Illuminate\Foundation\Application(
$app = new Gecche\Multidomain\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
update the two application Kernels (HTTP and CLI).
At the very top of the app/Http/Kernel.php file , do the following change:
//use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Gecche\Multidomain\Foundation\Http\Kernel as HttpKernel;
Similarly in the app/Console/Kernel.php file:
//use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Gecche\Multidomain\Foundation\Console\Kernel as ConsoleKernel;
Override the QueueServiceProvider with the extended one in the $providers array in the config/app.php file:
//Illuminate\Queue\QueueServiceProvider::class,
Gecche\Multidomain\Queue\QueueServiceProvider::class,
publish the config file.
php artisan vendor:publish
Usage
This package adds three commands to manage your application HTTP domains:
domain.add artisan command
The main command is the domain:add command which takes as argument the name of the HTTP domain to add to the application. Let us suppose we have two domains, site1.com and site2.com, sharing the same code.
We simply do:
php artisan domain:add site1.com
and
php artisan domain:add site2.com
These commands create two new environment files, .env.site1.com and .env.site2.com, in which you can put the specific configuration for each site (e.g. databases configuration, cache configuration and other configurations, as usually found in an environment file).
The command also adds an entry in the domains key in config/domains.php file.
In addition, two new folders are created, storage/site1_com/ and storage/site2_com/. They have the same folder structure as the main storage.
Customizations to this storage substructure must be matched by values in the config/domain.php file.
domain.remove artisan command
The domain:remove command removes the specified HTTP domain from the application by deleting its environment file. E.g.:
php artisan domain:remove site2.com
Adding the force option will delete the domain storage folder.
The command also removes the appropriate entry from, the domains key in config/domains.php file.
domain.update_env artisan command
The domain:update_env command passes a json encoded array of data to update one or all of the environment files. These values will be added at the end of the appropriate .env.
Update a single domain environment file by adding the domain option.
When the domain option is absent, the command updates all the environment files, including the standard .env one.
The list of domains to be updated is maintained in the domain.php config file.
E.g.:
php artisan domain:update_env --domain_values='{"TOM_DRIVER":"TOMMY"}'
will add the line TOM_DRIVER=TOMMY to all the domain environment files.
domain.list artisan command
The domain:list command lists the currently installed domains, with their .env file and storage path dir.
The list is maintained in the domains key of the config/domain.php config file.
This list is automatically updated at every domain:add and domain:remove commands run.
config:cache artisan command
The config:cache artisan command can be used with this package in the same way as any other artisan command.
Note that this command will generate a file config.php file for each domain under which the command has been executed. I.e. the command
php artisan config:cache --domain=site2.com
will generate the file
config-site2_com.php
Please visit the repository for more information
I have a Laravel 5.2 instance utilizing all the typical out-of-the-box routes for dashboard, cases, home, login/logout, and users (which is working well). I now need to create a wizard with steps (step1, step2, step3, etc.), and I need access to the session. I assigned them to the group middleware.
Route::group(['middleware' => 'web'], function () {
Route::get('/', function () {
// Uses Web middleware
});
Route::get('wizard/step1', [
'as' => 'wizard/step1', 'uses' => 'Wizard\WizardController#getStep1']);
Route::get('wizard/step2', [
'as' => 'wizard/step2', 'uses' => 'Wizard\WizardController#getStep2']);
});
However, when I go to the named route(s), I get a 404 error. So WizardController looks like the following.
namespace App\Http\Controllers\Wizard;
use App\Http\Controllers\Controller;
use App\Http\Requests;
class WizardController extends Controller
{
public function __construct()
{
//$this->middleware('guest');
}
public function getStep1()
{
return view('wizard.step1');
}
}
The defined views are resources/views/wizard/step1.php. Ideally, I'd like to refactor it so that Wizard is a separate group. However, nothing seems to work with the way I'm defining custom routing currently.
This happens when you cache the routes. The new route entries you add will never be recognized unless you clear the route cache.
You can delete cached routes using php artisan route:clear.
Since you will be changing the routes frequently in dev env, It is always better to not perform route caching while in dev environment.
You can do it by only running artisan route:cache as a post-deploy hook in Git, or just run it as a part of your Forge deploy process. So that every time you deploy your code in your server, your routes are cached automatically.
I resolved my issue by running the command:
php artisan route:clear
This lets Artisan delete your route cache and you can you can make sure routes are in sync.
Thanks for the help!