I'm using Laravel v5.8 and guzzlehttp of v7.4 and tried to write this Controller for getting some information:
public function __construct()
{
$client = new Client(['base_uri' => 'https://jsonplaceholder.typicode.com/']);
}
public function getInfo(Request $request)
{
try {
$response = $this->client->request('GET', 'posts');
dd($response->getContents());
} catch (ClientException $e) {
dd($e);
}
}
But now when I call the method getInfo, I get this error message:
Undefined property: App\Http\Controllers\Tavanmand\AppResultController::$client
However the docs says calling the uri's like this.
So what's going wrong here? How can I solve this issue?
The scope of your $client variable is limited to inside your constructor. You want to assign it to some sort of class property if you want to access it elsewhere;
private $client;
public function __construct()
{
$this->client = new Client(['base_uri' => 'https://jsonplaceholder.typicode.com/']);
}
public function getInfo(Request $request)
{
try {
$response = $this->client->request('GET', 'posts');
//...
}
Make $client as global variable of this class.
then set value at construction like:
public $client
public function __construct()
{
$this->client = new Client(['base_uri' => 'https://jsonplaceholder.typicode.com/']);
}
Happy coding...
Related
I have Controller like this:
class TronController extends Controller
{
public $tron;
public function __construct(){
$this->fullNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
$this->solidityNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
$this->eventServer = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
}
public function totalUsers()
{
try {
$tron = new \IEXBase\TronAPI\Tron($this->fullNode, $this->solidityNode, $this->eventServer);
$TransactionBuilder = new \IEXBase\TronAPI\TransactionBuilder($tron);
} catch (\IEXBase\TronAPI\Exception\TronException $e) {
exit($e->getMessage());
}
$address = ADDRESS;
$addressH = $tron->toHex($address);
$contract = CONTRACT;
$contractH = $tron->toHex($contract);
...
}
public function totalTickets()
{
try {
$tron = new \IEXBase\TronAPI\Tron($this->fullNode, $this->solidityNode, $this->eventServer);
$TransactionBuilder = new \IEXBase\TronAPI\TransactionBuilder($tron);
} catch (\IEXBase\TronAPI\Exception\TronException $e) {
exit($e->getMessage());
}
...
}
}
So as you can see, at the two functions of this Controller, I have repeated the same try..catch() for setting up $trone variable.
Now in order to refactor this, I added try..catch() to the __construct() function:
public function __construct(){
$this->fullNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
$this->solidityNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
$this->eventServer = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
try {
$tron = new \IEXBase\TronAPI\Tron($this->fullNode, $this->solidityNode, $this->eventServer);
$TransactionBuilder = new \IEXBase\TronAPI\TransactionBuilder($tron);
} catch (\IEXBase\TronAPI\Exception\TronException $e) {
exit($e->getMessage());
}
}
But this will return an ErrorException:
Undefined variable: tron
Which is referring to this line:
$addressH = $tron->toHex($address);
So the question is, how can I write less code by minimizing the try..catch() and use that in the functions without getting error ?
I would really appreciate any idea or suggestion from you guys...
Thanks in advance.
It looks like you're setting an instance variable but you're not really using it: public $tron;
Then you simply create a new local variable instead:
$tron = new ...
I suggest making use of the class variable by using $this->tron
In your case I would place it in the constructor, much like Stephen said, like so:
(I also made some changes to your class declaration)
class TronController extends Controller
{
public $tron; // Consider whether you really need this public or not
protected $fullNode; // Adding these definitions as protected so you can change/use them in child classes
protected $solidityNode;
protected $eventServer;
protected $transactionBuilder; // This may need to be public, I'm not familiar with tron. I saw you weren't using this so maybe you didn't want it to be local, so here you go, now it's a class member
public function __construct(){
$this->fullNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
$this->solidityNode = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
$this->eventServer = new \IEXBase\TronAPI\Provider\HttpProvider('https://api.trongrid.io');
try {
$this->tron = new \IEXBase\TronAPI\Tron($this->fullNode, $this->solidityNode, $this->eventServer);
$this->transactionBuilder = new \IEXBase\TronAPI\TransactionBuilder($this->tron);
} catch (\IEXBase\TronAPI\Exception\TronException $e) {
exit($e->getMessage());
}
}
public function totalUsers()
{
$tron = $this->tron; // Avoiding the repetition of '$this'
$address = ADDRESS;
$addressH = $tron->toHex($address);
$contract = CONTRACT;
$contractH = $tron->toHex($contract);
//
}
public function totalTickets()
{
//
}
}
P.S.: It seems to me like you may need to read a bit more about OOP in PHP, particularly about visibility. Check the documentation out :)
It looks like $addressH = $tron->toHex($address); references a local variable $tron within the totalUsers and totalTickets methods.
Try this instead:
<?php
namespace App\Http\Controllers;
use IEXBase\TronAPI\Provider\HttpProvider;
use IEXBase\TronAPI\TransactionBuilder;
use IEXBase\TronAPI\Tron;
use IEXBase\TronAPI\Exception\TronException;
class TronController extends Controller
{
public Tron $tron;
public function __construct()
{
// Not sure why you're doing the same thing three times
$fullNode = new HttpProvider('https://api.trongrid.io');
$solidityNode = new HttpProvider('https://api.trongrid.io');
$eventServer = new HttpProvider('https://api.trongrid.io');
try {
$this->tron = new Tron($fullNode, $solidityNode, $eventServer);
// You don't appear to be using this anywhere...
$transactionBuilder = new TransactionBuilder($this->tron);
} catch (TronException $e) {
exit($e->getMessage());
}
}
public function totalUsers()
{
// Reference the class instance variable $tron
$addressH = $this->tron->toHex('Some address');
}
public function totalTickets()
{
// Reference the class instance variable $tron
$addressH = $this->tron->toHex('Some address');
}
}
What is the best way to have one instance of some object in class? I'm using yii2-httpclient and use Client class. I have different methods thank make requests for differents web API but I think its bad idea to creating new object when calling method. I'm trying make instance in constructor but have error "Call to a member function createRequest() on a non-object". How can I do this another way?
My Controller:
<?php
namespace app\models;
use yii;
use yii\base\Model;
use yii\httpclient\Client;
class ExchangeRates extends Model
{
function __construct()
{
$client = new Client();
}
public function getPrivatRate()
{
// $client = new Client();
$privatResponse = $client->createRequest()
->setMethod('get')
->setUrl('https://api.privatbank.ua/p24api/pubinfo?json&exchange&coursid=5')
->send();
return json_decode($privatResponse->content, true);
}
public function getNationalRate()
{
// $client = new Client();
$nbuResponse = $client->createRequest()
->setMethod('get')
->setUrl('https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange?valcode=USD&date=20161101&json')
->send();
return json_decode($nbuResponse->content, true);
}
public function getMejBankRate()
{
// $client = new Client();
$mejResponse = $client->createRequest()
->setMethod('get')
->setUrl('http://api.cashex.com.ua/api/v1/exchange/mejbank')
->send();
return json_decode($mejResponse->content, true);
}
public function getBlackMarketRate()
{
// $client = new Client();
$blackMarketResponse = $client->createRequest()
->setMethod('get')
->setUrl('http://api.cashex.com.ua/api/v1/exchange/black-market')
->send();
return json_decode($blackMarketResponse->content, true);
}
}
You can something like this:
class ExchangeRates extends Model
{
public $client;
public function init()
{
parent::init();
$this->client = new Client();
}
Now you can use it like this:
$privatResponse = $this->client->createRequest()->...
I'm using Guzzle's asynchronous request and have them implemented in a service that I would like to test now.
My method looks like this (pseudo, so if it's not 100% valid, please excuse)
public function getPlayer(string $uiid, array &$player = [])
{
$options['query'] = ['id' => $uiid];
$promise = $this->requestAsync('GET', $this->endpoint, $options);
$promise->then(function (ResponseInterface $response) use (&$player) {
$player = $response->getBody()->getContents();
});
return $players;
}
Now I want to test it, but I don't really know how to mock the callable, because I'm always getting the error
1) tzfrs\PlayerBundle\Tests\Api\Player\PlayerServiceTest::testGetPlayer
Prophecy\Exception\InvalidArgumentException: Expected callable or instance of PromiseInterface, but got object.
This is how I have it implemented currently
/** #var ObjectProphecy|PromiseInterface $response */
$promise = $this->prophesize(PromiseInterface::class);
$promise->then()->will($this->returnCallback(function (ResponseInterface $response) use (&$player){}));
Didn't work. And this
$this->returnCallback(function (ResponseInterface $response) use (&$player){})
didn't work either. Same error. And when simply trying a dummy callback
$promise->then(function(){});
I get the error Error: Call to a member function then() on string, even after ->reveal()ing the promise first. Any ideas?
I had another idea.
Make a dependency that will make what you make now in requestAsync();
And then create it's mock that will return another mock of promise.
class PromiseMock
{
private $response;
public function __construct(ResponseInterface $response)
{
$this->response = $response;
}
public function then($callable)
{
$callable($this->response);
}
}
test looks like
public function testGetPlayer()
{
$response = new Response(200, [], "Your test response");
$promiseMock = new PromiseMock($response);
$mockDependency = $this->getMockBuilder('YourDependencyClass')
->getMock()
->expects("requestAsync")->willReturn($promiseMock);
$service = new YouServiceClass($mockDependency);
$service->getPlayer("76245914-d56d-4bac-8419-9e409f43e777");
}
And in your class changes only
$promise = $this->someNameService->requestAsync('GET', $this->endpoint, $options);
I would inject a processor to your class and call it's callable. Check it out, the rest is quite obvious:
public function __construct(Processor $processor) {
$this->processor = $processor;
}
public function getPlayer(string $uiid, array &$player = [])
{
$options['query'] = ['id' => $uiid];
$promise = $this->requestAsync('GET', $this->endpoint, $options);
$promise->then([$this->processor, "processResponse"]);
$player = $this->processor->getPlayer();
return $players;
}
And processor:
class Processor {
private $player;
public function processResponse (ResponseInterface $response) {
$this->player = $response->getBody()->getContents();
}
public function getPlayer() { return $this->player;}
}
How to get access to $app inside a controller as the Slim 3.3 injects only the ContainerInterface?
Code to illustrate the question:
$app = new \Slim\App;
$app->get('/home', 'HomeController:get');
$app->run();
class HomeController {
private $ci;
public function _construct($ci) {
$this->ci = $ci;
}
public function get($request, $response) {
$this->ci->get(...);
// How to access $app and dependencies like $app->jwt?
}
}
This was a tough one.
Slim 3 heavily uses dependency injection, so you might want to use it too.
First inside your dependencies.php you need to grab the $app and throw it in a container to inject it to the Controller later.
$container['slim'] = function ($c) {
global $app;
return $app;
};
Then you got to inject it:
// Generic Controller
$container['App\Controllers\_Controller'] = function ($c) {
return new _Controller($c->get('slim'));
};
Now on your controller.php:
private $slim;
/**
* #param \Psr\Log\LoggerInterface $logger
* #param \App\DataAccess $dataaccess
* #param \App\$app $slim
*/
public function __construct(LoggerInterface $logger, _DataAccess $dataaccess, $slim)
{
$this->logger = $logger;
$this->dataaccess = $dataaccess;
$this->slim = $slim;
}
Now you just got call it like this:
$this->slim->doSomething();
You can make your own 'singleton' to mimic Slim::getInstance(); ;)
class Anorexic extends \Slim\App {
private static $_instance;
public static function getInstance(){
if(empty(self::$_instance){
self::$_instance = new self();
}
return self::$_instance;
}
}
Then change your initialization like this:
// $app = new \Slim\App;
$app = Anorexic::getInstance();
Now you can get your \Slim\App instance anywhere in your code by calling Anorexic::getInstance(); Ofcourse you should never try this at home :P
So I was trying to make a CRUD for my app. I have setup a controller, like this:
class Clients extends CI_Controller {
public function __construct() {
parent::__construct();
session_start();
$this->c = new Client();
$this->u = new User();
}
function index() {
$data['current_view'] = 'client_view';
$data['header'] = 'Меню клиентов';
$data['clients'] = $this->getClients();
$this->load->view('main_view',$data);
}
function getClients() {
$this->u->where('username',$_SESSION['username'])->get();
$c = $this->c->where('user_id',$this->u->id)->get();
return $c;
}
function delClient() {
$this->c->where('client_id', $this->uri->segment(3))->delete();
$this->c->skip_validation()->save();
$this->index();
}
}
However, when I'm trying to perform a delete on a client, i get a db error:
You must use the "set" method to update an entry.
Filename: C:\OpenServer\domains\localhost\system\database\DB_active_rec.php
Line Number: 1174
What might be the cause of this? I found a similar question here, but I don't think that's the case.
EDIT: Client model:
class Client extends DataMapper {
public $has_one = array('user');
public function __construct($id = NULL) {
parent::__construct($id);
}
}
you have not passed the uri segment to the function delClient() and this is your model right...
function delClient($id) {
//$id is the value which you want to delete
$this->c->where('client_id', $id)->delete();
$this->c->skip_validation()->save();
$this->index();
}