Symfony2: Customize database connection, cache and log folders, based on incoming request - php

I am trying to build a multi-tenanted solution, using an existing codebase, but with segregated databases, cache and log files.
An effective way of doing this would be to dynamically compute the following, based on an incoming requests domain name:
database connection parameters
cache folder
log folder
In Symfony1.x, I could do this by creating a custom filter class, but I don't know how to do this in Symfony2.
My question then is this:
How may I modify the above parameters for a symfon2 application - based on an incoming request?

I agree with Jurgis Gregov.
You should change in your web/app.php line:
$kernel = new AppKernel('prod', false);
to:
$kernel = new AppKernel($_SERVER['HTTP_HOST'], false);
The first argument of AppKernel is environment name
After that you can create in app/config a new config files with name:
config_name.yml
Where you can set doctrine configuration.
Your cache will be in folder: app/cache/name
And your logs will be in: app/logs/name.log (You can also set path to log in config yml file)
I don't know if there is other way to change doctrine parameters but i know that you can also chanage logs and cache folder by override methods:
getLogDir() and getCacheDir() in your app/AppKernel.php where you can use REQUEST to set it.

Related

Symfony 3 web front controller with env

Symfony 3 uses two web front controllers app.php and app_dev.php. How could I change it to use one controller with usage of env variables to set the env (prod, dev) and debug.
I've tried to remove the app_dev.php but symfony tries to load that file.
Maybe this is not the way to do it, just learning symfony. Other suggestions are welcome.
The frontend controller files app.php and app_dev.php exist as examples of prod and dev respectively. As the documentation (http://symfony.com/doc/3.4/configuration/environments.html) suggests, you'll want to remove one or the other for your production deployment, or simply create a custom frontend controller for each environment, and have your apache or nginx configuration only load the appropriate file to launch Symfony.
You'll see in the file, the environment is declared and passed along with whether or not debug should be enabled.
//dev environment, with debug enabled
$kernel = new AppKernel('dev', true);
By contrast, for production:
//prod environment, debug disabled
$kernel = new AppKernel('prod', false);
If you want a single controller with some dynamic elements, I'd recommend removing app_dev.php and using app.php with custom logic before AppKernel is instantiated to do what you want.

Laravel 5 load env file based on .env.master?

I'm planing to do something like, specifying what env file to load during application bootstrap time by creating a variable in .env.master such as ENV_VER = dev.env
This is because I have several branches such as development, release-1.1 and etc. Thus by loading the env file based on the name specified in a master env file, developers no longer have to copy and paste new variables into their local copy of .env and instead, just specify what version of env file to load in the master env file. By the way, I have several env files such as dev.env, 1.6.env etc.
Is it possible?
Definitely, me myself I tend to "bend" a framework little bit everytime and there's always a way, not always the best solution though. I'm not giving a whole implementation here, just pointing you in some direction, that might work for you.
You can extend Laravel's base application class Illuminate\Foundation\Application, which contains $environmentFile variable storing environment file loaded during appplication bootstrap or possibly override function loadEnvironmentFrom($file) or environmentFile(). The whole logic is up to you.
So basically all you need to do in order to be able to "play" with .env loading is...
Create a new application class extending Laravel's one:
namespace MyNamespace;
...
use Illuminate\Foundation\Application as App;
/**
* I'm doing alots of magic with .env in this class
*/
class Application extends App
{
...
/**
* The environment file to load during bootstrapping.
*
* #var string
*/
protected $environmentFile = '.env.main';
...
}
And now, because we have a new bootstraping class, we have to tell Laravel we want to use it. So you'll have to modify bootstrap/app.php file in point where a new instance is being created:
...
$app = new MyNamespace\Application(
realpath(__DIR__.'/../')
);
...
Note: For inspiration I recommend you to really look at Illuminate\Foundation\Application class.
In laravel 5.5+ you can, maybe earlier, you can set your server have a APP_ENV environment or server variable that the process can see (apache, command line...)
this will allow you to use a suffix or file extension on your .env files for auto loading those files...
APP_ENV=dev :: .env.dev
APP_ENV=production :: .env.production
much easier than other solutions.
If you want to look into how this is done, it starts with
1. application bootstrappers
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\SetRequestForConsole::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
2. LoadEnvironmentVariables
first the environment is determined…
\Illuminate\Foundation\Bootstrap\LoadConfiguration::bootstrap calls
\Illuminate\Foundation\Application::detectEnvironment
If
--env={env} for CLI then it will use that for APP_ENV.
Else
\Illuminate\Foundation\EnvironmentDetector::detectWebEnvironment
is called which uses a callback…
function () use ($config) {
return $config->get('app.env', 'production');
}
where app.env defaults to env('APP_ENV', 'production')
3 LoadConfiguration
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::bootstrap calls...
\Illuminate\Foundation\Application::afterLoadingEnvironment
which eventually gets to
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::checkForSpecificEnvironmentFile
which sets the environment file based on app env IF the file exists.
$this->setEnvironmentFilePath(
$app, $app->environmentFile().'.'.env('APP_ENV')
);
allowing it to load the .env.{APP_ENV} instead of .env
NOTE: Testing.
when running php unit tests. the Illuminate/Foundation will try to load the .env.testing file for configurations!

How to set a configuration parameter in config.yml in Symfony without creating a service

I am using Heroku to host my Symfony 3 application. I am going to switch to a new database provider that requires me to set custom certificates.
I don't want to store the certificates under version control, so I decided to write a new command that downloads the certificate files on deployment from a URL that is set as an environment variable.
To read environment variables I use incenteev-parameters in composer.json, this then sets the appropriate values at parameters.yml. However as far as I know there is no way to read this value directly from parameters.yml, so I have to have something in config.yml that uses my new url parameter at parameters.yml, but this would require me to create a new service.
I simply want to read this new url parameter from my new command so that I can download my certificates on deployment.
You can extend your command from ContainerAwareCommand class and access your parameter from parameters.yml using the following way:
$this->getContainer()->getParameter('your_parameter_key');
Or you can define your command as a service and inject required parameters.

How Can I Configure Lumen To Broadcast Events via Pusher?

Lumen's docs state that "Lumen supports several broadcast drivers out of the box: Pusher, Redis, and a log driver for local development and debugging. A configuration example is included for each of these drivers. The BROADCAST_DRIVER configuration option may be used to set the default driver."
In my .env file I have set BROADCAST_DRIVER=pusher. Where/how can I configure my pusher ID, key, and secret? I see that in Laravel a configuration file for setting these options lies within config/broadcasting.php. Where can I set these options within Lumen?
Temporarily I have edited Illuminate\Broadcasting\BroadcastManager and hard coded my values in.
protected function createPusherDriver(array $config)
{
// override
$app_id = 'hidden';
$key = 'hidden';
$secret = 'hidden';
return new PusherBroadcaster(
new Pusher($key, $secret, $app_id, Arr::get($config, 'options', []))
);
}
Okay, I figured it out. Essentially you have to add in the config files yourself.
Create a config directory in the root of your app.
Copy config/broadcasting.php from a working laravel installation into this directory
Add the following keys to your .env file: PUSHER_SECRET, PUSHER_KEY, PUSHER_APP_ID
In general, Lumen supports two modes of configuration:
Setting environment variables consumed by Lumen's internal config files
Creating project config files that override Lumen's internal configuration
Lumen provides environment-based configuration variables needed to configure most of the framework's components, including Pusher. Though unclear from the docs, we can also configure Lumen through config files like Laravel. This enables advanced configuration that Lumen may not support through its built-in configuration structure.
By default, new Lumen projects don't provide configuration files like new Laravel projects do in the config/ directory. As #Feek discovered, we can create the config/ directory and add any needed config files. For example, we can create the config/broadcasting.php file to set up broadcast connections.
When creating a config file in the project like this, Lumen will automatically read configuration values from the file if it matches the name of one of the Lumen built-in config files. If we want to add a custom configuration file that doesn't match one of Lumen's internal config filenames, we need to manually instruct Lumen to read the config file in a service provider or in bootstrap/app.php.
For instance, to load configuration values from config/my-custom-config.php, add this line to the application's boot process:
$app->configure('my-custom-config');
In the particular case of this question, Lumen's built-in broadcasting.php config file reads Pusher environment variables for us, so we don't need to create a config file in the project for these. Simply set the BROADCAST_DRIVER, PUSHER_SECRET, PUSHER_KEY, and PUSHER_APP_ID in .env.

Cache issue with dynamic parameter in config.yml

I have a multisite aplication with a log system depends on each site and I want that each of them has its own log file so I did that :
1) I use a dynamic variable in config.yml with depends on $_SERVER['HTTP_HOST'].
config.yml:
imports:
- { resource: param.php }
...
monolog:
handlers:
user:
type: stream
path: %kernel.logs_dir%/%domain_name%.%log_filename%
channels: [user]
=> %domain_name% is my dynamic variable
2) I set it in app/config/param.php:
<?php
$url = $_SERVER['HTTP_HOST'];
$domain = preg_replace('/^www./', '', $url);
$container->setParameter('domain_name', $domain); // this set my variable `%domain_name%` with the current domain (ex: site1.com)
Problem :
This code works fine but when I switch to an other site, the value of %domain_name% remains equal to its initial value.
Exemple :
I am in www.site1.com => %domain_name% value is "site1.com" so it works
fine
I switch to www.site2.com => %domain_name% value is always "site1.com" and
not "site2.com"
Why ?
Cache issue. Indeed, Symfony load in cache config.yml so it keeps the first value of %domain_name%
To delete this value I have to execute this command: rm -rf app/cache/* but it is not a solution.
So nobody has a solution for this problem ? Thanks
We had the same problem in our multi-site CMS environment. Symfony is primarily designed to run a single site, so the config.yml gets run once, and then cached. That means if you inject dynamic parameters into the config.yml (by any means: Globals, Constants, Apache Environment, etc), after the first run they are cached for each subsequent request :( So the next unique domain site request, will be using the cached values from the first site. In most cases, this creates problems.
Some people have suggested creating different environments for each site. But that's not practical when you host hundreds, thousands or millions of sites.
Alternative Cache Location
At the moment, the best solution we can conceive is to specify a different cache folder for each site. So you extend the app/appKernel.php and customize the getCacheDir function.
//app/appKernel.php
class AppKernel extends Kernel
{
public function getCacheDir()
{
return $this->rootDir.'/cache/'.$this->environment.'/'.$_SERVER['HTTP_HOST'];
}
}
See API Reference for Kernel.
I am not quite sure, if you are using the parameter file/configuration as intended.
For me there is information missing, to answer your question directly, so here are the two possible scenarios:
1) Site www.site1.com and www.site2.com is pointing at the SAME source (same server):
In this case I wouldn't define the domain_name as an configuration value, course it is not an distinct configuration parameter it is more like a variable. Build a service or think about defining an additional environment.
2) www.site1.com and www.site2.com are two distinct projects:
In that case you shouldn't deploy the contents of the cache directory (which you should do anyway; but in this case it would be the issue ;)).
EDIT
You could implement different configuration files to solve:
config_site1.yml # Configuration for www.site1.com
config_site2.yml # Configuration for www.site2.com
Controller in your /web directory:
app_site1.php
app_site2.php
initialize the AppKernel with the requested environment, and point to the controller in your vhost config or your htaccess file.
I think you could change the app.php controller even to something like:
[...]
require_once __DIR__.'/../app/AppKernel.php';
$request = Request::createFromGlobals();
$kernel = new AppKernel($request->giveMeMyHostEnvironmentMappingShizzle(), false);
$kernel->loadClassCache();
[...]
kind regards

Categories