I've been writing a test for a simple Datamapper class, and I know the method is working, however the test fails and gives me the error "Fatal error: Call to a member function fetchAll() on a non-object in C:\xampp\htdocs\Call log\tests\model_tests.php on line 13." Obviously, this can't be right, because I can verify that the method works.
Here is the code that it's supposedly erroring on:
function all() {
$calls = $this->pdo->query('SELECT * from calls');
return $calls->fetchAll();
}
Here's my test code:
class TestOfCallMapper extends UnitTestCase {
function testOfReturnsAll() {
$this->createSchema();
$mapper = Callmapper::getInstance();
$results = $mapper->all();
print_r($results);
}
private function createSchema() {
$mapper = CallMapper::getInstance();
$mapper->pdo->exec(file_get_contents('../database/create_schema.sql'));
}
private function destroySchema() {
$mapper = CallMapper::getInstance();
$mapper->pdo->exec(file_get_contents('../database/destroy_schema.sql'));
}
}
$test = new TestOfCallMapper('Test of CallMapper Methods');
$test->run(new HTMLReporter());
If I do this, it works just fine:
$mapper = CallMapper::getInstance();
$test = $mapper->all();
print_r($test->fetchAll());
Your PDO Query is returning false therefore its not and instance of PDO but a boolean, Try something like this!
function all() {
$calls = $this->pdo->query('SELECT * from calls');
if($calls === false)
{
throw new Exception("Unable to perform query");
}
return $calls->fetchAll();
}
and then within your TestOfCallMapper you can do:
function testOfReturnsAll()
{
$this->createSchema();
$mapper = Callmapper::getInstance();
try
{
$results = $mapper->all();
}catch(Exception $e)
{
//Some Logging for $e->getMessage();
return;
}
//Use $results here
}
The pdo query is evidently failing, thus you are trying to call fetchAll on false.
I'd check why it's failing.
Related
How can we create member functions in php that can be called similarly to the way laravel does?
ex:
User::with('package')->where('id', $user_id)->get();
i tried creating a static function with a member function inside:
public static function getAll() {
public function withJoin($join_table, $local_key, $foreign_key) {
global $wpdb;
$table_name = $wpdb->prefix.'qre_events';
$query = "SELECT * from $table_name INNER JOIN $join_table ON $local_key = $foreign_key";
try {
$results = $wpdb->get_results($query);
} catch(Throwable $e) {
$results = null;
echo($e);
echo "Error retrieving data.";
}
var_dump($results);
}
global $wpdb;
$table_name = $wpdb->prefix. static::$table_name;
$query = "SELECT * from $table_name";
try {
$results = static::convertArrayToModelArray($wpdb->get_results($query),
static::$attributes);
} catch(Throwable $e) {
$results = null;
echo($e);
echo "Error retrieving data.";
}
return $results;
}
and tried calling it like so:
$results = Event::getAll()->withJoin($user_table, 'user_id', 'ID');
but it returned this error:
Fatal error: Uncaught Error: Call to a member function withJoin() on array
The method chaining is archived by returning $this for each method.
Like this:
class Event
{
public function getAll()
{
// Do stuff
return $this;
}
public function withJoin()
{
// Do stuff
return $this;
}
}
Now they can be called like this:
(new Event)->getAll()->withJoin();
So you have to save you parameters to properties on the class.
You can take a look inside Laravel builder to see exactly how it was archived there: https://github.com/laravel/framework/blob/6.x/src/Illuminate/Database/Eloquent/Builder.php
Try .
$results = Event::withJoin($user_table, 'user_id', 'ID')->get();
The chaining of methods works because each function returns an instance of an object that has functions on it.
// This
User::with('package')->where('id', $user_id)->get();
// Is basicly the same as
$user_query = User::with('package');
$user = $user_query->where('id', $user_id)->get();
You need to create two objects and the static function will return an instance of the other object. If you want method chaining as well, you just need to make sure that chainable functions return their own instance.
For example
class Event
{
public static function getAll()
{
return new EventObject();
}
}
class EventObject
{
public function withJoin()
{
// Do stuff here.
// return `$this` if you want to chain functions.
}
}
In this example, using Event::getAll() will give you an EventObject instance on which you can call the withJoin function. So you can now do the following:
Event::getAll()->withJoin();
I have a Service class and a test for that, follow below:
Class
class MyCustomService
{
public function job()
{
while($this->getResponseFromThirdPartyApi()->data) {
// do some stuff...
}
return ...
}
protected function getResponseFromThirdPartyApi()
{
// Here do some curl and return stdClass
// data attribute is populated only on first curl request
}
}
Test mocking getResponseFromThirdPartyApi method
class MyCustomServiceTest
{
public function testJobImportingData()
{
$myCustomServiceMock = $this->getMockBuilder('MyCustomService')
->setMethods(array('getResponseFromThirdPartyApi'))
->getMock();
$myCustomServiceMock->expects($this->any())
->method('getResponseFromThirdPartyApi')
->willReturn($this->getResponseWithData());
$jobResult = $myCustomServiceMock->job();
// here some assertions on $jobResult
}
protected function getResponseWithData()
{
$response = new \stdClass;
$response->data = ['foo', 'bar'];
return $response;
}
}
How can I change getResponseWithData return after first call on MyCustomService while loop?
I've tried creating a custom flag on MyCustomServiceTest and checking on getResponseWithData, but fails once that mocked object doesn't call getResponseWithData method again on MyCustomServiceTest.
Any direction?
As Nico Haase suggested above, the path is to use callback.
After some research I achieved passing mock object reference to method mocked and checking flag, this results on:
class MyCustomServiceTest
{
public function testJobImportingData()
{
$myCustomServiceMock = $this->getMockBuilder('MyCustomService')
->setMethods(array('getResponseFromThirdPartyApi'))
->getMock();
$myCustomServiceMock->expects($this->any())
->method('getResponseFromThirdPartyApi')
->will($this->returnCallback(
function () use ($myCustomServiceMock) {
return $this->getResponseWithData($myCustomServiceMock)
}
));
$jobResult = $myCustomServiceMock->job();
// here some assertions on $jobResult
}
protected function getResponseWithData(&$myCustomServiceMock)
{
$response = new \stdClass;
if (isset($myCustomServiceMock->imported)) {
$response->data = false;
return $response;
}
$myCustomServiceMock->imported = true;
$response = new \stdClass;
$response->data = ['foo', 'bar'];
return $response;
}
}
Then while loop will be called only once and we be able to test without forever loop.
New to OOP, figured I'd practice a bit by sending back data from PHP via ajax. What am I doing wrong here? It works if I change the code to procedural. Here's the OOP:
if (isset($_POST['fruity'])) {
$start_fruity = new Fruity_draft();
$start_fruity->send_json();
}
class Fruity_draft {
public $banned = $_POST['banned'];
public $players = $_POST['players'];
public $random_civs = $_POST['random_civs'];
public $array_list = [];
public $send_json['banned'] = $banned;
function __construct($send_json) {
$this->send_json = $send_json;
}
function send_json() {
echo json_encode($this->send_json);
}
}
First of all, you forgot about passing a parameter to the constructor, it expects an array.
function __construct($send_json) {
In your call, you don't send anything
$start_fruity = new Fruity_draft();
This throws a warning, Warning: Missing Argument 1
and a notice, Notice: Undefined variable: send_json
Second, you should move the initialization of the class variables in the constructor.
class Fruity_draft {
public $banned;
public $players;
public $random_civs;
public $array_list;
public $send_json;
function __construct($send_json) {
$this->banned = 'banned';
$this->players = 'players';
$this->random_civs = 'random_civs';
$this->send_json = $send_json;
$this->send_json['banned'] = $this->banned;
}
...
}
That's not really OOP :). You should return something from the class, not echo.
Also, you should send data from other function to the class.. in the constructor or with a method set_post_data() or something...
Simple:
if (isset($_POST['fruity'])) {
$start_fruity = new Fruity_draft($_POST);
echo $start_fruity->get_json_response();
}
class Fruity_draft {
private $postData;
function __construct($postData) {
$this->postData = $postData;
}
function get_json_response() {
return json_encode($this->postData['banned']);
}
}
I searched around on stackoverflow about the "Using $this when not in object context" but I could't get my answer out of it so I ask it here.
I will just copy my code here so you and me dont get confused:
the error is in this code on the first IF statement($this->checkConnection()):
public function randomLocation() {
if ($this->checkConnection()) {
$fetch = array();
$character = $this->getCharacter($_GET['action']);
$locations = Controller_Core_Database::getObjects('Model_Game_Location');
foreach ($locations as $location) {
$fetchLocation = $location->getLocationNameShort();
$fetch[] = $fetchLocation;
}
$newLocation = rand(0,31);
$character->setLocation($fetch[$newLocation]);
Controller_Core_Database::update($character);
}
}
But I have many functions that begins with this 'checkConnection' and those work, except for this one. which is strange in my opinion(although i'm still a beginner with OOP PHP)
so this is my checkConnection function:
public function checkConnection() {
$mysqli = Controller_Core_Database::getDB();
if ($mysqli != null) {
return TRUE;
} else {
return FALSE;
}
}
the Controller_Core_Database::getDB() code is:
public static function getDB() {
self::$mysqli = new mysqli('','','','');
return self::$mysqli;
}
I have remove the information for security purposes.
And here is a example of a function that works perfectly:
public function createItem() {
$view = new Controller_Core_View();
$view->setViewFile("game/createItem.php");
if ($this->checkConnection()) {
$item = Controller_Core_Database::getObjects('Model_Game_Item');
$enchantment = Controller_Core_Database::getObjects('Model_Game_Itemenchantment');
$view->assign("item", $item);
$view->assign("enchantment", $enchantment);
}
return $view->render();
}
I dont see any different between the 2 functions that works and not works, I hope you can help me
You didn't specified, how randomLocation() method is called.
I supose you are calling that non-static method staticaly - thats why you are not in an object context.
If so, consider rewriting either call to randomLocation() method to use object context - e.i.
$x = new YourObjectName();
$x->randomLocation();
or rewrite checkConnection() method to be static and call it staticaly in randomLocation() method.
Can anyone see why my check_multi function would return a --
Fatal error: Call to undefined function check_multi() in
/var/www/vhosts/aero.onelinksoftware.com/application/models/Design.php
on line 21
The above error shows up and I am not sure what I am doing wrong. I tried setting my function as public, private, static, and other combinations but no matter what I try; the system still errors out. Can you not call a function inside a Model in Zend? I don't understand why I cannot use a function I created if it is inside a class I made.
If I echo and die before the check_multi call; I can see my text and such. I have also performed a php test for syntax and it is valid as far as it reports.
class Model_Design
{
/**
* Constructs our partials.
*
* #return void
*/
public function __construct($key)
{
// Get the DB Connection
$db = Zend_Registry::Get('db');
// Setup the SQL Statement
$sql = $db->select()->from('design', array('id'));
// Get the Result
$result = $sql->query();
// Get our Row
$row = $result->fetchAll();
if(check_multi($key, $row)) {
echo "omg"; die();
}
// Make sure the id isn't empty
if (empty($key)) {
throw new Exception('You have a disturbing lack of variables.');
}
// Store the id
$this->variables = $key;
// Construct our query
$sql = $db->select()->from('design')->where('`id` = ?', $key);
// Get the result
//$result = $sql->query();
//$row = $result->fetch();
}
private function check_multi($n, $arr)
{
foreach ($arr as $key => $val) {
if ($n===$key) {
return $key;
}
}
return false;
}
}
Try:
$this->check_multi($key, $row);
To access a variable or function from inside its container class you must use $this. $this is the current instance of the class.
How do you call the function ? use $this->check_multi($n,$arr); or you can try function_exists() to check if function really exist