I want to execute ZF3 action with zf-console.
I can do this using zend-mvc-console module and it works fine.
For example.
Application/config/module.config.php:
'console' => [
'router' => [
'routes' => [
'cronroute' => [
'options' => [
'route' => 'sync',
'defaults' => [
'controller' => Controller\ConsoleController::class,
'action' => 'syncEvents'
]
]
]
]
]
],
Application/src/Controller/ConsoleController.php
class ConsoleController extends AbstractActionController
{
/**
* Entity manager.
* #var Doctrine\ORM\EntityManager
*/
private $entityManager;
/**
* User Manager
* #var Application\Service\UserManager
*/
private $userManager;
/**
* Constructor.
*/
public function __construct($entityManager, $userManager)
{
$this->entityManager = $entityManager;
$this->userManager = $userManager;
}
public function syncAction()
{
$response = $this->userManager->syncUserInfo();
return $response ? 'Sync Success' : 'Failed to sync';
}
}
But it says that it will be deprecated:
https://zendframework.github.io/zend-mvc-console/intro/#deprecated
It suggest to use zf-console from zfcampus:
https://github.com/zfcampus/zf-console
But I cannot find a way to execute Controller action or to use my build services (like UserManager).
There is example to build Zend Application and retrieve Service manager:
use Zend\Console\Console;
use Zend\Console\ColorInterface as Color;
use ZF\Console\Application;
use ZF\Console\Dispatcher;
chdir(dirname(__DIR__));
require __DIR__ . '/../vendor/autoload.php'; // Composer autoloader
$application = Zend\Mvc\Application::init(require 'config/application.config.php');
$services = $application->getServiceManager();
$buildModel = $services->get('My\BuildModel');
Is there a way to execute Controller action with it? Or Can I load my UserManager service?
I tried to get My UserManager:
$buildModel = $services->get('Application\Service\UserManager');
But receiving error:
PHP Fatal error: Uncaught exception 'Zend\ServiceManager\Exception\ServiceNotFoundException' with message 'Unable to resolve service "Application\Service\UserManager" to a factory; are you certain you provided it during configuration?' in /var/www/html/vendor/zendframework/zend-servicemanager/src/ServiceManager.php:687
The zend-mvc-console module does seem to be on the edge of deprecation. Just like you I was trying to implement zfcampus/zf-console. Since the mvc-console module seems to be (almost) deprecated, I suggest you use something different than (mvc) controllers for your console work. I used a class that can handle the call (in a way zf-console expects).
This is a dummy example I was working on for my project;
This is script that is called on the command line:
use Zend\Console\Console;
use Zend\ServiceManager\ServiceManager;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\Glob;
use ZF\Console\Application;
use ZF\Console\Dispatcher;
require_once __DIR__ . '/vendor/autoload.php'; // Composer autoloader
$configuration = [];
foreach (Glob::glob('config/{{*}}{{,*.local}}.php', Glob::GLOB_BRACE) as $file) {
$configuration = ArrayUtils::merge($configuration, include $file);
}
// Prepare the service manager
$smConfig = isset($config['service_manager']) ? $configuration['service_manager'] : [];
$smConfig = new \Zend\Mvc\Service\ServiceManagerConfig($smConfig);
$serviceManager = new ServiceManager();
$smConfig->configureServiceManager($serviceManager);
$serviceManager->setService('ApplicationConfig', $configuration);
// Load modules
$serviceManager->get('ModuleManager')->loadModules();
$routes = [
[
'name' => 'dumb',
'route' => '[--foo=]',
'description' => 'Some really cool feature',
'short_description' => 'Cool feature',
'options_descriptions' => [
'foo' => 'Lorem Ipsum',
],
'defaults' => [
'foo' => 'bar',
],
'handler' => function($route, $console) use ($serviceManager) {
$handler = new \Application\Command\DumbCommand();
return $handler($route, $console);
}
],
];
$config = $serviceManager->get('config');
$application = new Application(
$config['app'],
$config['version'],
$routes,
Console::getInstance(),
new Dispatcher()
);
$exit = $application->run();
exit($exit);
The handler function can use the service manager to inject any dependencies to the command handler:
'handler' => function($route, $console) use ($serviceManager) {
/** #var \Doctrine\ORM\EntityManager $entityManager */
$entityManager = $serviceManager->get(\Doctrine\ORM\EntityManager::class);
/** #var mixed $repository */
$contactRepository = $entityManager->getRepository(\Application\Entity\Contact::class);
$handler = new \Application\Command\DumbCommand($contactRepository);
return $handler($route, $console);
}
The command class is placed in a Command folder, it looks like:
<?php
namespace Application\Command;
use Application\Entity\Contact;
use Application\Repository\ContactRepository;
use Zend\Console\Adapter\AdapterInterface;
use ZF\Console\Route;
class DumbCommand
{
/** #var ContactRepository */
private $contactRepository;
public function __construct($contactRepository)
{
$this->contactRepository = $contactRepository;
}
/**
* #param Route $route
* #param AdapterInterface $console
* #throws \Doctrine\ORM\ORMException
*/
public function __invoke(Route $route, AdapterInterface $console)
{
$console->writeLine('Bob was here');
foreach ($this->contactRepository->findAll() as $item) {
/** #var Contact $item */
$console->writeLine($item->getFirstName() . ' was here');
}
}
}
(
This is my solution:
I addedd console command routes to my module.config.php files
'console' => array(
'commands' => array(
array(
'name' => 'sendemail',
'handler' => PostCommand::class,
),
array(
'name' => 'sendsms',
'handler' => SmsTransferCommand::class,
)
)
),
I created a console.php in /public (this will be run with arguments to start a CLI app)
use Zend\Console\Console;
use Zend\ServiceManager\ServiceManager;
use ZF\Console\Application;
use ZF\Console\Dispatcher;
chdir(dirname(__DIR__));
require_once 'vendor/autoload.php'; // Composer autoloader
// Prepare application and service manager
$appConfig = require 'config/application.config.php';
$application = Zend\Mvc\Application::init($appConfig);
$serviceManager = $application->getServiceManager();
// Load modules
$serviceManager->get('ModuleManager')->loadModules();
$config = $serviceManager->get('config');
$routes = $config['console']['commands']; // This depends on your structure, this is what I created (see. 1.)
$application = new Application(
$config['app'],
$config['version'],
$routes,
Console::getInstance(),
new Dispatcher($serviceManager) // Use service manager as a dependency injection container
);
$exit = $application->run();
exit($exit);
I separated my CLI command handlers into the src/Command folder. My CLI command handlers are services I have defined, created by factories. (This is why I use the service manager as the container - see. 2.)
[serviceEmail here is a local class variable, which is loaded by the factory of this command handler.]
/**
* #param Route $route
* #param AdapterInterface $console
*
* #return int
*/
public function __invoke(Route $route, AdapterInterface $console)
{
$mails = $this->serviceEmail->sendMailFromDb();
$console->writeLine('Sent mails: ' . \count($mails), ColorInterface::WHITE, ColorInterface::RED);
return 0;
}
Related
I have a project with multiple migration files, according to different modules of the project. I want to pass a key (associative array) and run all the files on that index.
//EXAMPLE
$files = [
'blog' => [
//array of file names
],
'storage' => [
//array of file names
],
'sales' => [
//array of file names
],
]
runAll($files['sales'])
I've read the docs but it just allow specified class name in terminal.
Edit: Just checked again and it either allow class name but namespace on migrate command.
For those coming here wondering how to run one specific migration file in Codeigniter 4:
Unfortunately, at the time of writing this post, I couldn't find a direct command/way to handle this task.
Luckily, you can have access to the methods available within the MigrationRunner class in your own source code.
Usage Example Here
Even better, you have the ability to create your own custom commands.
With that in mind, I created a custom command to support running a single migration file.
Custom Command
Step 1:
Generate the basic command file by running the command below in your terminal:
php spark make:command MigrateFile --command migrate:file --group Database --suffix Command
This will create/generate a command file in the path: APPPATH\Commands\MigrateFileCommand.php
Step 2:
Edit this new command file (app/Commands/MigrateFileCommand.php) to something similar to the source code below:
<?php
namespace App\Commands;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use Config\Services;
class MigrateFileCommand extends BaseCommand
{
/**
* The Command's Group
*
* #var string
*/
protected $group = 'Database';
/**
* The Command's Name
*
* #var string
*/
protected $name = 'migrate:file';
/**
* The Command's Description
*
* #var string
*/
protected $description = 'Migrates a single migration file.';
/**
* The Command's Usage
*
* #var string
*/
protected $usage = 'migrate:file [arguments] [options]';
/**
* The Command's Arguments
*
* #var array
*/
protected $arguments = [
'name' => 'The valid migration file path beginning from the ROOTPATH. For example: php spark migrate:file "app\Database\Migrations\2022-02-16-101819_AddBlogMigration.php"'
];
/**
* The Command's Options
*
* #var array
*/
protected $options = [
'--namespace' => 'Set migration namespace. Default: "App".',
'--dbgroup' => 'Set database group. Default: "default".',
];
/**
* Actually execute a command.
*
* #param array $params
*/
public function run(array $params)
{
CLI::write("Running migration...", 'yellow');
$message = "";
$paramsSize = count($params);
if (!$paramsSize) {
$message = 'Too few arguments passed. Missing "migration file path."';
} else if ($paramsSize > 1) {
$message = 'Too many arguments passed.';
}
if ($paramsSize !== 1) {
CLI::write(sprintf('Invalid Params: %s', $message), 'red');
CLI::newLine();
$this->showHelp();
return;
}
$runner = Services::migrations();
$namespace = ($params['namespace'] ?? CLI::getOption('namespace')) ?: "App";
$dbgroup = ($params['dbgroup'] ?? CLI::getOption('dbgroup')) ?: "default";
try {
if (!$runner->force(ROOTPATH . $params[0], $namespace, $dbgroup)) {
CLI::error(lang('Migrations.generalFault'), 'light_gray', 'red'); // #codeCoverageIgnore
}
$messages = $runner->getCliMessages();
foreach ($messages as $message) {
CLI::write($message);
}
CLI::write('Done migration.', 'green');
// #codeCoverageIgnoreStart
} catch (\Throwable $e) {
$this->showError($e);
// #codeCoverageIgnoreEnd
}
}
}
The source code above is essentially making use of the force(...) method to execute a single migration file.
Step 3:
Now moving forward, you can easily run a single migration file using the command below in your terminal.
php spark migrate:file "app\Database\Migrations\2022-02-16-101819_AddBlogMigration.php"
Sample Output:
CodeIgniter v4.1.4 Command Line Tool - Server Time: 2022-02-16 13:09:34 UTC+01:00
Running migration...
Running: (App) 2022-02-16-101819_App\Database\Migrations\AddBlogMigration
Done migration.
If in case your migration file resides in a different namespace other than App, for example in a different module ('Modules\Sales'), you can pass an option defining the specific namespace.
The command also supports passing a different database group other than 'default'. I.e:
php spark migrate:file "app\Database\Migrations\2022-02-16-101819_AddBlogMigration.php" --namespace "Modules\Sales" --dbgroup "tests"
You can view the documentation of the new command by running the command below:
php spark help migrate:file
Sample Output:
CodeIgniter v4.1.4 Command Line Tool - Server Time: 2022-02-16 15:16:31 UTC+01:00
Usage:
migrate:file [arguments] [options]
Description:
Migrates a single migration file.
Arguments:
name The valid migration file path beginning from the ROOTPATH. For example: php spark migrate:file "app\Database\Migrations\2022-02-16-101819_AddBlogMigration.php"
Options:
--namespace Set migration namespace. Default: "App".
--dbgroup Set database group. Default: "default".
Bonus Tip
If for some reason you wish to run the new command from within your own code or Controller, this is possible by using:
echo command('migrate:file "app\Database\Migrations\2022-02-16-101819_AddBlogMigration.php"');
You can migrate a single file regardless of order or batches using the method force(string $path, string $namespace, ?string $group = null) available within the MigrationRunner class.
force($path, $namespace, $group)
This forces a single file to migrate regardless of order or batches.
Method “up” or “down” is detected based on whether it has already been
migrated.
Note:
This method is recommended only for testing and could cause data
consistency issues.
So in your case, you would just run a for loop through the files as you pass the expected parameters in the force(...) method. I.e:
Assuming the array keys of your $files array represent the various 'modules' of the project:
$migrate = \Config\Services::migrations();
$psr4 = config(\Config\Autoload::class)->psr4;
foreach ($files as $module => $filenames) {
$namespace = "Modules\\" . ucwords($module);
foreach ($filenames as $filename) {
try {
$migrate->force($psr4[$namespace] . "\\" . $filename, $namespace);
} catch (\Throwable $e) {
// Do something with the error here...
}
}
}
NOTES:
The above solution assumes that you already mapped your $psr4 namespaces in your application modules to their respective locations on the file system: I.e:
File: app/Config/Autoload.php
// ...
public $psr4 = [
APP_NAMESPACE => APPPATH, // For custom app namespace
'Config' => APPPATH . 'Config',
'Modules\Blog' => ROOTPATH . 'module/blog',
'Modules\Storage' => ROOTPATH . 'module/storage',
'Modules\Sales' => ROOTPATH . 'module/sales',
];
// ...
let's do it like me,
see codes step by step
this my codes modules
in app/config/autoload.php
<?php
namespace Config;
use CodeIgniter\Config\AutoloadConfig;
/**
* -------------------------------------------------------------------
* AUTOLOADER CONFIGURATION
* -------------------------------------------------------------------
*
* This file defines the namespaces and class maps so the Autoloader
* can find the files as needed.
*
* NOTE: If you use an identical key in $psr4 or $classmap, then
* the values in this file will overwrite the framework's values.
*/
class Autoload extends AutoloadConfig
{
/**
* -------------------------------------------------------------------
* Namespaces
* -------------------------------------------------------------------
* This maps the locations of any namespaces in your application to
* their location on the file system. These are used by the autoloader
* to locate files the first time they have been instantiated.
*
* The '/app' and '/system' directories are already mapped for you.
* you may change the name of the 'App' namespace if you wish,
* but this should be done prior to creating any namespaced classes,
* else you will need to modify all of those classes for this to work.
*
* Prototype:
*```
* $psr4 = [
* 'CodeIgniter' => SYSTEMPATH,
* 'App' => APPPATH
* ];
*```
*
* #var array<string, string>
*/
public $psr4 = [
APP_NAMESPACE => APPPATH, // For custom app namespace
'Config' => APPPATH . 'Config',
'Myth\Auth' => APPPATH .'ThirdParty/myth-auth/src',
'Modules\Shared' => ROOTPATH . 'module/shared',
'Modules\Common' => ROOTPATH . 'module/common',
'Modules\Auth' => ROOTPATH . 'module/auth',
'Modules\Home' => ROOTPATH . 'module/home',
'Modules\Payment' => ROOTPATH . 'module/payment',
'Modules\App' => ROOTPATH . 'module/app',
];
/**
* -------------------------------------------------------------------
* Class Map
* -------------------------------------------------------------------
* The class map provides a map of class names and their exact
* location on the drive. Classes loaded in this manner will have
* slightly faster performance because they will not have to be
* searched for within one or more directories as they would if they
* were being autoloaded through a namespace.
*
* Prototype:
*```
* $classmap = [
* 'MyClass' => '/path/to/class/file.php'
* ];
*```
*
* #var array<string, string>
*/
public $classmap = [];
/**
* -------------------------------------------------------------------
* Files
* -------------------------------------------------------------------
* The files array provides a list of paths to __non-class__ files
* that will be autoloaded. This can be useful for bootstrap operations
* or for loading functions.
*
* Prototype:
* ```
* $files = [
* '/path/to/my/file.php',
* ];
* ```
*
* #var array<int, string>
*/
public $files = [];
}
this is my migration
path file =Modules\Common\Database\Migrations\Settting
<?php namespace Modules\Common\Database\Migrations;
use CodeIgniter\Database\Migration;
class Setting extends Migration
{
public function up()
{
//
/*
* Setting
*/
$this->forge->addField([
'id' => ['type' => 'int', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'key' => ['type' => 'varchar', 'constraint' => 255],
'value' => ['type' => 'varchar', 'constraint' => 255],
'description' => ['type' => 'varchar', 'constraint' => 300],
'status' => ['type' => 'tinyint', 'constraint' => 1, 'null' => 0, 'default' => 1],
'created_at' => ['type' => 'datetime', 'null' => true],
'updated_at' => ['type' => 'datetime', 'null' => true],
'deleted_at' => ['type' => 'datetime', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey('key');
$this->forge->createTable('setting', true);
}
//--------------------------------------------------------------------
public function down()
{
// drop constraints first to prevent errors
$this->forge->dropTable('setting', true);
}
}
to call it
php spark migrate setting -n 'Module\Common'
for more info go there
https://codeigniter.com/user_guide/dbmgmt/migration.html
So I use a Service Class (extends from TYPO3\CMS\Core\Authentication\AuthenticationService) to authenticate our Frontend Users using OAuth2. These Services are automatically instantiated and called via Typos own Middleware: FrontendUserAuthenticator.
In this class I used to save data from the authentication result to $GLOBALS['TSFE']->fe_user using setKey('ses', 'key', 'data'), which seems is not possible anymore since v10. How would I go about still doing this?
The documentation is sparse
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/9.4/Deprecation-85878-EidUtilityAndVariousTSFEMethods.html
https://docs.typo3.org/m/typo3/reference-coreapi/10.4/en-us/ApiOverview/Context/Index.html
I've tried the following:
constructor injecting the TSFE using DI
class FrontendOAuthService extends AuthenticationService
{
public function __construct(TypoScriptFrontendController $TSFE) {
=> LogicException: TypoScriptFrontendController was tried to be injected before initial creation
changing the Middlewares order to have it instantiate before the Auth Middleware
(packages/extension_name/Configuration/RequestMiddlewares.php)
return [
'frontend' => [
'typo3/cms-frontend/tsfe' => [
'disabled' => true,
],
'vendor/extension_name/frontend-oauth' => [
'target' => \TYPO3\CMS\Frontend\Middleware\TypoScriptFrontendInitialization::class,
'before' => [
'typo3/cms-frontend/authentication',
],
'after' => [
'typo3/cms-frontend/eid',
'typo3/cms-frontend/page-argument-validator',
],
],
],
];
=> UnexpectedValueException: Your dependencies have cycles. That will not work out.
instantiating the TSFE myself
/** #var ObjectManager $objectManager */
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
/** #var DealerService $dealerService */
$lang = $site->getDefaultLanguage();
$siteLanguage = $objectManager->get(SiteLanguage::class, $lang->getLanguageId(), $lang->getLocale(), $lang->getBase(), []);
/** #var TypoScriptFrontendController $TSFE */
$TSFE = $objectManager->get(
TypoScriptFrontendController::class,
GeneralUtility::makeInstance(Context::class),
$site,
$siteLanguage,
GeneralUtility::_GP('no_cache'),
GeneralUtility::_GP('cHash')
);
=> the $TSFE->fe_user is an emptystring ("")
using the UserAspect
/** #var Context $context */
$context = GeneralUtility::makeInstance(Context::class);
$feUser = $context->getAspect('frontend.user');
$feUser->set...
=> Aspects are read-only
adding vars to the user data in the getUser method of the AuthenticationService
(packages/extension_name/Classes/Service/FrontendOAuthService.php)
public function getUser()
{
$user = allBusinessCodeHere();
$user['my_own_key'] = 'myData';
return $user;
=> is not propagated to the UserAspect(frontend.user) nor the $TSFE->fe_user
I'm out of ideas guys.
I had a similar problem when i wanted to use redirects with record links.
I ended up disabling the original redirect middleware and adding my own with a mocked version of tsfe.
The extension can be found here:
https://github.com/BenjaminBeck/bdm_middleware_redirect_with_tsfe
Late to the party, but I had the same issue and was able to solve it:
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/10.0/Breaking-88540-ChangedRequestWorkflowForFrontendRequests.html states:
Storing session data from a Frontend User Session / Anonymous session
is now triggered within the Frontend User
(frontend-user-authenticator) Middleware, at a later point - once the
page was generated. Up until TYPO3 v9, this was part of the
RequestHandler logic right after content was put together. This was
due to legacy reasons of the previous hook execution order. Migration
Consider using a PSR-15 middleware instead of using a hook, or
explicitly call storeSessionData() within the PHP hook if necessary.
In my MyAuthenticationService extends AbstractAuthenticationService in method getUser() I set $_SESSION['myvendor/myextension/accessToken'] to the token received by the external oauth service. In my SaveSessionMiddleware I save this token to the FrontendUserAuthentication object using setKey() which by then is available:
EXT:myextension/Configuration/RequestMiddlewares.php
return [
'frontend' => [
'myvendor/myextension/save-session-middleware' => [
'target' => \MyVendor\MyExtension\Middleware\SaveSessionMiddleware::class,
'after' => [
'typo3/cms-frontend/authentication',
],
]
]
];
EXT:myextension/Classes/Middleware/SaveSessionMiddleware.php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
class SaveSessionMiddleware implements MiddlewareInterface {
/**
* #param ServerRequestInterface $request
* #param RequestHandlerInterface $handler
* #return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
if (!empty($_SESSION['myvendor/myextension/accessToken'])) {
$this->getFrontendUserAuthentication()->setKey(
'ses',
'myvendor/myextension/accessToken',
$_SESSION['myvendor/myextension/accessToken']);
unset($_SESSION['myvendor/myextension/accessToken']);
}
return $handler->handle($request);
}
private function getFrontendUserAuthentication(): FrontendUserAuthentication {
return $GLOBALS['TSFE']->fe_user;
}
}
I want to be able to add a unique id (Uid) to my logging.
In Example 1: Which is depended on config/logging.php and ProcessorTap files below is not working as expected. The logging is configured to use stdout which refers to the ProcessorTap class that is suppose to add a Uid, when the log statement is created (in accordance with UidProcessor)
Example 2: Which uses purely Mono classes works as expected.
Why isnt Example 1 adding the Uid to the logs, when laravel ("laravel/framework": "5.7.*") should be using Monolog classes as well ?
Example 1: When this api is invoked, the output for Log::info('test') does not include UiD
Route::get('/test', function () {
Log::info('test'); //output = [2020-03-24 04:51:16] local.INFO: test
});
config/logging.php:
'default' => env('LOG_CHANNEL', 'stdout'), //.env LOG_CHANNEL=stdout
'stdout' => [
'driver' => 'monolog',
'handler' => StreamHandler::class,
'with' => [
'stream' => 'php://stdout',
],
'tap' => [
ProcessorTap::class,
],
]
ProcessorTap:
use Monolog\Processor\UidProcessor;
class ProcessorTap
{
/**
* Customize the given logger instance.
*
* #param \Illuminate\Log\Logger $logger
* #return void
*/
public function __invoke($logger)
{
$logger->pushProcessor(new UidProcessor());
}
}
Example 2: Working correctly the Uid (a484a6729e14996c0af1)
is added to the log for $logger->info('test')
use Monolog\Logger;
use Monolog\Processor\UidProcessor;
Route::get('/test', function () {
$logger = new Logger('main');
$logger->pushProcessor(new UidProcessor(20));
$logger->info('test'); // output = [2020-03-24 04:57:26] main.INFO: test [] {"uid":"a484a6729e14996c0af1"}
});
This might be a laravel (5.7)/mono version specific issue, but I was able to resolve the via iterating via the handlers and calling pushProcessor
use Monolog\Processor\UidProcessor;
class ProcessorTap
{
/**
* Customize the given logger instance.
*
* #param \Illuminate\Log\Logger $logger
* #return void
*/
public function __invoke($logger)
{
collect($logger->getHandlers())->each(function ($handler) {
$handler->pushProcessor(new UidProcessor());
});
}
}
I am using Braintree PHP SDK with Laravel framework.
I installed Braintree through composer.
Then, in AppServiceProvider.php, I have added below code in boot():
Braintree_Configuration::environment('sandbox');
Braintree_Configuration::merchantId('merchand_id');
Braintree_Configuration::publicKey('public_key');
Braintree_Configuration::privateKey('private_key');
When trying to generate client_token, I get below error:
Symfony\Component\Debug\Exception\FatalThrowableError: Class
'App\Providers\Braintree_Configuration' not found in
AppServiceProvider.php on line 34
The way you are using braintree seems to follow a deprecated example (my guess previous version of Braintre_php) as Braintree_Configuration class does not exist in the current package.
And you also need to use an "\" before calling the autoloaded class, ex : \Braintree.
This should work in your app/Providers/AppServiceProvider.php file with Braintree 5.x :
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
$gateway = new \Braintree\Gateway([
'environment' => 'sandbox',
'merchantId' => 'use_your_merchant_id',
'publicKey' => 'use_your_public_key',
'privateKey' => 'use_your_private_key'
]);
}
You can have an up to date example here to see some basics sdk function to get started :
https://developers.braintreepayments.com/start/hello-server/php
Please see examples below for braintree/paypal specific examples. This is the most elegant solution that worked for me:
app\Providers\AppServiceProvider.php:
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
// braintree setup
$environment = env('BRAINTREE_ENV');
$braintree = new \Braintree\Gateway([
'environment' => $environment,
'merchantId' => 'merchant_id_example',
'publicKey' => 'public_key_example',
'privateKey' => 'private_key_example'
]);
config(['braintree' => $braintree]);
// braintree setup for specifically for paypal direct integration for those who need it
/*$accessToken = env('PAYPAL_ACCESS_TOKEN');
$braintree = new \Braintree\Gateway([
'accessToken' => $accessToken
]);
config(['braintree' => $braintree]);*/
}
// examplefile.php:
public function token()
{
$braintree = config('braintree');
$clienttoken = $braintree->clientToken()->generate();
}
public function sale()
{
$braintree = config('braintree');
$result = $braintree->transaction()->sale([
'amount' => $amount,
'paymentMethodNonce' => $nonce
]);
}
Did you add a use statement to your AppServiceProvider?
use App\Providers\Braintree_Configuration;
So I'm gonna use laravel broadcasting for a chat app,
I followed Laravel Broadcasting approach,
Uncommented App\Providers\BroadcastServiceProvider
from providers array inside config/app.php
Registered in pusher website, made a channel
and filled the fields below inside .env file with my pusher channel info
PUSHER_APP_ID
PUSHER_APP_KEY
PUSHER_APP_SECRET
PUSHER_APP_CLUSTER
Inside my broadcast.php config file where I set the default driver to pusher, I also added
'options' => [
'cluster' => 'us2',
'encrypted' => true,
],
to pusher array inside connections array based on my channel info in pusher panel
Installed pusher php package on my laravel project using composer require pusher/pusher-php-server "~3.0" command
Here is my event class
<?php
namespace App\Events;
use App\User;
use App\TherapyMessage;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use App\AppLog;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
class TherapyMessageSent implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* User that sent the message
*
* #var User
*/
public $user;
/**
* Message details
*
* #var Message
*/
public $message;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(User $user, TherapyMessage $message)
{
$this->user = $user;
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
$message_id = $this->message->id;
$user = $this->user;
AppLog::create([
'file_name' => __FILE__,
'message' => "broadcast before send with Message ID of $message_id from $user->full_name"
]);
return new PrivateChannel("therapy-chat.$message_id");
}
}
The AppLog is a model that I use for logging inside the project
I tried implementing ShouldBroadcast interface at first but that didn't work either
I also registered my event inside EventServiceProvider.php file and run php artisan event:generate command, here is the EventServiceProvider $listen array:
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
TherapyMessageSent::class
],
];
I also imported the event namespace next to other namespaces inside the file:
use \App\Events\TherapyMessageSent;
Here is the channel that I defined inside routes/channels.php file:
use App\AppLog;
Broadcast::channel('therapy-chat.{message_id}', function ($user, $message_id) {
AppLog::create([
'file_name' => __FILE__,
'message' => "broadcast sending with Message ID of $message_id to $user->full_name"
]);
if (!Auth::check()) return false;
$message = \App\TherapyMessage::find($message_id);
if (!$message) {
AppLog::create([
'file_name' => __FILE__,
'message' => "Message with ID of $message_id Not Found for broadcasting"
]);
return false;
}
$will_send = false;
if ($therapist = $user->therapist) {
$will_send = $therapist->id === $message->therapist_id;
} else if ($patient = $user->patient) {
$will_send = $message->patient_id === $patient->id;
}
if ($will_send) {
AppLog::create([
'file_name' => __FILE__,
'message' => "Message with ID of $message_id broadcasted to $user->full_name"
]);
}
return $will_send;
});
Finally, this is my controller method:
public function sendToTherapist(Request $request) {
$validation = \Validator::make($request->all(), ['message' => 'required']);
if ($validation->fails()) return $this->validationError($validation);
$user = \Auth::user();
$patient = $user->patient;
$therapist = $patient->therapist;
if (!$therapist) return $this->errorWithMessage('Patient does not have Therapist');
$message = \App\TherapyMessage::create([
'patient_id' => $patient->id,
'therapist_id' => $therapist->id,
'type' => TherapyMessageType::TEXT,
'text' => $request->message,
'sender_role' => TherapyMessageSenderRole::PATIENT
]);
broadcast(new TherapyMessageSent($user, $message))->toOthers();
return $this->success(['id' => $message->id]);
}
My controller class extends from BaseController which is a custom controller class with helper methods such as success(), validationError() and errorWithMessage()
As you see in the code above
I filled $user and $message with correct values and the request works without any error
I think the channel method won't even be fired,
as I check the AppLog table when I call broadcast method, only the log inside TherapyMessageSent event broadcastOn function is saved
and even the log that I save at the beginning of channels.php method, isn't saved so I think this method is never executed.
If anyone could help me with the problem, I'd be thankful.