Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

limoncello app on subfolders usage #35

Open
dreamsbond opened this issue Nov 2, 2017 · 19 comments
Open

limoncello app on subfolders usage #35

dreamsbond opened this issue Nov 2, 2017 · 19 comments
Assignees

Comments

@dreamsbond
Copy link

i am going to put multiple apps on a single subdomains, i.e api.domain.com;

where each subfolder house corresponding limoncello app like:
api.domain.com/school1/subschool1/...
where api endpoint api.domain.com/school1/subschool1/v1/users... etc...

i did some adjustment on nginx like

server {
        listen 80;
        server_name api.domain.com;

        access_log off;
        error_log off;

        root /srv/www;
        index index.php index.html;

        location / {
                try_files $uri $uri/ /index.php?$args;
        }

        location /school1/subschool {
                root /srv/www/api/school1/subschool1/public/;

                rewrite ^/school1/subschool1/(.*)$ /$1 break;

                try_files $uri $uri/ /index.php?$args;
        }

        location ~ \.php$ {
                try_files $uri $uri/ /index.php?$args = 404;
                set $newurl $request_uri;
                if ($newurl ~ ^/school1/subschool1(.*)$) {
                        set $newurl $1;
                        root /srv/www/api/school1/subschool1/public/;
                }

                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
                fastcgi_index index.php;
                include fastcgi_params;
                fastcgi_param REQUEST_URI $newurl;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_intercept_errors off;
                fastcgi_buffer_size 16k;
                fastcgi_buffers 4 16k;
        }
}

is it a right approach to bring up limoncello app on subfolder usage ?

@neomerx
Copy link
Collaborator

neomerx commented Nov 2, 2017

As far as I understand you have a single database for your users. If so why not having a single domain for API as well?

@dreamsbond
Copy link
Author

got it. yea. sounds like it was redundant.
btw,
suppose a single database for users exists (that was a limoncello-php/app)
and application for api (crud operation, that was another limoncello-php/app)

  • how do the application for api be authenticated? as the application for api do not hold the token information, and;
  • how do single database for users to authorise application?

they are entirely two (limoncello-php/app)

@neomerx
Copy link
Collaborator

neomerx commented Nov 3, 2017

They both can work with the same database. Both apps can share some tables (e.g. users) and have their own ones. Auth tokens could be issued by any of them or both. As they share users and tokens a token will work for both apps.

@neomerx neomerx self-assigned this Nov 3, 2017
@dreamsbond
Copy link
Author

how to set up?

@neomerx
Copy link
Collaborator

neomerx commented Nov 3, 2017

image

@dreamsbond
Copy link
Author

noted. let me try it out and namespace the table to differentiate.

@dreamsbond
Copy link
Author

this approach was good. but as more applications created. more different namespaced table will be store on same database. will it be a confusion and lack of isolation?

@neomerx
Copy link
Collaborator

neomerx commented Nov 6, 2017

You're asking about a very generic topic of software solutions design. My response was a sample high-level design for your requirement of having monolith solution.

@dreamsbond
Copy link
Author

ya. agree :p

@dreamsbond
Copy link
Author

could i define two group of database setting in limoncello-app?

@neomerx
Copy link
Collaborator

neomerx commented Nov 6, 2017

I think the easiest way would be adding custom config(s) and create database Connection objects out them.

@dreamsbond
Copy link
Author

dreamsbond commented Nov 6, 2017

i see, so would have to add custom setting via class Doctrine and .env

@neomerx neomerx added the fixed label Nov 6, 2017
@dreamsbond
Copy link
Author

though having subfolder usage on limoncello app was not a good practice.

but i observed there was a trailing "/" slash problem:

is there any workaround on it?

@neomerx
Copy link
Collaborator

neomerx commented Nov 14, 2017

@dreamsbond
Copy link
Author

I think the easiest way would be adding custom config(s) and create database Connection objects out them.

having tried to create custom config and create database connection,

  • the container configurator set up properly
  • A custom class "AnotherConnection" extended the generic DBAL Connection Class

all these steps works found.

but i found the return connection object still capturing the .env config settings.

@dreamsbond
Copy link
Author

reproduce the issue as follow:

  • created a "services" folder under "server"
  • autoloaded "Services" namespace in composer.json
    "Services\\": "server/services/",
  • create new class AnotherDoctrineSettings implements SettingsInterface as like as DoctrineSettings in
<?php namespace Services\AnotherDoctrine;

use Limoncello\Contracts\Settings\SettingsInterface;

/**
 * Class AnotherDoctrineSettings
 *
 * @package Services\AnotherDoctrine
 */
class AnotherDoctrineSettings implements SettingsInterface
{
    /**
     * Settings key
     */
    const KEY_USER_NAME = 0;

    /**
     * Settings key
     */
    const KEY_PASSWORD = self::KEY_USER_NAME + 1;

    /**
     * Settings key
     */
    const KEY_DATABASE_NAME = self::KEY_PASSWORD + 1;

    /**
     * Settings key
     */
    const KEY_HOST = self::KEY_DATABASE_NAME + 1;

    /**
     * Settings key
     */
    const KEY_PORT = self::KEY_HOST + 1;

    /**
     * Settings key
     */
    const KEY_CHARSET = self::KEY_PORT + 1;

    /**
     * Settings key
     */
    const KEY_DRIVER = self::KEY_CHARSET + 1;

    /**
     * Settings key
     */
    const KEY_URL = self::KEY_DRIVER + 1;

    /**
     * Settings key
     */
    const KEY_MEMORY = self::KEY_URL + 1;

    /**
     * Settings key
     */
    const KEY_EXTRA = self::KEY_MEMORY + 1;

    /**
     * Settings key
     */
    const KEY_PATH = self::KEY_EXTRA + 1;

    /**
     * Settings key
     */
    protected const KEY_LAST = self::KEY_PATH;

    /**
     * @inheritDoc
     */
    final public function get(array $appConfig): array
    {
        $defaults = $this->getSettings();

        $pathToDbFile = $defaults[ static::KEY_PATH ] ?? null;
        assert(
            ($pathToDbFile === null || file_exists($pathToDbFile) === true),
            "Invalid database file `$pathToDbFile`."
        );

        return $defaults;
    }

    /**
     * @return array
     */
    protected function getSettings(): array
    {
        return [];
    }
}
  • created new class AnotherDoctrine extends AnotherDoctrineSettings with custom config '.env.another.doctrine'
# Sample configuration for Doctrine (MySQL)
DB_DATABASE_NAME = 'another_doctrine'
DB_USER_NAME     = 'another_doctrine'
DB_USER_PASSWORD = 'secret'
DB_HOST          = '127.0.0.1'
DB_PORT          = 3306
DB_CHARSET       = 'UTF8'
DB_DRIVER        = 'pdo_mysql'

<?php namespace Settings;

use Dotenv\Dotenv;
use Services\AnotherDoctrine\AnotherDoctrineSettings;

/**
 * Class AnotherDoctrine
 *
 * @package Settings
 */
class AnotherDoctrine extends AnotherDoctrineSettings
{
    /**
     * @inheritDoc
     */
    protected function getSettings(): array
    {
        (new Dotenv(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', '..']), '.env.another.doctrine'))->load();

        $dbFile = getenv('DB_FILE');
        $dbPath = empty($dbFile) === true ? null : implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'storage', $dbFile]);

        return [
                static::KEY_DATABASE_NAME => getenv('DB_DATABASE_NAME'),
                static::KEY_USER_NAME     => getenv('DB_USER_NAME'),
                static::KEY_PASSWORD      => getenv('DB_USER_PASSWORD'),
                static::KEY_HOST          => getenv('DB_HOST'),
                static::KEY_PORT          => getenv('DB_PORT'),
                static::KEY_CHARSET       => getenv('DB_CHARSET'),
                static::KEY_DRIVER        => getenv('DB_DRIVER'),
                static::KEY_PATH          => $dbPath,
            ] + parent::getSettings();
    }
}

@dreamsbond
Copy link
Author

sorry, i found i shall change those 'DB_' suffixed environment variable to another one like 'DB_OAUTH_'... etc..
isnt' it?

@neomerx
Copy link
Collaborator

neomerx commented Nov 22, 2017

I see you're trying to emulate built-in class. Custom classes do not need to support all doctrine connection possibilities and are not needed to be extensible as standard settings. So it can be significantly simplified.

  1. Custom second connection (for simplicity I put it into Container folder however you should find a better place for it)
<?php namespace App\Container;

use Doctrine\DBAL\Connection;

/**
 * @package App
 */
class AnotherDoctrineConnection extends Connection
{
}
  1. Settings for it
<?php namespace Settings;

use Dotenv\Dotenv;
use Limoncello\Contracts\Settings\SettingsInterface;

/**
 * @package Settings
 */
class AnotherDoctrine implements SettingsInterface
{
    const KEY_FILE_PATH = 0;

    /**
     * @inheritdoc
     */
    public function get(array $appConfig): array
    {
        (new Dotenv(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', '..'])))->load();

        $dbFile = getenv('DB_FILE');
        $dbPath = realpath(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'storage', $dbFile]));

        return [
            self::KEY_FILE_PATH => $dbPath,
        ];
    }
}
  1. Container configurator
<?php namespace App\Container;

use Doctrine\DBAL\DriverManager;
use Limoncello\Contracts\Application\ContainerConfiguratorInterface;
use Limoncello\Contracts\Container\ContainerInterface;
use Limoncello\Contracts\Settings\SettingsProviderInterface;
use Psr\Container\ContainerInterface as PsrContainerInterface;
use Settings\AnotherDoctrine;

/**
 * @package App
 */
class AnotherDoctrineConfigurator implements ContainerConfiguratorInterface
{
    /**
     * @inheritdoc
     */
    public static function configureContainer(ContainerInterface $container): void
    {
        $container[AnotherDoctrineConnection::class] = function (PsrContainerInterface $container) {
            list(AnotherDoctrine::KEY_FILE_PATH => $filePath) =
                $container->get(SettingsProviderInterface::class)->get(AnotherDoctrine::class);

            $connection = DriverManager::getConnection([
                'driver'   => 'pdo_sqlite',
                'user'     => '',
                'password' => '',
                'path'     => $filePath,
                'charset'  => 'UTF8',
            ]);

            return $connection;
        };
    }
}
  1. Now you can create it anywhere from container
        $connection = $container->get(AnotherDoctrineConnection::class);

@neomerx
Copy link
Collaborator

neomerx commented Nov 22, 2017

sorry, i found i shall change those 'DB_' suffixed environment variable to another one like 'DB_OAUTH_'... etc..
isnt' it?

You're free to use any prefixes you like 😉 And btw do you really need to put second file path into env variable? Can it be just set in settings?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants