Create new application class and improve application building process using bootstrappers

parent 124034cb
<?php
declare(strict_types=1);
namespace Longman\TelegramBot;
use Illuminate\Container\Container;
use Illuminate\Support\Str;
use const DIRECTORY_SEPARATOR;
class Application extends Telegram
{
protected $basePath;
protected $appPath;
protected $environmentPath;
protected $environmentFile = '.env';
protected $hasBeenBootstrapped = false;
public function __construct(string $basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$bootstrappers = [
\Longman\TelegramBot\Bootstrap\LoadEnvironmentVariables::class,
\Longman\TelegramBot\Bootstrap\SetupDatabase::class,
\Longman\TelegramBot\Bootstrap\SetupMigrations::class,
];
$this->bootstrapWith($bootstrappers);
parent::__construct(env('TG_API_KEY'), env('TG_BOT_NAME'));
}
protected function registerBaseBindings(): void
{
static::setInstance($this);
$this->instance('app', $this);
$this->instance(Container::class, $this);
}
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;
foreach ($bootstrappers as $bootstrapper) {
$this->make($bootstrapper)->bootstrap($this);
}
}
public function hasBeenBootstrapped(): bool
{
return $this->hasBeenBootstrapped;
}
public function setBasePath($basePath): Application
{
$this->basePath = rtrim($basePath, '\/');
$this->bindPathsInContainer();
return $this;
}
protected function bindPathsInContainer()
{
$this->instance('path', $this->path());
$this->instance('path.base', $this->basePath());
//$this->instance('path.config', $this->configPath());
}
public function path(string $path = ''): string
{
$appPath = $this->appPath ?: $this->basePath . DIRECTORY_SEPARATOR . 'app';
return $appPath . ($path ? DIRECTORY_SEPARATOR . $path : $path);
}
public function basePath(string $path = ''): string
{
return $this->basePath . ($path ? DIRECTORY_SEPARATOR . $path : $path);
}
public function environmentPath(): string
{
return $this->environmentPath ?: $this->basePath;
}
public function configPath(string $path = ''): string
{
return $this->basePath . DIRECTORY_SEPARATOR . 'config' . ($path ? DIRECTORY_SEPARATOR . $path : $path);
}
public function bootstrapPath(string $path = ''): string
{
return $this->basePath . DIRECTORY_SEPARATOR . 'bootstrap' . ($path ? DIRECTORY_SEPARATOR . $path : $path);
}
public function getCachedConfigPath(): string
{
return $_ENV['APP_CONFIG_CACHE'] ?? $this->bootstrapPath() . '/cache/config.php';
}
public function loadEnvironmentFrom($file): Application
{
$this->environmentFile = $file;
return $this;
}
public function environmentFile(): string
{
return $this->environmentFile ?: '.env';
}
public function environmentFilePath(): string
{
return $this->environmentPath() . DIRECTORY_SEPARATOR . $this->environmentFile();
}
public function environment(...$environments)
{
if (count($environments) > 0) {
$patterns = is_array($environments[0]) ? $environments[0] : $environments;
return Str::is($patterns, $this['env']);
}
return $this['env'];
}
public function isLocal(): bool
{
return $this['env'] === 'local';
}
}
<?php
declare(strict_types=1);
namespace Longman\TelegramBot\Bootstrap;
use Dotenv\Dotenv;
use Dotenv\Environment\Adapter\EnvConstAdapter;
use Dotenv\Environment\Adapter\PutenvAdapter;
use Dotenv\Environment\Adapter\ServerConstAdapter;
use Dotenv\Environment\DotenvFactory;
use Exception;
use Longman\TelegramBot\Application;
class LoadEnvironmentVariables
{
public function bootstrap(Application $app)
{
try {
$this->createDotenv($app)->load();
} catch (Exception $e) {
$this->writeErrorAndDie($e);
}
}
protected function createDotenv(Application $app): Dotenv
{
return Dotenv::create(
$app->environmentPath(),
$app->environmentFile(),
new DotenvFactory([new EnvConstAdapter, new ServerConstAdapter, new PutenvAdapter])
);
}
protected function writeErrorAndDie(Exception $e)
{
$message = 'The environment file is invalid! ' . $e->getMessage();
die($message);
}
}
<?php
declare(strict_types=1);
namespace Longman\TelegramBot\Bootstrap;
use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Database\ConnectionResolver;
use Illuminate\Events\Dispatcher;
use Longman\TelegramBot\Application;
use PDO;
use function array_filter;
use function env;
use function extension_loaded;
class SetupDatabase
{
public function bootstrap(Application $app)
{
$config = $this->getConfig();
$capsule = new Capsule($app);
$capsule->addConnection($config);
$capsule->setEventDispatcher(new Dispatcher($app));
$capsule->setAsGlobal();
// $capsule->bootEloquent();
$app->instance('db', $capsule);
$app->bind('db.connection', function ($app) {
return $app['db']->connection();
});
$app->bind('db.resolver', function ($app) {
$resolver = new ConnectionResolver(['default' => $app['db.connection']]);
$resolver->setDefaultConnection('default');
return $resolver;
});
$app->enableExternalMySql($capsule->getConnection()->getPdo());
}
private function getConfig(): array
{
return [
'driver' => 'mysql',
'host' => env('TG_DB_HOST', '127.0.0.1'),
'port' => env('TG_DB_PORT', '3306'),
'database' => env('TG_DB_DATABASE', 'forge'),
'username' => env('TG_DB_USERNAME', 'forge'),
'password' => env('TG_DB_PASSWORD', ''),
'unix_socket' => env('TG_DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => env('TG_DB_PREFIX', ''),
'prefix_indexes' => true,
'strict' => env('TG_DB_STRICT', false),
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
];
}
}
<?php
declare(strict_types=1);
namespace Longman\TelegramBot\Bootstrap;
use Illuminate\Database\Migrations\DatabaseMigrationRepository;
use Illuminate\Database\Migrations\MigrationCreator;
use Illuminate\Database\Migrations\Migrator;
use Illuminate\Filesystem\Filesystem;
use Longman\TelegramBot\Application;
use function env;
class SetupMigrations
{
public function bootstrap(Application $app)
{
$app->singleton('files', function (Application $app) {
return new Filesystem();
});
$app->singleton('migration.repository', function (Application $app) {
$table = env('TG_DB_MIGRATIONS_TABLE', 'migrations');
return new DatabaseMigrationRepository($app['db.resolver'], $table);
});
$app->singleton('migrator', function (Application $app) {
$repository = $app['migration.repository'];
return new Migrator($repository, $app['db.resolver'], $app['files']);
});
$app->singleton('migration.creator', function (Application $app) {
return new MigrationCreator($app['files']);
});
}
}
<?php
declare(strict_types=1);
namespace Longman\TelegramBot\Console;
use Illuminate\Console\Command;
use function array_merge;
use function collect;
use const DIRECTORY_SEPARATOR;
class MigrateCommand extends Command
{
protected $signature = 'migrate
{--path= : The path to the migrations files to be executed}
{--realpath : Indicate any provided migration file paths are pre-resolved absolute paths}
{--pretend : Dump the SQL queries that would be run}
{--step : Force the migrations to be run so they can be rolled back individually}';
protected $description = 'Run the database migrations';
/** @var \Illuminate\Database\Migrations\Migrator */
protected $migrator;
public function handle(): void
{
$app = $this->getApplication()->getLaravel();
$this->migrator = $app['migrator'];
$this->prepareDatabase();
$this->migrator->setOutput($this->output)
->run($this->getMigrationPaths(), [
'pretend' => $this->option('pretend'),
'step' => $this->option('step'),
]);
}
protected function prepareDatabase(): void
{
$this->migrator->setConnection('default');
if (! $this->migrator->repositoryExists()) {
$this->call('migrate:install');
}
}
protected function getMigrationPaths(): array
{
// Here, we will check to see if a path option has been defined. If it has we will
// use the path relative to the root of the installation folder so our database
// migrations may be run for any customized path from within the application.
if ($this->input->hasOption('path') && $this->option('path')) {
return collect($this->option('path'))->map(function ($path) {
return ! $this->usingRealPath()
? $this->laravel->basePath() . '/' . $path
: $path;
})->all();
}
return array_merge(
$this->migrator->paths(), [$this->getMigrationPath()]
);
}
protected function usingRealPath(): bool
{
return $this->input->hasOption('realpath') && $this->option('realpath');
}
protected function getMigrationPath(): string
{
return $this->laravel->basePath() . DIRECTORY_SEPARATOR . 'migrations';
}
}
<?php
declare(strict_types=1);
namespace Longman\TelegramBot\Console;
use Illuminate\Console\Command;
use Illuminate\Database\Migrations\DatabaseMigrationRepository;
class MigrateInstallCommand extends Command
{
protected $signature = 'migrate:install';
protected $description = 'Create the migration repository';
public function handle(): void
{
$app = $this->getApplication()->getLaravel();
$repository = new DatabaseMigrationRepository($app['db.resolver'], 'migrations');
$repository->createRepository();
$this->info('Migration table created successfully.');
}
}
<?php
declare(strict_types=1);
namespace Longman\TelegramBot\Database;
use Illuminate\Database\Migrations\Migration as BaseMigration;
use Illuminate\Database\Schema\Builder;
use function app;
abstract class Migration extends BaseMigration
{
public function getSchemaBuilder(): Builder
{
return app('db')->connection($this->getConnection())->getSchemaBuilder();
}
}
<?php
declare(strict_types=1);
use Dotenv\Environment\Adapter\EnvConstAdapter;
use Dotenv\Environment\Adapter\PutenvAdapter;
use Dotenv\Environment\Adapter\ServerConstAdapter;
use Dotenv\Environment\DotenvFactory;
use Illuminate\Container\Container;
use PhpOption\Option;
if (! function_exists('value')) {
function value($value)
{
return $value instanceof Closure ? $value() : $value;
}
}
if (! function_exists('env')) {
function env($key, $default = null)
{
static $variables;
if ($variables === null) {
$variables = (new DotenvFactory([new EnvConstAdapter, new PutenvAdapter, new ServerConstAdapter]))->createImmutable();
}
return Option::fromValue($variables->get($key))
->map(function ($value) {
switch (strtolower($value)) {
case 'true':
case '(true)':
return true;
case 'false':
case '(false)':
return false;
case 'empty':
case '(empty)':
return '';
case 'null':
case '(null)':
return;
}
if (preg_match('/\A([\'"])(.*)\1\z/', $value, $matches)) {
return $matches[2];
}
return $value;
})
->getOrCall(function () use ($default) {
return value($default);
});
}
}
if (! function_exists('app')) {
function app($abstract = null, array $parameters = [])
{
if (is_null($abstract)) {
return Container::getInstance();
}
return Container::getInstance()->make($abstract, $parameters);
}
}
#!/usr/bin/env php
<?php
declare(strict_types=1);
require __DIR__.'/vendor/autoload.php';
use Longman\TelegramBot\Application;
use Illuminate\Events\Dispatcher;
use Illuminate\Console\Application as ConsoleApplication;
$container = new Application(getcwd());
$events = new Dispatcher($container);
$console = new ConsoleApplication($container, $events, 'Version 1');
$console->setName('TelegramBot CLI');
// Bind a command
$console->resolve(Longman\TelegramBot\Console\MigrateCommand::class);
$console->resolve(Longman\TelegramBot\Console\MigrateInstallCommand::class);
$console->run();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment