The instantiation of Illuminate\Config\Repository is hardcoded on the start file Illuminate\Foundation\start.php. Is there any way we can setup so it makes it easier to extend. Currently I have to copy the start file and change the default config use namespace to the extended config.
The reason is for a dynamic multi tenant solution. I know about the environment configuration found here but that requires hard coding to setup tenants with the proper config. My current solution is to overwrite the load method found in Config\Repository. There I load default config items and replace with the tenant config if there are any.
Example: (shorten for tenant focus)
protected function load($group, $namespace, $collection)
{
...
$items = $this->loader->load($env, $group, $namespace);
if($this->tenant_loader)
{
$tenant = $this->tenant_loader->load($env, $group, $namespace);
$items = array_replace_recursive($items,$tenant);
}
...
}
Thus, I can change the database and session config to specific tenants at start of the app giving me multi tenancy and not having to commit it to the app source.
Let me know if this is a possibility or if anyone has any other suggestions on how to achieve this.
If I were making a multi-tenant app I would rather extend the DatabaseManager and make it read from something other than the config. It is somewhat unavoidable to have the config hard-coded in like this, I think.
I agree and thats how I started out. I extended the session, database and cookie managers and replaced their service providers respectively, however I noticed that the code was getting pretty repetitive. It would be easier to catch it at the source, which all I need to do is change the config file dynamically. The above code does work. I am able to change the database (user / password), mail (postmark app api), and session path without having to add custom logic for each manager. In addition, I don't want to commit production username, passwords and api keys because it isn't secure if you want to outsource some development.
It seems like we can extend almost everything in Laravel, so it would be nice if we could figure out a solution to extend the Config.
I can certainly help you out here, having written a package which does work just like this.
The way I'd attack it is not by overriding the config repository. That class is abstract enough to not need modifying. Essentially how I do it is in a few steps.
Create a new loader (your tenant loader):
class TenantLoader extends Illuminate\Config\FileLoader {
/**
* {@inheritDoc}
*/
public function load($environment, $group, $namespace = null)
{
$items = parent::load($environment, $group, $namespace);
return array_replace_recursive($items, $this->methodForFindingItems());
}
/**
* {@inheritDoc}
*/
public function exists($group, $namespace = null)
{
// Same logic, check the file loader first and then yours
}
/**
* {@inheritDoc}
*/
public function cascadePackage($environment, $package, $group, $items)
{
$items = parent::load($environment, $group, $namespace);
// You may not need to cascade tenant config. In which case, just remove
// this whole cascadePackage() method
return array_replace_recursive($items, $this->methodForCascadingEnvironmentTenantConfig());
}
}
Then, you need to instantiate your config (I would suggest making your own ServiceProvider and doing this in the register() method):
$loader = new TenantLoader($app['files'], $app['path'].'/config');
Then, you need to set the loader instance on the repository (once again, in a boot() method of your repository):
$app['config']->setLoader($loader);
I would put your service provider right up the top of the list in app/config/app.php, to ensure it loads before anybody else references config values.
If you want to be extra sure that you're loading all the correct items you may flush already loaded config, you could always flush any config cache (this shouldn't be needed if your service provider is booted early enough. If you use it, put it in boot()):
foreach (array_keys($app['config']->getItems()) as $collection)
{
unset($app['config'][$collection]);
}
@bencorlett thanks for the suggestion! This looks like a great alternative and I will try to implement this in my current app.
That being said, it still looks like a few configurations are set before the providers are loaded. If you look at the start file, lines 150 - exception handling loads config.app.debug, line 167 loads the config.app.timezone, line 182 - registers config.app.aliases, then finally we get to loading the providers. I guess the solution here could be reapply these at run time, though it would be nice to avoid this.
Also in your example above, I have concern that $this->methodForFindingItems() would implement duplicate logic. Where if we can extend the config we can use the same logic but just update the file path and recursively replace the results.
What a pickle.
The package I wrote is a database loader, so naturally it couldn't load everything as you need config to connect to a database to load config, so naturally it kicked in after that.
The alternative you have is, to not load Illuminate's start file, but rather copy it to another file and load that instead... Would love to see other suggestions too!
Sent from my iPhone
Please excuse my brevity
On 4 Feb 2014, at 12:06 pm, Dean Mraz [email protected] wrote:
@bencorlett thanks for the suggestion! This looks like a great alternative and I will try to implement this in my current app.
That being said, it still looks like a few configurations are set before the providers are loaded. If you look at the start file, lines 150 - exception handling loads config.app.debug, line 167 loads the config.app.timezone, line 182 - registers config.app.aliases, then finally we get to loading the providers. I guess the solution here could be reapply these at run time, though it would be nice to avoid this.
Also in your example above, I have concern that $this->methodForFindingItems() would implement duplicate logic. Where if we can extend the config we can use the same logic but just update the file path and recursively replace the results.
—
Reply to this email directly or view it on GitHub.
Yes, that is what I am doing currently and works fine. Just got to make sure I keep an eye on that file and make updates when it changes.
A better implementation maybe to have the config loaded in the bootstrap/start.php vs framework foundation start file, so I don't have to overwrite framework code.
No plans to change things here at the moment.
I had a similar issue so, I just wanted to shout out my solution to this problem. I ended up creating my own Application class that extends Illuminate\Foundation\Application and then created my own FileLoader that extended Illuminate\Config\FileLoader. In Application, I overloaded getConfigLoader() to return an instance of my File Loader.