Skip to content
This repository was archived by the owner on Feb 7, 2024. It is now read-only.

Add Async MySQL support #766

Merged
merged 2 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"cboden/ratchet": "^0.4.1",
"clue/block-react": "^1.4",
"clue/reactphp-sqlite": "^1.0",
"react/mysql": "^0.5",
"clue/redis-react": "^2.3",
"doctrine/dbal": "^2.9",
"evenement/evenement": "^2.0|^3.0",
Expand Down
17 changes: 17 additions & 0 deletions config/websockets.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,27 @@
| The SQLite database to use when using the SQLite application manager.
|
*/

'sqlite' => [
'database' => storage_path('laravel-websockets.sqlite'),
],

/*
|--------------------------------------------------------------------------
| MySql application manager
|--------------------------------------------------------------------------
|
| The MySql database to use when using the MySql application manager.
|
*/

'mysql' => [
'host' => 'localhost',
'port' => 3306,
'database' => 'default',
'username' => 'root',
'password' => 'root',
]
],

/*
Expand Down
12 changes: 12 additions & 0 deletions database/migrations/mysql/0000_00_00_000000_create_apps_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS apps (
`id` VARCHAR(255) NOT NULL,
`key` VARCHAR(255) NOT NULL,
`secret` VARCHAR(255) NOT NULL,
`name` VARCHAR(255) NOT NULL,
`host` VARCHAR(255),
`path` VARCHAR(255),
`enable_client_messages` BOOLEAN DEFAULT 0,
`enable_statistics` BOOLEAN DEFAULT 1,
`capacity` INT,
`allowed_origins` VARCHAR(255)
)
166 changes: 166 additions & 0 deletions src/Apps/MysqlAppManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

namespace BeyondCode\LaravelWebSockets\Apps;

use BeyondCode\LaravelWebSockets\Contracts\AppManager;
use React\MySQL\ConnectionInterface;
use React\MySQL\QueryResult;
use React\Promise\Deferred;
use React\Promise\PromiseInterface;

class MysqlAppManager implements AppManager
{
/**
* The database connection.
*
* @var ConnectionInterface
*/
protected $database;

/**
* Initialize the class.
*
* @param ConnectionInterface $database
*/
public function __construct(ConnectionInterface $database)
{
$this->database = $database;
}

/**
* Get all apps.
*
* @return PromiseInterface
*/
public function all(): PromiseInterface
{
$deferred = new Deferred();

$this->database->query('SELECT * FROM `apps`')
->then(function (QueryResult $result) use ($deferred) {
$deferred->resolve($result->resultRows);
}, function ($error) use ($deferred) {
$deferred->reject($error);
});

return $deferred->promise();
}

/**
* Get app by id.
*
* @param string|int $appId
* @return PromiseInterface
*/
public function findById($appId): PromiseInterface
{
$deferred = new Deferred();

$this->database->query('SELECT * from `apps` WHERE `id` = ?', [$appId])
->then(function (QueryResult $result) use ($deferred) {
$deferred->resolve($this->convertIntoApp($result->resultRows[0]));
}, function ($error) use ($deferred) {
$deferred->reject($error);
});

return $deferred->promise();
}

/**
* Get app by app key.
*
* @param string $appKey
* @return PromiseInterface
*/
public function findByKey($appKey): PromiseInterface
{
$deferred = new Deferred();

$this->database->query('SELECT * from `apps` WHERE `key` = ?', [$appKey])
->then(function (QueryResult $result) use ($deferred) {
$deferred->resolve($this->convertIntoApp($result->resultRows[0]));
}, function ($error) use ($deferred) {
$deferred->reject($error);
});

return $deferred->promise();
}

/**
* Get app by secret.
*
* @param string $appSecret
* @return PromiseInterface
*/
public function findBySecret($appSecret): PromiseInterface
{
$deferred = new Deferred();

$this->database->query('SELECT * from `apps` WHERE `secret` = ?', [$appSecret])
->then(function (QueryResult $result) use ($deferred) {
$deferred->resolve($this->convertIntoApp($result->resultRows[0]));
}, function ($error) use ($deferred) {
$deferred->reject($error);
});

return $deferred->promise();
}

/**
* Map the app into an App instance.
*
* @param array|null $app
* @return \BeyondCode\LaravelWebSockets\Apps\App|null
*/
protected function convertIntoApp(?array $appAttributes): ?App
{
if (! $appAttributes) {
return null;
}

$app = new App(
$appAttributes['id'],
$appAttributes['key'],
$appAttributes['secret']
);

if (isset($appAttributes['name'])) {
$app->setName($appAttributes['name']);
}

if (isset($appAttributes['host'])) {
$app->setHost($appAttributes['host']);
}

if (isset($appAttributes['path'])) {
$app->setPath($appAttributes['path']);
}

$app
->enableClientMessages((bool) $appAttributes['enable_client_messages'])
->enableStatistics((bool) $appAttributes['enable_statistics'])
->setCapacity($appAttributes['capacity'] ?? null)
->setAllowedOrigins(array_filter(explode(',', $appAttributes['allowed_origins'])));

return $app;
}

