I am learning Slim Framework v4 and decided to use Respect\Validation to validate inputted data and have hit a snag where I do not know how to inject the PDO into my custom rule I created.
The idea is to validate some inputs against the database if the provided data exist (or in another instances, if it was inputted correctly). In this specific case, I am tying to validate user's credentials for log in. My idea is this:
AuthController.php:
v::with('app\\Validators\\');
$userValidation = v::notBlank()->email()->length(null, 255)->EmailExists()->setName('email');
EmailExists() is my custom rule.
EmailExists.php:
namespace app\Validators;
use PDO;
use Respect\Validation\Rules\AbstractRule;
class EmailExists extends AbstractRule
{
protected $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function validate($input, $id = null)
{
// a PDO query that checks if the email exists in database
}
}
But I get an error of Too few arguments to function app\Validators\EmailExists::__construct(), 0 passed and exactly 1 expected, which is somewhat expected since the AbstractRule does not have a PDO injected and my class extends it.
So how to inject the PDO interface so that I can use it in my custom rules?
Are you guys using another approach in validating this kind of data? Do note that I am writing an API, so the database validation is somewhat a must and after Googling for past two days, I have no solutions at hand.
I am also using a PHP-DI where I create PDO interface. This is my dependencies.php file:
declare(strict_types=1);
use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;
use app\Handlers\SessionMiddleware;
return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
PDO::class => function (ContainerInterface $c) {
$settings = $c->get('settings')['db'];
$db = new PDO("mysql:host={$settings['host']};dbname={$settings['database']};charset={$settings['charset']},{$settings['username']},{$settings['password']}");
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8',time_zone='{$offset}'");
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return $db;
},
'session' => function(ContainerInterface $c) {
return new SessionMiddleware;
}
]);
};
And (part of) index.php:
declare(strict_types=1);
use DI\ContainerBuilder;
use Slim\Factory\AppFactory;
// Instantiate PHP-DI ContainerBuilder
$containerBuilder = new ContainerBuilder();
// Set up settings
$settings = require __DIR__ . '/../app/settings.php';
$settings($containerBuilder);
// Set up dependencies
$dependencies = require __DIR__ . '/../app/dependencies.php';
$dependencies($containerBuilder);
// Build PHP-DI Container instance
$container = $containerBuilder->build();
// Instantiate the app
AppFactory::setContainer($container);
$app = AppFactory::create();
// Register middleware
$middleware = require __DIR__ . '/../app/middleware.php';
$middleware($app);
// Register routes
$routes = require __DIR__ . '/../app/routes.php';
$routes($app);
// Add Routing Middleware
$app->addRoutingMiddleware();
// Run App & Emit Response
$response = $app->handle($request);
$responseEmitter = new ResponseEmitter();
$responseEmitter->emit($response);
Any help would be appreciated.
Use your user model to count the number of rows in the user table where there is a hit.
If it is not exactly 0, the check returns false, if it is exactly 0, the check passes.
So you don't have to include a PDO at this point. I use Slim 3 and that works quite well.
namespace app\Validators;
use Respect\Validation\Rules\AbstractRule;
class EmailAvailable extends AbstractRule {
/**
* #param $input
*
* #return bool
*/
public function validate ($sInput) {
return User::where('user_email', $sInput)->count() === 0;
}
}
class EmailAvailable extends AbstractRule {
/**
* #param $input
*
* #return bool
*/
public function validate ($sInput) {
return User::where('user_email', $sInput)->count() === 0;
}
}
Related
i'm using the following code for my soap call.
If i add the wsdl and make my client call i just get the response without the whole soap wrap.
declare(strict_types=1);
namespace Vendor\DocBasics\Controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use Vendor\DocBasics\Domain\Repository\EventsRepository;
use Vendor\CartExtended\Domain\Repository\Order\ItemRepository;
require_once(PATH_site . 'typo3conf/ext/doc_basics/Classes/Libs/nusoap/nusoap.php');
class EventsController
{
protected $action = '';
protected $order;
protected $Vbeln = '';
protected $Zaehl = '';
protected $objectManager;
/**
* #var array
*/
protected $responseArray = [
'hasErrors' => false,
'message' => 'Nothing to declare'
];
/**
* #param ServerRequestInterface $request
* #param ResponseInterface $response
* #return ResponseInterface
*/
public function processRequest(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$this->initializeData(file_get_contents('php://input')); //xml datas from soap call
switch (isset($request->getQueryParams()['action']) ? (string)$request->getQueryParams()['action'] : '') {
case 'create':
$this->createAction();
break;
case 'update':
$this->updateAction();
break;
default:
$this->updateAction(); //call it as default, so i can call it as endpoint without action parameter
}
$this->prepareResponse($response,$request->getQueryParams()['action']);
return $response;
}
/**
* action create
*
* #return void
*/
public function createAction()
{
$server = new \soap_server();
$server->configureWSDL("updateorderservice", "https://domain.tld/updateorderservice", "https://domain.tld/index.php?eID=update_order");
$server->register(
"update",
array("Vbeln" => 'xsd:string', "Zaehl" => 'xsd:integer'),
array("return" => 'xsd:string'),
"https://domain.tld/updateorderservice",
"update",
"rpc",
"encoded",
"Update a given order"
);
$this->responseArray['message']= $server->service(file_get_contents('php://input'));
}
public function updateAction()
{
$this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
$this->itemRepository = $this->objectManager->get(ItemRepository::class);
$order=$this->itemRepository->findOrderByOrder($this->Vbeln);
if($order){
$order->setCancelDate($this->Veindat);
$this->itemRepository->update($order);
$this->persistenceManager->persistAll();
$msg= '<MESSAGE><TYPE>S</TYPE><MSGTXT>Auftrag '.$this->Vbeln.' aktualisiert!</MSGTXT></MESSAGE>';
}
else $msg= '<MESSAGE><TYPE>E</TYPE><MSGTXT>Auftrag '.$this->Vbeln.' konnte nicht aktualisiert!</MSGTXT></MESSAGE>';
$this->responseArray['message'] = $msg; //receive the message but don't know how to wrap it
}
/**
* #param ResponseInterface $response
* #param String $action
* #return void
*/
protected function prepareResponse(ResponseInterface &$response, $action)
{
if($action=='create'){
$response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
$response->getBody()->write($this->responseArray['message']);
}
else{
$response = $response->withHeader('Content-Type', 'text/xml; charset=utf-8');
$response->getBody()->write($this->responseArray['message']);
}
}
/**
* #param $request
* #return void
*/
protected function initializeData($request)
{
$resp= $this->parseResult($request);
if($resp->Vbeln[0]) $this->Vbeln = (string)($resp->Vbeln[0]);
if($resp->Zaehl[0]) $this->Zaehl = intval($resp->Zaehl[0]);
}
public function parseResult($result){
$result = str_ireplace(['soapenv:','soap:','upd:'], '', $result);
$result = simplexml_load_string($result);
$notification = $result->Body->Update;
return $notification;
}
}
My response is just the small xml i'm writing as return to the updateAction(). My response should be wrapped between and so on
May be i'm missing something or the way i'm using the eID concept is wrong.
your case makes much more sense here, than on facebook, but in your future posts on stackoverflow you should write more background information for all the other devs who have no background information as I have.
In general: You overcomplicate things. :-)
First, you told me on facebook, That your soap server as such (without TYPO3 integration as eID) works. Is it so? I can not see that from your code :-)
You process some control http parameter "action" and create the SOAP server only if the value is "create".
But for the "action" value "update", there is no server initialization? How can that work?
You must remember, that a SOAP server must be initialized on each request.
It is not a deamon, which gets started once and runs in the background.
There is absolute no need for such an "action" control parameter on the input side. This is what a "SOAP remote method" registration of the NuSOAP server is for - a method with a distinguished name, which you call explicitelly on the client side.
Then your parseResult and parseResponse methods? Are you trying to handle the SOAP protocol manually? NuSOAP is supposed to handle all that for you.
You just have to register appropriate data types (ComplexType).
You need to get much more background knowledge on NuSOAP itself first.
Here is a simple working example I used in a very old project. I reduced it to show you how NuSOAP is supposed to work.
The server defines one single Method "echoStringArray", which takes one array as attribute named "inputStringArray" and echoes it back without any modifications.
You can take and copy paste without modifications into your eID script and so you will have immediate basic TYPO3 integration.
Then add other things one by one, such as database layer and so on.
Try not to use classes first, but the same procedural approach from my example.
So here is the server definition soap-server.php:
<?php
// Pull in the NuSOAP code
require_once('./nusoap-0.9.5/lib/nusoap.php');
function logRequest($userAgent, $methodName, $request, $response, $result) {
$fp = fopen("./soap.log","a+");
fputs($fp,"$userAgent\n$methodName\n$request\n$response\n$result\n=======================================\n");
fclose($fp);
}
$log = true;
// Create the server instance
$SOAP_server = new soap_server;
$SOAP_server->configureWSDL(
'Test Service',
'http://my-soap-server.local/xsd'
);
// Set schema target namespace
$SOAP_server->wsdl->schemaTargetNamespace = 'http://my-soap-server/xsd';
// Define SOAP-Types which we will need. In this case a simple array with strings
$SOAP_server->wsdl->addComplexType(
'ArrayOfstring',
'complexType',
'array',
'',
'SOAP-ENC:Array',
array(),
array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]')),
'xsd:string'
);
// Define SOAP endpoints (remote methods)
$SOAP_server->register(
'echoStringArray', // this is the name of the remote method and the handler identifier below at the same time
array('inputStringArray'=>'tns:ArrayOfstring'),
array('return'=>'tns:ArrayOfstring'),
'http://soapinterop.org/'
);
// Define SOAP method handlers
// This is the handler for the registered echoStringArray SOAP method. It just receives an array with strings and echoes it back unmodified
function echoStringArray($inputStringArray){
$outputData = $inputStringArray;
return $outputData;
}
// Now let the SOAP service work on the request
$SOAP_server->service(file_get_contents("php://input"));
if(isset($log) and $log == true){
logRequest($SOAP_server->headers['User-Agent'],$SOAP_server->methodname,$SOAP_server->request,$SOAP_server->response,$SOAP_server->result);
}
And here is the appropriate client soap-client.php:
<?php
require_once('./nusoap-0.9.5/lib/nusoap.php');
// This is your Web service server WSDL URL address
$wsdl = "http://my-soap-server.local/soap-server.php?wsdl";
// Create client object
$client = new nusoap_client($wsdl, 'wsdl');
$err = $client->getError();
if ($err) {
// Display the error
echo '<h2>Constructor error</h2>' . $err;
// At this point, you know the call that follows will fail
exit();
}
// Call the hello method
$result1 = $client->call('echoStringArray', ['inputStringArray' => ['Hello', 'World', '!']]);
print_r($result1);
As you can see there is absolutely no custom handling of the message body, XML, headers and so on. All this is being taken care for by NuSOAP itself.
You just provide an array under the key inputStringArray in $client->call() and get the same array on the server side as parameter named inputStringArray of the method handler echoStringArray.
And last but not least, you may try something more recent than nuSOAP, e.g. zend-soap. It seems to be simpler, check out this short tutorial https://odan.github.io/2017/11/20/implementing-a-soap-api-with-php-7.html
YES! now it works. Ur last remark was the point: "SOAP Server must be initialized on each request". Tought this server initialisation was only use for creating the wsdl. The other difficulty i had was how to call my function. If the function is in the same class it'll not get call (probably due to some autoload issues), i had to make another class with the function to get things working.
Here is my whole solution.
in ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include']['update_order'] = Vendor\DocBasics\Controller\EventsController::class . '::processRequest';
My class EventsController
<?php
declare(strict_types=1);
namespace Vendor\DocBasics\Controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
require_once(PATH_site . 'typo3conf/ext/doc_basics/Classes/Libs/nusoap/nusoap.php');
require_once(PATH_site . 'typo3conf/ext/doc_basics/Classes/Libs/Utility.php');
class EventsController
{
protected $objectManager;
/**
* #var array
*/
protected $responseArray = [
'hasErrors' => false,
'message' => 'Nothing to declare'
];
/**
* #param ServerRequestInterface $request
* #param ResponseInterface $response
* #return ResponseInterface
*/
public function processRequest(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$server = new \soap_server();
$server->soap_defencoding='utf-8';
$server->configureWSDL("updateorderservice", "https://domain.tld/updateorderservice", "https://domain.tld/index.php?eID=update_order");
$server->register(
"Utility.updateOrder",
array("Vbeln" => 'xsd:string', "Zaehl" => 'xsd:integer'),
array("return" => 'xsd:string'),
"https://domain.tld/updateorderservice",
"update",
"rpc",
"encoded",
"Update a given order"
);
$this->prepareResponse($response);
return $response;
}
/**
* #param ResponseInterface $response
* #param String $action
* #return void
*/
protected function prepareResponse(ResponseInterface &$response)
{
$response = $response->withHeader('Content-Type', 'text/xml; charset=utf-8');
$response->getBody()->write($this->responseArray['message']);
}
}
And my class Utility
class Utility
{
public function updateOrder($Vbeln,$Zaehl)
{
//do ur stuff
return "Order ".$Vbeln." done";
}
}
U can call ur wsdl with https://domain.tld/index.php?eID=update_order&wsdl
Thanks again Artur for helping me solving this. Dziekuje ;-)
I have this middleware on my app that checks the user role for a route:
public function handle($request, Closure $next, ...$roles)
{
if (in_array($request->user()->rol, $roles)) {
return $next($request);
} else {
return redirect()->action('SecurityController#noAutorizado');
}
}
And I'm triying to make a test for this middleware (phpUnit):
public function testUsuarioLogadoPuedeAccederAPantallaUsuarios()
{
$user = UsuariosTestFixtures::unAsignador();
$this->actingAs($user);
$request = Request::create('/usuarios', 'GET');
$middleware = new CheckRole();
$response = $middleware->handle($request,Closure $next,$user->getRole(), function () {});
$this->assertEquals($response, true);
}
But i'm retreiving this error: Argument 2 passed to App\Http\Middleware\CheckRole::handle() must be an instance of Closure, null given
I don't know how I have to pass the "Closure $next" on the $middleware->handle
I've tryed this:
public function testUsuarioLogadoPuedeAccederAPantallaUsuarios(Closure $next){...}
But It returns an error: Too few arguments to function UsuarioControllerTest::testUsuarioLogadoPuedeAccederAPantallaUsuarios(), 0 passed in C:\www\APPS\catsa\vendor\phpunit\phpunit\src\Framework\TestCase.php
What's the solution?
Thanks a lot!
A Closure in PHP is simply a function, so you need to pass a function as the second argument of your handle method.
In the context of Laravel middleware, the $next function represent the full pipeline of steps that the request goes through.
Obviously you can't (and don't need to) execute this pipeline during a test. What you need is just a function that return some values that your can test in an assertion.
What you can do is something like this:
//... setup code here
$middleware = new CheckRole();
$roles = ['role1', 'role2']; // change this with the desired roles
$result = $middleware->handle($request,function($request) {
return 'success';
},$roles);
$this->assertEquals('success', $result);
So, what is happening here?
If everything goes as planned (the user has the required role), the $next closure is executed and it returns success; on the other hand, if the user doesn't have the required role, the code takes the other path and it returns a RedirectResponse.
Finally, the assertion checks if success is returned, and it reports a failure if that doesn't happen.
I am trying to build a real-time notification system in an app I am working on. one of the requirements is, when an ID is expired, that particular user should be sent a notification. Since this task needs to be run on daily basis at the maximum, I developed an artisan command that is easy to run with CRON jobs i.e. Laravel Scheduler. Every thing is working fine i.e. the artisan command is run and notification is generated & stored in database & all the related stuff. but each time a notification is generated, the page needs to be reload and this is where I am stuck. I am trying to make it happen in real time but a very strange error is being thrown & I don't know what it means.
Here is the necessary code:
Artisan.file
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\User;
use Carbon\Carbon;
use App\Notifications\UserIdExpired;
class UpdateCatalog extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'check:expiry';
/**
* The console command description.
*
* #var string
*/
protected $description = 'dummy command to check its purpose';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$ZERO = 0;
$MONTH = 30;
$today = Carbon::today();
$users = User::all();
foreach($users as $user){
$today = Carbon::today();
$expiryDate = $user->qidexpire_on;
if($today->diffInDays($expiryDate, false) <= $MONTH && $today->diffInDays($expiryDate, false) >= $ZERO){
$this->info($user);
$this->info($expiryDate->diffInDays($today));
$user->notify(new UserIdExpired);
} else {
}
}
}
}
}
Notification.file
<?php
namespace App\Notifications;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\BroadcastMessage;
class UserIdExpired extends Notification
{
use Queueable;
public function via($notifiable)
{
return ['database', 'broadcast'];
}
public function toDatabase($notifiable)
{
return [
'user' => $notifiable,
'id_expired' => Carbon::now()
];
}
public function toBroadcast($notifiable)
{
return new BroadcastMessage([
'user' => $notifiable,
'id_expired' => Carbon::now()
]);
}
}
when I run php artisan check:expiry from console, Notification is generated & on page reload it updates number of notifications but its not happening in real time. Following is the error that is shown on console
[Illuminate\Broadcasting\BroadcastException]
Note: Whenever i reload the page, Pusher Console shows the respective log like connected private channel and host & all that stuff which means the problem is not on the client side, (yet)
just found the answer on this issue
had to encrypt false since I am developing locally
How can I instantiate a model and get all columns already declared in the instance?
$modelA = User::findFirst();
echo $modelA->id; //OK
$modelB = new User();
echo $modelA->id; //Id is undeclared causing errors instead of returning blank
Currently I need to declare by hand the model columns in the event onConstruct but I want to make use of the database introspection strategy and get properties declared from the table schema schema when I use new User();.
Anyone knows something that can help me out?!
Try the Annotations Strategy mentioned on that page.
So, something like
class Test extends \Phalcon\Mvc\Model
{
/**
* #Primary
* #Identity
* #Column(type="integer", nullable=false)
*/
public $id;
...
Get an instance of the meta-data adapter from the services container:
<?php
use Phalcon\Mvc\Model\MetaData\Apc as ApcMetaData,
Phalcon\Mvc\Model\MetaData\Strategy\Annotations as StrategyAnnotations;
$di['modelsMetadata'] = function() {
// Instantiate a meta-data adapter
$metaData = new ApcMetaData(array(
"lifetime" => 86400,
"prefix" => "my-prefix"
));
Get the columns for your model like so:
<?php
$test = new Test();
// Get Phalcon\Mvc\Model\Metadata instance
$metaData = $test->getModelsMetaData();
// Get fields names
$attributes = $metaData->getAttributes($test);
print_r($attributes);
// Get fields data types
$dataTypes = $metaData->getDataTypes($test);
print_r($dataTypes);
All of the above can be found in the link.
Also check out the MetaData APIs.
Hope that helps.
I have a console command to do a consumer time, AND I need to know how to call (execute) it in a web application action in YII.
class MyCommand extends CConsoleCommand{
public function actionIndex(){
$model = new Product();
$model->title = 'my product';
...
$model->save();
.
.
.
}
}
I want to execute this code.
try this:
Yii::import('application.commands.*');
$command = new MyCommand("test", "test");
$command->run(null);
The 2 parameters with value "test" must be set but do not have an impact, they are used for the --help option when using the console.
/**
* Constructor.
* #param string $name name of the command
* #param CConsoleCommandRunner $runner the command runner
*/
public function __construct($name,$runner)
{
$this->_name=$name;
$this->_runner=$runner;
$this->attachBehaviors($this->behaviors());
}
https://github.com/yiisoft/yii/blob/master/framework/console/CConsoleCommand.php#L65
Try this
Yii::import('application.commands.*');
$command = new GearmanCommand('start', Yii::app()->commandRunner);
$command->run(array('start', '--daemonize', '--initd'));
where array('start', '--daemonize', '--initd') is a action and action parameters
I had same problem - i need to call action from inside controller and from command
I said same problem because it actually same - you have action which you need to call from console, and call it from controller too.
If you need to call an action(command) as a part of controller action, then i think you need to modify this solution a little. Or is my solution is enough for you?
So here is my solution:
first create action as said in http://www.yiichina.net/doc/guide/1.1/en/basics.controller#action
class NotifyUnsharedItemsAction extends CAction
{
public function run()
{
echo "ok";
}
}
then in controller action is loaded as usuall:
class TestController extends Controller
{
public function actions() {
return array(
'notifyUnsharedItems'=>'application.controllers.actions.NotifyUnsharedItemsAction',
);
}
and in command i run action in such way:
class NotifyUnsharedItemsCommand extends CConsoleCommand
{
public function run($args)
{
$action = Yii::createComponent('application.controllers.actions.NotifyUnsharedItemsAction',$this,'notify');
$action->run();
}
}
Accepting that we are on linux server, for Yii 1.1 real life example would be:
$run = '/usr/bin/php ' . Yii::getPathOfAlias('root').'/yiic' [command]
exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $run, '/dev/null', '/dev/null'));
This will run Yii console command in the background.
Yii is PHP -> you can use the standard php constructs specified at http://php.net/manual/en/function.exec.php and the related methods near the bottom of the page, depending on what exactly you want to achieve.
Also, another very clean solution from cebe on gist:
<?php
// ...
$runner=new CConsoleCommandRunner();
$runner->commands=array(
'commandName' => array(
'class' => 'application.commands.myCommand',
),
);
ob_start();
$runner->run(array(
'yiic',
'idbrights',
));
echo nl2br(htmlentities(ob_get_clean(), null, Yii::app()->charset));
Yii::app()->end();
Typically what you should do in these situations is refactor.
Move the "common" code out of the MyCommand and place it into a class located in the components folder.
Now you can place any head on top of the "common" code without altering your functionality. For example:
protected/components/Mywork.php:
<?php
class Mywork
{
public function doWork()
{
$model = new Product();
$model->title = 'my product';
...
$model->save();
...
}
}
protected/controller/MyworkController.php:
<?php
class MyworkController
{
public function actionDowork()
{
$mywork = new Mywork;
...
}
}
protected/commands/MyworkCommand.php:
<?php
class MyworkCommand extends CConsoleCommand
{
public function run($args)
{
$mywork = new Mywork;
...
}
}
This approach makes testing easier too as you can test Mywork as a single unit outside of the view you are using.