Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2: Load config-files depending on domain

Tags:

php

symfony

At the moment we are running an online-shop for germany. Now we want also offer our products in UK with an own domain.

Depending on the domain their are several settings which should be loaded:

  • Google Analytics ID
  • Payment API Secrets/Keys, ...
  • Currency
  • Language
  • Admin Mail
  • Tracking Pixel (FB)
  • and more....

In a previous project we solved it by putting this settings in a domain-table in the database. But I think with the whole payment service information and key and and and.. it is not the best solution.

like image 816
rakete Avatar asked Feb 27 '16 10:02

rakete


4 Answers

You can write a bundle Extension class to load your configuration depending on the host.

The bundle Extension:

// src/AcmeBundle/DependencyInjection/AcmeExtension.php

<?php

namespace AcmeBundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

class AcmeExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);
        $rootdir = $container->getParameter('kernel.root_dir');

        // Load the bundle's services.yml
        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');

        // Load parameters depending on current host
        $paramLoader = new Loader\YamlFileLoader($container, new FileLocator($rootdir.'/config')); // Access the root config directory
        $parameters = sprintf('parameters_%s.yml', $container->getParameter('router.request_context.host'));

        if (!file_exists($rootdir.'/config/'.$parameters)) {
            $parameters = 'parameters.yml'; // Default
        }

        $paramLoader->load($parameters); 
    }
}

The corresponding bundle Configuration:

// src/AcmeBundle/DependencyInjection/Configuration.php

<?php

namespace AcmeBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

class Configuration implements ConfigurationInterface
{
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('acme');

        return $treeBuilder;
    }
}

Like this, you can create a file named parameters_localhost.yml and it will be automatically loaded. If the file is not found, the default parameters.yml will be used.

You can apply this logic based on every parameters you want (like _locale used for your translations, I guess).

Hope this help.

like image 192
chalasr Avatar answered Nov 06 '22 14:11

chalasr


I know that the best answer is already chosen, but I found other approach with parameters.php:

<?php

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader;

$hostArray = explode('.', $_SERVER['HTTP_HOST']);
$hostArrayCount = count($hostArray);
$host = $hostArray[$hostArrayCount - 2] . '.' . $hostArray[$hostArrayCount - 1];
$hostDir = $container->getParameter('kernel.root_dir') . '/config/domains/';
$parameters = sprintf('%s.yml', $host);

if (file_exists($hostDir . $parameters)) {
    $hostParamLoader = new Loader\YamlFileLoader($container, new FileLocator($hostDir));
    $hostParamLoader->load($parameters);
}

and in config.yml after:

imports:
    - { resource: parameters.yml }

add string:

    - { resource: parameters.php }
like image 21
Oleksandr Savchenko Avatar answered Nov 06 '22 14:11

Oleksandr Savchenko


If each of your shop is a different app installation, then why not keep your config in parameters.yml file ? You can also take a look at ParameterHandler script which will help you manage all parameters.

If you use single code base, maybe you should totally rethink your implementation, because for example, what if in the future you will need to make products from UK shop independent from products in DE shop etc. ? (there are more use cases like that)

I think that your current solution is a short term solution and if you plan to expand to different countries, you should invest some time for a long term solution. Of course it depends all from your requirements and use cases.

An option is splitting your application into channels where each channel will be a shop in UK and DE and so on..

You could then also have a different settings, products, payments, currencies per channel etc..

You could take a look at Sylius project on GitHub. They developed this nice ChannelBundle which you can see how it works.

like image 34
takeit Avatar answered Nov 06 '22 14:11

takeit


You can use SetEnv in your (apache?) configuration for the database variables and other variables for each domain (ref)

# /etc/apache2/sites-available/one-domain.conf
<VirtualHost *:80>
    ServerName      onedomain.uk
    DocumentRoot    "/path/to/onedomain/web"
    DirectoryIndex  index.php index.html
    SetEnv          SYMFONY__GOOGLE__ANALYTICSID mygoogleanalyticsid
    SetEnv          SYMFONY__PAYMENT__APISECRET mysecret
    # ...
    <Directory "/path/to/onedomain/web">
        AllowOverride All
        Allow from All
    </Directory>
</VirtualHost>

Then use a common parameters.yml setting the variables:

# app/config/parameters.yml
parameters:
    analytics_id: "google.analyticsid"
    payment_secret: "payment.apisecret"
    # ...

They all have their own db settings and any variable you want to set for each.

You can also separate your cache and logs folders, by setting a variable identifying each domain (ref):

# app/AppKernel.php
    public function getCacheDir()
    {
        if(getenv("SYMFONY__DOMAIN__NAME")){
            return dirname(__DIR__).'/var/cache/'.$this->getEnvironment().'/'.getenv("SYMFONY__DOMAIN__NAME");
        } else {
            return dirname(__DIR__).'/var/cache/'.$this->getEnvironment();
        }
    }

    public function getLogDir()
    {
        if(getenv("SYMFONY__DOMAIN__NAME")){
            return dirname(__DIR__).'/var/logs/'.getenv("SYMFONY__DOMAIN__NAME");
        } else {
            return dirname(__DIR__).'/var/logs/';
        }
    }

In case you store sessions in files, you can also do this for the sessions folder:

# app/config/config.yml
framework:
    # ...
    session:
        handler_id:  session.handler.native_file
        save_path:   "%kernel.root_dir%/../var/sessions/%kernel.environment%/%domain.name%"
like image 41
Diego Avatar answered Nov 06 '22 14:11

Diego