Im new to Lumen, I have managed to create and register the following ArangoDB service provider along with the service below and got it to work, but Im confused how I actually use them in another service or helper.
registered the provider in bootstrap/app.php
$app->register(App\Providers\ArangoServiceProvider::class);
ArangoServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ArangoServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('ArangoService', function ($app) {
return new ArangoService($app);
});
}
public function boot()
{
//
}
}
ArangoService.php
<?
namespace App\Services;
use ArangoDBClient\Collection as ArangoCollection;
use ArangoDBClient\CollectionHandler as ArangoCollectionHandler;
use ArangoDBClient\Connection as ArangoConnection;
use ArangoDBClient\ConnectionOptions as ArangoConnectionOptions;
use ArangoDBClient\DocumentHandler as ArangoDocumentHandler;
use ArangoDBClient\Document as ArangoDocument;
use ArangoDBClient\Exception as ArangoException;
use ArangoDBClient\Export as ArangoExport;
use ArangoDBClient\ConnectException as ArangoConnectException;
use ArangoDBClient\ClientException as ArangoClientException;
use ArangoDBClient\ServerException as ArangoServerException;
use ArangoDBClient\Statement as ArangoStatement;
use ArangoDBClient\UpdatePolicy as ArangoUpdatePolicy;
class ArangoService{
public function __construct() {
$connectionOptions = [
// database name
ArangoConnectionOptions::OPTION_DATABASE => 'dbname',
// server endpoint to connect to
ArangoConnectionOptions::OPTION_ENDPOINT => 'tcp://123456789',
// authorization type to use (currently supported: 'Basic')
ArangoConnectionOptions::OPTION_AUTH_TYPE => 'Basic',
// user for basic authorization
ArangoConnectionOptions::OPTION_AUTH_USER => 'root',
// password for basic authorization
ArangoConnectionOptions::OPTION_AUTH_PASSWD => 'passwd',
// connection persistence on server. can use either 'Close' (one-time connections) or 'Keep-Alive' (re-used connections)
ArangoConnectionOptions::OPTION_CONNECTION => 'Keep-Alive',
// connect timeout in seconds
ArangoConnectionOptions::OPTION_TIMEOUT => 3,
// whether or not to reconnect when a keep-alive connection has timed out on server
ArangoConnectionOptions::OPTION_RECONNECT => true,
// optionally create new collections when inserting documents
ArangoConnectionOptions::OPTION_CREATE => true,
// optionally create new collections when inserting documents
ArangoConnectionOptions::OPTION_UPDATE_POLICY => ArangoUpdatePolicy::LAST,
];
// turn on exception logging (logs to whatever PHP is configured)
ArangoException::enableLogging();
$connection = new ArangoConnection($connectionOptions);
$collectionHandler = new ArangoCollectionHandler($connection);
$handler = new ArangoDocumentHandler($connection);
}
public function main(){
print "hello world!<br/>";
}
}
This all above works, but how do I in a controller, call a service, lets call it "UsersService" that has functions like GetUser() , how does this "UsersService" use the $connection, $collectionHandler and $handler , that I have created in my ArangoDB service.
route pointing to a controller (got that one)
controller calling UsersService
UsersService using the arango service and passing something back to the controller
grateful for any help!
Related
I am trying to use this package to push notifications to users via OneSignal. However I needed to make a little change. My API serves two (related) apps and I have two OneSignal configs. I am trying to override its ServiceProvider (using this technique).
The ServiceProvider presents itself as follows
<?php
namespace NotificationChannels\OneSignal;
use Berkayk\OneSignal\OneSignalClient;
use Illuminate\Support\ServiceProvider;
use NotificationChannels\OneSignal\Exceptions\InvalidConfiguration;
class OneSignalServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*/
public function boot()
{
$this->app->when(OneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
}
}
The behavior that I want to change is located in the line
$oneSignalConfig = config('services.onesignal');
As it assumes that my config/services.php has the following entry (stated in the doc) :
// config/services.php
...
'onesignal' => [
'app_id' => env('ONESIGNAL_APP_ID'),
'rest_api_key' => env('ONESIGNAL_REST_API_KEY')
],
...
Whereas I want to set my config/services.php as follows
// config/services.php
...
'onesignal' => [
'app1' => [
'app_id' => env('ONESIGNAL_1_APP_ID'),
'rest_api_key' => env('ONESIGNAL_1_REST_API_KEY')
],
'app2' => [
'app_id' => env('ONESIGNAL_2_APP_ID'),
'rest_api_key' => env('ONESIGNAL_2_REST_API_KEY')
],
],
...
And I want somehow to tell my ServiceProvider (through some kind of parameter) to either do
$oneSignalConfig = config('services.onesignal.app1');
OR
$oneSignalConfig = config('services.onesignal.app2');
But I didn't find any way to pass a parameter to the class, the boot function or the give method (and if I understood well I shouldn't even be doing that).
The only way I could think of is to create two classes that extend the OneSignalChannel::class
and duplicate code in the boot function so it becomes as follows :
public function boot()
{
$this->app->when(FirstOneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal.app1');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
$this->app->when(SecondOneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal.app2');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
}
The difference in the when provoking a difference in the config but it seems a lot of duplication and not extensible (what if I had three apps).
Should I use this method, or is there a way to pass a parameter to this ServiceProvider or is there another solution ?
https://stackoverflow.com/a/34224082/10371024
I could see what you need, but to pass parameter to boot method is not a good idea according to Laravel architecture. You may try to get what you want with using events as Vladislav suggested.
I have recently been learning about the AppServiceProvider. I have registered a service in the AppServiceProvider which creates a singleton - an instantiated GuzzleHttp Client, like so:
$this->app->singleton('GuzzleHttp\Client', function($api) {
return new Client([
'base_uri' => env('ELASTICSEARCH_HOST'),
'auth' => [
env('ELASTICSEARCH_USER'),
env('ELASTICSEARCH_PASS')
],
]);
});
This is connecting to an ElasticSearch API, and that currently works:
$response = app('GuzzleHttp\Client')->request('GET');
I have set up a facade called ElasticSearchFacade, which contains only the getFacadeAccessor():
protected static function getFacadeAccessor()
{
return 'elasticSearch';
}
I have also registered elasticSearch in my AppServiceProvider, like so:
$this->app->bind('elasticSearch', function() {
return new ElasticSearch();
});
This creates a new ElasticSearch instance. However, I would love to pass the GuzzleHttp\Client into the elasticSearch service. So I have tried adding the following to my ElasticSearch.php file:
use GuzzleHttp\Client;
class ElasticSearch
{
protected $client;
public function __contruct(Client $client)
{
$this->client = $client;
}
public function handle()
{
$response = $this->client->request('GET');
die($response->getBody()->getContents());
}
}
I have now changed the registered service to pass through the GuzzleHttp Client like so:
$this->app->bind('elasticSearch', function() {
return new ElasticSearch(app('GuzzleHttp\Client'));
});
However I am getting the error:
PHP Error: Call to a member function request() on null
The constructor method is __construct not __contruct. You have not defined a custom constructor for your ElasticSearch class. So that member variable is null.
Side Note: do not call env outside of the configuration files.
To avoid having to make these env calls outside of configuration files you can just add configuration files as needed or add to current configuration files. Something like Elastic Search credentials can probably get added to the services.php configuration file:
<?php
return [
...
'elasticsearch' => [
'host' => env('ELASTICSEARCH_HOST'),
'user' => env('ELASTICSEARCH_USER'),
'password' => env('ELASTICSEARCH_PASS'),
],
...
];
Now that you have these in the configuration you can use the configuration system to pull these values:
config('services.elasticsearch'); // that whole array of values
config('services.elasticsearch.host'); // just that host value
Config::get('services.elasticsearch');
app('config')->get(...);
There are multiple ways to access the configuration system.
We are using Doctrine 2 in our app, but due to our infrastructure, we do not have a static configuration for database connections. Instead, we have a collection of singletons in a service provider for each database we need to connect to, and we select a random database host for then when we connect.
Unfortunately, we are seeing some performance degradation in Doctrine's getRepository() function. I believe the issue is that Doctrine needs to generate its proxy classes at runtime (even in production) because we cannot figure out how to configure the CLI tools in order to create them at build time.
We are using the Laravel framework for the application.
Here's an example of our Laravel service provider which makes the repositories available for dependency injection.
<?php
use App\Database\Doctrine\Manager as DoctrineManager;
use Proprietary\ConnectionFactory;
use App\Database\Entities;
use App\Database\Repositories;
use App\Database\Constants\EntityConstants;
class DoctrineServiceProvider extends ServiceProvider
{
// Create a singleton for the Doctrine Manager. This class will handle entity manager generation.
$this->app->singleton(DoctrineManager::class, function ($app)
{
return new DoctrineManager(
$app->make(ConnectionFactory::class),
[
EntityConstants::ENTITY_CLASS_DATABASE1 => [app_path('Database/Entities/Database1')],
EntityConstants::ENTITY_CLASS_DATABASE2 => [app_path('Database/Entities/Database2')],
],
config('app.debug'),
$this->app->make(LoggerInterface::class)
);
});
// Register the first repository
$this->app->singleton(Repositories\Database1\RepositoryA1::class, function ($app)
{
return $app[DoctrineManager::class]
->getEntityManager(EntityConstants::ENTITY_CLASS_DATABASE1)
->getRepository(Entities\Database1\RepositoryA1::class);
});
// Register the second repository
$this->app->singleton(Repositories\Database1\RepositoryA2::class, function ($app)
{
return $app[DoctrineManager::class]
->getEntityManager(EntityConstants::ENTITY_CLASS_DATABASE1)
->getRepository(Entities\Database1\RepositoryA2::class);
});
// Register a repository for the second database
$this->app->singleton(Repositories\Database2\RepositoryB1::class, function ($app)
{
return $app[DoctrineManager::class]
->getEntityManager(EntityConstants::ENTITY_CLASS_DATABASE2)
->getRepository(Entities\Database2\RepositoryB1::class);
});
}
Here's the class that generates EntityManagers for Doctrine:
<?php
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Connections\MasterSlaveConnection;
use Proprietary\ConnectionFactory;
class Manager
{
private $c_factory;
private $paths;
private $connections = [];
private $entity_managers = [];
public function __construct(
ConnectionFactory $cf,
array $paths
)
{
$this->c_factory = $cf;
$this->paths = $paths;
}
public function getConnection($name, $partition = false, $region = false)
{
// Get a list of servers for this database and format them for use with Doctrine
$servers = self::formatServers($name, $this->c_factory->getServers($name, true, $partition, $region));
// Generate a connection for the entity manager using the servers we have.
$connection = DriverManager::getConnection(
array_merge([
'wrapperClass' => MasterSlaveConnection::class,
'driver' => 'pdo_mysql',
], $servers)
);
return $connection;
}
public function getEntityManager($name, $partition = false, $region = false)
{
// Should these things be cached somehow at build time?
$config = Setup::createAnnotationMetadataConfiguration($this->paths[$name], false);
$config->setAutoGenerateProxyClasses(true);
// Set up the connection
$connection = $this->getConnection($name, $partition, $region);
$entity_manager = EntityManager::create($connection, $config);
return $entity_manager;
}
// Converts servers from a format provided by our proprietary code to a format Doctrine can use.
private static function formatServers($db_name, array $servers)
{
$doctrine_servers = [
'slaves' => [],
];
foreach ($servers as $server)
{
// Format for Doctrine
$server = [
'user' => $server['username'],
'password' => $server['password'],
'host' => $server['hostname'],
'dbname' => $db_name,
'charset' => 'utf8',
];
// Masters can also be used as slaves.
$doctrine_servers['slaves'][] = $server;
// Servers are ordered by which is closest, and Doctrine only allows a
// single master, so if we already set one, don't overwrite it.
if ($server['is_master'] && !isset($doctrine_servers['master']))
{
$doctrine_servers['master'] = $server;
}
}
return $doctrine_servers;
}
}
Our service classes use dependency injection to get the repository singletons defined in the service provider. When we use the singletons for the first time, Doctrine will use the entity class defined in the service provider and get the connection associated with the repository.
Is there any way we can enable the CLI tools with this configuration? Are there any other ways that we can optimize this for use in production?
Thanks.
I was able to solve the problem thanks to a suggestion from the Doctrine IRC channel. Since the CLI tools can only handle a single database, I created a doctrine-cli directory containing a base-config.php file and a subdirectory for each of the databases we use.
Here's an example file structure:
doctrine-cli/
|- database1/
| |- cli-config.php
|- database2/
| |- cli-config.php
|- base-config.php
The base-config.php file looks like this:
<?php
use Symfony\Component\Console\Helper\HelperSet;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
use App\Database\Doctrine\Manager as DoctrineManager;
use Proprietary\ConnectionFactory;
require __DIR__ . '/../bootstrap/autoload.php';
class DoctrineCLIBaseConfig
{
private $helper_set;
public function __construct($entity_constant, $entity_namespace)
{
$app = require_once __DIR__ . '/../bootstrap/app.php';
// Proprietary factory for getting our databse details
$connection_factory = new ConnectionFactory(...);
// Our class that parses the results from above and handles our Doctrine connection.
$manager = new DoctrineManager(
$connection_factory,
[$entity_constant => [app_path('Database/Entities/' . $entity_namespace)]],
false,
null,
null
);
$em = $manager->getEntityManager($entity_constant);
$this->helper_set = new HelperSet([
'db' => new ConnectionHelper($em->getConnection()),
'em' => new EntityManagerHelper($em),
]);
}
public function getHelperSet()
{
return $this->helper_set;
}
}
Here's an example cli-config.php from the database directory:
<?php
use App\Database\Constants\EntityConstants;
require __DIR__ . "/../base-config.php";
$config = new DoctrineCLIBaseConfig(
EntityConstants::ENTITY_CLASS_DATABASE1,
"database1"
);
return $config->getHelperSet();
Now, I'm able to cycle through each of the directories and run commands like so:
php ../../vendor/bin/doctrine orm:generate-proxies
For our build process, I wrote a simple shell script that cycles through the directories and runs the orm:generate-proxies command.
I have a question regarding Authentication in Laravel 5.x. I’ve been specifically looking at tymondesigns/jwt-auth and irazasyed/jwt-auth-guard packages to do the JSON web token authentication token handling in my Laravel application.
I am not using a local database whatsoever, nor do I want to. I have environment variables set up in .env for my API’s URL, USERNAME & PASSWORD. The Guzzle PHP HTTP client is doing the trick just fine, connecting and returning data between the API and my application as needed.
However, I need to set up Authentication within my Laravel instance. I run into problems here, and the auth is wanting a DB connection.
$token = JWTAuth::attempt($credentials)
Here's the exception:
PDOException in Connector.php line 55:
SQLSTATE[HY000] [14] unable to open database file
How can I make use of JWT without using a database?
How can I COMPLETELY shut-off database connections within Laravel?
Thanks.
UPDATE:
Using tymon/jwt-auth, I've set things up within the routes, Kernel, Middleware, etc.
I created a "claim" successfully, but I need to create the token by encoding the "payload."
$this->username = $request->username;
$sub = $this->username;
$iat = time();
$jti = md5($sub . $iat);
$aud = env('APP_URL');
$this->claims = [
'sub' => $sub,
'iat' => $iat,
'exp' => time() + (2 * 7 * 24 * 60 * 60),
'nbf' => $iat,
'iss' => 'khill',
'jti' => $jti,
'aud' => $aud,
];
$payload = JWTFactory::make($this->claims);
How do I get the custom token?
You should define a custom Authentication Provider and set it in config/jwt.php.
Example of provider
Put this class anywhere you like.
namespace MyNamespace;
use Tymon\JWTAuth\Providers\Auth\AuthInterface;
class MyCustomAuthenticationProvider implements AuthInterface
{
public function byCredentials(array $credentials = [])
{
return $credentials['username'] == env('USERNAME') && $credentials['password'] == env('PASSWORD');
}
public function byId($id)
{
// maybe throw an expection?
}
public function user()
{
// you will have to implement this maybe.
}
}
Example of configuration
In the providers array in config/jwt.php, change this:
'auth' => 'Tymon\JWTAuth\Providers\Auth\IlluminateAuthAdapter',
to this:
'auth' => 'MyNamespace\MyCustomAuthenticationProvider',
Other considerations
Using the env() function anywhere is not good practice. It's better to use it in your config files, and then use the config() function anywhere else.
You may need to reimplement also the User Provider.
JWTAuth::attempt() won't help you with this, because it hits the database for you behind the scenes. You need some other way to check the environment credentials.
Add a custom method to a class somewhere which will do that for you or pass the credentials against the API you are hitting with Guzzle.
Code example:
public function authenticate($username, $password)
{
if(!$username === env('USERNAME') or !$password === env('PASSWORD')) {
// return a message that the user could not be authenticated or false.
}
// Generate the JWT token here and store it somewhere.
}
As a quick fix I decided to implement the following custom code...
1) Created custom middleware to handle the logic.
class CustomMiddleware
{
protected $loginPath = 'login';
public function handle($request, Closure $next) {
$logged_in = $request->session()->get('logged_in');
if (!$logged_in) {
return redirect()->guest('login')->with('flag','1');
}
return $next($request);
}
}
2) Added a reference to the middleware class.
class Kernel extends HttpKernel
{
protected $routeMiddleware = [
'custom' => \App\Http\Middleware\CustomMiddleware::class,
];
}
3) Added it to routes.php.
Route::group(['middleware' => ['custom']], function () {
// Add routes here
}
yes.
you can create jwt token without database using tymondesigns/jwt-auth package...
for that you have to use jwt::encode method...
let me explain ...
first you have to put your credential in .env file...
then i am recomending you to use custom claims ...
after that you can create jwt token using below code ...
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
$factory = JWTFactory::customClaims($customClaims);
$token = JWTAuth::encode($payload);
for further details you can refer below link
wiki
I have setup a security firewall and a SuccessHandler for my application. The relevant snippets are:
$app -> register(new SecurityServiceProvider(), array(
'security.firewalls' => array(
'auth' => array(
'pattern' => "^/auth",
'http' => true,
'users' => $app -> share(function() use ($app) {
return new \Model\Manager\Account($app);
})
)
'security.access_rules' => array(
array('^/auth.*$', 'ROLE_USER')
),
));
$app['security.authentication.success_handler.auth'] = $app -> share(function() use ($app) {
return new Handlers\Authentication\Auth\SuccessHandler($app['security.http_utils'], array(), $app);
});
The 'auth' has got the 'http' authentication set to true and indeed when I go to the url 'http://myserver/auth' I get a Basic Authentication challenge.
However when I log in correctly I get the page that I wanted, but I have not gone via the SuccessHandler that I have setup. Is this supported when using HTTP auth or only when using form based authentication?
If it is not supported is there a way I can achieve the same thing? I have been looking at EventSubscriber but I did not know how to wire this up in Silex to listen for the appropriate event.
Thanks, Russell
UPDATE:
My SuccessHandler has the following.
```
<?php
namespace Handlers\Authentication\Auth;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Silex\Application;
class SuccessHandler extends DefaultAuthenticationSuccessHandler {
protected $app = null;
public function __construct(HttpUtils $httpUtils, array $options, Application $app) {
parent::__construct($httpUtils, $options);
$this -> app = $app;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token) {
// get the user from the token
$user = $token -> getUser();
dump($user);
exit;
// redirect user to the page they requested
return $this -> httpUtils -> createRedirectResponse($request, $this -> determineTargetUrl($request));
}
}
```
As you can see all I am trying to do is show the user details and then exit out. I know this is not what I would normally do but I am trying to make sure the onAuthenticationSuccess gets called, which it is not, although authentication is working.