/**
* @inheritDoc
*/
public function createApp($appData): PromiseInterface
{
$deferred = new Deferred();

$this->database->query(
'INSERT INTO `apps` (id, key, secret, name, enable_client_messages, enable_statistics, allowed_origins) VALUES (?, ?, ?, ?, ?, ?, ?)',
[$appData['id'], $appData['key'], $appData['secret'], $appData['name'], $appData['enable_client_messages'], $appData['enable_statistics'], $appData['allowed_origins']])
->then(function () use ($deferred) {
$deferred->resolve();
}, function ($error) use ($deferred) {
$deferred->reject($error);
});

return $deferred->promise();
}
}
18 changes: 14 additions & 4 deletions src/Apps/SQLiteAppManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ public function all(): PromiseInterface

$this->database->query('SELECT * FROM `apps`')
->then(function (Result $result) use ($deferred) {
return $deferred->resolve($result->rows);
$deferred->resolve($result->rows);
}, function ($error) use ($deferred) {
$deferred->reject($error);
});

return $deferred->promise();
Expand All @@ -56,7 +58,9 @@ public function findById($appId): PromiseInterface

$this->database->query('SELECT * from apps WHERE `id` = :id', ['id' => $appId])
->then(function (Result $result) use ($deferred) {
return $deferred->resolve($this->convertIntoApp($result->rows[0]));
$deferred->resolve($this->convertIntoApp($result->rows[0]));
}, function ($error) use ($deferred) {
$deferred->reject($error);
});

return $deferred->promise();
Expand All @@ -74,7 +78,9 @@ public function findByKey($appKey): PromiseInterface

$this->database->query('SELECT * from apps WHERE `key` = :key', ['key' => $appKey])
->then(function (Result $result) use ($deferred) {
return $deferred->resolve($this->convertIntoApp($result->rows[0]));
$deferred->resolve($this->convertIntoApp($result->rows[0]));
}, function ($error) use ($deferred) {
$deferred->reject($error);
});

return $deferred->promise();
Expand All @@ -92,7 +98,9 @@ public function findBySecret($appSecret): PromiseInterface

$this->database->query('SELECT * from apps WHERE `secret` = :secret', ['secret' => $appSecret])
->then(function (Result $result) use ($deferred) {
return $deferred->resolve($this->convertIntoApp($result->rows[0]));
$deferred->resolve($this->convertIntoApp($result->rows[0]));
}, function ($error) use ($deferred) {
$deferred->reject($error);
});

return $deferred->promise();
Expand Down Expand Up @@ -150,6 +158,8 @@ public function createApp($appData): PromiseInterface
', $appData)
->then(function (Result $result) use ($deferred) {
$deferred->resolve();
}, function ($error) use ($deferred) {
$deferred->reject($error);
});

return $deferred->promise();
Expand Down
30 changes: 30 additions & 0 deletions src/WebSocketsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
use Illuminate\Support\ServiceProvider;
use React\EventLoop\Factory;
use React\EventLoop\LoopInterface;
use React\MySQL\ConnectionInterface;
use React\MySQL\Factory as MySQLFactory;
use SplFileInfo;
use Symfony\Component\Finder\Finder;

Expand Down Expand Up @@ -50,6 +52,8 @@ public function boot()

$this->registerSQLiteDatabase();

$this->registerMySqlDatabase();

$this->registerAsyncRedisQueueDriver();

$this->registerRouter();
Expand Down Expand Up @@ -116,6 +120,32 @@ protected function registerSQLiteDatabase()
});
}

protected function registerMySqlDatabase()
{
$this->app->singleton(ConnectionInterface::class, function () {
$factory = new MySQLFactory($this->app->make(LoopInterface::class));

$auth = trim(config('websockets.managers.mysql.username').':'.config('websockets.managers.mysql.password'), ':');
$connection = trim(config('websockets.managers.mysql.host').':'.config('websockets.managers.mysql.port'), ':');
$database = config('websockets.managers.mysql.database');

$database = $factory->createLazyConnection(trim("{$auth}@{$connection}/{$database}", '@'));

$migrations = (new Finder())
->files()
->ignoreDotFiles(true)
->in(__DIR__.'/../database/migrations/mysql')
->name('*.sql');

/** @var SplFileInfo $migration */
foreach ($migrations as $migration) {
$database->query($migration->getContents());
}

return $database;
});
}

/**
* Register the statistics-related contracts.
*
Expand Down